gakonst / ethers-rs

Complete Ethereum & Celo library and wallet implementation in Rust. https://docs.rs/ethers
Apache License 2.0
2.49k stars 796 forks source link

feat: automatically batch consecutive `eth_call`s with a multicall #2508

Open mds1 opened 1 year ago

mds1 commented 1 year ago

Is your feature request related to a problem? Please describe. viem has a really nice feature where consecutive calls are held in a queue then automatically batched using Multicall3. As you can see from the docs, this enables really nice UX such as:

const contract = getContract({ address, abi })

// The below will send a single request to the RPC Provider.
const [name, totalSupply, symbol, tokenUri, balance] = await Promise.all([
  contract.read.name(),
  contract.read.totalSupply(),
  contract.read.symbol(),
  contract.read.tokenURI([420n]),
  contract.read.balanceOf([address]),
])

This UX is much better than manually configuring the multicall. And a multicall results in significantly better performance and reduced RPC bills compared to individual calls

The other motivation for asking this is related to upstream usage in foundry. When users write forge tests or scripts, they'll often make many view calls to fetch data from the chain. Currently each call is a single RPC request, so you quickly end up with a lot of RPC calls, resulting in throttling and slower tests.

Supporting this feature and leveraging it in forge can result in significantly faster fork tests and scripts.

Describe the solution you'd like I'm not sure how feasible a viem-like solution is in rust, but that's the ideal solution here: abstract away the need for users to worry about multicall but automatically batching calls, e.g. the below should be a single RPC call:

// Currently the below snippet would send 5 RPC requests to the provider, but ideally
// it should just send a single multicall request.
let (name, total_supply, symbol, token_uri, balance) = tokio::try_join!(
    contract.method::<_, String>("name", (), None)?.call(),
    contract.method::<_, U256>("totalSupply", (), None)?.call(),
    contract.method::<_, String>("symbol", (), None)?.call(),
    contract.method::<_, String>("tokenURI", (U256::from(420),), None)?.call(),
    contract.method::<_, U256>("balanceOf", (contract_address,), None)?.call(),
)?;

Describe alternatives you've considered Explicit multicall batching is the main alternative

Additional context Idea came out of a convo in foundry support telegram: https://t.me/foundry_support/40543

yorhodes commented 10 months ago

I would like to work on this

This UX is much better than manually configuring the multicall.

Motivated by this exact pain point in our already large codebase

yorhodes commented 10 months ago

hey @mds1, happy to report I have completed the implementation in #2684 supporting the ideal DX you describe

yorhodes commented 10 months ago

should probably mirror the config options here https://viem.sh/docs/clients/public.html#batch-multicall-optional

HenriBrg commented 4 months ago

Hey, looking also for the RPC batch request feature in Rust, is there a new solution available since opening this issue/PR ? TY