Incorrect encoding of integer arrays
TorstenStueber opened this issue · 2 comments
Describe the bug
Integer arrays returned by a Solidity smart contract are incorrectly Scale encoded when the contract is compiled for the Substrate compilation target.
To Reproduce
Compile the following contract via Solang using solang compile --target substrate ...
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;
contract Test {
function test(
)
external view
returns (uint256[] memory _amounts)
{
_amounts = new uint256[](2);
_amounts[0] = 0x201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201;
_amounts[1] = 0x201f1e1d1c1b1a191817161514131211100f0e0d0c0b0a090807060504030201;
}
}
Then instantiate the wasm smart contract on a Substrate chain and send the message test
. This message will return the following binary Scale encoded value consisting of 66 bytes:
0x080102030405060708090a0b0c0d0e0f...1415161718191a1b1c1d1e1f2000
However, the correct encoding of the vector should be the 65 bytes (1 byte for the initial 0x08
being the compact encoding of the number 2 for the length of the vector and then 32 bytes for each entry):
0x080102030405060708090a0b0c0d0e0f...1415161718191a1b1c1d1e1f20
i.e., there is an incorrect additional trailing 00
-byte.
When sending the message test
via the npm module @polkadot/api
, then it will show the following error message:
createType(Vec<u256>):: Vec<u256>:: Decoded input doesn't match input, received 0x080102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f…02030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2000 (66 bytes), created 0x080102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f…0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20 (65 bytes)
Similar errors
The contract will generate similar errors when using other uint
data types whose number of bytes is not a power of two. For example, consider the following smart contract:
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;
contract Test {
function test(
)
external view
returns (uint24[] memory _amounts)
{
_amounts = new uint24[](2);
_amounts[0] = 0x030201;
_amounts[1] = 0x030201;
}
}
The ABI of the compiled smart contract specifies the return type of test
as Vec<u32>
. However, it returns 0x08010203000102
instead of the correct 0x080102030001020300
. The library @polkadot/api
will show the following error message:
createType(Vec<u32>):: Vec<u32>:: Decoded input doesn't match input, received 0x08010203000102 (7 bytes), created 0x080102030001020000 (9 bytes)
Potential further problem
I didn't test but find it plausible that also the decoding of message arguments that are integer arrays is not working properly.
Solang version
solang version v0.3.1
@TorstenStueber thanks for reporting, good finds.
#1503 will fix the issue with the dynamic uint256 array.
Regarding the second problem. It seems that the encoder (and decoder) does not round up the integer size to the next power of two, I'm gonna look at this too.
Until we have #1344, in the metadata we have to round up integer size not the power of 2 to the next size that is a power of two.