From 254344d12b3126a7ff3048fc6255378bf09d201b Mon Sep 17 00:00:00 2001 From: sc0Vu Date: Sun, 28 Jan 2024 14:43:28 +0800 Subject: [PATCH] Fix decoding and make test happier --- src/Contracts/Ethabi.php | 2 +- src/Contracts/SolidityType.php | 22 ++++++++++++++++++++++ src/Contracts/Types/BaseArray.php | 12 +----------- src/Contracts/Types/DynamicArray.php | 10 ++++++++-- src/Contracts/Types/SizedArray.php | 21 ++++++++++++--------- src/Contracts/Types/Str.php | 3 +++ src/Contracts/Types/Tuple.php | 6 ++++-- test/unit/EthabiTest.php | 5 ++--- 8 files changed, 53 insertions(+), 28 deletions(-) diff --git a/src/Contracts/Ethabi.php b/src/Contracts/Ethabi.php index c6a1e88..cd8eb39 100644 --- a/src/Contracts/Ethabi.php +++ b/src/Contracts/Ethabi.php @@ -454,7 +454,7 @@ public function decodeParameter($type, $param) public function decodeParameters($types, $param) { if (!is_string($param)) { - throw new InvalidArgumentException('The type or param to decodeParameters must be string.'); + throw new InvalidArgumentException('The param must be string.'); } // change json to array diff --git a/src/Contracts/SolidityType.php b/src/Contracts/SolidityType.php index a30e170..4875a10 100644 --- a/src/Contracts/SolidityType.php +++ b/src/Contracts/SolidityType.php @@ -189,6 +189,28 @@ public function isDynamicType() return false; } + /** + * deepCalculateDataLength + * Calculate static data size recursively. + * TODO: Improve this function, or calculate data length when parse abi. + * + * @param array $data + * @return integer + */ + public function deepCalculateDataLength($data) + { + if (!is_array($data)) return 1; + $dataCount = 0; + foreach ($data as $d) { + if (is_array($d)) { + $dataCount += $this->deepCalculateDataLength($d); + } else { + $dataCount += 1; + } + } + return $dataCount; + } + /** * encode * diff --git a/src/Contracts/Types/BaseArray.php b/src/Contracts/Types/BaseArray.php index 4e3978a..3fb70fa 100644 --- a/src/Contracts/Types/BaseArray.php +++ b/src/Contracts/Types/BaseArray.php @@ -111,16 +111,6 @@ public function inputFormat($value, $name) */ public function outputFormat($value, $name) { - $checkZero = str_replace('0', '', $value); - - if (empty($checkZero)) { - return '0'; - } - if (preg_match('/^bytes([0-9]*)/', $name, $match) === 1) { - $size = intval($match[1]); - $length = 2 * $size; - $value = mb_substr($value, 0, $length); - } - return '0x' . $value; + throw new InvalidArgumentException('Should not call outputFormat in BaseArray directly'); } } \ No newline at end of file diff --git a/src/Contracts/Types/DynamicArray.php b/src/Contracts/Types/DynamicArray.php index a3b49af..a4f40f4 100644 --- a/src/Contracts/Types/DynamicArray.php +++ b/src/Contracts/Types/DynamicArray.php @@ -77,11 +77,17 @@ public function outputFormat($value, $abiType) } $lengthHex = mb_substr($value, 0, 64); $length = (int) Utils::hexToNumber($lengthHex); - $offset = 64; + $offset = 0; + $value = mb_substr($value, 64); $results = []; $decoder = $abiType['coders']; for ($i = 0; $i < $length; $i++) { - $results[] = $decoder['solidityType']->decode($value, $offset, $decoder); + $decodeValueOffset = $offset; + if ($decoder['dynamic']) { + $decodeValueOffsetHex = mb_substr($value, $offset, 64); + $decodeValueOffset = (int) Utils::hexToNumber($decodeValueOffsetHex) * 2; + } + $results[] = $decoder['solidityType']->decode($value, $decodeValueOffset, $decoder); $offset += 64; } return $results; diff --git a/src/Contracts/Types/SizedArray.php b/src/Contracts/Types/SizedArray.php index 46e193d..a2feb95 100644 --- a/src/Contracts/Types/SizedArray.php +++ b/src/Contracts/Types/SizedArray.php @@ -82,18 +82,21 @@ public function outputFormat($value, $abiType) } $length = is_array($abiType) ? $this->staticArrayLength($abiType['type']) : 0; $offset = 0; - if ($abiType['dynamic']) { - $valueLengthHex = mb_substr($value, 0, 64); - $valueLength = (int) Utils::hexToNumber($valueLengthHex) / 32; - if ($length !== $valueLength) { - throw new InvalidArgumentException('Invalid sized array length decode, expected: ' . $lenght . ', but got ' . $valueLength); - } - $offset += 64; - } $results = []; $decoder = $abiType['coders']; for ($i = 0; $i < $length; $i++) { - $results[] = $decoder['solidityType']->decode($value, $offset, $decoder); + $decodeValueOffset = $offset; + if ($decoder['dynamic']) { + $decodeValueOffsetHex = mb_substr($value, $offset, 64); + $decodeValueOffset = (int) Utils::hexToNumber($decodeValueOffsetHex) * 2; + } + $decoded = $decoder['solidityType']->decode($value, $decodeValueOffset, $decoder); + $results[] = $decoded; + $dataCount = 1; + if (!$decoder['dynamic']) { + $dataCount = $this->deepCalculateDataLength($decoded); + } + $offset += (64 * $dataCount); } return $results; } diff --git a/src/Contracts/Types/Str.php b/src/Contracts/Types/Str.php index cb8ec01..2bee6d0 100644 --- a/src/Contracts/Types/Str.php +++ b/src/Contracts/Types/Str.php @@ -83,6 +83,9 @@ public function outputFormat($value, $abiType) if (preg_match('/^[0]+([a-f0-9]+)$/', $strLen, $match) === 1) { $strLen = BigNumberFormatter::format('0x' . $match[1])->toString(); } + if (is_float((int) $strLen * 2)) { + var_dump($strValue, $strLen, mb_substr($value, 0, 64), BigNumberFormatter::format('0x' . mb_substr($value, 0, 64))->toString()); + } $strValue = mb_substr($strValue, 0, (int) $strLen * 2); return Utils::hexToBin($strValue); diff --git a/src/Contracts/Types/Tuple.php b/src/Contracts/Types/Tuple.php index 2067d33..b95841f 100644 --- a/src/Contracts/Types/Tuple.php +++ b/src/Contracts/Types/Tuple.php @@ -119,8 +119,10 @@ public function outputFormat($value, $abiTypes) $staticOffset += 64; $results[] = $abiType['solidityType']->decode($value, $startPos * 2, $abiType); } else { - $results[] = $abiType['solidityType']->decode($value, $staticOffset, $abiType); - $staticOffset += 64; + $decoded = $abiType['solidityType']->decode($value, $staticOffset, $abiType); + $dataCount = $this->deepCalculateDataLength($decoded); + $staticOffset += 64 * $dataCount; + $results[] = $decoded; } } return $results; diff --git a/test/unit/EthabiTest.php b/test/unit/EthabiTest.php index 12d35c7..884996e 100644 --- a/test/unit/EthabiTest.php +++ b/test/unit/EthabiTest.php @@ -326,11 +326,10 @@ public function testAbiFixtures() $testFixtures = $this->loadFixtureJsonFile(dirname(__DIR__) . '/fixtures/abi.json'); $abi = $this->abi; foreach ($testFixtures as $test) { - // if (is_string($test['value']) || is_string($test['type'])) { - // var_dump($test); - // } $result = $abi->encodeParameters([$test['type']], [$test['value']]); $this->assertEquals($test['encoded'], $result); + $decodeResult = $abi->decodeParameters([$test['type']], $result); + $this->assertTrue(!is_null($decodeResult)); } } } \ No newline at end of file