// SPDX-License-Identifier: MIT pragma solidity ^0.8.13; import {Test} from "../src/Test.sol"; import {Variable, Type, TypeKind, LibVariable} from "../src/LibVariable.sol"; contract LibVariableTest is Test { using LibVariable for Type; using LibVariable for TypeKind; LibVariableHelper internal helper; bytes internal expectedErr; Variable internal uninitVar; Variable internal boolVar; Variable internal addressVar; Variable internal bytes32Var; Variable internal uintVar; Variable internal intVar; Variable internal stringVar; Variable internal bytesVar; Variable internal boolArrayVar; Variable internal addressArrayVar; Variable internal bytes32ArrayVar; Variable internal uintArrayVar; Variable internal intArrayVar; Variable internal stringArrayVar; Variable internal bytesArrayVar; function setUp() public { helper = new LibVariableHelper(); // UNINITIALIZED uninitVar = Variable(Type(TypeKind.None, false), ""); // SINGLE VALUES boolVar = Variable(Type(TypeKind.Bool, false), abi.encode(true)); addressVar = Variable(Type(TypeKind.Address, false), abi.encode(address(0xdeadbeef))); bytes32Var = Variable(Type(TypeKind.Bytes32, false), abi.encode(bytes32(uint256(42)))); uintVar = Variable(Type(TypeKind.Uint256, false), abi.encode(uint256(123))); intVar = Variable(Type(TypeKind.Int256, false), abi.encode(int256(-123))); stringVar = Variable(Type(TypeKind.String, false), abi.encode("hello world")); bytesVar = Variable(Type(TypeKind.Bytes, false), abi.encode(hex"c0ffee")); // ARRAY VALUES bool[] memory bools = new bool[](2); bools[0] = true; bools[1] = false; boolArrayVar = Variable(Type(TypeKind.Bool, true), abi.encode(bools)); address[] memory addrs = new address[](2); addrs[0] = address(0x1); addrs[1] = address(0x2); addressArrayVar = Variable(Type(TypeKind.Address, true), abi.encode(addrs)); bytes32[] memory b32s = new bytes32[](2); b32s[0] = bytes32(uint256(1)); b32s[1] = bytes32(uint256(2)); bytes32ArrayVar = Variable(Type(TypeKind.Bytes32, true), abi.encode(b32s)); uint256[] memory uints = new uint256[](2); uints[0] = 1; uints[1] = 2; uintArrayVar = Variable(Type(TypeKind.Uint256, true), abi.encode(uints)); int256[] memory ints = new int256[](2); ints[0] = -1; ints[1] = 2; intArrayVar = Variable(Type(TypeKind.Int256, true), abi.encode(ints)); string[] memory strings = new string[](2); strings[0] = "one"; strings[1] = "two"; stringArrayVar = Variable(Type(TypeKind.String, true), abi.encode(strings)); bytes[] memory b = new bytes[](2); b[0] = hex"01"; b[1] = hex"02"; bytesArrayVar = Variable(Type(TypeKind.Bytes, true), abi.encode(b)); } // -- SUCCESS CASES -------------------------------------------------------- function test_TypeHelpers() public view { // TypeKind.toString() assertEq(TypeKind.None.toString(), "none"); assertEq(TypeKind.Bool.toString(), "bool"); assertEq(TypeKind.Address.toString(), "address"); assertEq(TypeKind.Bytes32.toString(), "bytes32"); assertEq(TypeKind.Uint256.toString(), "uint256"); assertEq(TypeKind.Int256.toString(), "int256"); assertEq(TypeKind.String.toString(), "string"); assertEq(TypeKind.Bytes.toString(), "bytes"); // TypeKind.toTomlKey() assertEq(TypeKind.Uint256.toTomlKey(), "uint"); assertEq(TypeKind.Int256.toTomlKey(), "int"); assertEq(TypeKind.Bytes32.toTomlKey(), "bytes32"); // Type.toString() assertEq(boolVar.ty.toString(), "bool"); assertEq(boolArrayVar.ty.toString(), "bool[]"); assertEq(uintVar.ty.toString(), "uint256"); assertEq(uintArrayVar.ty.toString(), "uint256[]"); assertEq(uninitVar.ty.toString(), "none"); // Type.isEqual() assertTrue(boolVar.ty.isEqual(Type(TypeKind.Bool, false))); assertFalse(boolVar.ty.isEqual(Type(TypeKind.Bool, true))); assertFalse(boolVar.ty.isEqual(Type(TypeKind.Address, false))); // Type.assertEq() boolVar.ty.assertEq(Type(TypeKind.Bool, false)); uintArrayVar.ty.assertEq(Type(TypeKind.Uint256, true)); } function test_Coercion() public view { // Single values assertTrue(helper.toBool(boolVar)); assertEq(helper.toAddress(addressVar), address(0xdeadbeef)); assertEq(helper.toBytes32(bytes32Var), bytes32(uint256(42))); assertEq(helper.toUint256(uintVar), 123); assertEq(helper.toInt256(intVar), -123); assertEq(helper.toString(stringVar), "hello world"); assertEq(helper.toBytes(bytesVar), hex"c0ffee"); // Bool array bool[] memory bools = helper.toBoolArray(boolArrayVar); assertEq(bools.length, 2); assertTrue(bools[0]); assertFalse(bools[1]); // Address array address[] memory addrs = helper.toAddressArray(addressArrayVar); assertEq(addrs.length, 2); assertEq(addrs[0], address(0x1)); assertEq(addrs[1], address(0x2)); // String array string[] memory strings = helper.toStringArray(stringArrayVar); assertEq(strings.length, 2); assertEq(strings[0], "one"); assertEq(strings[1], "two"); } function test_Downcasting() public view { // Uint downcasting Variable memory v_uint_small = Variable(Type(TypeKind.Uint256, false), abi.encode(uint256(100))); assertEq(helper.toUint128(v_uint_small), 100); assertEq(helper.toUint64(v_uint_small), 100); assertEq(helper.toUint32(v_uint_small), 100); assertEq(helper.toUint16(v_uint_small), 100); assertEq(helper.toUint8(v_uint_small), 100); // Uint array downcasting uint256[] memory small_uints = new uint256[](2); small_uints[0] = 10; small_uints[1] = 20; Variable memory v_uint_array_small = Variable(Type(TypeKind.Uint256, true), abi.encode(small_uints)); uint8[] memory u8_array = helper.toUint8Array(v_uint_array_small); assertEq(u8_array[0], 10); assertEq(u8_array[1], 20); // Int downcasting Variable memory v_int_small_pos = Variable(Type(TypeKind.Int256, false), abi.encode(int256(100))); Variable memory v_int_small_neg = Variable(Type(TypeKind.Int256, false), abi.encode(int256(-100))); assertEq(helper.toInt128(v_int_small_pos), 100); assertEq(helper.toInt64(v_int_small_neg), -100); assertEq(helper.toInt32(v_int_small_pos), 100); assertEq(helper.toInt16(v_int_small_neg), -100); assertEq(helper.toInt8(v_int_small_pos), 100); // Int array downcasting int256[] memory small_ints = new int256[](2); small_ints[0] = -10; small_ints[1] = 20; Variable memory intArraySmall = Variable(Type(TypeKind.Int256, true), abi.encode(small_ints)); int8[] memory i8_array = helper.toInt8Array(intArraySmall); assertEq(i8_array[0], -10); assertEq(i8_array[1], 20); } // -- REVERT CASES --------------------------------------------------------- function testRevert_NotInitialized() public { vm.expectRevert(LibVariable.NotInitialized.selector); helper.toBool(uninitVar); vm.expectRevert(LibVariable.NotInitialized.selector); helper.toAddressArray(uninitVar); } function testRevert_assertExists() public { vm.expectRevert(LibVariable.NotInitialized.selector); helper.assertExists(uninitVar); } function testRevert_TypeMismatch() public { // Single values vm.expectRevert(abi.encodeWithSelector(LibVariable.TypeMismatch.selector, "uint256", "bool")); helper.toUint256(boolVar); vm.expectRevert(abi.encodeWithSelector(LibVariable.TypeMismatch.selector, "address", "string")); helper.toAddress(stringVar); // Arrays vm.expectRevert(abi.encodeWithSelector(LibVariable.TypeMismatch.selector, "uint256[]", "bool[]")); helper.toUint256Array(boolArrayVar); vm.expectRevert(abi.encodeWithSelector(LibVariable.TypeMismatch.selector, "address[]", "string[]")); helper.toAddressArray(stringArrayVar); // Single value to array vm.expectRevert(abi.encodeWithSelector(LibVariable.TypeMismatch.selector, "bool[]", "bool")); helper.toBoolArray(boolVar); // Array to single value vm.expectRevert(abi.encodeWithSelector(LibVariable.TypeMismatch.selector, "bool", "bool[]")); helper.toBool(boolArrayVar); // assertEq reverts vm.expectRevert(abi.encodeWithSelector(LibVariable.TypeMismatch.selector, "uint256", "bool")); helper.assertEq(boolVar.ty, Type(TypeKind.Uint256, false)); } function testRevert_UnsafeCast() public { // uint overflow Variable memory uintLarge = Variable(Type(TypeKind.Uint256, false), abi.encode(uint256(type(uint128).max) + 1)); expectedErr = abi.encodeWithSelector(LibVariable.UnsafeCast.selector, "value does not fit in 'uint128'"); vm.expectRevert(expectedErr); helper.toUint128(uintLarge); // int overflow Variable memory intLarge = Variable(Type(TypeKind.Int256, false), abi.encode(int256(type(int128).max) + 1)); expectedErr = abi.encodeWithSelector(LibVariable.UnsafeCast.selector, "value does not fit in 'int128'"); vm.expectRevert(expectedErr); helper.toInt128(intLarge); // int underflow Variable memory intSmall = Variable(Type(TypeKind.Int256, false), abi.encode(int256(type(int128).min) - 1)); expectedErr = abi.encodeWithSelector(LibVariable.UnsafeCast.selector, "value does not fit in 'int128'"); vm.expectRevert(expectedErr); helper.toInt128(intSmall); // uint array overflow uint256[] memory uintArray = new uint256[](2); uintArray[0] = 10; uintArray[1] = uint256(type(uint64).max) + 1; Variable memory uintArrayLarge = Variable(Type(TypeKind.Uint256, true), abi.encode(uintArray)); expectedErr = abi.encodeWithSelector(LibVariable.UnsafeCast.selector, "value in array does not fit in 'uint64'"); vm.expectRevert(expectedErr); helper.toUint64Array(uintArrayLarge); // int array overflow int256[] memory intArray = new int256[](2); intArray[0] = 10; intArray[1] = int256(type(int64).max) + 1; Variable memory intArrayLarge = Variable(Type(TypeKind.Int256, true), abi.encode(intArray)); expectedErr = abi.encodeWithSelector(LibVariable.UnsafeCast.selector, "value in array does not fit in 'int64'"); vm.expectRevert(expectedErr); helper.toInt64Array(intArrayLarge); // int array underflow intArray[0] = 10; intArray[1] = int256(type(int64).min) - 1; Variable memory intArraySmall = Variable(Type(TypeKind.Int256, true), abi.encode(intArray)); expectedErr = abi.encodeWithSelector(LibVariable.UnsafeCast.selector, "value in array does not fit in 'int64'"); vm.expectRevert(expectedErr); helper.toInt64Array(intArraySmall); } } /// @dev We must use an external helper contract to ensure proper call depth for `vm.expectRevert`, /// as direct library calls are inlined by the compiler, causing call depth issues. contract LibVariableHelper { using LibVariable for Type; using LibVariable for TypeKind; // Assertions function assertExists(Variable memory v) external pure { v.assertExists(); } function assertEq(Type memory t1, Type memory t2) external pure { t1.assertEq(t2); } // Single Value Coercion function toBool(Variable memory v) external pure returns (bool) { return v.toBool(); } function toAddress(Variable memory v) external pure returns (address) { return v.toAddress(); } function toBytes32(Variable memory v) external pure returns (bytes32) { return v.toBytes32(); } function toUint256(Variable memory v) external pure returns (uint256) { return v.toUint256(); } function toInt256(Variable memory v) external pure returns (int256) { return v.toInt256(); } function toString(Variable memory v) external pure returns (string memory) { return v.toString(); } function toBytes(Variable memory v) external pure returns (bytes memory) { return v.toBytes(); } // Array Coercion function toBoolArray(Variable memory v) external pure returns (bool[] memory) { return v.toBoolArray(); } function toAddressArray(Variable memory v) external pure returns (address[] memory) { return v.toAddressArray(); } function toBytes32Array(Variable memory v) external pure returns (bytes32[] memory) { return v.toBytes32Array(); } function toUint256Array(Variable memory v) external pure returns (uint256[] memory) { return v.toUint256Array(); } function toInt256Array(Variable memory v) external pure returns (int256[] memory) { return v.toInt256Array(); } function toStringArray(Variable memory v) external pure returns (string[] memory) { return v.toStringArray(); } function toBytesArray(Variable memory v) external pure returns (bytes[] memory) { return v.toBytesArray(); } // Uint Downcasting function toUint128(Variable memory v) external pure returns (uint128) { return v.toUint128(); } function toUint64(Variable memory v) external pure returns (uint64) { return v.toUint64(); } function toUint32(Variable memory v) external pure returns (uint32) { return v.toUint32(); } function toUint16(Variable memory v) external pure returns (uint16) { return v.toUint16(); } function toUint8(Variable memory v) external pure returns (uint8) { return v.toUint8(); } // Int Downcasting function toInt128(Variable memory v) external pure returns (int128) { return v.toInt128(); } function toInt64(Variable memory v) external pure returns (int64) { return v.toInt64(); } function toInt32(Variable memory v) external pure returns (int32) { return v.toInt32(); } function toInt16(Variable memory v) external pure returns (int16) { return v.toInt16(); } function toInt8(Variable memory v) external pure returns (int8) { return v.toInt8(); } // Uint Array Downcasting function toUint128Array(Variable memory v) external pure returns (uint128[] memory) { return v.toUint128Array(); } function toUint64Array(Variable memory v) external pure returns (uint64[] memory) { return v.toUint64Array(); } function toUint32Array(Variable memory v) external pure returns (uint32[] memory) { return v.toUint32Array(); } function toUint16Array(Variable memory v) external pure returns (uint16[] memory) { return v.toUint16Array(); } function toUint8Array(Variable memory v) external pure returns (uint8[] memory) { return v.toUint8Array(); } // Int Array Downcasting function toInt128Array(Variable memory v) external pure returns (int128[] memory) { return v.toInt128Array(); } function toInt64Array(Variable memory v) external pure returns (int64[] memory) { return v.toInt64Array(); } function toInt32Array(Variable memory v) external pure returns (int32[] memory) { return v.toInt32Array(); } function toInt16Array(Variable memory v) external pure returns (int16[] memory) { return v.toInt16Array(); } function toInt8Array(Variable memory v) external pure returns (int8[] memory) { return v.toInt8Array(); } }