0

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:

ABI examples

1 Answer 1

0
 import { encodeAbiParameters, decodeAbiParameters, parseAbiParameters } from 'viem' const blockNumber = 58256789n const returnData = [ encodeAbiParameters(parseAbiParameters('string'), ['BNB']), encodeAbiParameters(parseAbiParameters('uint8'), [18]), encodeAbiParameters(parseAbiParameters('string'), ['Cake']), encodeAbiParameters(parseAbiParameters('uint8'), [18]), encodeAbiParameters(parseAbiParameters('string'), ['USDC']), encodeAbiParameters(parseAbiParameters('uint8'), [18]) ] const encoded = encodeAbiParameters( parseAbiParameters('uint256, bytes[]'), [blockNumber, returnData] ) console.log('Encoded for WireMock:', encoded) const [decodedBlock, decodedData] = decodeAbiParameters( parseAbiParameters('uint256, bytes[]'), encoded ) console.log('Block:', decodedBlock) // Decode each return value decodedData.forEach((data, i) => { if (i % 2 === 0) { const [symbol] = decodeAbiParameters(parseAbiParameters('string'), data) console.log(`Token ${i/2 + 1} symbol:`, symbol) } else { const [decimals] = decodeAbiParameters(parseAbiParameters('uint8'), data) console.log(`Token ${Math.floor(i/2) + 1} decimals:`, decimals) } }) 

You can try this script with viem library:

https://viem.sh/docs/getting-started

Sign up to request clarification or add additional context in comments.

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.