reach-sh/reach-lang

@reach-sh/stdlib: Make it possible to reset the Algorand provider during the application lifecycle

flyingleafe opened this issue · 3 comments

We have two use cases which we would like to support in our Algorand application:

  1. Allow the user to disconnect his current wallet and connect another one (e.g. if the user has both MyAlgo and Pera wallet)
    1.1. This would also help with automatic reconnection of the previously connected wallet without blocking the user from changing the wallet at some point in the future.

  2. Fetch the contract's global views when the user has not yet connected the wallet (e.g., displaying some TVLs and other parameters of a contract for everybody on the front page).

Currently, both of those use cases are blocked by the behaviour of the Reach stdlib, specifically by the definition of getProvider and setProvider functions

export const [getProvider, setProvider] = replaceableThunk(async () => {
  if ( window.algorand ) {
    // @ts-ignore
    return await makeProviderByWallet(window.algorand, process.env);
  } else {
    debug(`making default provider based on process.env`);
    return await makeProviderByEnv(process.env);
  }
});

Using the replaceableThunk function here in its current implementation leads to the fact that the value of the first call to getProvider() gets remembered forever, and there is no way to reset the memorized provider. In turn, this leads to:

  1. reach.setWalletFallback() and reach.getDefaultAccount() do nothing if called in the second time of the application lifecycle, since the result of getDefaultAccount() is derived from getProvider(), and therefore it always stays the same no matter what
  2. Using some default randomly generated account to create a contract handle (ctc = account.contract(bin, id)) and then doing ctc.views.someGlobalView() causes getProvider() function to get called internally, the wallet-less provider value gets memorized and afterwards it is impossible to change it to a wallet-based provider, so the user cannot connect the wallet using setWalletFallback anymore.

Both of this issues could be mitigated easily if e.g. setProvider function could be actually called more than once. Are there some fundamental concerns which dictate that it should not be possible? I understand that allowing this will open the door to many possibilities for a developer to do weird bugs when resetting the provider at the wrong time, but providing some function like unsafeSetProvider which emits warnings rather than errors would be better than prohibiting the use cases I present altogether. I think that those are pretty common among all Web3 apps, not only our one.

NB: For the second use case (reading the global contract views without having an account connected), it would be nice to provide some function like reach.contractPublicView(bin, id), which does not require an account to be created, and which only contains views which do not touch the local state. I anticipate that it should not be very hard to do in principle (the current code already understands which views touch local state and which don't, otherwise it wouldn't work), but probably hard to do in practice (requires extensive refactoring or something). Anyway, just allowing the provider to be set allows the workaround with a default account and it seems to be easy to do.

We'll work on doing this fully. Before then, let me try to help with what we have today...

  • You can make multiple instances of the standard library
  • If you use stdlib.createAccount, you will by-pass the wallet's enableAccounts call, so for wallets that don't provide node access, you'll just connect directly.

It is now possible to load independent instances of the standard library easily...

https://docs.reach.sh/frontend/#ref-frontends-js-loader

And we've added a discussion about how to use setWalletFallback multiple times effectively....

https://docs.reach.sh/frontend/#js_stdlib.setWalletFallback

When Jay says "it is now possible", he means, it will be possible starting with @reach-sh/stdlib@0.1.11-rc.6, which will be released next week.