alloy-rs / alloy

Transports, Middleware, and Networks for the Alloy project
https://alloy.rs
Apache License 2.0
584 stars 209 forks source link

[Feature] Multicall Support #328

Open zilayo opened 6 months ago

zilayo commented 6 months ago

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

developeruche commented 6 months ago

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 commented 6 months ago

I have a WIP implementation for the core ABI part here https://github.com/alloy-rs/core/pull/492 that may be of interest

developeruche commented 6 months ago

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

this is ineresting...

developeruche commented 6 months ago

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 commented 6 months ago

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 commented 6 months ago

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 commented 6 months ago

@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 commented 6 months ago

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.