-
Notifications
You must be signed in to change notification settings - Fork 465
Description
ABIDecoder.decodeSingleType() is not able to decode dynamic arrays contained in structs.
Consider the following Solidity structure:
struct Offer {
bool isCreated;
bool isTaken;
address maker;
bytes interfaceId;
address stablecoin;
uint256 amountLowerBound;
uint256 amountUpperBound;
uint256 securityDepositAmount;
SwapDirection direction;
bytes price;
bytes[] settlementMethods;
uint256 protocolVersion;
bytes32 extraData;
}
And the following Solidity getter method:
function getOffer(bytes16 offerID) view public returns (Offer memory) {
return offers[offerID];
}
When the getOffer method is called, it returns a result containing the following data: (descriptive comments added for clarity)
0000000000000000000000000000000000000000000000000000000000000001 // isCreated
0000000000000000000000000000000000000000000000000000000000000000 // isTaken
00000000000000000000000070997970c51812dc3a010c7d01b50e0d17dc79c8 // maker
00000000000000000000000000000000000000000000000000000000000001a0 // pointer to interfaceId value
0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3 // stablecoin
0000000000000000000000000000000000000000000000000000000000000064 // amountLowerBound
0000000000000000000000000000000000000000000000000000000000000064 // amountUpperBound
000000000000000000000000000000000000000000000000000000000000000a // securityDepositAmount
0000000000000000000000000000000000000000000000000000000000000001 // direction
00000000000000000000000000000000000000000000000000000000000001e0 // pointer to price value
0000000000000000000000000000000000000000000000000000000000000220 // pointer to settlementMethods
0000000000000000000000000000000000000000000000000000000000000001 // protocolVersion
04a51ef96096879e46f8146b37521314c45fa88371bff7dd9c0f09872c68fa43 // extraData
00000000000000000000000000000000000000000000000000000000000000196d616b6572277320696e74657266616365204964206865726500000000000000 // interfaceId value
000000000000000000000000000000000000000000000000000000000000000c6120707269636520686572650000000000000000000000000000000000000000 // price value
0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000095553442d53574946540000000000000000000000000000000000000000000000 //settlementMethods value
In the process of calling this function from Web3Swift and decoding its return value, ABIEncoder.decodeSignleType
is called to decode the tuple.
decodeSignleType
begins calling itself again in order to decode its subtypes, and everything goes smoothly until decodeSignleType
attempts to decode bytes[] settlementMethods
.
At this point, consumed
, which points to the beginning of the portion of the data returned from the function call that still needs to be decoded, has a value of 320 (and thus points to the beginning of 0000000000000000000000000000000000000000000000000000000000000220 // pointer to settlementMethods
).
decodeSignleType
calls itself again on order to decode bytes[] settlementMethods
, and receives a tuple containing two values.
The first value is the successfully decoded array of byte arrays. The second is a pointer to the beginning of the portion of the data returned from the function call that still needs to be decoded.
This second value equals 352 (which points to the beginning of 0000000000000000000000000000000000000000000000000000000000000001 // protocolVersion
).
At this point, decodeSignleType
should begin at index 352 of the data returned from the function call, and attempt to decode protocolVersion
from the next 32 bytes.
However, decodeSignleType
instead adds this value to consumed
, giving consumed
a value of 672. Then decodeSignleType
tries to use the bytes from index 672 to 704 in order to decode protocolVersion
. Obviously, since the data is only 672 bytes long in total, these bytes don't actually exist and decoding fails.
I was able to resolve this issue by replacing line 176 in ABIDecoder
https://github.com/skywinder/web3swift/blob/2c755ba0424f0ca573670a519e930162f5f197e0/Sources/web3swift/EthereumABI/ABIDecoding.swift#L176
with the following:
switch subTypes[i] {
case .array(type: _, length: _):
if !subTypes[i].isStatic {
consumed = consumedUnwrapped
} else {
fallthrough
}
default:
consumed = consumed + consumedUnwrapped
}