Arbitrum Stylus logo

Stylus by Example

Multicall

An Arbitrum Stylus version implementation of Solidity Multi Call contract that aggregates multiple queries using a for loop and RawCall.

Example implementation of a Multi Call contract written in Rust: Here is the interface for TimeLock.

1/**
2 * This file was automatically generated by Stylus and represents a Rust program.
3 * For more information, please see [The Stylus SDK](https://github.com/OffchainLabs/stylus-sdk-rs).
4 */
5
6// SPDX-License-Identifier: MIT-OR-APACHE-2.0
7pragma solidity ^0.8.23;
8
9interface IMultiCall {
10    function multicall(address[] memory addresses, bytes[] memory data) external view returns (bytes[] memory);
11
12    error ArraySizeNotMatch();
13
14    error CallFailed(uint256);
15}
1/**
2 * This file was automatically generated by Stylus and represents a Rust program.
3 * For more information, please see [The Stylus SDK](https://github.com/OffchainLabs/stylus-sdk-rs).
4 */
5
6// SPDX-License-Identifier: MIT-OR-APACHE-2.0
7pragma solidity ^0.8.23;
8
9interface IMultiCall {
10    function multicall(address[] memory addresses, bytes[] memory data) external view returns (bytes[] memory);
11
12    error ArraySizeNotMatch();
13
14    error CallFailed(uint256);
15}

src/lib.rs

1#![cfg_attr(not(feature = "export-abi"), no_main)]
2extern crate alloc;
3
4#[global_allocator]
5static ALLOC: mini_alloc::MiniAlloc = mini_alloc::MiniAlloc::INIT;
6
7use alloy_primitives::U256;
8use alloy_sol_types::sol;
9use stylus_sdk::{abi::Bytes, alloy_primitives::Address, call::RawCall, prelude::*};
10
11#[solidity_storage]
12#[entrypoint]
13pub struct MultiCall;
14
15// Declare events and Solidity error types
16sol! {
17    error ArraySizeNotMatch();
18    error CallFailed(uint256 call_index);
19}
20
21#[derive(SolidityError)]
22pub enum MultiCallErrors {
23    ArraySizeNotMatch(ArraySizeNotMatch),
24    CallFailed(CallFailed),
25}
26
27#[external]
28impl MultiCall {
29    pub fn multicall(
30        &self,
31        addresses: Vec<Address>,
32        data: Vec<Bytes>,
33    ) -> Result<Vec<Bytes>, MultiCallErrors> {
34        let addr_len = addresses.len();
35        let data_len = data.len();
36        let mut results: Vec<Bytes> = Vec::new();
37        if addr_len != data_len {
38            return Err(MultiCallErrors::ArraySizeNotMatch(ArraySizeNotMatch {}));
39        }
40        for i in 0..addr_len {
41            let result = RawCall::new().call(addresses[i], data[i].to_vec().as_slice())
42                .map_err(|_| MultiCallErrors::CallFailed(CallFailed { call_index: U256::from(i) }))?;
43            results.push(result.into());
44}
45        Ok(results)
46    }
47}
1#![cfg_attr(not(feature = "export-abi"), no_main)]
2extern crate alloc;
3
4#[global_allocator]
5static ALLOC: mini_alloc::MiniAlloc = mini_alloc::MiniAlloc::INIT;
6
7use alloy_primitives::U256;
8use alloy_sol_types::sol;
9use stylus_sdk::{abi::Bytes, alloy_primitives::Address, call::RawCall, prelude::*};
10
11#[solidity_storage]
12#[entrypoint]
13pub struct MultiCall;
14
15// Declare events and Solidity error types
16sol! {
17    error ArraySizeNotMatch();
18    error CallFailed(uint256 call_index);
19}
20
21#[derive(SolidityError)]
22pub enum MultiCallErrors {
23    ArraySizeNotMatch(ArraySizeNotMatch),
24    CallFailed(CallFailed),
25}
26
27#[external]
28impl MultiCall {
29    pub fn multicall(
30        &self,
31        addresses: Vec<Address>,
32        data: Vec<Bytes>,
33    ) -> Result<Vec<Bytes>, MultiCallErrors> {
34        let addr_len = addresses.len();
35        let data_len = data.len();
36        let mut results: Vec<Bytes> = Vec::new();
37        if addr_len != data_len {
38            return Err(MultiCallErrors::ArraySizeNotMatch(ArraySizeNotMatch {}));
39        }
40        for i in 0..addr_len {
41            let result = RawCall::new().call(addresses[i], data[i].to_vec().as_slice())
42                .map_err(|_| MultiCallErrors::CallFailed(CallFailed { call_index: U256::from(i) }))?;
43            results.push(result.into());
44}
45        Ok(results)
46    }
47}

Cargo.toml

1[package]
2name = "stylus-multi-call-contract"
3version = "0.1.5"
4edition = "2021"
5license = "MIT OR Apache-2.0"
6keywords = ["arbitrum", "ethereum", "stylus", "alloy"]
7description = "Stylus multi call example"
8
9[dependencies]
10alloy-primitives = "0.3.1"
11alloy-sol-types = "0.3.1"
12mini-alloc = "0.4.2"
13stylus-sdk = "0.5.0"
14hex = "0.4.3"
15
16[dev-dependencies]
17tokio = { version = "1.12.0", features = ["full"] }
18ethers = "2.0"
19eyre = "0.6.8"
20
21[features]
22export-abi = ["stylus-sdk/export-abi"]
23
24[[bin]]
25name = "stylus-multi-call"
26path = "src/main.rs"
27
28[lib]
29crate-type = ["lib", "cdylib"]
30
31[profile.release]
32codegen-units = 1
33strip = true
34lto = true
35panic = "abort"
36opt-level = "s"
1[package]
2name = "stylus-multi-call-contract"
3version = "0.1.5"
4edition = "2021"
5license = "MIT OR Apache-2.0"
6keywords = ["arbitrum", "ethereum", "stylus", "alloy"]
7description = "Stylus multi call example"
8
9[dependencies]
10alloy-primitives = "0.3.1"
11alloy-sol-types = "0.3.1"
12mini-alloc = "0.4.2"
13stylus-sdk = "0.5.0"
14hex = "0.4.3"
15
16[dev-dependencies]
17tokio = { version = "1.12.0", features = ["full"] }
18ethers = "2.0"
19eyre = "0.6.8"
20
21[features]
22export-abi = ["stylus-sdk/export-abi"]
23
24[[bin]]
25name = "stylus-multi-call"
26path = "src/main.rs"
27
28[lib]
29crate-type = ["lib", "cdylib"]
30
31[profile.release]
32codegen-units = 1
33strip = true
34lto = true
35panic = "abort"
36opt-level = "s"