Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feature] Multicall Support #328

Open
zilayo opened this issue Mar 15, 2024 · 8 comments
Open

[Feature] Multicall Support #328

zilayo opened this issue Mar 15, 2024 · 8 comments
Labels
enhancement New feature or request

Comments

@zilayo
Copy link
Contributor

zilayo commented Mar 15, 2024

Component

contract

Describe the feature you would like

Similar to ethers-rs, it would be great to port over multicall to alloy.

https://github.com/gakonst/ethers-rs/tree/master/ethers-contract/src/multicall

Additional context

No response

@zilayo zilayo added the enhancement New feature or request label Mar 15, 2024
@developeruche
Copy link
Contributor

Would like to work on this, but I don't think this is a core implementation that should be in the contract crate... I think it would be more oraganised to create another crate named contract-adapter or alloy-adapter, this could house contracts like;

  1. Multicall
  2. Permit2

and so on...

what do you think? @zilayo

@DaniPopes
Copy link
Member

I have a WIP implementation for the core ABI part here alloy-rs/core#492 that may be of interest

@developeruche
Copy link
Contributor

I have a WIP implementation for the core ABI part here alloy-rs/core#492 that may be of interest

this is ineresting...

@developeruche
Copy link
Contributor

fn basic_builder() {
        let token = Address::with_last_byte(1);

        let builder: MulticallBuilder<Aggregate, ()> = MulticallBuilder::new_aggregate();
        assert_eq!(builder.calls(), &[]);

        let call1 = ERC20::totalSupplyCall {};
        let builder: MulticallBuilder<Aggregate, (ERC20::totalSupplyCall,)> =
            builder.push(&call1, token);
        assert_eq!(
            builder.calls(),
            &[IMulticall3::Call { target: token, callData: call1.abi_encode() }]
        );

        let call2 = ERC20::balanceOfCall { owner: Address::with_last_byte(2) };
        let builder: MulticallBuilder<Aggregate, (ERC20::totalSupplyCall, ERC20::balanceOfCall)> =
            builder.push(&call2, token);
        assert_eq!(
            builder.calls(),
            &[
                IMulticall3::Call { target: token, callData: call1.abi_encode() },
                IMulticall3::Call { target: token, callData: call2.abi_encode() },
            ]
        );

        let encoded_data = builder.abi_encode();
        let expected = IMulticall3::aggregateCall {
            calls: vec![
                IMulticall3::Call {
                    target: token,
                    callData: ERC20::totalSupplyCall {}.abi_encode(),
                },
                IMulticall3::Call {
                    target: token,
                    callData: ERC20::balanceOfCall { owner: Address::with_last_byte(2) }
                        .abi_encode(),
                },
            ],
        };
        assert_eq!(encoded_data, expected.abi_encode());

        let return_data = IMulticall3::aggregateCall::abi_encode_returns(&(
            U256::from(1),
            &[U256::from(2).abi_encode(), U256::from(3).abi_encode()][..],
        ));
        eprintln!("return_data: {:?}", hex::encode(&return_data));
        let decoded = builder.abi_decode(&return_data, true).unwrap();
        assert_eq!(
            decoded,
            (
                U256::from(1),
                (
                    ERC20::totalSupplyReturn { totalSupply: U256::from(2) },
                    ERC20::balanceOfReturn { balance: U256::from(3) }
                )
            )
        );
    }

this seems complete to me.... 👀

@zilayo
Copy link
Contributor Author

zilayo commented Mar 24, 2024

Would like to work on this, but I don't think this is a core implementation that should be in the contract crate... I think it would be more oraganised to create another crate named contract-adapter or alloy-adapter, this could house contracts like;

  1. Multicall
  2. Permit2

and so on...

what do you think? @zilayo

yeah I think multicall should be separate from the core contract logic.

The version @DaniPopes built looks great for an initial implementation.

@gakonst mentioned in tg about integrating multicalls with a Tower Layer, so not sure if there's already been discussions around what multicall in Alloy should look like (https://t.me/ethers_rs/34873).

I think long term it would be nice to have to have the multicall task configurable similar to viem's implementation:

  1. user can create a multicall with a slice of x amount of calls, and optionally a batch size.
  2. alloy handles splitting the calls into chunks of the batch size
  3. alloy sends an eth_call message to the multicall contract for each chunk & aggregates the results.
  4. The aggregated results are returned to the end user in a slice in the same order that they provided the calls in.

@developeruche
Copy link
Contributor

Would like to work on this, but I don't think this is a core implementation that should be in the contract crate... I think it would be more oraganised to create another crate named contract-adapter or alloy-adapter, this could house contracts like;

  1. Multicall
  2. Permit2

and so on...
what do you think? @zilayo

yeah I think multicall should be separate from the core contract logic.

The version @DaniPopes built looks great for an initial implementation.

@gakonst mentioned in tg about integrating multicalls with a Tower Layer, so not sure if there's already been discussions around what multicall in Alloy should look like (https://t.me/ethers_rs/34873).

I think long term it would be nice to have to have the multicall task configurable similar to viem's implementation:

  1. user can create a multicall with a slice of x amount of calls, and optionally a batch size.
  2. alloy handles splitting the calls into chunks of the batch size
  3. alloy sends an eth_call message to the multicall contract for each chunk & aggregates the results.
  4. The aggregated results are returned to the end user in a slice in the same order that they provided the calls in.

This is interesting...

I would get more context, rollout a POC, and make a draft PR

@developeruche
Copy link
Contributor

@DaniPopes would it be possible to merge your WIP? I would like to also add interfaces for Mulitcall1 and Multicall2... Lastly was there a reason this was closed?

@zilayo
Copy link
Contributor Author

zilayo commented Apr 1, 2024

Currently have a working initial implementation with Multicall V1/V2/V3 support.

Will hopefully have a draft PR ready within the next day or 2.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants