Files
assetxContracts/lib/forge-std/test/LibVariable.t.sol

435 lines
16 KiB
Solidity
Raw Permalink Normal View History

2025-12-18 13:07:35 +08:00
// 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();
}
}