Hand-encoding Multicall aggregate's bytes[] returnData for stubbed tests: each element is a length-prefixed, independently ABI-encoded return value
Task: you are stubbing JSON-RPC responses (WireMock, fixtures, a mock provider) for Multicall3.aggregate(Call[]) returns (uint256 blockNumber, bytes[] returnData) where the batched calls are ERC-20 decimals() and symbol(). Hand-written hex keeps failing to decode, usually with an out-of-bounds or "data out-of-bounds" style error.
The layout, word by word (32 bytes each):
1. blockNumber.
2. Offset to returnData — 0x40, measured from the start of the return payload.
3. Array length, e.g. 0x06 for six calls.
4. One offset per element, each measured **from the start of this offset table** (i.e. from the word right after the length), not from the start of the payload.
5. Each element: a length word (byte length of that element's bytes) followed by its content, padded to a multiple of 32.
The trap is step 5: an element of bytes[] holds the *complete ABI encoding of that call's return tuple*, so dynamic returns get their own internal head.
- decimals() → element length 0x20, content one word (0x12 for 18). Two words total.
- symbol() → element length 0x60, content 0x20 (offset to the string inside this element), 0x04 (string length), then 57424e42... ("WBNB" right-padded). Four words total.
So a string return costs an extra offset word that a naive "length + data" encoding omits — that missing word is the usual cause of the decode failure, and it shifts every later element offset.
Practical shortcut: rather than counting bytes, generate the fixture with a real encoder — ethers.AbiCoder.defaultAbiCoder().encode(['uint256','bytes[]'], [blockNumber, [encodedDecimals, encodedSymbol]]) where each inner value is itself encode(['uint8'],[18]) / encode(['string'],['WBNB']) — then paste the result into the stub. Note decimals() is uint8 but still occupies a full word.
Source: adapted from a Stack Exchange answer by Joel Earps (question by Joel Earps), https://ethereum.stackexchange.com/questions/170525/how-to-correctly-abi-decode-multicall-results-erc-20-decimals-symbol-in-test — CC BY-SA 4.0