joshstevens19 / ethereum-multicall

Ability to call many ethereum constant function calls in 1 JSONRPC request
MIT License
361 stars 92 forks source link

Custom block number #2

Closed jaggedsoft closed 2 years ago

jaggedsoft commented 3 years ago

Love the library, thank you for sharing!

Is there any way to request information from a specific block number? this example code gets your token balance from 4 days ago compared to now

    const blocksAgo = n => provider.getBlockNumber().then(b => b - n);
    let blockNumber = await blocksAgo(30000);

    const tokenContract = new Contract(tokenAddress, erc20Abi);
    let balance1 = await tokenContract.balanceOf(address, 'latest');
    console.log(`latestBalance: ${ethers.utils.formatEther(balance1)}`);
    let balance2 = await tokenContract.balanceOf(address, blockNumber);
    console.log(`prevBalance: ${ethers.utils.formatEther(balance2)}`);
joshstevens19 commented 3 years ago

Hey thanks for raising this ticket. I am not in the office at the moment. Should be back next week and will look into it then.

joshstevens19 commented 3 years ago

Hey so just had a quick look at this, for this to work you will have to only do a multi-call request PER custom block so what I mean by that is if you want to query block 1000 and block 1003 they would have to be in 2 different eth_call but that can be abstracted away for you.

What I can do to make this easy is to put a block number on ContractCallContext object now it be the default latest but if you pass a custom one in it will try to match up any others on that block number and split the eth_call in 2(how ever many custom blocks are defined on the array) requests and bring back the same info format as you have it now. I will also expose a way to just set the custom block number globally as well.

Keep in mind it depends on what nodes you are on if this can even work as by default nodes don't keep all historical state. For example geth doesn't know what any of the state means. problem is, it's entirely up to the node you're talking to what it's pruned and what it's still got. But still I am happy to support this if it would help you.

Let me know and if so il get something merged and deployed for you.

Thanks @jaggedsoft

iwankruger commented 3 years ago

@joshstevens19 awesome library

Would just like to followup on this issue. Might be missing it but I am not seeing functionality that would allow for specify a custom block as described in your last post.

Has support for specifying a specific block been added to the repository?

joshstevens19 commented 3 years ago

This never got completed due to nodes not saving old data, we couldn’t support this globally on all nodes so it got parked. Not sure it get picked up anytime soon mainly because I don’t think we can globally support it.

jaggedsoft commented 3 years ago

awesome thanks, I rarely use my archive node anymore but that would definitely be a very useful case as there's no other multicalls that support a specific block number. Not a pressing need however

while I have your attention.. Is there an easy way to read ether balances from multiple addresses at once or do we need to use a separate library for that?

appreciate you, cheers.

joshstevens19 commented 3 years ago

Il take a look into old data.

In terms of multiple eth balances you can use contract https://etherscan.io/address/0xb1f8e55c7f64d203c1400b9d8555d050f94adf39#code with this lib if you wanted to get many eth balances queried with many calls.

Deployed Addresses

mainnet: 0xb1f8e55c7f64d203c1400b9d8555d050f94adf39 ropsten: 0x8D9708f3F514206486D7E988533f770a16d074a7 rinkeby: 0x3183B673f4816C94BeF53958BaF93C671B7F8Cf2 kovan: 0x55ABBa8d669D60A10c104CC493ec5ef389EC92bb binance smart chain mainnet: 0xB12aeC3A7e0B8CFbA307203a33c88a3BBC0D9622 binance smart chain testnet: 0x5E6F706c8Ca87c5FCbdBbfa74d69999dCDa46B24

jaggedsoft commented 3 years ago

yeah thanks, I saw your project https://github.com/joshstevens19/ethereum-erc20-token-balances-multicall and your other projects for uniswap v3 and sushiswap -- very awesome, keep up the great work!

specifically I want to return the ETH balances of several accounts at once Looks like you let people get the eth balances by passing token 0x0 to the contract. excellent 😁 that answers my question, thank you.

for provider.getEthBalance I currently use ethcall, their implementation is awkward but fast

    let queue = [];
    for ( let address of addresses ) {
        queue.push(ethcallProvider.getEthBalance(address));
        queue.push(tokenContract.balanceOf(address));
        queue.push(usdtContract.balanceOf(address));
        queue.push(usdcContract.balanceOf(address));
    }
    const data = await ethcallProvider.all(queue);
    for ( let address of addresses ) {
        const index = (i/4)+1, ethBalance = data[i] / 1e18, tokenBalance = data[i+1] / (10 ** decimals), usdtBalance = data[i+2] / 1e6, usdcBalance = data[i+3] / 1e6; // FIXME: Ugly and evil.
    }

Yours is much cleaner. Appreciate you very much, thanks for all you do!

joshstevens19 commented 3 years ago

Yes pass 0x0 it actually be really good if that logic was in that library balances you just sent if you wanted to do a PR to add this contract call to bring back the eth balances alongside the tokens that make that library even cooler !! If not il take a look when I get some time!

Thanks so much for your kind words dude!!! I try my best - I see your open source work as well mate so thank you as well! I have used your binance API which is just 🔥🔥🔥🔥

oh jeeeze that is not very nice way of them making you write this logic, much easier just passing addresses and getting it back.

Defo adding that contract call to get the eth value back in the balances multicall lib above will be a huge benefit as said welcome to do a PR for that if you wanted!!

thanks dude!!!

sjuanati commented 2 years ago

It would be really great if we could send a custom block in ContractCallContext. Agree that not all nodes save old data, but for those that do, this library would become very useful. If using ethers + alchemy, we could easily use {blockTag: blockNumber}. Perhaps for nodes not supporting old data, the library could just return the latest block or a warning message that was not able to retrieve data at a certain block. Otherwise, there are many use cases that require an specific block and there aren't other multicall alternatives as of today :/

daryledesilva commented 2 years ago

second this. i use moralis archive node to lookup historical data. im running out of requests because i cant use non-latest blocks for multicalls, is this option introduced in some branch that's not released yet?

put a block number on ContractCallContext object now it be the default latest

@joshstevens19 this sounds good, doesn't need to be a global option. but atleast the functionality is there on a granular level for those who need it

awesome package btw, my favorite muticall package!

EDIT forked the repo and this works for me now https://github.com/daryledesilva/ethereum-multicall/commit/017ad2c144c0ea1494bc3083f8f99cd61d6a7af3#diff-dce38be9bbc70841ebcc4df53e419e5635cc6d7acd355bc2c6093e8fbd24cf74R355-R356 if there's an official fix ill use that next time :)

EDIT so i just need to init 2 multicall instances (one using latest block, one using a nearest block to today minus 1 day)

const multicall = new Multicall({
    web3Instance: web3,
    tryAggregate: true,
});
const multicall_yesterday = new Multicall({
    web3Instance: web3_archive,
    tryAggregate: true,
    defaultBlock: blockYesterday.data.result,
});

still better than nothing. max 2 calls atleast haha

joshstevens19 commented 2 years ago

Your dreams have come true

https://github.com/joshstevens19/ethereum-multicall/pull/39

Top work will deploy tomorrow! @jsertx