clbrge / svelte-web3

web3.js library integration as a Svelte store
MIT License
138 stars 32 forks source link

Can't make makeChainStore work #20

Closed Garito closed 2 years ago

Garito commented 2 years ago

Here is my code:

let cData;
const customChain = makeChainStore('xDai');
customChain.chainData.subscribe(c => cData = c)();

cData is always empty dict Any idea why? Thanks

clbrge commented 2 years ago

You should not have the last () since it would invoke the unsubscribe function (subscribe returns it) immediately after subscription.

Garito commented 2 years ago

Sorry, I've tried this:

let cData;
const customChain = makeChainStore('xDai');
const unsubscribe = customChain.chainData.subscribe(c => cData = c);
console.log(cData);
unsubscribe();

with the same result

PS: would be nice if you don't close the issue before being sure it is resolved

clbrge commented 2 years ago

Hum. I really though it was the reason. Anyway, there is no problem to reopen an issue if you want ... I tried a similar code as yours inside svelte-web3/examples/svelte-app-template-web3 and i cannot reproduce your bug. could you try the same patch :

diff --git a/examples/svelte-app-template-web3/src/RPC.svelte b/examples/svelte-a>
index f8031f1..7076691 100644
--- a/examples/svelte-app-template-web3/src/RPC.svelte
+++ b/examples/svelte-app-template-web3/src/RPC.svelte
@@ -9,6 +9,12 @@

   ethStore.setProvider(provider)

+  let cData
+  chainData.subscribe(c => {
+    console.log('store has been updated', c)
+    cData = c
+  })
+
 </script>

 <div class="card">
@@ -23,6 +29,9 @@
       <p>
         chainData = {JSON.stringify($chainData)}
       </p>
+      <p>
+        cData = {JSON.stringify(cData)}
+      </p>
       {/if}
     </div>
   </div>
clbrge commented 2 years ago

Sorry, I've tried this:

let cData;
const customChain = makeChainStore('xDai');
const unsubscribe = customChain.chainData.subscribe(c => cData = c);
console.log(cData);
unsubscribe();

with the same result

PS: would be nice if you don't close the issue before being sure it is resolved

This second example by the way will follow the same workflow has the first since you will unsubscribe before the state of the store changes and the subscribe handler get called.

Garito commented 2 years ago

I don't know if your test makes sense in my case cause my code runs in a store

About my 2nd example: when should I unsubscribe from your store? I'm using the same pattern with other stores without any problem

clbrge commented 2 years ago

I don't know if your test makes sense in my case cause my code runs in a store

About my 2nd example: when should I unsubscribe from your store? I'm using the same pattern with other stores without any problem

I don't know since it all depends a global context I know nothing of. It just means that when you subscribe with the handler c => cData = c, chainData state (aka c) is still undefined. Which is quite logical because I don't see a customChain.setProvider(<provider>) before the console.log and the unsubscribe.

Garito commented 2 years ago

Not so obvious, should specify this in the docs? Besides, some info is already there before connect (or should be) Ex: chainData, my case Meanwhile I've tried this:

let cData;
const customChain = makeChainStore('xDai');
customChain.setProvider('https://rpc.xdaichain.com/');
const unsubscribe = customChain.chainData.subscribe(c => cData = c);
console.log(cData);
unsubscribe();

no luck either I guess it is a matter of how the info it is instantiated?

Garito commented 2 years ago

I've tried without unsubscribe and eventually the info shows up It is possible that some stores don't wait the provider? chains.json is already there Or how to wait for the info to be loaded?

clbrge commented 2 years ago

Yes, the setProvider is async so the lines after setProvider get executed before the state has changed. I'll review the doc to make this more clear. There is no easy way to wait outside of your handler but you might consider just making the logic that needs the chain inside the handler like :

customChain.chainData.subscribe(c => {
if (c) {
 <your logic here>
 unsubscribe()
 }
});
Garito commented 2 years ago

I rather prefer if this library fills up the stores that aren't dependent from the provider which will solve this issue and will work in a more logical way, don't you think?

In my case it is difficult to do it without breaking the logic of what is happening in my code

How hard will be to make this change?

clbrge commented 2 years ago

I'm not sure to understand what you suggest. There is always a need for a provider to use web3 (except the utils part). If you want to use the raw data information from src/chains.js, you may want to use that file directly without creating a store. To be more precise, 'xDai' is just an id here, not a key to fetch xDai information.

Garito commented 2 years ago

can you load this info in chainData without the provider? chainId... those stores? The point is that make total sense and will be more usable in more cases... Should not be super hard to do?

clbrge commented 2 years ago

no because the name 'xDai' doesn't imply the provider will be necessarily dai. But i can make an accessor for information in chains.js outside of stores

Garito commented 2 years ago

but the chains.json info will not be different, isn't it? I mean if you choose xDai as network no matter if the provider is set or not that the chain info can be known and the chain id etc if you choose network 3 (ropsten) the chainData and the chainId can be setted without the provider

clbrge commented 2 years ago

oups, forgot to answer that. No data returned would be different depending on the provider selected. In the case of xDai, it would be

  {
    name: 'xDAI Chain',
    chain: 'XDAI',
    network: 'mainnet',
    ...
}

In the case of ropsten

  {
    name: 'Ethereum Testnet Ropsten',
    chain: 'ETH',
    network: 'ropsten',
   ...
}

I added a getter allChainsData to last release 2.3.1 and rewrote the README to make clear that chainData return undefined if not connected.

Garito commented 2 years ago

Thanks for that

About the issue: I think you are confusing provider with network. Let's try to be in the same page: Network is

Provider is determined by the rpc url

The network data is already in the chains.json the moment you choose the network The provider for that network it is only available after the connection

Which should mean that makeChainStore can fill part of the stores it provides (example chainData) before the provider is connected

clbrge commented 2 years ago

I think i know the difference :) In your opinion, & how makeChainStore would know which network is going to be connected before setProvider ?

Garito commented 2 years ago

chains.filter(chain => chain.chainId === chainId)?

clbrge commented 2 years ago

We don't know chainId before setProvider

Garito commented 2 years ago

oh god... chains.filter(chain => chain.rpc.includes(providerUrl))

clbrge commented 2 years ago

I'm not asking for some js lesson. Please try to be less condescending and try to understand what i'm saying during this whole thread. makeChainStore creates an Object of empty stores. There is no indication on which chain/network is going to be used before you invoke setProvider.

Garito commented 2 years ago

I'm not condescending, I'm amazed you don't get the point (seems to me really obvious, so sorry if it is not so for you and I'm being harsh with you, understand that I can interpret your responses as if you are tolling me, it is not the first time we don't understand each other)

The makeChainStore needs an information that it is included in the chains.json which make a transitive relation between the provider and the chain info

Checking your code, it is a matter of when you initialize the bunch of stores makeChainStore returns chainData, chainId and this kind of information can be filled before the provider is set and, when you want to interact with the network, then set the provider and fill web3, selectedAccount and so on

clbrge commented 2 years ago

I get your point since the start. I just trying to explain you why your are wrong. The function works this way:

  ({ web3, connected, selectedAccount, chainId, chainData, ...ethStore } = makeChainStore('<id>'))
  ethStore.setProvider('https://rpc.slock.it/goerli')

<id> can be goerli, 'XXX', infura, another-cool-id or xDai. In all cases, the chainData store will be set when we know about the chain (through the fact that a provider is set). In the example above, it will be goerli chainData in all cases. Again, you can't guess which which chain is going to be used, so no early initialization of chainData should happen.

Garito commented 2 years ago

what is the point of chains.json, then?

clbrge commented 2 years ago

You mean chains.js ? initializing chainData when it gets possible. If you want to use the whole list, you can use the new getter added in release 2.3.1.

Garito commented 2 years ago

chains.js === https://chainid.network/chains.json Quote: initializing chainData when it gets possible

are you trolling me?

clbrge commented 2 years ago

reread the thread paying more attention to " To be more precise, 'xDai' is just an id here, not a key to fetch xDai information." "no because the name 'xDai' doesn't imply the provider will be necessarily dai. " " can be goerli, 'XXX', infura, another-cool-id or xDai" I don't have the time to explain more than that sorry. That would be my last comment on this issue.

Garito commented 2 years ago

Yeah, me neither I think that you are the one that should reread the threat Your attitude kills a lot of help for this library