Context
I have recently been interested in how calldata is formed and interpreted. The Docs have a great section on how Dynamic Types are encoded into calldata. However, in practice I have trouble understanding this structure with more complex structs.
The Setup
I created two simple contracts to test how they encode and decode the struct. One contract simply forms a relatively complex struct (I used DyDx's ActionArgs for a "real" example)
Contract PassItToMe is simply used to receive the an array of action items. It doesn't do anything in particular, just lets me read the call data it receives.
Contract SimpleCallDataTest forms a hardcoded ActionArgs struct array and then calls PassItToMe's actionTest function, thats it.
Code Snippet
interface IPassItToMe {
function actionTest(Actions.ActionArgs[] memory actions) external;
}
contract SimpleCallDataTest {
function actionCallDataTest(address contAddr) public {
Actions.ActionArgs[] memory operations = new Actions.ActionArgs[](1);
operations[0] = Actions.ActionArgs({
actionType: Actions.ActionType.Call,
accountId: 7,
amount: Types.AssetAmount({
sign: true,
denomination: Types.AssetDenomination.Wei,
ref: Types.AssetReference.Delta,
value: 1000
}),
primaryMarketId: 8,
secondaryMarketId: 9,
otherAddress: address(this),
otherAccountId: 5,
data: abi.encode(
msg.sender,
1000
)
});
IPassItToMe(contAddr).actionTest(operations);
}
}
Result
When we call the function actionCallDataTest, we can see the following is used as input to the CALL opcode.
Raw Hex
0x6a919f900000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000700000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003e800000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000009000000000000000000000000e0679aa631740f3dae1d3ce302f1f3d45f3ed61d00000000000000000000000000000000000000000000000000000000000000050000000000000000000000000000000000000000000000000000000000000160000000000000000000000000000000000000000000000000000000000000004000000000000000000000000031c8e1cb963d9cc02fcbc5d0cf881bbb1ffaf15f00000000000000000000000000000000000000000000000000000000000003e8
The Problem
When I format the input into the 32byte words to make it more human readable, we see the following:
In the above I have added notes to what my understanding what each word maps to and the byte count.
Issues:
- Line 4: What is this value? it looks like an offset but I dont know what for.
- Line 15: I understand that this is a offset for the start of the data attribute. Why is it 352? (0x160 = 352 decimal) I expected 448, as from this offset we can see the tail encoding for data
My Question
What do I misunderstand about the two above issues? The above encoding is correct and all data is correctly decoded by PassItToMe but I don't understand how.