Sitelet https://github.com/njelich/examples/commit/57f0345af3e1281e7b286f40c6da6e23fd83dc18
Skip to content

Commit 57f0345

Browse files
feat: multicall (alloy-rs#176)
* feat: multicall * Update examples/providers/examples/multicall.rs * nit --------- Co-authored-by: zerosnacks <95942363+zerosnacks@users.noreply.github.com>
1 parent 27a56b1 commit 57f0345

4 files changed

Lines changed: 378 additions & 4 deletions

File tree

‎Cargo.toml‎

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,3 @@ tokio = "1.42"
126126
eyre = "0.6"
127127
serde = "1.0"
128128
serde_json = "1.0"
129-
130-
[patch.crates-io]
131-
# alloy = { git = "https://github.com/alloy-rs/alloy", rev = "65dfbe" }
132-
# foundry-fork-db = { git = "https://github.com/foundry-rs/foundry-fork-db", rev = "d113d6e" }

‎README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ This repository contains the following examples:
6969
- [x] [HTTP](./examples/providers/examples/http.rs)
7070
- [x] [IPC](./examples/providers/examples/ipc.rs)
7171
- [x] [WS](./examples/providers/examples/ws.rs)
72+
- [x] [Multicall Builder](./examples/providers/examples/multicall.rs)
7273
- [x] [WS with authentication](./examples/providers/examples/ws_with_auth.rs)
7374
- [x] [JSON-RPC Batch Request](./examples/providers/examples/batch_rpc.rs)
7475
- [x] Queries
Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
1+
[
2+
{
3+
"constant": true,
4+
"inputs": [],
5+
"name": "name",
6+
"outputs": [
7+
{
8+
"name": "",
9+
"type": "string"
10+
}
11+
],
12+
"payable": false,
13+
"stateMutability": "view",
14+
"type": "function"
15+
},
16+
{
17+
"constant": false,
18+
"inputs": [
19+
{
20+
"name": "guy",
21+
"type": "address"
22+
},
23+
{
24+
"name": "wad",
25+
"type": "uint256"
26+
}
27+
],
28+
"name": "approve",
29+
"outputs": [
30+
{
31+
"name": "",
32+
"type": "bool"
33+
}
34+
],
35+
"payable": false,
36+
"stateMutability": "nonpayable",
37+
"type": "function"
38+
},
39+
{
40+
"constant": true,
41+
"inputs": [],
42+
"name": "totalSupply",
43+
"outputs": [
44+
{
45+
"name": "",
46+
"type": "uint256"
47+
}
48+
],
49+
"payable": false,
50+
"stateMutability": "view",
51+
"type": "function"
52+
},
53+
{
54+
"constant": false,
55+
"inputs": [
56+
{
57+
"name": "src",
58+
"type": "address"
59+
},
60+
{
61+
"name": "dst",
62+
"type": "address"
63+
},
64+
{
65+
"name": "wad",
66+
"type": "uint256"
67+
}
68+
],
69+
"name": "transferFrom",
70+
"outputs": [
71+
{
72+
"name": "",
73+
"type": "bool"
74+
}
75+
],
76+
"payable": false,
77+
"stateMutability": "nonpayable",
78+
"type": "function"
79+
},
80+
{
81+
"constant": false,
82+
"inputs": [
83+
{
84+
"name": "wad",
85+
"type": "uint256"
86+
}
87+
],
88+
"name": "withdraw",
89+
"outputs": [],
90+
"payable": false,
91+
"stateMutability": "nonpayable",
92+
"type": "function"
93+
},
94+
{
95+
"constant": true,
96+
"inputs": [],
97+
"name": "decimals",
98+
"outputs": [
99+
{
100+
"name": "",
101+
"type": "uint8"
102+
}
103+
],
104+
"payable": false,
105+
"stateMutability": "view",
106+
"type": "function"
107+
},
108+
{
109+
"constant": true,
110+
"inputs": [
111+
{
112+
"name": "",
113+
"type": "address"
114+
}
115+
],
116+
"name": "balanceOf",
117+
"outputs": [
118+
{
119+
"name": "",
120+
"type": "uint256"
121+
}
122+
],
123+
"payable": false,
124+
"stateMutability": "view",
125+
"type": "function"
126+
},
127+
{
128+
"constant": true,
129+
"inputs": [],
130+
"name": "symbol",
131+
"outputs": [
132+
{
133+
"name": "",
134+
"type": "string"
135+
}
136+
],
137+
"payable": false,
138+
"stateMutability": "view",
139+
"type": "function"
140+
},
141+
{
142+
"constant": false,
143+
"inputs": [
144+
{
145+
"name": "dst",
146+
"type": "address"
147+
},
148+
{
149+
"name": "wad",
150+
"type": "uint256"
151+
}
152+
],
153+
"name": "transfer",
154+
"outputs": [
155+
{
156+
"name": "",
157+
"type": "bool"
158+
}
159+
],
160+
"payable": false,
161+
"stateMutability": "nonpayable",
162+
"type": "function"
163+
},
164+
{
165+
"constant": false,
166+
"inputs": [],
167+
"name": "deposit",
168+
"outputs": [],
169+
"payable": true,
170+
"stateMutability": "payable",
171+
"type": "function"
172+
},
173+
{
174+
"constant": true,
175+
"inputs": [
176+
{
177+
"name": "",
178+
"type": "address"
179+
},
180+
{
181+
"name": "",
182+
"type": "address"
183+
}
184+
],
185+
"name": "allowance",
186+
"outputs": [
187+
{
188+
"name": "",
189+
"type": "uint256"
190+
}
191+
],
192+
"payable": false,
193+
"stateMutability": "view",
194+
"type": "function"
195+
},
196+
{
197+
"payable": true,
198+
"stateMutability": "payable",
199+
"type": "fallback"
200+
},
201+
{
202+
"anonymous": false,
203+
"inputs": [
204+
{
205+
"indexed": true,
206+
"name": "src",
207+
"type": "address"
208+
},
209+
{
210+
"indexed": true,
211+
"name": "guy",
212+
"type": "address"
213+
},
214+
{
215+
"indexed": false,
216+
"name": "wad",
217+
"type": "uint256"
218+
}
219+
],
220+
"name": "Approval",
221+
"type": "event"
222+
},
223+
{
224+
"anonymous": false,
225+
"inputs": [
226+
{
227+
"indexed": true,
228+
"name": "src",
229+
"type": "address"
230+
},
231+
{
232+
"indexed": true,
233+
"name": "dst",
234+
"type": "address"
235+
},
236+
{
237+
"indexed": false,
238+
"name": "wad",
239+
"type": "uint256"
240+
}
241+
],
242+
"name": "Transfer",
243+
"type": "event"
244+
},
245+
{
246+
"anonymous": false,
247+
"inputs": [
248+
{
249+
"indexed": true,
250+
"name": "dst",
251+
"type": "address"
252+
},
253+
{
254+
"indexed": false,
255+
"name": "wad",
256+
"type": "uint256"
257+
}
258+
],
259+
"name": "Deposit",
260+
"type": "event"
261+
},
262+
{
263+
"anonymous": false,
264+
"inputs": [
265+
{
266+
"indexed": true,
267+
"name": "src",
268+
"type": "address"
269+
},
270+
{
271+
"indexed": false,
272+
"name": "wad",
273+
"type": "uint256"
274+
}
275+
],
276+
"name": "Withdrawal",
277+
"type": "event"
278+
}
279+
]
Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
//! This example demonstrates how to use the [`MulticallBuilder`] to make multicalls using the
2+
//! [`IMulticall3`] contract.
3+
4+
use alloy::{
5+
primitives::{address, U256},
6+
providers::{CallItemBuilder, Failure, Provider, ProviderBuilder},
7+
sol,
8+
};
9+
use IWETH9::IWETH9Instance;
10+
11+
sol!(
12+
#[allow(missing_docs)]
13+
#[sol(rpc)]
14+
#[derive(Debug)]
15+
IWETH9,
16+
"examples/abi/IWETH9.json"
17+
);
18+
19+
#[tokio::main]
20+
async fn main() -> eyre::Result<()> {
21+
// Create a new provider
22+
let provider = ProviderBuilder::new()
23+
.on_anvil_with_wallet_and_config(|a| a.fork("https://eth.merkle.io"))?;
24+
// Deploy the Multicall3 contract
25+
// let multicall3 = deploy_multicall3(&provider).await;
26+
// Deploy the WETH contract
27+
// let weth = deploy_weth(&provider).await;
28+
let weth =
29+
IWETH9Instance::new(address!("0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"), &provider);
30+
31+
let alice = address!("70997970C51812dc3A010C7d01b50e0d17dc79C8");
32+
let bob = address!("0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266");
33+
34+
let multicall = provider
35+
.multicall()
36+
// Set the address of the Multicall3 contract. If unset it uses the default address from <https://github.com/mds1/multicall>: 0xcA11bde05977b3631167028862bE2a173976CA11
37+
// .address(multicall3)
38+
// Get the total supply of WETH on our anvil fork.
39+
.add(weth.totalSupply())
40+
// Get Alice's WETH balance.
41+
.add(weth.balanceOf(alice))
42+
// Also fetch Alice's ETH balance.
43+
.get_eth_balance(alice);
44+
45+
let (init_total_supply, alice_weth, alice_eth_bal) = multicall.aggregate().await?;
46+
47+
println!(
48+
"Initial total supply: {}, Alice's WETH balance: {}, Alice's ETH balance: {}",
49+
init_total_supply._0, alice_weth._0, alice_eth_bal.balance
50+
);
51+
52+
// Simulate a transfer of WETH from Alice to Bob.
53+
let wad = U256::from(20);
54+
55+
// This would fail as Alice doesn't have any WETH.
56+
let tx = CallItemBuilder::new(weth.transfer(bob, U256::from(10))).allow_failure(true);
57+
let deposit = CallItemBuilder::new(weth.deposit()).value(wad); // Set the amount of eth that should be deposited into the contract.
58+
let multicall = provider
59+
.multicall()
60+
// Bob's intial WETH balance.
61+
.add(weth.balanceOf(bob))
62+
// Attempted WETH transfer from Alice to Bob which would fail.
63+
.add_call(tx.clone())
64+
// Alices deposits ETH and mints WETH.
65+
.add_call(deposit)
66+
// Attempt transfer again. Succeeds!
67+
.add_call(tx)
68+
// Alice's WETH balance after the transfer.
69+
.add(weth.balanceOf(alice))
70+
// Bob's final balance.
71+
.add(weth.balanceOf(bob));
72+
73+
assert_eq!(multicall.len(), 6);
74+
75+
// It is important to use `aggregate3_value` as we're trying to simulate calls to payable
76+
// functions that should be sent a value, using any other multicall3 method would result in an
77+
// error.
78+
let (init_bob, failed_transfer, deposit, succ_transfer, alice_weth, bob_weth) =
79+
multicall.aggregate3_value().await?;
80+
81+
// Since, `aggregate3_value` allows for calls to fail without reverting, it returns a tuple of
82+
// results which contain the decoded return value in Ok(_) variant and the `Failure` type in the
83+
// Err(_) variant.
84+
assert!(matches!(failed_transfer.unwrap_err(), Failure { idx: 1, return_data: _ }));
85+
86+
let init_bob = init_bob?;
87+
assert_eq!(init_bob._0, U256::ZERO);
88+
89+
assert!(deposit.is_ok());
90+
assert!(succ_transfer.is_ok());
91+
92+
let alice_weth = alice_weth?;
93+
let bob_weth = bob_weth?;
94+
95+
println!("Alice's WETH balance: {}, Bob's WETH balance: {}", alice_weth._0, bob_weth._0);
96+
97+
Ok(())
98+
}

0 commit comments

Comments
 (0)