I’m trying to understand ABI decoding of multicall RPC calls so I can create stubbed tests using WireMock for my Rust EVM application.
The multicall function signature is:
function aggregate(Call[] calldata calls) external payable
returns (uint256 blockNumber, bytes[] memory returnData);
The two ERC-20 functions I’m calling inside it are:
function symbol() public view returns (string);
function decimals() public view returns (uint8);
What I Tried
I attempted to manually construct the encoded response.
For three tokens I tried this:
0x
1 - 000000000000000000000000000000000000000000000000000000000378cc49 // block number
2 - 0000000000000000000000000000000000000000000000000000000000000040 // Offset to start of array
3 - 0000000000000000000000000000000000000000000000000000000000000006 // Length of array
4 - 0000000000000000000000000000000000000000000000000000000000000080 // Offset to string 1
5 - 00000000000000000000000000000000000000000000000000000000000000e0 // Offset to string 2
6 - 0000000000000000000000000000000000000000000000000000000000000140 // Offset to string 3
7 - 0000000000000000000000000000000000000000000000000000000000000012 // Decimals for coin 1
8 - 0000000000000000000000000000000000000000000000000000000000000004 // Length of string 1
9 - 57424e4200000000000000000000000000000000000000000000000000000000 // String contents
10 - 0000000000000000000000000000000000000000000000000000000000000012 // Decimals for coin
11 - 0000000000000000000000000000000000000000000000000000000000000004 // Length of string
12 - 43616b6500000000000000000000000000000000000000000000000000000000 // String contents
13 - 0000000000000000000000000000000000000000000000000000000000000012 // Decimals for coin
14 - 0000000000000000000000000000000000000000000000000000000000000004 // Length of string
15 - 5553444300000000000000000000000000000000000000000000000000000000 // String contents
Then this failed so I tried to do this for a single return:
"0x
000000000000000000000000000000000000000000000000000000000378cc49 // Block number
0000000000000000000000000000000000000000000000000000000000000040 // Offset to array
0000000000000000000000000000000000000000000000000000000000000002 // Length of array
0000000000000000000000000000000000000000000000000000000000000040 // Offset to string
0000000000000000000000000000000000000000000000000000000000000012 // Decimals
0000000000000000000000000000000000000000000000000000000000000004 // String Length
57424e4200000000000000000000000000000000000000000000000000000000" // String contents
But when I run these through WireMock and hit them from Rust, I get:
type check failed for "offset (usize)" with data: 0000000000000000000000000040000000000000000000000000000000000000
What Actually Happens
When I make a real multicall, the response looks like this:
aggregateReturn { blockNumber: 58256789, returnData: [ "0x0000000000000000000000000000000000000000000000000000000000000012", "0x0000000000000000000000000000000000000000000000000000000000000020 0000000000000000000000000000000000000000000000000000000000000004 57424e4200000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000012", "0x0000000000000000000000000000000000000000000000000000000000000020 0000000000000000000000000000000000000000000000000000000000000004 43616b6500000000000000000000000000000000000000000000000000000000", "0x0000000000000000000000000000000000000000000000000000000000000012", "0x0000000000000000000000000000000000000000000000000000000000000020 0000000000000000000000000000000000000000000000000000000000000004 5553444300000000000000000000000000000000000000000000000000000000" ] }
My Question
How should I correctly construct a stubbed multicall response for testing (e.g. for 3 tokens with decimals = 18 and symbols "BNB", "Cake", "USDC")?
Thanks in advance
Edit: I tried to follow this as a tutorial so really can't see what I am missing: