Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ethexe): introduce ScaleCodec library for Solidity #4237

Open
wants to merge 15 commits into
base: master
Choose a base branch
from
479 changes: 479 additions & 0 deletions ethexe/contracts/src/ScaleCodec.sol

Large diffs are not rendered by default.

53 changes: 53 additions & 0 deletions ethexe/contracts/test/ScaleCodec/Array.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.25;
osipov-mit marked this conversation as resolved.
Show resolved Hide resolved

import {ScaleCodec} from "src/ScaleCodec.sol";
import "forge-std/Test.sol";

contract TestArrayScaleCodec is Test {
function encodeArray(string[5] memory _value) internal pure returns (bytes memory) {
bytes[] memory result = new bytes[](5);
for (uint256 i = 0; i < 5; i++) {
result[i] = ScaleCodec.encodeString(_value[i]);
}

return ScaleCodec.concatBytes(result);
}

function decodeArray(bytes memory _value) internal pure returns (string[5] memory) {
string[] memory result = new string[](5);

uint256 offset = 0;

for (uint256 i = 0; i < 5; i++) {
ScaleCodec.DecodedString memory item =
ScaleCodec.decodeString(ScaleCodec.sliceBytes(_value, offset, _value.length));
result[i] = item.value;
offset += item.offset;
}

return [result[0], result[1], result[2], result[3], result[4]];
}

function test_arrayEncode() public pure {
string[5] memory _array = ["Gear", "is", "awesome", "and", "cool"];

bytes memory encoded = hex"10476561720869731c617765736f6d650c616e6410636f6f6c";

assertEq(encodeArray(_array), encoded);
}

function test_arrayDecode() public pure {
string[5] memory _array = ["Gear", "is", "awesome", "and", "cool"];

bytes memory encoded = hex"10476561720869731c617765736f6d650c616e6410636f6f6c";

string[5] memory decoded = decodeArray(encoded);

assertEq(decoded[0], _array[0]);
assertEq(decoded[1], _array[1]);
assertEq(decoded[2], _array[2]);
assertEq(decoded[3], _array[3]);
assertEq(decoded[4], _array[4]);
}
}
23 changes: 23 additions & 0 deletions ethexe/contracts/test/ScaleCodec/Bool.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import {ScaleCodec} from "src/ScaleCodec.sol";
import "forge-std/Test.sol";

contract TestBoolScaleCodec is Test {
function test_boolEncodeTrue() public pure {
assertEq(ScaleCodec.encodeBool(true), hex"01");
}

function test_boolEncodeFalse() public pure {
assertEq(ScaleCodec.encodeBool(false), hex"00");
}

function test_boolDecodeTrue() public pure {
assertEq(ScaleCodec.decodeBool(hex"01"), true);
}

function test_boolDecodeFalse() public pure {
assertEq(ScaleCodec.decodeBool(hex"00"), false);
}
}
14 changes: 14 additions & 0 deletions ethexe/contracts/test/ScaleCodec/Bytes.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import {ScaleCodec} from "src/ScaleCodec.sol";
import "forge-std/Test.sol";

contract TestBytesScaleCodec is Test {
function test_bytesToBytes32() public pure {
assertEq(
ScaleCodec.bytesToBytes32(hex"00000000000000000000000000000000000000000000000000000000000000000000"),
hex"0000000000000000000000000000000000000000000000000000000000000000"
);
}
}
51 changes: 51 additions & 0 deletions ethexe/contracts/test/ScaleCodec/Int.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import {ScaleCodec} from "src/ScaleCodec.sol";
import "forge-std/Test.sol";

contract TestIntScaleCodec is Test {
function test_int8EncodeDecode() public pure {
assertEq(ScaleCodec.encodeInt8(int8(69)), hex"45");
assertEq(ScaleCodec.decodeInt8(hex"45"), int8(69));

assertEq(ScaleCodec.encodeInt8(int8(-69)), hex"bb");
assertEq(ScaleCodec.decodeInt8(hex"bb"), int8(-69));
}

function test_int16EncodeDecode() public pure {
assertEq(ScaleCodec.encodeInt16(int16(42)), hex"2a00");
assertEq(ScaleCodec.decodeInt16(hex"2a00"), int16(42));

assertEq(ScaleCodec.encodeInt16(int16(-42)), hex"d6ff");
assertEq(ScaleCodec.decodeInt16(hex"d6ff"), int16(-42));
}

function test_int32EncodeDecode() public pure {
assertEq(ScaleCodec.encodeInt32(int32(16777215)), hex"ffffff00");
assertEq(ScaleCodec.decodeInt32(hex"ffffff00"), int32(16777215));

assertEq(ScaleCodec.encodeInt32(int32(-16777215)), hex"010000ff");
assertEq(ScaleCodec.decodeInt32(hex"010000ff"), int32(-16777215));
}

function test_int64EncodeDecode18446744073709() public pure {
assertEq(ScaleCodec.encodeInt64(int64(18446744073709)), hex"edb5a0f7c6100000");
assertEq(ScaleCodec.decodeInt64(hex"edb5a0f7c6100000"), int64(18446744073709));

assertEq(ScaleCodec.encodeInt64(int64(-18446744073709)), hex"134a5f0839efffff");
assertEq(ScaleCodec.decodeInt64(hex"134a5f0839efffff"), int64(-18446744073709));
}

function test_int128EncodeDecode() public pure {
assertEq(ScaleCodec.encodeInt128(int128(340282366920938463463374607431)), hex"4754408bb92ca5b509fa824b04000000");
assertEq(ScaleCodec.decodeInt128(hex"4754408bb92ca5b509fa824b04000000"), int128(340282366920938463463374607431));

assertEq(
ScaleCodec.encodeInt128(int128(-340282366920938463463374607431)), hex"b9abbf7446d35a4af6057db4fbffffff"
);
assertEq(
ScaleCodec.decodeInt128(hex"b9abbf7446d35a4af6057db4fbffffff"), int128(-340282366920938463463374607431)
);
}
}
51 changes: 51 additions & 0 deletions ethexe/contracts/test/ScaleCodec/Optional.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import {ScaleCodec} from "src/ScaleCodec.sol";
import "forge-std/Test.sol";

contract TestOptionalScaleCodec is Test {
struct OptionalString {
bool isSome;
string value;
}

function encodeOptionalString(OptionalString memory _value) internal pure returns (bytes memory) {
return ScaleCodec.encodeOptional(
ScaleCodec.Optional({
isSome: _value.isSome,
value: _value.isSome ? ScaleCodec.encodeString(_value.value) : new bytes(0)
})
);
}

function decodeOptionalString(bytes memory _bytes) internal pure returns (OptionalString memory) {
ScaleCodec.Optional memory decoded = ScaleCodec.decodeOptional(_bytes);

return OptionalString({
isSome: decoded.isSome,
value: decoded.isSome ? ScaleCodec.decodeString(decoded.value).value : ""
});
}

function test_OptionalNoneEncodeDecode() public pure {
OptionalString memory _optional = OptionalString({isSome: false, value: ""});

assertEq(encodeOptionalString(_optional), hex"00");

OptionalString memory _decoded = decodeOptionalString(hex"00");

assertEq(_decoded.isSome, false);
}

function test_OptionalSomeEncodeDecode() public pure {
OptionalString memory _optional = OptionalString({isSome: true, value: "Gear"});

assertEq(encodeOptionalString(_optional), hex"011047656172");

OptionalString memory _decoded = decodeOptionalString(hex"011047656172");

assertEq(_decoded.isSome, true);
assertEq(_decoded.value, "Gear");
}
}
58 changes: 58 additions & 0 deletions ethexe/contracts/test/ScaleCodec/Result.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import {ScaleCodec} from "src/ScaleCodec.sol";
import "forge-std/Test.sol";

contract TestResultScaleCodec is Test {
struct ResultStringU8 {
bool isOk;
bool isErr;
string ok;
uint8 err;
}

function encodeResultStringU8(ResultStringU8 memory _value) internal pure returns (bytes memory) {
if (_value.isOk) {
return ScaleCodec.encodeResult(
ScaleCodec.Result({isOk: true, isErr: false, value: ScaleCodec.encodeString(_value.ok)})
);
} else {
return ScaleCodec.encodeResult(
ScaleCodec.Result({isOk: false, isErr: true, value: ScaleCodec.encodeUint8(_value.err)})
);
}
}

function decodeResultStringU8(bytes memory _value) public pure returns (ResultStringU8 memory) {
ScaleCodec.Result memory decoded = ScaleCodec.decodeResult(_value);

if (decoded.isOk) {
return ResultStringU8({isOk: true, isErr: false, ok: ScaleCodec.decodeString(decoded.value).value, err: 0});
} else {
return ResultStringU8({isOk: false, isErr: true, ok: "", err: ScaleCodec.decodeUint8(decoded.value)});
}
}

function test_ResultOkEncodeDecode() public pure {
ResultStringU8 memory _result = ResultStringU8({isOk: true, isErr: false, ok: "Gear", err: 0});

assertEq(encodeResultStringU8(_result), hex"001047656172");

ResultStringU8 memory _decoded = decodeResultStringU8(hex"001047656172");

assertEq(_decoded.isOk, true);
assertEq(_decoded.ok, "Gear");
}

function test_ResultErrEncodeDecode() public pure {
ResultStringU8 memory _result = ResultStringU8({isOk: false, isErr: true, ok: "", err: 1});

assertEq(encodeResultStringU8(_result), hex"0101");

ResultStringU8 memory _decoded = decodeResultStringU8(hex"0101");

assertEq(_decoded.isErr, true);
assertEq(_decoded.err, 1);
}
}
15 changes: 15 additions & 0 deletions ethexe/contracts/test/ScaleCodec/String.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import {ScaleCodec} from "src/ScaleCodec.sol";
import "forge-std/Test.sol";

contract TestStringScaleCodec is Test {
function test_stringEncode() public pure {
assertEq(ScaleCodec.encodeString("hello"), hex"1468656c6c6f");
}

function test_stringDecode() public pure {
assertEq(ScaleCodec.decodeString(hex"1468656c6c6f").value, "hello");
}
}
42 changes: 42 additions & 0 deletions ethexe/contracts/test/ScaleCodec/Struct.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import {ScaleCodec} from "src/ScaleCodec.sol";
import "forge-std/Test.sol";

contract TestStructScaleCodec is Test {
struct MyStruct {
string name;
uint8 age;
}

function encodeMyStruct(string memory _name, uint8 _age) internal pure returns (bytes memory) {
MyStruct memory myStruct = MyStruct(_name, _age);

bytes[] memory encoded_items = new bytes[](2);

encoded_items[0] = ScaleCodec.encodeString(myStruct.name);
encoded_items[1] = ScaleCodec.encodeUint8(myStruct.age);

return ScaleCodec.concatBytes(encoded_items);
}

function decodeMyStruct(bytes memory _value) internal pure returns (MyStruct memory) {
ScaleCodec.DecodedString memory name = ScaleCodec.decodeString(_value);
uint8 age = ScaleCodec.decodeUint8(ScaleCodec.sliceBytes(_value, name.offset, _value.length));

return MyStruct(name.value, age);
}

function test_MyStructEncode() public pure {
MyStruct memory _myStruct = MyStruct({name: "Gear", age: 3});

assertEq(encodeMyStruct(_myStruct.name, _myStruct.age), hex"104765617203");
}

function test_MyStructEncodeDecode() public pure {
MyStruct memory _decoded = decodeMyStruct(hex"104765617203");
assertEq(_decoded.name, "Gear");
assertEq(_decoded.age, 3);
}
}
63 changes: 63 additions & 0 deletions ethexe/contracts/test/ScaleCodec/Uint.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.19;

import {ScaleCodec} from "src/ScaleCodec.sol";
import "forge-std/Test.sol";

contract TestUintScaleCodec is Test {
function test_uint8EncodeDecode() public pure {
assertEq(ScaleCodec.encodeUint8(uint8(69)), hex"45");
assertEq(ScaleCodec.decodeUint8(hex"45"), uint8(69));
}

function test_uint16EncodeDecode() public pure {
assertEq(ScaleCodec.encodeUint16(uint16(42)), hex"2a00");
assertEq(ScaleCodec.decodeUint16(hex"2a00"), uint16(42));
}

function test_uint32EncodeDecode() public pure {
assertEq(ScaleCodec.encodeUint32(uint32(16777215)), hex"ffffff00");
assertEq(ScaleCodec.decodeUint32(hex"ffffff00"), uint32(16777215));
}

function test_uint64EncodeDecode() public pure {
assertEq(ScaleCodec.encodeUint64(uint64(18446744073709)), hex"edb5a0f7c6100000");
assertEq(ScaleCodec.decodeUint64(hex"edb5a0f7c6100000"), uint64(18446744073709));
}

function test_uint128EncodeDecode() public pure {
assertEq(
ScaleCodec.encodeUint128(uint128(340282366920938463463374607431)), hex"4754408bb92ca5b509fa824b04000000"
);
assertEq(
ScaleCodec.decodeUint128(hex"4754408bb92ca5b509fa824b04000000"), uint128(340282366920938463463374607431)
);
}

function tedt_Uint256EncodeDecode() public pure {
assertEq(
ScaleCodec.encodeUint256(uint256(340282366920938463463374607431)),
hex"0000000000000000000000000000000000000000000000000000000000000080"
);
assertEq(
ScaleCodec.decodeUint256(hex"0000000000000000000000000000000000000000000000000000000000000080"),
uint256(340282366920938463463374607431)
);
}

function test_CompactInt() public pure {
assertEq(ScaleCodec.encodeCompactInt(0), hex"00");
assertEq(ScaleCodec.encodeCompactInt(1), hex"04");
assertEq(ScaleCodec.encodeCompactInt(42), hex"a8");
assertEq(ScaleCodec.encodeCompactInt(69), hex"1501");
assertEq(ScaleCodec.encodeCompactInt(65535), hex"feff0300");
assertEq(ScaleCodec.encodeCompactInt(100000000000000), hex"0b00407a10f35a");

assertEq(ScaleCodec.decodeCompactInt(hex"00").value, 0);
assertEq(ScaleCodec.decodeCompactInt(hex"04").value, 1);
assertEq(ScaleCodec.decodeCompactInt(hex"a8").value, 42);
assertEq(ScaleCodec.decodeCompactInt(hex"1501").value, 69);
assertEq(ScaleCodec.decodeCompactInt(hex"feff0300").value, 65535);
assertEq(ScaleCodec.decodeCompactInt(hex"0b00407a10f35a").value, 100000000000000);
}
}
Loading
Loading