This commit is contained in:
2025-12-18 13:07:35 +08:00
commit 76b7f838db
271 changed files with 88812 additions and 0 deletions

View File

@@ -0,0 +1,44 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
import {CommonBase} from "../src/Base.sol";
import {StdConstants} from "../src/StdConstants.sol";
import {Test} from "../src/Test.sol";
contract CommonBaseTest is Test {
function testVmAddressValue() public pure {
assertEq(VM_ADDRESS, address(StdConstants.VM));
}
function testConsoleValue() public pure {
assertEq(CONSOLE, StdConstants.CONSOLE);
}
function testCreate2FactoryValue() public pure {
assertEq(CREATE2_FACTORY, StdConstants.CREATE2_FACTORY);
}
function testDefaultSenderValue() public pure {
assertEq(DEFAULT_SENDER, StdConstants.DEFAULT_SENDER);
}
function testDefaultTestContractValue() public pure {
assertEq(DEFAULT_TEST_CONTRACT, StdConstants.DEFAULT_TEST_CONTRACT);
}
function testMulticall3AddressValue() public pure {
assertEq(MULTICALL3_ADDRESS, address(StdConstants.MULTICALL3_ADDRESS));
}
function testSecp256k1OrderValue() public pure {
assertEq(SECP256K1_ORDER, StdConstants.SECP256K1_ORDER);
}
function testUint256MaxValue() public pure {
assertEq(UINT256_MAX, type(uint256).max);
}
function testVmValue() public pure {
assertEq(address(vm), address(StdConstants.VM));
}
}

View File

@@ -0,0 +1,352 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;
import {Test} from "../src/Test.sol";
import {Config} from "../src/Config.sol";
import {StdConfig} from "../src/StdConfig.sol";
contract ConfigTest is Test, Config {
function setUp() public {
vm.setEnv("MAINNET_RPC", "https://reth-ethereum.ithaca.xyz/rpc");
vm.setEnv("WETH_MAINNET", "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2");
vm.setEnv("OPTIMISM_RPC", "https://mainnet.optimism.io");
vm.setEnv("WETH_OPTIMISM", "0x4200000000000000000000000000000000000006");
}
function test_loadConfig() public {
// Deploy the config contract with the test fixture.
_loadConfig("./test/fixtures/config.toml", false);
// -- MAINNET --------------------------------------------------------------
// Read and assert RPC URL for Mainnet (chain ID 1)
assertEq(config.getRpcUrl(1), "https://reth-ethereum.ithaca.xyz/rpc");
// Read and assert boolean values
assertTrue(config.get(1, "is_live").toBool());
bool[] memory bool_array = config.get(1, "bool_array").toBoolArray();
assertTrue(bool_array[0]);
assertFalse(bool_array[1]);
// Read and assert address values
assertEq(config.get(1, "weth").toAddress(), 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
address[] memory address_array = config.get(1, "deps").toAddressArray();
assertEq(address_array[0], 0x0000000000000000000000000000000000000000);
assertEq(address_array[1], 0x1111111111111111111111111111111111111111);
// Read and assert bytes32 values
assertEq(config.get(1, "word").toBytes32(), bytes32(uint256(1234)));
bytes32[] memory bytes32_array = config.get(1, "word_array").toBytes32Array();
assertEq(bytes32_array[0], bytes32(uint256(5678)));
assertEq(bytes32_array[1], bytes32(uint256(9999)));
// Read and assert uint values
assertEq(config.get(1, "number").toUint256(), 1234);
uint256[] memory uint_array = config.get(1, "number_array").toUint256Array();
assertEq(uint_array[0], 5678);
assertEq(uint_array[1], 9999);
// Read and assert int values
assertEq(config.get(1, "signed_number").toInt256(), -1234);
int256[] memory int_array = config.get(1, "signed_number_array").toInt256Array();
assertEq(int_array[0], -5678);
assertEq(int_array[1], 9999);
// Read and assert bytes values
assertEq(config.get(1, "b").toBytes(), hex"abcd");
bytes[] memory bytes_array = config.get(1, "b_array").toBytesArray();
assertEq(bytes_array[0], hex"dead");
assertEq(bytes_array[1], hex"beef");
// Read and assert string values
assertEq(config.get(1, "str").toString(), "foo");
string[] memory string_array = config.get(1, "str_array").toStringArray();
assertEq(string_array[0], "bar");
assertEq(string_array[1], "baz");
// -- OPTIMISM ------------------------------------------------------------
// Read and assert RPC URL for Optimism (chain ID 10)
assertEq(config.getRpcUrl(10), "https://mainnet.optimism.io");
// Read and assert boolean values
assertFalse(config.get(10, "is_live").toBool());
bool_array = config.get(10, "bool_array").toBoolArray();
assertFalse(bool_array[0]);
assertTrue(bool_array[1]);
// Read and assert address values
assertEq(config.get(10, "weth").toAddress(), 0x4200000000000000000000000000000000000006);
address_array = config.get(10, "deps").toAddressArray();
assertEq(address_array[0], 0x2222222222222222222222222222222222222222);
assertEq(address_array[1], 0x3333333333333333333333333333333333333333);
// Read and assert bytes32 values
assertEq(config.get(10, "word").toBytes32(), bytes32(uint256(9999)));
bytes32_array = config.get(10, "word_array").toBytes32Array();
assertEq(bytes32_array[0], bytes32(uint256(1234)));
assertEq(bytes32_array[1], bytes32(uint256(5678)));
// Read and assert uint values
assertEq(config.get(10, "number").toUint256(), 9999);
uint_array = config.get(10, "number_array").toUint256Array();
assertEq(uint_array[0], 1234);
assertEq(uint_array[1], 5678);
// Read and assert int values
assertEq(config.get(10, "signed_number").toInt256(), 9999);
int_array = config.get(10, "signed_number_array").toInt256Array();
assertEq(int_array[0], -1234);
assertEq(int_array[1], -5678);
// Read and assert bytes values
assertEq(config.get(10, "b").toBytes(), hex"dcba");
bytes_array = config.get(10, "b_array").toBytesArray();
assertEq(bytes_array[0], hex"c0ffee");
assertEq(bytes_array[1], hex"babe");
// Read and assert string values
assertEq(config.get(10, "str").toString(), "alice");
string_array = config.get(10, "str_array").toStringArray();
assertEq(string_array[0], "bob");
assertEq(string_array[1], "charlie");
}
function test_loadConfigAndForks() public {
_loadConfigAndForks("./test/fixtures/config.toml", false);
// assert that the map of chain id and fork ids is created and that the chain ids actually match
assertEq(forkOf[1], 0);
vm.selectFork(forkOf[1]);
assertEq(vm.getChainId(), 1);
assertEq(forkOf[10], 1);
vm.selectFork(forkOf[10]);
assertEq(vm.getChainId(), 10);
}
function test_writeConfig() public {
// Create a temporary copy of the config file to avoid modifying the original.
string memory originalConfig = "./test/fixtures/config.toml";
string memory testConfig = "./test/fixtures/config.t.toml";
vm.copyFile(originalConfig, testConfig);
// Deploy the config contract with the temporary fixture.
_loadConfig(testConfig, false);
// Enable writing to file bypassing the context check.
vm.store(address(config), bytes32(uint256(5)), bytes32(uint256(1)));
{
// Update a single boolean value and verify the change.
config.set(1, "is_live", false);
assertFalse(config.get(1, "is_live").toBool());
string memory content = vm.readFile(testConfig);
assertFalse(vm.parseTomlBool(content, "$.mainnet.bool.is_live"));
// Update a single address value and verify the change.
address new_addr = 0xDeaDbeefdEAdbeefdEadbEEFdeadbeEFdEaDbeeF;
config.set(1, "weth", new_addr);
assertEq(config.get(1, "weth").toAddress(), new_addr);
content = vm.readFile(testConfig);
assertEq(vm.parseTomlAddress(content, "$.mainnet.address.weth"), new_addr);
// Update a uint array and verify the change.
uint256[] memory new_numbers = new uint256[](3);
new_numbers[0] = 1;
new_numbers[1] = 2;
new_numbers[2] = 3;
config.set(10, "number_array", new_numbers);
uint256[] memory updated_numbers_mem = config.get(10, "number_array").toUint256Array();
assertEq(updated_numbers_mem.length, 3);
assertEq(updated_numbers_mem[0], 1);
assertEq(updated_numbers_mem[1], 2);
assertEq(updated_numbers_mem[2], 3);
content = vm.readFile(testConfig);
uint256[] memory updated_numbers_disk = vm.parseTomlUintArray(content, "$.optimism.uint.number_array");
assertEq(updated_numbers_disk.length, 3);
assertEq(updated_numbers_disk[0], 1);
assertEq(updated_numbers_disk[1], 2);
assertEq(updated_numbers_disk[2], 3);
// Update a string array and verify the change.
string[] memory new_strings = new string[](2);
new_strings[0] = "hello";
new_strings[1] = "world";
config.set(1, "str_array", new_strings);
string[] memory updated_strings_mem = config.get(1, "str_array").toStringArray();
assertEq(updated_strings_mem.length, 2);
assertEq(updated_strings_mem[0], "hello");
assertEq(updated_strings_mem[1], "world");
content = vm.readFile(testConfig);
string[] memory updated_strings_disk = vm.parseTomlStringArray(content, "$.mainnet.string.str_array");
assertEq(updated_strings_disk.length, 2);
assertEq(updated_strings_disk[0], "hello");
assertEq(updated_strings_disk[1], "world");
// Create a new uint variable and verify the change.
config.set(1, "new_uint", uint256(42));
assertEq(config.get(1, "new_uint").toUint256(), 42);
content = vm.readFile(testConfig);
assertEq(vm.parseTomlUint(content, "$.mainnet.uint.new_uint"), 42);
// Create a new int variable and verify the change.
config.set(1, "new_int", int256(-42));
assertEq(config.get(1, "new_int").toInt256(), -42);
content = vm.readFile(testConfig);
assertEq(vm.parseTomlInt(content, "$.mainnet.int.new_int"), -42);
// Create a new int array and verify the change.
int256[] memory new_ints = new int256[](2);
new_ints[0] = -100;
new_ints[1] = 200;
config.set(10, "new_ints", new_ints);
int256[] memory updated_ints_mem = config.get(10, "new_ints").toInt256Array();
assertEq(updated_ints_mem.length, 2);
assertEq(updated_ints_mem[0], -100);
assertEq(updated_ints_mem[1], 200);
content = vm.readFile(testConfig);
int256[] memory updated_ints_disk = vm.parseTomlIntArray(content, "$.optimism.int.new_ints");
assertEq(updated_ints_disk.length, 2);
assertEq(updated_ints_disk[0], -100);
assertEq(updated_ints_disk[1], 200);
// Create a new bytes32 array and verify the change.
bytes32[] memory new_words = new bytes32[](2);
new_words[0] = bytes32(uint256(0xDEAD));
new_words[1] = bytes32(uint256(0xBEEF));
config.set(10, "new_words", new_words);
bytes32[] memory updated_words_mem = config.get(10, "new_words").toBytes32Array();
assertEq(updated_words_mem.length, 2);
assertEq(updated_words_mem[0], new_words[0]);
assertEq(updated_words_mem[1], new_words[1]);
content = vm.readFile(testConfig);
bytes32[] memory updated_words_disk = vm.parseTomlBytes32Array(content, "$.optimism.bytes32.new_words");
assertEq(updated_words_disk.length, 2);
assertEq(vm.toString(updated_words_disk[0]), vm.toString(new_words[0]));
assertEq(vm.toString(updated_words_disk[1]), vm.toString(new_words[1]));
}
// Clean up the temporary file.
vm.removeFile(testConfig);
}
function test_writeUpdatesBackToFile() public {
// Create a temporary copy of the config file to avoid modifying the original.
string memory originalConfig = "./test/fixtures/config.toml";
string memory testConfig = "./test/fixtures/write_config.t.toml";
vm.copyFile(originalConfig, testConfig);
// Deploy the config contract with `writeToFile = false` (disabled).
_loadConfig(testConfig, false);
// Update a single boolean value and verify the file is NOT changed.
config.set(1, "is_live", false);
string memory content = vm.readFile(testConfig);
assertTrue(vm.parseTomlBool(content, "$.mainnet.bool.is_live"), "File should not be updated yet");
// Enable writing to file bypassing the context check.
vm.store(address(config), bytes32(uint256(5)), bytes32(uint256(1)));
// Update the value again and verify the file IS changed.
config.set(1, "is_live", false);
content = vm.readFile(testConfig);
assertFalse(vm.parseTomlBool(content, "$.mainnet.bool.is_live"), "File should be updated now");
// Disable writing to file.
config.writeUpdatesBackToFile(false);
// Update the value again and verify the file is NOT changed.
config.set(1, "is_live", true);
content = vm.readFile(testConfig);
assertFalse(vm.parseTomlBool(content, "$.mainnet.bool.is_live"), "File should not be updated again");
// Clean up the temporary file.
vm.removeFile(testConfig);
}
function testRevert_WriteToFileInForbiddenCtxt() public {
// Cannot initialize enabling writing to file unless we are in SCRIPT mode.
vm.expectRevert(StdConfig.WriteToFileInForbiddenCtxt.selector);
_loadConfig("./test/fixtures/config.toml", true);
// Initialize with `writeToFile = false`.
_loadConfig("./test/fixtures/config.toml", false);
// Cannot enable writing to file unless we are in SCRIPT mode.
vm.expectRevert(StdConfig.WriteToFileInForbiddenCtxt.selector);
config.writeUpdatesBackToFile(true);
}
function testRevert_InvalidChainKey() public {
// Create a fixture with an invalid chain key
string memory invalidChainConfig = "./test/fixtures/config_invalid_chain.toml";
vm.writeFile(
invalidChainConfig,
string.concat(
"[mainnet]\n",
"endpoint_url = \"https://reth-ethereum.ithaca.xyz/rpc\"\n",
"\n",
"[mainnet.uint]\n",
"valid_number = 123\n",
"\n",
"# Invalid chain key (not a number and not a valid alias)\n",
"[invalid_chain]\n",
"endpoint_url = \"https://invalid.com\"\n",
"\n",
"[invalid_chain_9999.uint]\n",
"some_value = 456\n"
)
);
vm.expectRevert(abi.encodeWithSelector(StdConfig.InvalidChainKey.selector, "invalid_chain"));
new StdConfig(invalidChainConfig, false);
vm.removeFile(invalidChainConfig);
}
function testRevert_ChainNotInitialized() public {
_loadConfig("./test/fixtures/config.toml", false);
// Enable writing to file bypassing the context check.
vm.store(address(config), bytes32(uint256(5)), bytes32(uint256(1)));
// Try to write a value for a non-existent chain ID
vm.expectRevert(abi.encodeWithSelector(StdConfig.ChainNotInitialized.selector, uint256(999999)));
config.set(999999, "some_key", uint256(123));
}
function testRevert_UnableToParseVariable() public {
// Create a temporary fixture with an unparsable variable
string memory badParseConfig = "./test/fixtures/config_bad_parse.toml";
vm.writeFile(
badParseConfig,
string.concat(
"[mainnet]\n",
"endpoint_url = \"https://reth-ethereum.ithaca.xyz/rpc\"\n",
"\n",
"[mainnet.uint]\n",
"bad_value = \"not_a_number\"\n"
)
);
vm.expectRevert(abi.encodeWithSelector(StdConfig.UnableToParseVariable.selector, "bad_value"));
new StdConfig(badParseConfig, false);
vm.removeFile(badParseConfig);
}
}

View File

@@ -0,0 +1,434 @@
// 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();
}
}

View File

@@ -0,0 +1,141 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
import {StdAssertions} from "../src/StdAssertions.sol";
import {Vm} from "../src/Vm.sol";
interface VmInternal is Vm {
function _expectCheatcodeRevert(bytes memory message) external;
}
contract StdAssertionsTest is StdAssertions {
string constant errorMessage = "User provided message";
uint256 constant maxDecimals = 77;
bool constant SHOULD_REVERT = true;
bool constant SHOULD_RETURN = false;
bool constant STRICT_REVERT_DATA = true;
bool constant NON_STRICT_REVERT_DATA = false;
VmInternal constant vm = VmInternal(address(uint160(uint256(keccak256("hevm cheat code")))));
function testFuzz_AssertEqCall_Return_Pass(
bytes memory callDataA,
bytes memory callDataB,
bytes memory returnData,
bool strictRevertData
) external {
address targetA = address(new TestMockCall(returnData, SHOULD_RETURN));
address targetB = address(new TestMockCall(returnData, SHOULD_RETURN));
assertEqCall(targetA, callDataA, targetB, callDataB, strictRevertData);
}
function testFuzz_RevertWhenCalled_AssertEqCall_Return_Fail(
bytes memory callDataA,
bytes memory callDataB,
bytes memory returnDataA,
bytes memory returnDataB,
bool strictRevertData
) external {
vm.assume(keccak256(returnDataA) != keccak256(returnDataB));
address targetA = address(new TestMockCall(returnDataA, SHOULD_RETURN));
address targetB = address(new TestMockCall(returnDataB, SHOULD_RETURN));
vm._expectCheatcodeRevert(
bytes(
string.concat(
"Call return data does not match: ", vm.toString(returnDataA), " != ", vm.toString(returnDataB)
)
)
);
assertEqCall(targetA, callDataA, targetB, callDataB, strictRevertData);
}
function testFuzz_AssertEqCall_Revert_Pass(
bytes memory callDataA,
bytes memory callDataB,
bytes memory revertDataA,
bytes memory revertDataB
) external {
address targetA = address(new TestMockCall(revertDataA, SHOULD_REVERT));
address targetB = address(new TestMockCall(revertDataB, SHOULD_REVERT));
assertEqCall(targetA, callDataA, targetB, callDataB, NON_STRICT_REVERT_DATA);
}
function testFuzz_RevertWhenCalled_AssertEqCall_Revert_Fail(
bytes memory callDataA,
bytes memory callDataB,
bytes memory revertDataA,
bytes memory revertDataB
) external {
vm.assume(keccak256(revertDataA) != keccak256(revertDataB));
address targetA = address(new TestMockCall(revertDataA, SHOULD_REVERT));
address targetB = address(new TestMockCall(revertDataB, SHOULD_REVERT));
vm._expectCheatcodeRevert(
bytes(
string.concat(
"Call revert data does not match: ", vm.toString(revertDataA), " != ", vm.toString(revertDataB)
)
)
);
assertEqCall(targetA, callDataA, targetB, callDataB, STRICT_REVERT_DATA);
}
function testFuzz_RevertWhenCalled_AssertEqCall_Fail(
bytes memory callDataA,
bytes memory callDataB,
bytes memory returnDataA,
bytes memory returnDataB,
bool strictRevertData
) external {
address targetA = address(new TestMockCall(returnDataA, SHOULD_RETURN));
address targetB = address(new TestMockCall(returnDataB, SHOULD_REVERT));
vm.expectRevert(bytes("assertion failed"));
this.assertEqCallExternal(targetA, callDataA, targetB, callDataB, strictRevertData);
vm.expectRevert(bytes("assertion failed"));
this.assertEqCallExternal(targetB, callDataB, targetA, callDataA, strictRevertData);
}
// Helper function to test outcome of assertEqCall via `expect` cheatcodes
function assertEqCallExternal(
address targetA,
bytes memory callDataA,
address targetB,
bytes memory callDataB,
bool strictRevertData
) public {
assertEqCall(targetA, callDataA, targetB, callDataB, strictRevertData);
}
}
contract TestMockCall {
bytes returnData;
bool shouldRevert;
constructor(bytes memory returnData_, bool shouldRevert_) {
returnData = returnData_;
shouldRevert = shouldRevert_;
}
fallback() external payable {
bytes memory returnData_ = returnData;
if (shouldRevert) {
assembly {
revert(add(returnData_, 0x20), mload(returnData_))
}
} else {
assembly {
return(add(returnData_, 0x20), mload(returnData_))
}
}
}
}

View File

@@ -0,0 +1,227 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
import {Test} from "../src/Test.sol";
contract StdChainsMock is Test {
function exposedGetChain(string memory chainAlias) public returns (Chain memory) {
return getChain(chainAlias);
}
function exposedGetChain(uint256 chainId) public returns (Chain memory) {
return getChain(chainId);
}
function exposedSetChain(string memory chainAlias, ChainData memory chainData) public {
setChain(chainAlias, chainData);
}
function exposedSetFallbackToDefaultRpcUrls(bool useDefault) public {
setFallbackToDefaultRpcUrls(useDefault);
}
}
contract StdChainsTest is Test {
function test_ChainRpcInitialization() public {
// RPCs specified in `foundry.toml` should be updated.
assertEq(getChain(1).rpcUrl, "https://reth-ethereum.ithaca.xyz/rpc");
assertEq(getChain("optimism_sepolia").rpcUrl, "https://sepolia.optimism.io/");
assertEq(getChain("arbitrum_one_sepolia").rpcUrl, "https://sepolia-rollup.arbitrum.io/rpc/");
// Environment variables should be the next fallback
assertEq(getChain("arbitrum_nova").rpcUrl, "https://nova.arbitrum.io/rpc");
vm.setEnv("ARBITRUM_NOVA_RPC_URL", "myoverride");
assertEq(getChain("arbitrum_nova").rpcUrl, "myoverride");
vm.setEnv("ARBITRUM_NOVA_RPC_URL", "https://nova.arbitrum.io/rpc");
// Cannot override RPCs defined in `foundry.toml`
vm.setEnv("MAINNET_RPC_URL", "myoverride2");
assertEq(getChain("mainnet").rpcUrl, "https://reth-ethereum.ithaca.xyz/rpc");
// Other RPCs should remain unchanged.
assertEq(getChain(31337).rpcUrl, "http://127.0.0.1:8545");
assertEq(getChain("sepolia").rpcUrl, "https://sepolia.infura.io/v3/b9794ad1ddf84dfb8c34d6bb5dca2001");
}
// Named with a leading underscore to clarify this is not intended to be run as a normal test,
// and is intended to be used in the below `test_Rpcs` test.
function _testRpc(string memory rpcAlias) internal {
string memory rpcUrl = getChain(rpcAlias).rpcUrl;
vm.createSelectFork(rpcUrl);
}
// Ensure we can connect to the default RPC URL for each chain.
// Currently commented out since this is slow and public RPCs are flaky, often resulting in failing CI.
// function test_Rpcs() public {
// _testRpc("mainnet");
// _testRpc("sepolia");
// _testRpc("holesky");
// _testRpc("optimism");
// _testRpc("optimism_sepolia");
// _testRpc("arbitrum_one");
// _testRpc("arbitrum_one_sepolia");
// _testRpc("arbitrum_nova");
// _testRpc("polygon");
// _testRpc("polygon_amoy");
// _testRpc("avalanche");
// _testRpc("avalanche_fuji");
// _testRpc("bnb_smart_chain");
// _testRpc("bnb_smart_chain_testnet");
// _testRpc("gnosis_chain");
// _testRpc("moonbeam");
// _testRpc("moonriver");
// _testRpc("moonbase");
// _testRpc("base_sepolia");
// _testRpc("base");
// _testRpc("blast_sepolia");
// _testRpc("blast");
// _testRpc("fantom_opera");
// _testRpc("fantom_opera_testnet");
// _testRpc("fraxtal");
// _testRpc("fraxtal_testnet");
// _testRpc("berachain_bartio_testnet");
// _testRpc("flare");
// _testRpc("flare_coston2");
// }
function test_RevertIf_ChainNotFound() public {
// We deploy a mock to properly test the revert.
StdChainsMock stdChainsMock = new StdChainsMock();
vm.expectRevert("StdChains getChain(string): Chain with alias \"does_not_exist\" not found.");
stdChainsMock.exposedGetChain("does_not_exist");
}
function test_RevertIf_SetChain_ChainIdExist_FirstTest() public {
// We deploy a mock to properly test the revert.
StdChainsMock stdChainsMock = new StdChainsMock();
vm.expectRevert("StdChains setChain(string,ChainData): Chain ID 31337 already used by \"anvil\".");
stdChainsMock.exposedSetChain("anvil2", ChainData("Anvil", 31337, "URL"));
}
function test_RevertIf_ChainBubbleUp() public {
// We deploy a mock to properly test the revert.
StdChainsMock stdChainsMock = new StdChainsMock();
stdChainsMock.exposedSetChain("needs_undefined_env_var", ChainData("", 123456789, ""));
// Forge environment variable error.
vm.expectRevert();
stdChainsMock.exposedGetChain("needs_undefined_env_var");
}
function test_RevertIf_SetChain_ChainIdExists_SecondTest() public {
// We deploy a mock to properly test the revert.
StdChainsMock stdChainsMock = new StdChainsMock();
stdChainsMock.exposedSetChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/"));
vm.expectRevert('StdChains setChain(string,ChainData): Chain ID 123456789 already used by "custom_chain".');
stdChainsMock.exposedSetChain("another_custom_chain", ChainData("", 123456789, ""));
}
function test_SetChain() public {
setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/"));
Chain memory customChain = getChain("custom_chain");
assertEq(customChain.name, "Custom Chain");
assertEq(customChain.chainId, 123456789);
assertEq(customChain.chainAlias, "custom_chain");
assertEq(customChain.rpcUrl, "https://custom.chain/");
Chain memory chainById = getChain(123456789);
assertEq(chainById.name, customChain.name);
assertEq(chainById.chainId, customChain.chainId);
assertEq(chainById.chainAlias, customChain.chainAlias);
assertEq(chainById.rpcUrl, customChain.rpcUrl);
customChain.name = "Another Custom Chain";
customChain.chainId = 987654321;
setChain("another_custom_chain", customChain);
Chain memory anotherCustomChain = getChain("another_custom_chain");
assertEq(anotherCustomChain.name, "Another Custom Chain");
assertEq(anotherCustomChain.chainId, 987654321);
assertEq(anotherCustomChain.chainAlias, "another_custom_chain");
assertEq(anotherCustomChain.rpcUrl, "https://custom.chain/");
// Verify the first chain data was not overwritten
chainById = getChain(123456789);
assertEq(chainById.name, "Custom Chain");
assertEq(chainById.chainId, 123456789);
}
function test_RevertIf_SetEmptyAlias() public {
// We deploy a mock to properly test the revert.
StdChainsMock stdChainsMock = new StdChainsMock();
vm.expectRevert("StdChains setChain(string,ChainData): Chain alias cannot be the empty string.");
stdChainsMock.exposedSetChain("", ChainData("", 123456789, ""));
}
function test_RevertIf_SetNoChainId0() public {
// We deploy a mock to properly test the revert.
StdChainsMock stdChainsMock = new StdChainsMock();
vm.expectRevert("StdChains setChain(string,ChainData): Chain ID cannot be 0.");
stdChainsMock.exposedSetChain("alias", ChainData("", 0, ""));
}
function test_RevertIf_GetNoChainId0() public {
// We deploy a mock to properly test the revert.
StdChainsMock stdChainsMock = new StdChainsMock();
vm.expectRevert("StdChains getChain(uint256): Chain ID cannot be 0.");
stdChainsMock.exposedGetChain(0);
}
function test_RevertIf_GetNoEmptyAlias() public {
// We deploy a mock to properly test the revert.
StdChainsMock stdChainsMock = new StdChainsMock();
vm.expectRevert("StdChains getChain(string): Chain alias cannot be the empty string.");
stdChainsMock.exposedGetChain("");
}
function test_RevertIf_ChainNotInitialized() public {
// We deploy a mock to properly test the revert.
StdChainsMock stdChainsMock = new StdChainsMock();
vm.expectRevert("StdChains getChain(string): Chain with alias \"no_such_alias\" not found.");
stdChainsMock.exposedGetChain("no_such_alias");
}
function test_RevertIf_ChainAliasNotFound() public {
// We deploy a mock to properly test the revert.
StdChainsMock stdChainsMock = new StdChainsMock();
vm.expectRevert("StdChains getChain(uint256): Chain with ID 321 not found.");
stdChainsMock.exposedGetChain(321);
}
function test_SetChain_ExistingOne() public {
// We deploy a mock to properly test the revert.
StdChainsMock stdChainsMock = new StdChainsMock();
setChain("custom_chain", ChainData("Custom Chain", 123456789, "https://custom.chain/"));
assertEq(getChain(123456789).chainId, 123456789);
setChain("custom_chain", ChainData("Modified Chain", 9999999999999999999, "https://modified.chain/"));
vm.expectRevert("StdChains getChain(uint256): Chain with ID 123456789 not found.");
stdChainsMock.exposedGetChain(123456789);
Chain memory modifiedChain = getChain(9999999999999999999);
assertEq(modifiedChain.name, "Modified Chain");
assertEq(modifiedChain.chainId, 9999999999999999999);
assertEq(modifiedChain.rpcUrl, "https://modified.chain/");
}
function test_RevertIf_DontUseDefaultRpcUrl() public {
// We deploy a mock to properly test the revert.
StdChainsMock stdChainsMock = new StdChainsMock();
// Should error if default RPCs flag is set to false.
stdChainsMock.exposedSetFallbackToDefaultRpcUrls(false);
vm.expectRevert();
stdChainsMock.exposedGetChain(31337);
vm.expectRevert();
stdChainsMock.exposedGetChain("sepolia");
}
}

View File

@@ -0,0 +1,639 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
import {StdCheats} from "../src/StdCheats.sol";
import {Test} from "../src/Test.sol";
import {stdJson} from "../src/StdJson.sol";
import {stdToml} from "../src/StdToml.sol";
import {IERC20} from "../src/interfaces/IERC20.sol";
contract StdCheatsTest is Test {
Bar test;
using stdJson for string;
function setUp() public {
test = new Bar();
}
function test_Skip() public {
vm.warp(100);
skip(25);
assertEq(block.timestamp, 125);
}
function test_Rewind() public {
vm.warp(100);
rewind(25);
assertEq(block.timestamp, 75);
}
function test_Hoax() public {
hoax(address(1337));
test.bar{value: 100}(address(1337));
}
function test_HoaxOrigin() public {
hoax(address(1337), address(1337));
test.origin{value: 100}(address(1337));
}
function test_HoaxDifferentAddresses() public {
hoax(address(1337), address(7331));
test.origin{value: 100}(address(1337), address(7331));
}
function test_StartHoax() public {
startHoax(address(1337));
test.bar{value: 100}(address(1337));
test.bar{value: 100}(address(1337));
vm.stopPrank();
test.bar(address(this));
}
function test_StartHoaxOrigin() public {
startHoax(address(1337), address(1337));
test.origin{value: 100}(address(1337));
test.origin{value: 100}(address(1337));
vm.stopPrank();
test.bar(address(this));
}
function test_ChangePrankMsgSender() public {
vm.startPrank(address(1337));
test.bar(address(1337));
changePrank(address(0xdead));
test.bar(address(0xdead));
changePrank(address(1337));
test.bar(address(1337));
vm.stopPrank();
}
function test_ChangePrankMsgSenderAndTxOrigin() public {
vm.startPrank(address(1337), address(1338));
test.origin(address(1337), address(1338));
changePrank(address(0xdead), address(0xbeef));
test.origin(address(0xdead), address(0xbeef));
changePrank(address(1337), address(1338));
test.origin(address(1337), address(1338));
vm.stopPrank();
}
function test_MakeAccountEquivalence() public {
Account memory account = makeAccount("1337");
(address addr, uint256 key) = makeAddrAndKey("1337");
assertEq(account.addr, addr);
assertEq(account.key, key);
}
function test_MakeAddrEquivalence() public {
(address addr,) = makeAddrAndKey("1337");
assertEq(makeAddr("1337"), addr);
}
function test_MakeAddrSigning() public {
(address addr, uint256 key) = makeAddrAndKey("1337");
bytes32 hash = keccak256("some_message");
(uint8 v, bytes32 r, bytes32 s) = vm.sign(key, hash);
assertEq(ecrecover(hash, v, r, s), addr);
}
function test_Deal() public {
deal(address(this), 1 ether);
assertEq(address(this).balance, 1 ether);
}
function test_DealToken() public {
Bar barToken = new Bar();
address bar = address(barToken);
deal(bar, address(this), 10000e18);
assertEq(barToken.balanceOf(address(this)), 10000e18);
}
function test_DealTokenAdjustTotalSupply() public {
Bar barToken = new Bar();
address bar = address(barToken);
deal(bar, address(this), 10000e18, true);
assertEq(barToken.balanceOf(address(this)), 10000e18);
assertEq(barToken.totalSupply(), 20000e18);
deal(bar, address(this), 0, true);
assertEq(barToken.balanceOf(address(this)), 0);
assertEq(barToken.totalSupply(), 10000e18);
}
function test_DealERC1155Token() public {
BarERC1155 barToken = new BarERC1155();
address bar = address(barToken);
dealERC1155(bar, address(this), 0, 10000e18, false);
assertEq(barToken.balanceOf(address(this), 0), 10000e18);
}
function test_DealERC1155TokenAdjustTotalSupply() public {
BarERC1155 barToken = new BarERC1155();
address bar = address(barToken);
dealERC1155(bar, address(this), 0, 10000e18, true);
assertEq(barToken.balanceOf(address(this), 0), 10000e18);
assertEq(barToken.totalSupply(0), 20000e18);
dealERC1155(bar, address(this), 0, 0, true);
assertEq(barToken.balanceOf(address(this), 0), 0);
assertEq(barToken.totalSupply(0), 10000e18);
}
function test_DealERC721Token() public {
BarERC721 barToken = new BarERC721();
address bar = address(barToken);
dealERC721(bar, address(2), 1);
assertEq(barToken.balanceOf(address(2)), 1);
assertEq(barToken.balanceOf(address(1)), 0);
dealERC721(bar, address(1), 2);
assertEq(barToken.balanceOf(address(1)), 1);
assertEq(barToken.balanceOf(bar), 1);
}
function test_DeployCode() public {
address deployed = deployCode("StdCheats.t.sol:Bar", bytes(""));
assertEq(string(getCode(deployed)), string(getCode(address(test))));
}
function test_DestroyAccount() public {
// deploy something to destroy it
BarERC721 barToken = new BarERC721();
address bar = address(barToken);
vm.setNonce(bar, 10);
deal(bar, 100);
uint256 prevThisBalance = address(this).balance;
uint256 size;
assembly {
size := extcodesize(bar)
}
assertGt(size, 0);
assertEq(bar.balance, 100);
assertEq(vm.getNonce(bar), 10);
destroyAccount(bar, address(this));
assembly {
size := extcodesize(bar)
}
assertEq(address(this).balance, prevThisBalance + 100);
assertEq(vm.getNonce(bar), 0);
assertEq(size, 0);
assertEq(bar.balance, 0);
}
function test_DeployCodeNoArgs() public {
address deployed = deployCode("StdCheats.t.sol:Bar");
assertEq(string(getCode(deployed)), string(getCode(address(test))));
}
function test_DeployCodeVal() public {
address deployed = deployCode("StdCheats.t.sol:Bar", bytes(""), 1 ether);
assertEq(string(getCode(deployed)), string(getCode(address(test))));
assertEq(deployed.balance, 1 ether);
}
function test_DeployCodeValNoArgs() public {
address deployed = deployCode("StdCheats.t.sol:Bar", 1 ether);
assertEq(string(getCode(deployed)), string(getCode(address(test))));
assertEq(deployed.balance, 1 ether);
}
// We need this so we can call "this.deployCode" rather than "deployCode" directly
function deployCodeHelper(string memory what) external {
deployCode(what);
}
function test_RevertIf_DeployCodeFail() public {
vm.expectRevert(bytes("StdCheats deployCode(string): Deployment failed."));
this.deployCodeHelper("StdCheats.t.sol:RevertingContract");
}
function getCode(address who) internal view returns (bytes memory o_code) {
/// @solidity memory-safe-assembly
assembly {
// retrieve the size of the code, this needs assembly
let size := extcodesize(who)
// allocate output byte array - this could also be done without assembly
// by using o_code = new bytes(size)
o_code := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(o_code, and(add(add(size, 0x20), 0x1f), not(0x1f))))
// store length in memory
mstore(o_code, size)
// actually retrieve the code, this needs assembly
extcodecopy(who, add(o_code, 0x20), 0, size)
}
}
function test_DeriveRememberKey() public {
string memory mnemonic = "test test test test test test test test test test test junk";
(address deployer, uint256 privateKey) = deriveRememberKey(mnemonic, 0);
assertEq(deployer, 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266);
assertEq(privateKey, 0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80);
}
function test_BytesToUint() public pure {
assertEq(3, bytesToUint_test(hex"03"));
assertEq(2, bytesToUint_test(hex"02"));
assertEq(255, bytesToUint_test(hex"ff"));
assertEq(29625, bytesToUint_test(hex"73b9"));
}
function test_ParseJsonTxDetail() public view {
string memory root = vm.projectRoot();
string memory path = string.concat(root, "/test/fixtures/broadcast.log.json");
string memory json = vm.readFile(path);
bytes memory transactionDetails = json.parseRaw(".transactions[0].tx");
RawTx1559Detail memory rawTxDetail = abi.decode(transactionDetails, (RawTx1559Detail));
Tx1559Detail memory txDetail = rawToConvertedEIP1559Detail(rawTxDetail);
assertEq(txDetail.from, 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266);
assertEq(txDetail.to, 0xe7f1725E7734CE288F8367e1Bb143E90bb3F0512);
assertEq(
txDetail.data,
hex"23e99187000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000013370000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004"
);
assertEq(txDetail.nonce, 3);
assertEq(txDetail.txType, 2);
assertEq(txDetail.gas, 29625);
assertEq(txDetail.value, 0);
}
function test_ReadEIP1559Transaction() public view {
string memory root = vm.projectRoot();
string memory path = string.concat(root, "/test/fixtures/broadcast.log.json");
uint256 index = 0;
Tx1559 memory transaction = readTx1559(path, index);
transaction;
}
function test_ReadEIP1559Transactions() public view {
string memory root = vm.projectRoot();
string memory path = string.concat(root, "/test/fixtures/broadcast.log.json");
Tx1559[] memory transactions = readTx1559s(path);
transactions;
}
function test_ReadReceipt() public view {
string memory root = vm.projectRoot();
string memory path = string.concat(root, "/test/fixtures/broadcast.log.json");
uint256 index = 5;
Receipt memory receipt = readReceipt(path, index);
assertEq(
receipt.logsBloom,
hex"00000000000800000000000000000010000000000000000000000000000180000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100"
);
}
function test_ReadReceipts() public view {
string memory root = vm.projectRoot();
string memory path = string.concat(root, "/test/fixtures/broadcast.log.json");
Receipt[] memory receipts = readReceipts(path);
receipts;
}
function test_GasMeteringModifier() public {
uint256 gas_start_normal = gasleft();
addInLoop();
uint256 gas_used_normal = gas_start_normal - gasleft();
uint256 gas_start_single = gasleft();
addInLoopNoGas();
uint256 gas_used_single = gas_start_single - gasleft();
uint256 gas_start_double = gasleft();
addInLoopNoGasNoGas();
uint256 gas_used_double = gas_start_double - gasleft();
assertTrue(gas_used_double + gas_used_single < gas_used_normal);
}
function addInLoop() internal pure returns (uint256) {
uint256 b;
for (uint256 i; i < 10000; i++) {
b += i;
}
return b;
}
function addInLoopNoGas() internal noGasMetering returns (uint256) {
return addInLoop();
}
function addInLoopNoGasNoGas() internal noGasMetering returns (uint256) {
return addInLoopNoGas();
}
function bytesToUint_test(bytes memory b) private pure returns (uint256) {
uint256 number;
for (uint256 i = 0; i < b.length; i++) {
number = number + uint256(uint8(b[i])) * (2 ** (8 * (b.length - (i + 1))));
}
return number;
}
function testFuzz_AssumeAddressIsNot(address addr) external {
// skip over Payable and NonPayable enums
for (uint8 i = 2; i < uint8(type(AddressType).max); i++) {
assumeAddressIsNot(addr, AddressType(i));
}
assertTrue(addr != address(0));
assertTrue(addr < address(1) || addr > address(9));
assertTrue(addr != address(vm) || addr != 0x000000000000000000636F6e736F6c652e6c6f67);
}
function test_AssumePayable() external {
// We deploy a mock version so we can properly test the revert.
StdCheatsMock stdCheatsMock = new StdCheatsMock();
// all should revert since these addresses are not payable
// VM address
vm.expectRevert();
stdCheatsMock.exposedAssumePayable(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
// Console address
vm.expectRevert();
stdCheatsMock.exposedAssumePayable(0x000000000000000000636F6e736F6c652e6c6f67);
// Create2Deployer
vm.expectRevert();
stdCheatsMock.exposedAssumePayable(0x4e59b44847b379578588920cA78FbF26c0B4956C);
// all should pass since these addresses are payable
// vitalik.eth
stdCheatsMock.exposedAssumePayable(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045);
// mock payable contract
MockContractPayable cp = new MockContractPayable();
stdCheatsMock.exposedAssumePayable(address(cp));
}
function test_AssumeNotPayable() external {
// We deploy a mock version so we can properly test the revert.
StdCheatsMock stdCheatsMock = new StdCheatsMock();
// all should pass since these addresses are not payable
// VM address
stdCheatsMock.exposedAssumeNotPayable(0x7109709ECfa91a80626fF3989D68f67F5b1DD12D);
// Console address
stdCheatsMock.exposedAssumeNotPayable(0x000000000000000000636F6e736F6c652e6c6f67);
// Create2Deployer
stdCheatsMock.exposedAssumeNotPayable(0x4e59b44847b379578588920cA78FbF26c0B4956C);
// all should revert since these addresses are payable
// vitalik.eth
vm.expectRevert();
stdCheatsMock.exposedAssumeNotPayable(0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045);
// mock payable contract
MockContractPayable cp = new MockContractPayable();
vm.expectRevert();
stdCheatsMock.exposedAssumeNotPayable(address(cp));
}
function testFuzz_AssumeNotPrecompile(address addr) external {
assumeNotPrecompile(addr, getChain("optimism_sepolia").chainId);
assertTrue(
addr < address(1) || (addr > address(9) && addr < address(0x4200000000000000000000000000000000000000))
|| addr > address(0x4200000000000000000000000000000000000800)
);
}
function testFuzz_AssumeNotForgeAddress(address addr) external pure {
assumeNotForgeAddress(addr);
assertTrue(
addr != address(vm) && addr != 0x000000000000000000636F6e736F6c652e6c6f67
&& addr != 0x4e59b44847b379578588920cA78FbF26c0B4956C
);
}
function test_RevertIf_CannotDeployCodeTo() external {
vm.expectRevert("StdCheats deployCodeTo(string,bytes,uint256,address): Failed to create runtime bytecode.");
this._revertDeployCodeTo();
}
function _revertDeployCodeTo() external {
deployCodeTo("StdCheats.t.sol:RevertingContract", address(0));
}
function test_DeployCodeTo() external {
address arbitraryAddress = makeAddr("arbitraryAddress");
deployCodeTo(
"StdCheats.t.sol:MockContractWithConstructorArgs",
abi.encode(uint256(6), true, bytes20(arbitraryAddress)),
1 ether,
arbitraryAddress
);
MockContractWithConstructorArgs ct = MockContractWithConstructorArgs(arbitraryAddress);
assertEq(arbitraryAddress.balance, 1 ether);
assertEq(ct.x(), 6);
assertTrue(ct.y());
assertEq(ct.z(), bytes20(arbitraryAddress));
}
}
contract StdCheatsMock is StdCheats {
function exposedAssumePayable(address addr) external {
assumePayable(addr);
}
function exposedAssumeNotPayable(address addr) external {
assumeNotPayable(addr);
}
// We deploy a mock version so we can properly test expected reverts.
function exposedAssumeNotBlacklisted(address token, address addr) external view {
return assumeNotBlacklisted(token, addr);
}
}
contract StdCheatsForkTest is Test {
address internal constant USDC_BLACKLISTED_USER = 0x1E34A77868E19A6647b1f2F47B51ed72dEDE95DD;
address internal constant USDT_BLACKLISTED_USER = 0x8f8a8F4B54a2aAC7799d7bc81368aC27b852822A;
MockUSDT public USDT;
MockUSDC public USDC;
function setUp() public {
USDT = new MockUSDT();
USDC = new MockUSDC();
USDC.setBlacklisted(USDC_BLACKLISTED_USER, true);
USDT.setBlacklisted(USDT_BLACKLISTED_USER, true);
}
function test_RevertIf_CannotAssumeNoBlacklisted_EOA() external {
// We deploy a mock version so we can properly test the revert.
StdCheatsMock stdCheatsMock = new StdCheatsMock();
address eoa = vm.addr({privateKey: 1});
vm.expectRevert("StdCheats assumeNotBlacklisted(address,address): Token address is not a contract.");
stdCheatsMock.exposedAssumeNotBlacklisted(eoa, address(0));
}
function testFuzz_AssumeNotBlacklisted_TokenWithoutBlacklist(address addr) external view {
assumeNotBlacklisted(address(USDC), addr);
assumeNotBlacklisted(address(USDT), addr);
assertTrue(true);
}
function test_RevertIf_AssumeNoBlacklisted_USDC() external {
// We deploy a mock version so we can properly test the revert.
StdCheatsMock stdCheatsMock = new StdCheatsMock();
vm.expectRevert();
stdCheatsMock.exposedAssumeNotBlacklisted(address(USDC), USDC_BLACKLISTED_USER);
}
function testFuzz_AssumeNotBlacklisted_USDC(address addr) external view {
assumeNotBlacklisted(address(USDC), addr);
assertFalse(USDCLike(USDC).isBlacklisted(addr));
}
function test_RevertIf_AssumeNoBlacklisted_USDT() external {
// We deploy a mock version so we can properly test the revert.
StdCheatsMock stdCheatsMock = new StdCheatsMock();
vm.expectRevert();
stdCheatsMock.exposedAssumeNotBlacklisted(address(USDT), USDT_BLACKLISTED_USER);
}
function testFuzz_AssumeNotBlacklisted_USDT(address addr) external view {
assumeNotBlacklisted(address(USDT), addr);
assertFalse(USDTLike(USDT).isBlackListed(addr));
}
}
/// @dev https://etherscan.io/token/0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48#readProxyContract
interface USDCLike {
function isBlacklisted(address) external view returns (bool);
}
/// @dev https://etherscan.io/token/0xdac17f958d2ee523a2206206994597c13d831ec7#readContract
interface USDTLike {
function isBlackListed(address) external view returns (bool);
}
contract MockUSDT is USDTLike {
mapping(address => bool) private blacklist;
function isBlackListed(address addr) external view returns (bool) {
return blacklist[addr];
}
function setBlacklisted(address addr, bool value) external {
blacklist[addr] = value;
}
}
contract MockUSDC is USDCLike {
mapping(address => bool) private blacklist;
function isBlacklisted(address addr) external view returns (bool) {
return blacklist[addr];
}
function setBlacklisted(address addr, bool value) external {
blacklist[addr] = value;
}
}
contract Bar {
constructor() payable {
/// `DEAL` STDCHEAT
totalSupply = 10000e18;
balanceOf[address(this)] = totalSupply;
}
/// `HOAX` and `CHANGEPRANK` STDCHEATS
function bar(address expectedSender) public payable {
require(msg.sender == expectedSender, "!prank");
}
function origin(address expectedSender) public payable {
require(msg.sender == expectedSender, "!prank");
require(tx.origin == expectedSender, "!prank");
}
function origin(address expectedSender, address expectedOrigin) public payable {
require(msg.sender == expectedSender, "!prank");
require(tx.origin == expectedOrigin, "!prank");
}
/// `DEAL` STDCHEAT
mapping(address => uint256) public balanceOf;
uint256 public totalSupply;
}
contract BarERC1155 {
constructor() payable {
/// `DEALERC1155` STDCHEAT
_totalSupply[0] = 10000e18;
_balances[0][address(this)] = _totalSupply[0];
}
function balanceOf(address account, uint256 id) public view virtual returns (uint256) {
return _balances[id][account];
}
function totalSupply(uint256 id) public view virtual returns (uint256) {
return _totalSupply[id];
}
/// `DEALERC1155` STDCHEAT
mapping(uint256 => mapping(address => uint256)) private _balances;
mapping(uint256 => uint256) private _totalSupply;
}
contract BarERC721 {
constructor() payable {
/// `DEALERC721` STDCHEAT
_owners[1] = address(1);
_balances[address(1)] = 1;
_owners[2] = address(this);
_owners[3] = address(this);
_balances[address(this)] = 2;
}
function balanceOf(address owner) public view virtual returns (uint256) {
return _balances[owner];
}
function ownerOf(uint256 tokenId) public view virtual returns (address) {
address owner = _owners[tokenId];
return owner;
}
mapping(uint256 => address) private _owners;
mapping(address => uint256) private _balances;
}
contract RevertingContract {
constructor() {
revert();
}
}
contract MockContractWithConstructorArgs {
uint256 public immutable x;
bool public y;
bytes20 public z;
constructor(uint256 _x, bool _y, bytes20 _z) payable {
x = _x;
y = _y;
z = _z;
}
}
contract MockContractPayable {
receive() external payable {}
}

View File

@@ -0,0 +1,38 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
import {StdConstants} from "../src/StdConstants.sol";
import {Test} from "../src/Test.sol";
contract StdConstantsTest is Test {
function testVm() public view {
assertEq(StdConstants.VM.getBlockNumber(), 1);
}
function testVmDerivation() public pure {
assertEq(address(StdConstants.VM), address(uint160(uint256(keccak256("hevm cheat code")))));
}
function testConsoleDerivation() public pure {
assertEq(StdConstants.CONSOLE, address(uint160(uint88(bytes11("console.log")))));
}
function testDefaultSender() public view {
assertEq(StdConstants.DEFAULT_SENDER, msg.sender);
}
function testDefaultSenderDerivation() public pure {
assertEq(StdConstants.DEFAULT_SENDER, address(uint160(uint256(keccak256("foundry default caller")))));
}
function testDefaultTestContract() public {
assertEq(StdConstants.DEFAULT_TEST_CONTRACT, address(new Dummy()));
}
function testDefaultTestContractDerivation() public view {
assertEq(address(this), StdConstants.VM.computeCreateAddress(StdConstants.DEFAULT_SENDER, 1));
assertEq(StdConstants.DEFAULT_TEST_CONTRACT, StdConstants.VM.computeCreateAddress(address(this), 1));
}
}
contract Dummy {}

View File

@@ -0,0 +1,120 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import {stdError} from "../src/StdError.sol";
import {Test} from "../src/Test.sol";
contract StdErrorsTest is Test {
ErrorsTest test;
function setUp() public {
test = new ErrorsTest();
}
function test_RevertIf_AssertionError() public {
vm.expectRevert(stdError.assertionError);
test.assertionError();
}
function test_RevertIf_ArithmeticError() public {
vm.expectRevert(stdError.arithmeticError);
test.arithmeticError(10);
}
function test_RevertIf_DivisionError() public {
vm.expectRevert(stdError.divisionError);
test.divError(0);
}
function test_RevertIf_ModError() public {
vm.expectRevert(stdError.divisionError);
test.modError(0);
}
function test_RevertIf_EnumConversionError() public {
vm.expectRevert(stdError.enumConversionError);
test.enumConversion(1);
}
function test_RevertIf_EncodeStgError() public {
vm.expectRevert(stdError.encodeStorageError);
test.encodeStgError();
}
function test_RevertIf_PopError() public {
vm.expectRevert(stdError.popError);
test.pop();
}
function test_RevertIf_IndexOOBError() public {
vm.expectRevert(stdError.indexOOBError);
test.indexOOBError(1);
}
function test_RevertIf_MemOverflowError() public {
vm.expectRevert(stdError.memOverflowError);
test.mem();
}
function test_RevertIf_InternError() public {
vm.expectRevert(stdError.zeroVarError);
test.intern();
}
}
contract ErrorsTest {
enum T {
T1
}
uint256[] public someArr;
bytes someBytes;
function assertionError() public pure {
assert(false);
}
function arithmeticError(uint256 a) public pure {
a -= 100;
}
function divError(uint256 a) public pure {
100 / a;
}
function modError(uint256 a) public pure {
100 % a;
}
function enumConversion(uint256 a) public pure {
T(a);
}
function encodeStgError() public {
/// @solidity memory-safe-assembly
assembly {
sstore(someBytes.slot, 1)
}
keccak256(someBytes);
}
function pop() public {
someArr.pop();
}
function indexOOBError(uint256 a) public pure {
uint256[] memory t = new uint256[](0);
t[a];
}
function mem() public pure {
uint256 l = 2 ** 256 / 32;
new uint256[](l);
}
function intern() public returns (uint256) {
function(uint256) internal returns (uint256) x;
x(2);
return 7;
}
}

View File

@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
import {Test, stdJson} from "../src/Test.sol";
contract StdJsonTest is Test {
using stdJson for string;
string root;
string path;
function setUp() public {
root = vm.projectRoot();
path = string.concat(root, "/test/fixtures/test.json");
}
struct SimpleJson {
uint256 a;
string b;
}
struct NestedJson {
uint256 a;
string b;
SimpleJson c;
}
function test_readJson() public view {
string memory json = vm.readFile(path);
assertEq(json.readUint(".a"), 123);
}
function test_writeJson() public {
string memory json = "json";
json.serialize("a", uint256(123));
string memory semiFinal = json.serialize("b", string("test"));
string memory finalJson = json.serialize("c", semiFinal);
finalJson.write(path);
string memory json_ = vm.readFile(path);
bytes memory data = json_.parseRaw("$");
NestedJson memory decodedData = abi.decode(data, (NestedJson));
assertEq(decodedData.a, 123);
assertEq(decodedData.b, "test");
assertEq(decodedData.c.a, 123);
assertEq(decodedData.c.b, "test");
}
}

View File

@@ -0,0 +1,202 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import {stdMath} from "../src/StdMath.sol";
import {Test, stdError} from "../src/Test.sol";
contract StdMathMock is Test {
function exposedPercentDelta(uint256 a, uint256 b) public pure returns (uint256) {
return stdMath.percentDelta(a, b);
}
function exposedPercentDelta(int256 a, int256 b) public pure returns (uint256) {
return stdMath.percentDelta(a, b);
}
}
contract StdMathTest is Test {
function test_GetAbs() external pure {
assertEq(stdMath.abs(-50), 50);
assertEq(stdMath.abs(50), 50);
assertEq(stdMath.abs(-1337), 1337);
assertEq(stdMath.abs(0), 0);
assertEq(stdMath.abs(type(int256).min), (type(uint256).max >> 1) + 1);
assertEq(stdMath.abs(type(int256).max), (type(uint256).max >> 1));
}
function testFuzz_GetAbs(int256 a) external pure {
uint256 manualAbs = getAbs(a);
uint256 abs = stdMath.abs(a);
assertEq(abs, manualAbs);
}
function test_GetDelta_Uint() external pure {
assertEq(stdMath.delta(uint256(0), uint256(0)), 0);
assertEq(stdMath.delta(uint256(0), uint256(1337)), 1337);
assertEq(stdMath.delta(uint256(0), type(uint64).max), type(uint64).max);
assertEq(stdMath.delta(uint256(0), type(uint128).max), type(uint128).max);
assertEq(stdMath.delta(uint256(0), type(uint256).max), type(uint256).max);
assertEq(stdMath.delta(0, uint256(0)), 0);
assertEq(stdMath.delta(1337, uint256(0)), 1337);
assertEq(stdMath.delta(type(uint64).max, uint256(0)), type(uint64).max);
assertEq(stdMath.delta(type(uint128).max, uint256(0)), type(uint128).max);
assertEq(stdMath.delta(type(uint256).max, uint256(0)), type(uint256).max);
assertEq(stdMath.delta(1337, uint256(1337)), 0);
assertEq(stdMath.delta(type(uint256).max, type(uint256).max), 0);
assertEq(stdMath.delta(5000, uint256(1250)), 3750);
}
function testFuzz_GetDelta_Uint(uint256 a, uint256 b) external pure {
uint256 manualDelta = a > b ? a - b : b - a;
uint256 delta = stdMath.delta(a, b);
assertEq(delta, manualDelta);
}
function test_GetDelta_Int() external pure {
assertEq(stdMath.delta(int256(0), int256(0)), 0);
assertEq(stdMath.delta(int256(0), int256(1337)), 1337);
assertEq(stdMath.delta(int256(0), type(int64).max), type(uint64).max >> 1);
assertEq(stdMath.delta(int256(0), type(int128).max), type(uint128).max >> 1);
assertEq(stdMath.delta(int256(0), type(int256).max), type(uint256).max >> 1);
assertEq(stdMath.delta(0, int256(0)), 0);
assertEq(stdMath.delta(1337, int256(0)), 1337);
assertEq(stdMath.delta(type(int64).max, int256(0)), type(uint64).max >> 1);
assertEq(stdMath.delta(type(int128).max, int256(0)), type(uint128).max >> 1);
assertEq(stdMath.delta(type(int256).max, int256(0)), type(uint256).max >> 1);
assertEq(stdMath.delta(-0, int256(0)), 0);
assertEq(stdMath.delta(-1337, int256(0)), 1337);
assertEq(stdMath.delta(type(int64).min, int256(0)), (type(uint64).max >> 1) + 1);
assertEq(stdMath.delta(type(int128).min, int256(0)), (type(uint128).max >> 1) + 1);
assertEq(stdMath.delta(type(int256).min, int256(0)), (type(uint256).max >> 1) + 1);
assertEq(stdMath.delta(int256(0), -0), 0);
assertEq(stdMath.delta(int256(0), -1337), 1337);
assertEq(stdMath.delta(int256(0), type(int64).min), (type(uint64).max >> 1) + 1);
assertEq(stdMath.delta(int256(0), type(int128).min), (type(uint128).max >> 1) + 1);
assertEq(stdMath.delta(int256(0), type(int256).min), (type(uint256).max >> 1) + 1);
assertEq(stdMath.delta(1337, int256(1337)), 0);
assertEq(stdMath.delta(type(int256).max, type(int256).max), 0);
assertEq(stdMath.delta(type(int256).min, type(int256).min), 0);
assertEq(stdMath.delta(type(int256).min, type(int256).max), type(uint256).max);
assertEq(stdMath.delta(5000, int256(1250)), 3750);
}
function testFuzz_GetDelta_Int(int256 a, int256 b) external pure {
uint256 absA = getAbs(a);
uint256 absB = getAbs(b);
uint256 absDelta = absA > absB ? absA - absB : absB - absA;
uint256 manualDelta;
if ((a >= 0 && b >= 0) || (a < 0 && b < 0)) {
manualDelta = absDelta;
}
// (a < 0 && b >= 0) || (a >= 0 && b < 0)
else {
manualDelta = absA + absB;
}
uint256 delta = stdMath.delta(a, b);
assertEq(delta, manualDelta);
}
function test_GetPercentDelta_Uint() external {
StdMathMock stdMathMock = new StdMathMock();
assertEq(stdMath.percentDelta(uint256(0), uint256(1337)), 1e18);
assertEq(stdMath.percentDelta(uint256(0), type(uint64).max), 1e18);
assertEq(stdMath.percentDelta(uint256(0), type(uint128).max), 1e18);
assertEq(stdMath.percentDelta(uint256(0), type(uint192).max), 1e18);
assertEq(stdMath.percentDelta(1337, uint256(1337)), 0);
assertEq(stdMath.percentDelta(type(uint192).max, type(uint192).max), 0);
assertEq(stdMath.percentDelta(0, uint256(2500)), 1e18);
assertEq(stdMath.percentDelta(2500, uint256(2500)), 0);
assertEq(stdMath.percentDelta(5000, uint256(2500)), 1e18);
assertEq(stdMath.percentDelta(7500, uint256(2500)), 2e18);
vm.expectRevert("stdMath percentDelta(uint256,uint256): Divisor is zero");
stdMathMock.exposedPercentDelta(uint256(1), 0);
}
function testFuzz_GetPercentDelta_Uint(uint192 a, uint192 b) external pure {
vm.assume(b != 0);
uint256 manualDelta = a > b ? a - b : b - a;
uint256 manualPercentDelta = manualDelta * 1e18 / b;
uint256 percentDelta = stdMath.percentDelta(a, b);
assertEq(percentDelta, manualPercentDelta);
}
function test_GetPercentDelta_Int() external {
// We deploy a mock version so we can properly test the revert.
StdMathMock stdMathMock = new StdMathMock();
assertEq(stdMath.percentDelta(int256(0), int256(1337)), 1e18);
assertEq(stdMath.percentDelta(int256(0), -1337), 1e18);
assertEq(stdMath.percentDelta(int256(0), type(int64).min), 1e18);
assertEq(stdMath.percentDelta(int256(0), type(int128).min), 1e18);
assertEq(stdMath.percentDelta(int256(0), type(int192).min), 1e18);
assertEq(stdMath.percentDelta(int256(0), type(int64).max), 1e18);
assertEq(stdMath.percentDelta(int256(0), type(int128).max), 1e18);
assertEq(stdMath.percentDelta(int256(0), type(int192).max), 1e18);
assertEq(stdMath.percentDelta(1337, int256(1337)), 0);
assertEq(stdMath.percentDelta(type(int192).max, type(int192).max), 0);
assertEq(stdMath.percentDelta(type(int192).min, type(int192).min), 0);
assertEq(stdMath.percentDelta(type(int192).min, type(int192).max), 2e18); // rounds the 1 wei diff down
assertEq(stdMath.percentDelta(type(int192).max, type(int192).min), 2e18 - 1); // rounds the 1 wei diff down
assertEq(stdMath.percentDelta(0, int256(2500)), 1e18);
assertEq(stdMath.percentDelta(2500, int256(2500)), 0);
assertEq(stdMath.percentDelta(5000, int256(2500)), 1e18);
assertEq(stdMath.percentDelta(7500, int256(2500)), 2e18);
vm.expectRevert("stdMath percentDelta(int256,int256): Divisor is zero");
stdMathMock.exposedPercentDelta(int256(1), 0);
}
function testFuzz_GetPercentDelta_Int(int192 a, int192 b) external pure {
vm.assume(b != 0);
uint256 absA = getAbs(a);
uint256 absB = getAbs(b);
uint256 absDelta = absA > absB ? absA - absB : absB - absA;
uint256 manualDelta;
if ((a >= 0 && b >= 0) || (a < 0 && b < 0)) {
manualDelta = absDelta;
}
// (a < 0 && b >= 0) || (a >= 0 && b < 0)
else {
manualDelta = absA + absB;
}
uint256 manualPercentDelta = manualDelta * 1e18 / absB;
uint256 percentDelta = stdMath.percentDelta(a, b);
assertEq(percentDelta, manualPercentDelta);
}
/*//////////////////////////////////////////////////////////////////////////
HELPERS
//////////////////////////////////////////////////////////////////////////*/
function getAbs(int256 a) private pure returns (uint256) {
if (a < 0) {
return a == type(int256).min ? uint256(type(int256).max) + 1 : uint256(-a);
}
return uint256(a);
}
}

View File

@@ -0,0 +1,486 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
import {stdStorage, StdStorage} from "../src/StdStorage.sol";
import {Test} from "../src/Test.sol";
contract StdStorageTest is Test {
using stdStorage for StdStorage;
StorageTest internal test;
function setUp() public {
test = new StorageTest();
}
function test_StorageHidden() public {
assertEq(uint256(keccak256("my.random.var")), stdstore.target(address(test)).sig("hidden()").find());
}
function test_StorageObvious() public {
assertEq(uint256(0), stdstore.target(address(test)).sig("exists()").find());
}
function test_StorageExtraSload() public {
assertEq(16, stdstore.target(address(test)).sig(test.extra_sload.selector).find());
}
function test_StorageCheckedWriteHidden() public {
stdstore.target(address(test)).sig(test.hidden.selector).checked_write(100);
assertEq(uint256(test.hidden()), 100);
}
function test_StorageCheckedWriteObvious() public {
stdstore.target(address(test)).sig(test.exists.selector).checked_write(100);
assertEq(test.exists(), 100);
}
function test_StorageCheckedWriteSignedIntegerHidden() public {
stdstore.target(address(test)).sig(test.hidden.selector).checked_write_int(-100);
assertEq(int256(uint256(test.hidden())), -100);
}
function test_StorageCheckedWriteSignedIntegerObvious() public {
stdstore.target(address(test)).sig(test.tG.selector).checked_write_int(-100);
assertEq(test.tG(), -100);
}
function test_StorageMapStructA() public {
uint256 slot =
stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(0).find();
assertEq(uint256(keccak256(abi.encode(address(this), 4))), slot);
}
function test_StorageMapStructB() public {
uint256 slot =
stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(1).find();
assertEq(uint256(keccak256(abi.encode(address(this), 4))) + 1, slot);
}
function test_StorageDeepMap() public {
uint256 slot = stdstore.target(address(test)).sig(test.deep_map.selector).with_key(address(this))
.with_key(address(this)).find();
assertEq(uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint256(5)))))), slot);
}
function test_StorageCheckedWriteDeepMap() public {
stdstore.target(address(test)).sig(test.deep_map.selector).with_key(address(this)).with_key(address(this))
.checked_write(100);
assertEq(100, test.deep_map(address(this), address(this)));
}
function test_StorageDeepMapStructA() public {
uint256 slot = stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this))
.with_key(address(this)).depth(0).find();
assertEq(
bytes32(
uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint256(6)))))) + 0
),
bytes32(slot)
);
}
function test_StorageDeepMapStructB() public {
uint256 slot = stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this))
.with_key(address(this)).depth(1).find();
assertEq(
bytes32(
uint256(keccak256(abi.encode(address(this), keccak256(abi.encode(address(this), uint256(6)))))) + 1
),
bytes32(slot)
);
}
function test_StorageCheckedWriteDeepMapStructA() public {
stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this))
.with_key(address(this)).depth(0).checked_write(100);
(uint256 a, uint256 b) = test.deep_map_struct(address(this), address(this));
assertEq(100, a);
assertEq(0, b);
}
function test_StorageCheckedWriteDeepMapStructB() public {
stdstore.target(address(test)).sig(test.deep_map_struct.selector).with_key(address(this))
.with_key(address(this)).depth(1).checked_write(100);
(uint256 a, uint256 b) = test.deep_map_struct(address(this), address(this));
assertEq(0, a);
assertEq(100, b);
}
function test_StorageCheckedWriteMapStructA() public {
stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(0).checked_write(100);
(uint256 a, uint256 b) = test.map_struct(address(this));
assertEq(a, 100);
assertEq(b, 0);
}
function test_StorageCheckedWriteMapStructB() public {
stdstore.target(address(test)).sig(test.map_struct.selector).with_key(address(this)).depth(1).checked_write(100);
(uint256 a, uint256 b) = test.map_struct(address(this));
assertEq(a, 0);
assertEq(b, 100);
}
function test_StorageStructA() public {
uint256 slot = stdstore.target(address(test)).sig(test.basic.selector).depth(0).find();
assertEq(uint256(7), slot);
}
function test_StorageStructB() public {
uint256 slot = stdstore.target(address(test)).sig(test.basic.selector).depth(1).find();
assertEq(uint256(7) + 1, slot);
}
function test_StorageCheckedWriteStructA() public {
stdstore.target(address(test)).sig(test.basic.selector).depth(0).checked_write(100);
(uint256 a, uint256 b) = test.basic();
assertEq(a, 100);
assertEq(b, 1337);
}
function test_StorageCheckedWriteStructB() public {
stdstore.target(address(test)).sig(test.basic.selector).depth(1).checked_write(100);
(uint256 a, uint256 b) = test.basic();
assertEq(a, 1337);
assertEq(b, 100);
}
function test_StorageMapAddrFound() public {
uint256 slot = stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).find();
assertEq(uint256(keccak256(abi.encode(address(this), uint256(1)))), slot);
}
function test_StorageMapAddrRoot() public {
(uint256 slot, bytes32 key) =
stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).parent();
assertEq(address(uint160(uint256(key))), address(this));
assertEq(uint256(1), slot);
slot = stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).root();
assertEq(uint256(1), slot);
}
function test_StorageMapUintFound() public {
uint256 slot = stdstore.target(address(test)).sig(test.map_uint.selector).with_key(100).find();
assertEq(uint256(keccak256(abi.encode(100, uint256(2)))), slot);
}
function test_StorageCheckedWriteMapUint() public {
stdstore.target(address(test)).sig(test.map_uint.selector).with_key(100).checked_write(100);
assertEq(100, test.map_uint(100));
}
function test_StorageCheckedWriteMapAddr() public {
stdstore.target(address(test)).sig(test.map_addr.selector).with_key(address(this)).checked_write(100);
assertEq(100, test.map_addr(address(this)));
}
function test_StorageCheckedWriteMapBool() public {
stdstore.target(address(test)).sig(test.map_bool.selector).with_key(address(this)).checked_write(true);
assertTrue(test.map_bool(address(this)));
}
function testFuzz_StorageCheckedWriteMapPacked(address addr, uint128 value) public {
stdstore.enable_packed_slots().target(address(test)).sig(test.read_struct_lower.selector).with_key(addr)
.checked_write(value);
assertEq(test.read_struct_lower(addr), value);
stdstore.enable_packed_slots().target(address(test)).sig(test.read_struct_upper.selector).with_key(addr)
.checked_write(value);
assertEq(test.read_struct_upper(addr), value);
}
function test_StorageCheckedWriteMapPackedFullSuccess() public {
uint256 full = test.map_packed(address(1337));
// keep upper 128, set lower 128 to 1337
full = (full & (uint256((1 << 128) - 1) << 128)) | 1337;
stdstore.target(address(test)).sig(test.map_packed.selector).with_key(address(uint160(1337)))
.checked_write(full);
assertEq(1337, test.read_struct_lower(address(1337)));
}
function test_RevertStorageConst() public {
StorageTestTarget target = new StorageTestTarget(test);
vm.expectRevert("stdStorage find(StdStorage): No storage use detected for target.");
target.expectRevertStorageConst();
}
function testFuzz_StorageNativePack(uint248 val1, uint248 val2, bool boolVal1, bool boolVal2) public {
stdstore.enable_packed_slots().target(address(test)).sig(test.tA.selector).checked_write(val1);
stdstore.enable_packed_slots().target(address(test)).sig(test.tB.selector).checked_write(boolVal1);
stdstore.enable_packed_slots().target(address(test)).sig(test.tC.selector).checked_write(boolVal2);
stdstore.enable_packed_slots().target(address(test)).sig(test.tD.selector).checked_write(val2);
assertEq(test.tA(), val1);
assertEq(test.tB(), boolVal1);
assertEq(test.tC(), boolVal2);
assertEq(test.tD(), val2);
}
function test_StorageReadBytes32() public {
bytes32 val = stdstore.target(address(test)).sig(test.tE.selector).read_bytes32();
assertEq(val, hex"1337");
}
function test_StorageReadBool_False() public {
bool val = stdstore.target(address(test)).sig(test.tB.selector).read_bool();
assertEq(val, false);
}
function test_StorageReadBool_True() public {
bool val = stdstore.target(address(test)).sig(test.tH.selector).read_bool();
assertEq(val, true);
}
function test_RevertIf_ReadingNonBoolValue() public {
vm.expectRevert("stdStorage read_bool(StdStorage): Cannot decode. Make sure you are reading a bool.");
this.readNonBoolValue();
}
function readNonBoolValue() public {
stdstore.target(address(test)).sig(test.tE.selector).read_bool();
}
function test_StorageReadAddress() public {
address val = stdstore.target(address(test)).sig(test.tF.selector).read_address();
assertEq(val, address(1337));
}
function test_StorageReadUint() public {
uint256 val = stdstore.target(address(test)).sig(test.exists.selector).read_uint();
assertEq(val, 1);
}
function test_StorageReadInt() public {
int256 val = stdstore.target(address(test)).sig(test.tG.selector).read_int();
assertEq(val, type(int256).min);
}
function testFuzz_Packed(uint256 val, uint8 elemToGet) public {
// This function tries an assortment of packed slots, shifts meaning number of elements
// that are packed. Shiftsizes are the size of each element, i.e. 8 means a data type that is 8 bits, 16 == 16 bits, etc.
// Combined, these determine how a slot is packed. Making it random is too hard to avoid global rejection limit
// and make it performant.
// change the number of shifts
for (uint256 i = 1; i < 5; i++) {
uint256 shifts = i;
elemToGet = uint8(bound(elemToGet, 0, shifts - 1));
uint256[] memory shiftSizes = new uint256[](shifts);
for (uint256 j; j < shifts; j++) {
shiftSizes[j] = 8 * (j + 1);
}
test.setRandomPacking(val);
uint256 leftBits;
uint256 rightBits;
for (uint256 j; j < shiftSizes.length; j++) {
if (j < elemToGet) {
leftBits += shiftSizes[j];
} else if (elemToGet != j) {
rightBits += shiftSizes[j];
}
}
// we may have some right bits unaccounted for
leftBits += 256 - (leftBits + shiftSizes[elemToGet] + rightBits);
// clear left bits, then clear right bits and realign
uint256 expectedValToRead = (val << leftBits) >> (leftBits + rightBits);
uint256 readVal = stdstore.target(address(test)).enable_packed_slots()
.sig("getRandomPacked(uint8,uint8[],uint8)").with_calldata(abi.encode(shifts, shiftSizes, elemToGet))
.read_uint();
assertEq(readVal, expectedValToRead);
}
}
function testFuzz_Packed2(uint256 nvars, uint256 seed) public {
// Number of random variables to generate.
nvars = bound(nvars, 1, 20);
// This will decrease as we generate values in the below loop.
uint256 bitsRemaining = 256;
// Generate a random value and size for each variable.
uint256[] memory vals = new uint256[](nvars);
uint256[] memory sizes = new uint256[](nvars);
uint256[] memory offsets = new uint256[](nvars);
for (uint256 i = 0; i < nvars; i++) {
// Generate a random value and size.
offsets[i] = i == 0 ? 0 : offsets[i - 1] + sizes[i - 1];
uint256 nvarsRemaining = nvars - i;
uint256 maxVarSize = bitsRemaining - nvarsRemaining + 1;
sizes[i] = bound(uint256(keccak256(abi.encodePacked(seed, i + 256))), 1, maxVarSize);
bitsRemaining -= sizes[i];
uint256 maxVal;
uint256 varSize = sizes[i];
assembly {
// mask = (1 << varSize) - 1
maxVal := sub(shl(varSize, 1), 1)
}
vals[i] = bound(uint256(keccak256(abi.encodePacked(seed, i))), 0, maxVal);
}
// Pack all values into the slot.
for (uint256 i = 0; i < nvars; i++) {
stdstore.enable_packed_slots().target(address(test)).sig("getRandomPacked(uint256,uint256)")
.with_key(sizes[i]).with_key(offsets[i]).checked_write(vals[i]);
}
// Verify the read data matches.
for (uint256 i = 0; i < nvars; i++) {
uint256 readVal = stdstore.enable_packed_slots().target(address(test))
.sig("getRandomPacked(uint256,uint256)").with_key(sizes[i]).with_key(offsets[i]).read_uint();
uint256 retVal = test.getRandomPacked(sizes[i], offsets[i]);
assertEq(readVal, vals[i]);
assertEq(retVal, vals[i]);
}
}
function testEdgeCaseArray() public {
stdstore.target(address(test)).sig("edgeCaseArray(uint256)").with_key(uint256(0)).checked_write(1);
assertEq(test.edgeCaseArray(0), 1);
}
}
contract StorageTestTarget {
using stdStorage for StdStorage;
StdStorage internal stdstore;
StorageTest internal test;
constructor(StorageTest test_) {
test = test_;
}
function expectRevertStorageConst() public {
stdstore.target(address(test)).sig("const()").find();
}
}
contract StorageTest {
uint256 public exists = 1;
mapping(address => uint256) public map_addr;
mapping(uint256 => uint256) public map_uint;
mapping(address => uint256) public map_packed;
mapping(address => UnpackedStruct) public map_struct;
mapping(address => mapping(address => uint256)) public deep_map;
mapping(address => mapping(address => UnpackedStruct)) public deep_map_struct;
UnpackedStruct public basic;
uint248 public tA;
bool public tB;
bool public tC = false;
uint248 public tD = 1;
struct UnpackedStruct {
uint256 a;
uint256 b;
}
mapping(address => bool) public map_bool;
bytes32 public tE = hex"1337";
address public tF = address(1337);
int256 public tG = type(int256).min;
bool public tH = true;
bytes32 private tI = ~bytes32(hex"1337");
uint256 randomPacking;
// Array with length matching values of elements.
uint256[] public edgeCaseArray = [3, 3, 3];
constructor() {
basic = UnpackedStruct({a: 1337, b: 1337});
uint256 two = (1 << 128) | 1;
map_packed[msg.sender] = two;
map_packed[address(uint160(1337))] = 1 << 128;
}
function read_struct_upper(address who) public view returns (uint256) {
return map_packed[who] >> 128;
}
function read_struct_lower(address who) public view returns (uint256) {
return map_packed[who] & ((1 << 128) - 1);
}
function hidden() public view returns (bytes32 t) {
bytes32 slot = keccak256("my.random.var");
/// @solidity memory-safe-assembly
assembly {
t := sload(slot)
}
}
function const() public pure returns (bytes32 t) {
t = bytes32(hex"1337");
}
function extra_sload() public view returns (bytes32 t) {
// trigger read on slot `tE`, and make a staticcall to make sure compiler doesn't optimize this SLOAD away
assembly {
pop(staticcall(gas(), sload(tE.slot), 0, 0, 0, 0))
}
t = tI;
}
function setRandomPacking(uint256 val) public {
randomPacking = val;
}
function _getMask(uint256 size) internal pure returns (uint256 mask) {
assembly {
// mask = (1 << size) - 1
mask := sub(shl(size, 1), 1)
}
}
function setRandomPacking(uint256 val, uint256 size, uint256 offset) public {
// Generate mask based on the size of the value
uint256 mask = _getMask(size);
// Zero out all bits for the word we're about to set
uint256 cleanedWord = randomPacking & ~(mask << offset);
// Place val in the correct spot of the cleaned word
randomPacking = cleanedWord | val << offset;
}
function getRandomPacked(uint256 size, uint256 offset) public view returns (uint256) {
// Generate mask based on the size of the value
uint256 mask = _getMask(size);
// Shift to place the bits in the correct position, and use mask to zero out remaining bits
return (randomPacking >> offset) & mask;
}
function getRandomPacked(uint8 shifts, uint8[] memory shiftSizes, uint8 elem) public view returns (uint256) {
require(elem < shifts, "!elem");
uint256 leftBits;
uint256 rightBits;
for (uint256 i; i < shiftSizes.length; i++) {
if (i < elem) {
leftBits += shiftSizes[i];
} else if (elem != i) {
rightBits += shiftSizes[i];
}
}
// we may have some right bits unaccounted for
leftBits += 256 - (leftBits + shiftSizes[elem] + rightBits);
// clear left bits, then clear right bits and realign
return (randomPacking << leftBits) >> (leftBits + rightBits);
}
}

View File

@@ -0,0 +1,110 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
import {Test, console2, StdStyle} from "../src/Test.sol";
contract StdStyleTest is Test {
function test_StyleColor() public pure {
console2.log(StdStyle.red("StdStyle.red String Test"));
console2.log(StdStyle.red(uint256(10e18)));
console2.log(StdStyle.red(int256(-10e18)));
console2.log(StdStyle.red(true));
console2.log(StdStyle.red(address(0)));
console2.log(StdStyle.redBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
console2.log(StdStyle.redBytes32("StdStyle.redBytes32"));
console2.log(StdStyle.green("StdStyle.green String Test"));
console2.log(StdStyle.green(uint256(10e18)));
console2.log(StdStyle.green(int256(-10e18)));
console2.log(StdStyle.green(true));
console2.log(StdStyle.green(address(0)));
console2.log(StdStyle.greenBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
console2.log(StdStyle.greenBytes32("StdStyle.greenBytes32"));
console2.log(StdStyle.yellow("StdStyle.yellow String Test"));
console2.log(StdStyle.yellow(uint256(10e18)));
console2.log(StdStyle.yellow(int256(-10e18)));
console2.log(StdStyle.yellow(true));
console2.log(StdStyle.yellow(address(0)));
console2.log(StdStyle.yellowBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
console2.log(StdStyle.yellowBytes32("StdStyle.yellowBytes32"));
console2.log(StdStyle.blue("StdStyle.blue String Test"));
console2.log(StdStyle.blue(uint256(10e18)));
console2.log(StdStyle.blue(int256(-10e18)));
console2.log(StdStyle.blue(true));
console2.log(StdStyle.blue(address(0)));
console2.log(StdStyle.blueBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
console2.log(StdStyle.blueBytes32("StdStyle.blueBytes32"));
console2.log(StdStyle.magenta("StdStyle.magenta String Test"));
console2.log(StdStyle.magenta(uint256(10e18)));
console2.log(StdStyle.magenta(int256(-10e18)));
console2.log(StdStyle.magenta(true));
console2.log(StdStyle.magenta(address(0)));
console2.log(StdStyle.magentaBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
console2.log(StdStyle.magentaBytes32("StdStyle.magentaBytes32"));
console2.log(StdStyle.cyan("StdStyle.cyan String Test"));
console2.log(StdStyle.cyan(uint256(10e18)));
console2.log(StdStyle.cyan(int256(-10e18)));
console2.log(StdStyle.cyan(true));
console2.log(StdStyle.cyan(address(0)));
console2.log(StdStyle.cyanBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
console2.log(StdStyle.cyanBytes32("StdStyle.cyanBytes32"));
}
function test_StyleFontWeight() public pure {
console2.log(StdStyle.bold("StdStyle.bold String Test"));
console2.log(StdStyle.bold(uint256(10e18)));
console2.log(StdStyle.bold(int256(-10e18)));
console2.log(StdStyle.bold(address(0)));
console2.log(StdStyle.bold(true));
console2.log(StdStyle.boldBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
console2.log(StdStyle.boldBytes32("StdStyle.boldBytes32"));
console2.log(StdStyle.dim("StdStyle.dim String Test"));
console2.log(StdStyle.dim(uint256(10e18)));
console2.log(StdStyle.dim(int256(-10e18)));
console2.log(StdStyle.dim(address(0)));
console2.log(StdStyle.dim(true));
console2.log(StdStyle.dimBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
console2.log(StdStyle.dimBytes32("StdStyle.dimBytes32"));
console2.log(StdStyle.italic("StdStyle.italic String Test"));
console2.log(StdStyle.italic(uint256(10e18)));
console2.log(StdStyle.italic(int256(-10e18)));
console2.log(StdStyle.italic(address(0)));
console2.log(StdStyle.italic(true));
console2.log(StdStyle.italicBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
console2.log(StdStyle.italicBytes32("StdStyle.italicBytes32"));
console2.log(StdStyle.underline("StdStyle.underline String Test"));
console2.log(StdStyle.underline(uint256(10e18)));
console2.log(StdStyle.underline(int256(-10e18)));
console2.log(StdStyle.underline(address(0)));
console2.log(StdStyle.underline(true));
console2.log(StdStyle.underlineBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
console2.log(StdStyle.underlineBytes32("StdStyle.underlineBytes32"));
console2.log(StdStyle.inverse("StdStyle.inverse String Test"));
console2.log(StdStyle.inverse(uint256(10e18)));
console2.log(StdStyle.inverse(int256(-10e18)));
console2.log(StdStyle.inverse(address(0)));
console2.log(StdStyle.inverse(true));
console2.log(StdStyle.inverseBytes(hex"7109709ECfa91a80626fF3989D68f67F5b1DD12D"));
console2.log(StdStyle.inverseBytes32("StdStyle.inverseBytes32"));
}
function test_StyleCombined() public pure {
console2.log(StdStyle.red(StdStyle.bold("Red Bold String Test")));
console2.log(StdStyle.green(StdStyle.dim(uint256(10e18))));
console2.log(StdStyle.yellow(StdStyle.italic(int256(-10e18))));
console2.log(StdStyle.blue(StdStyle.underline(address(0))));
console2.log(StdStyle.magenta(StdStyle.inverse(true)));
}
function test_StyleCustom() public pure {
console2.log(h1("Custom Style 1"));
console2.log(h2("Custom Style 2"));
}
function h1(string memory a) private pure returns (string memory) {
return StdStyle.cyan(StdStyle.inverse(StdStyle.bold(a)));
}
function h2(string memory a) private pure returns (string memory) {
return StdStyle.magenta(StdStyle.bold(StdStyle.underline(a)));
}
}

View File

@@ -0,0 +1,49 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
import {Test, stdToml} from "../src/Test.sol";
contract StdTomlTest is Test {
using stdToml for string;
string root;
string path;
function setUp() public {
root = vm.projectRoot();
path = string.concat(root, "/test/fixtures/test.toml");
}
struct SimpleToml {
uint256 a;
string b;
}
struct NestedToml {
uint256 a;
string b;
SimpleToml c;
}
function test_readToml() public view {
string memory json = vm.readFile(path);
assertEq(json.readUint(".a"), 123);
}
function test_writeToml() public {
string memory json = "json";
json.serialize("a", uint256(123));
string memory semiFinal = json.serialize("b", string("test"));
string memory finalJson = json.serialize("c", semiFinal);
finalJson.write(path);
string memory toml = vm.readFile(path);
bytes memory data = toml.parseRaw("$");
NestedToml memory decodedData = abi.decode(data, (NestedToml));
assertEq(decodedData.a, 123);
assertEq(decodedData.b, "test");
assertEq(decodedData.c.a, 123);
assertEq(decodedData.c.b, "test");
}
}

View File

@@ -0,0 +1,342 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.7.0 <0.9.0;
import {Test, StdUtils} from "../src/Test.sol";
contract StdUtilsMock is StdUtils {
// We deploy a mock version so we can properly test expected reverts.
function exposedGetTokenBalances(address token, address[] memory addresses)
external
returns (uint256[] memory balances)
{
return getTokenBalances(token, addresses);
}
function exposedBound(int256 num, int256 min, int256 max) external pure returns (int256) {
return bound(num, min, max);
}
function exposedBound(uint256 num, uint256 min, uint256 max) external pure returns (uint256) {
return bound(num, min, max);
}
function exposedBytesToUint(bytes memory b) external pure returns (uint256) {
return bytesToUint(b);
}
}
contract StdUtilsTest is Test {
/*//////////////////////////////////////////////////////////////////////////
BOUND UINT
//////////////////////////////////////////////////////////////////////////*/
function test_Bound() public pure {
assertEq(bound(uint256(5), 0, 4), 0);
assertEq(bound(uint256(0), 69, 69), 69);
assertEq(bound(uint256(0), 68, 69), 68);
assertEq(bound(uint256(10), 150, 190), 174);
assertEq(bound(uint256(300), 2800, 3200), 3107);
assertEq(bound(uint256(9999), 1337, 6666), 4669);
}
function test_Bound_WithinRange() public pure {
assertEq(bound(uint256(51), 50, 150), 51);
assertEq(bound(uint256(51), 50, 150), bound(bound(uint256(51), 50, 150), 50, 150));
assertEq(bound(uint256(149), 50, 150), 149);
assertEq(bound(uint256(149), 50, 150), bound(bound(uint256(149), 50, 150), 50, 150));
}
function test_Bound_EdgeCoverage() public pure {
assertEq(bound(uint256(0), 50, 150), 50);
assertEq(bound(uint256(1), 50, 150), 51);
assertEq(bound(uint256(2), 50, 150), 52);
assertEq(bound(uint256(3), 50, 150), 53);
assertEq(bound(type(uint256).max, 50, 150), 150);
assertEq(bound(type(uint256).max - 1, 50, 150), 149);
assertEq(bound(type(uint256).max - 2, 50, 150), 148);
assertEq(bound(type(uint256).max - 3, 50, 150), 147);
}
function testFuzz_Bound_DistributionIsEven(uint256 min, uint256 size) public pure {
size = size % 100 + 1;
min = bound(min, UINT256_MAX / 2, UINT256_MAX / 2 + size);
uint256 max = min + size - 1;
uint256 result;
for (uint256 i = 1; i <= size * 4; ++i) {
// x > max
result = bound(max + i, min, max);
assertEq(result, min + (i - 1) % size);
// x < min
result = bound(min - i, min, max);
assertEq(result, max - (i - 1) % size);
}
}
function testFuzz_Bound(uint256 num, uint256 min, uint256 max) public pure {
if (min > max) (min, max) = (max, min);
uint256 result = bound(num, min, max);
assertGe(result, min);
assertLe(result, max);
assertEq(result, bound(result, min, max));
if (num >= min && num <= max) assertEq(result, num);
}
function test_BoundUint256Max() public pure {
assertEq(bound(0, type(uint256).max - 1, type(uint256).max), type(uint256).max - 1);
assertEq(bound(1, type(uint256).max - 1, type(uint256).max), type(uint256).max);
}
function test_RevertIf_BoundMaxLessThanMin() public {
// We deploy a mock version so we can properly test the revert.
StdUtilsMock stdUtils = new StdUtilsMock();
vm.expectRevert(bytes("StdUtils bound(uint256,uint256,uint256): Max is less than min."));
stdUtils.exposedBound(uint256(5), 100, 10);
}
function testFuzz_RevertIf_BoundMaxLessThanMin(uint256 num, uint256 min, uint256 max) public {
// We deploy a mock version so we can properly test the revert.
StdUtilsMock stdUtils = new StdUtilsMock();
vm.assume(min > max);
vm.expectRevert(bytes("StdUtils bound(uint256,uint256,uint256): Max is less than min."));
stdUtils.exposedBound(num, min, max);
}
/*//////////////////////////////////////////////////////////////////////////
BOUND INT
//////////////////////////////////////////////////////////////////////////*/
function test_BoundInt() public pure {
assertEq(bound(-3, 0, 4), 2);
assertEq(bound(0, -69, -69), -69);
assertEq(bound(0, -69, -68), -68);
assertEq(bound(-10, 150, 190), 154);
assertEq(bound(-300, 2800, 3200), 2908);
assertEq(bound(9999, -1337, 6666), 1995);
}
function test_BoundInt_WithinRange() public pure {
assertEq(bound(51, -50, 150), 51);
assertEq(bound(51, -50, 150), bound(bound(51, -50, 150), -50, 150));
assertEq(bound(149, -50, 150), 149);
assertEq(bound(149, -50, 150), bound(bound(149, -50, 150), -50, 150));
}
function test_BoundInt_EdgeCoverage() public pure {
assertEq(bound(type(int256).min, -50, 150), -50);
assertEq(bound(type(int256).min + 1, -50, 150), -49);
assertEq(bound(type(int256).min + 2, -50, 150), -48);
assertEq(bound(type(int256).min + 3, -50, 150), -47);
assertEq(bound(type(int256).min, 10, 150), 10);
assertEq(bound(type(int256).min + 1, 10, 150), 11);
assertEq(bound(type(int256).min + 2, 10, 150), 12);
assertEq(bound(type(int256).min + 3, 10, 150), 13);
assertEq(bound(type(int256).max, -50, 150), 150);
assertEq(bound(type(int256).max - 1, -50, 150), 149);
assertEq(bound(type(int256).max - 2, -50, 150), 148);
assertEq(bound(type(int256).max - 3, -50, 150), 147);
assertEq(bound(type(int256).max, -50, -10), -10);
assertEq(bound(type(int256).max - 1, -50, -10), -11);
assertEq(bound(type(int256).max - 2, -50, -10), -12);
assertEq(bound(type(int256).max - 3, -50, -10), -13);
}
function testFuzz_BoundInt_DistributionIsEven(int256 min, uint256 size) public pure {
size = size % 100 + 1;
min = bound(min, -int256(size / 2), int256(size - size / 2));
int256 max = min + int256(size) - 1;
int256 result;
for (uint256 i = 1; i <= size * 4; ++i) {
// x > max
result = bound(max + int256(i), min, max);
assertEq(result, min + int256((i - 1) % size));
// x < min
result = bound(min - int256(i), min, max);
assertEq(result, max - int256((i - 1) % size));
}
}
function testFuzz_BoundInt(int256 num, int256 min, int256 max) public pure {
if (min > max) (min, max) = (max, min);
int256 result = bound(num, min, max);
assertGe(result, min);
assertLe(result, max);
assertEq(result, bound(result, min, max));
if (num >= min && num <= max) assertEq(result, num);
}
function test_BoundIntInt256Max() public pure {
assertEq(bound(0, type(int256).max - 1, type(int256).max), type(int256).max - 1);
assertEq(bound(1, type(int256).max - 1, type(int256).max), type(int256).max);
}
function test_BoundIntInt256Min() public pure {
assertEq(bound(0, type(int256).min, type(int256).min + 1), type(int256).min);
assertEq(bound(1, type(int256).min, type(int256).min + 1), type(int256).min + 1);
}
function test_RevertIf_BoundIntMaxLessThanMin() public {
// We deploy a mock version so we can properly test the revert.
StdUtilsMock stdUtils = new StdUtilsMock();
vm.expectRevert(bytes("StdUtils bound(int256,int256,int256): Max is less than min."));
stdUtils.exposedBound(-5, 100, 10);
}
function testFuzz_RevertIf_BoundIntMaxLessThanMin(int256 num, int256 min, int256 max) public {
// We deploy a mock version so we can properly test the revert.
StdUtilsMock stdUtils = new StdUtilsMock();
vm.assume(min > max);
vm.expectRevert(bytes("StdUtils bound(int256,int256,int256): Max is less than min."));
stdUtils.exposedBound(num, min, max);
}
/*//////////////////////////////////////////////////////////////////////////
BOUND PRIVATE KEY
//////////////////////////////////////////////////////////////////////////*/
function test_BoundPrivateKey() public pure {
assertEq(boundPrivateKey(0), 1);
assertEq(boundPrivateKey(1), 1);
assertEq(boundPrivateKey(300), 300);
assertEq(boundPrivateKey(9999), 9999);
assertEq(boundPrivateKey(SECP256K1_ORDER - 1), SECP256K1_ORDER - 1);
assertEq(boundPrivateKey(SECP256K1_ORDER), 1);
assertEq(boundPrivateKey(SECP256K1_ORDER + 1), 2);
assertEq(boundPrivateKey(UINT256_MAX), UINT256_MAX & SECP256K1_ORDER - 1); // x&y is equivalent to x-x%y
}
/*//////////////////////////////////////////////////////////////////////////
BYTES TO UINT
//////////////////////////////////////////////////////////////////////////*/
function test_BytesToUint() external pure {
bytes memory maxUint = hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
bytes memory two = hex"02";
bytes memory millionEther = hex"d3c21bcecceda1000000";
assertEq(bytesToUint(maxUint), type(uint256).max);
assertEq(bytesToUint(two), 2);
assertEq(bytesToUint(millionEther), 1_000_000 ether);
}
function test_RevertIf_BytesLengthExceeds32() external {
// We deploy a mock version so we can properly test the revert.
StdUtilsMock stdUtils = new StdUtilsMock();
bytes memory thirty3Bytes = hex"ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff";
vm.expectRevert("StdUtils bytesToUint(bytes): Bytes length exceeds 32.");
stdUtils.exposedBytesToUint(thirty3Bytes);
}
/*//////////////////////////////////////////////////////////////////////////
COMPUTE CREATE ADDRESS
//////////////////////////////////////////////////////////////////////////*/
function test_ComputeCreateAddress() external pure {
address deployer = 0x6C9FC64A53c1b71FB3f9Af64d1ae3A4931A5f4E9;
uint256 nonce = 14;
address createAddress = computeCreateAddress(deployer, nonce);
assertEq(createAddress, 0x68b3465833fb72A70ecDF485E0e4C7bD8665Fc45);
}
/*//////////////////////////////////////////////////////////////////////////
COMPUTE CREATE2 ADDRESS
//////////////////////////////////////////////////////////////////////////*/
function test_ComputeCreate2Address() external pure {
bytes32 salt = bytes32(uint256(31415));
bytes32 initcodeHash = keccak256(abi.encode(0x6080));
address deployer = 0x6C9FC64A53c1b71FB3f9Af64d1ae3A4931A5f4E9;
address create2Address = computeCreate2Address(salt, initcodeHash, deployer);
assertEq(create2Address, 0xB147a5d25748fda14b463EB04B111027C290f4d3);
}
function test_ComputeCreate2AddressWithDefaultDeployer() external pure {
bytes32 salt = 0xc290c670fde54e5ef686f9132cbc8711e76a98f0333a438a92daa442c71403c0;
bytes32 initcodeHash = hashInitCode(hex"6080", "");
assertEq(initcodeHash, 0x1a578b7a4b0b5755db6d121b4118d4bc68fe170dca840c59bc922f14175a76b0);
address create2Address = computeCreate2Address(salt, initcodeHash);
assertEq(create2Address, 0xc0ffEe2198a06235aAbFffe5Db0CacF1717f5Ac6);
}
}
contract StdUtilsForkTest is Test {
/*//////////////////////////////////////////////////////////////////////////
GET TOKEN BALANCES
//////////////////////////////////////////////////////////////////////////*/
address internal SHIB = 0x95aD61b0a150d79219dCF64E1E6Cc01f0B64C4cE;
address internal SHIB_HOLDER_0 = 0x855F5981e831D83e6A4b4EBFCAdAa68D92333170;
address internal SHIB_HOLDER_1 = 0x8F509A90c2e47779cA408Fe00d7A72e359229AdA;
address internal SHIB_HOLDER_2 = 0x0e3bbc0D04fF62211F71f3e4C45d82ad76224385;
address internal USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
address internal USDC_HOLDER_0 = 0xDa9CE944a37d218c3302F6B82a094844C6ECEb17;
address internal USDC_HOLDER_1 = 0x3e67F4721E6d1c41a015f645eFa37BEd854fcf52;
function setUp() public {
// All tests of the `getTokenBalances` method are fork tests using live contracts.
vm.createSelectFork({urlOrAlias: "mainnet", blockNumber: 16_428_900});
}
function test_RevertIf_CannotGetTokenBalances_NonTokenContract() external {
// We deploy a mock version so we can properly test the revert.
StdUtilsMock stdUtils = new StdUtilsMock();
// The UniswapV2Factory contract has neither a `balanceOf` function nor a fallback function,
// so the `balanceOf` call should revert.
address token = address(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f);
address[] memory addresses = new address[](1);
addresses[0] = USDC_HOLDER_0;
vm.expectRevert("Multicall3: call failed");
stdUtils.exposedGetTokenBalances(token, addresses);
}
function test_RevertIf_CannotGetTokenBalances_EOA() external {
// We deploy a mock version so we can properly test the revert.
StdUtilsMock stdUtils = new StdUtilsMock();
address eoa = vm.addr({privateKey: 1});
address[] memory addresses = new address[](1);
addresses[0] = USDC_HOLDER_0;
vm.expectRevert("StdUtils getTokenBalances(address,address[]): Token address is not a contract.");
stdUtils.exposedGetTokenBalances(eoa, addresses);
}
function test_GetTokenBalances_Empty() external {
address[] memory addresses = new address[](0);
uint256[] memory balances = getTokenBalances(USDC, addresses);
assertEq(balances.length, 0);
}
function test_GetTokenBalances_USDC() external {
address[] memory addresses = new address[](2);
addresses[0] = USDC_HOLDER_0;
addresses[1] = USDC_HOLDER_1;
uint256[] memory balances = getTokenBalances(USDC, addresses);
assertEq(balances[0], 159_000_000_000_000);
assertEq(balances[1], 131_350_000_000_000);
}
function test_GetTokenBalances_SHIB() external {
address[] memory addresses = new address[](3);
addresses[0] = SHIB_HOLDER_0;
addresses[1] = SHIB_HOLDER_1;
addresses[2] = SHIB_HOLDER_2;
uint256[] memory balances = getTokenBalances(SHIB, addresses);
assertEq(balances[0], 3_323_256_285_484.42e18);
assertEq(balances[1], 1_271_702_771_149.99999928e18);
assertEq(balances[2], 606_357_106_247e18);
}
}

View File

@@ -0,0 +1,18 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0 <0.9.0;
import {Test} from "../src/Test.sol";
import {Vm, VmSafe} from "../src/Vm.sol";
// These tests ensure that functions are never accidentally removed from a Vm interface, or
// inadvertently moved between Vm and VmSafe. These tests must be updated each time a function is
// added to or removed from Vm or VmSafe.
contract VmTest is Test {
function test_VmInterfaceId() public pure {
assertEq(type(Vm).interfaceId, bytes4(0xe835828d), "Vm");
}
function test_VmSafeInterfaceId() public pure {
assertEq(type(VmSafe).interfaceId, bytes4(0x7f58f7be), "VmSafe");
}
}

View File

@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
pragma experimental ABIEncoderV2;
import {Script} from "../../src/Script.sol";
// The purpose of this contract is to benchmark compilation time to avoid accidentally introducing
// a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207
contract CompilationScript is Script {}

View File

@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
pragma experimental ABIEncoderV2;
import {ScriptBase} from "../../src/Script.sol";
// The purpose of this contract is to benchmark compilation time to avoid accidentally introducing
// a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207
contract CompilationScriptBase is ScriptBase {}

View File

@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
pragma experimental ABIEncoderV2;
import {Test} from "../../src/Test.sol";
// The purpose of this contract is to benchmark compilation time to avoid accidentally introducing
// a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207
contract CompilationTest is Test {}

View File

@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
pragma experimental ABIEncoderV2;
import {TestBase} from "../../src/Test.sol";
// The purpose of this contract is to benchmark compilation time to avoid accidentally introducing
// a change that results in very long compilation times with via-ir. See https://github.com/foundry-rs/forge-std/issues/207
contract CompilationTestBase is TestBase {}

View File

@@ -0,0 +1,187 @@
{
"transactions": [
{
"hash": "0xc6006863c267735a11476b7f15b15bc718e117e2da114a2be815dd651e1a509f",
"type": "CALL",
"contractName": "Test",
"contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512",
"function": "multiple_arguments(uint256,address,uint256[]):(uint256)",
"arguments": ["1", "0000000000000000000000000000000000001337", "[3,4]"],
"tx": {
"type": "0x02",
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512",
"gas": "0x73b9",
"value": "0x0",
"data": "0x23e99187000000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000013370000000000000000000000000000000000000000000000000000000000000060000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000030000000000000000000000000000000000000000000000000000000000000004",
"nonce": "0x3",
"accessList": []
}
},
{
"hash": "0xedf2b38d8d896519a947a1acf720f859bb35c0c5ecb8dd7511995b67b9853298",
"type": "CALL",
"contractName": "Test",
"contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512",
"function": "inc():(uint256)",
"arguments": [],
"tx": {
"type": "0x02",
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512",
"gas": "0xdcb2",
"value": "0x0",
"data": "0x371303c0",
"nonce": "0x4",
"accessList": []
}
},
{
"hash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c",
"type": "CALL",
"contractName": "Test",
"contractAddress": "0x7c6b4bbe207d642d98d5c537142d85209e585087",
"function": "t(uint256):(uint256)",
"arguments": ["1"],
"tx": {
"type": "0x02",
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"to": "0x7c6b4bbe207d642d98d5c537142d85209e585087",
"gas": "0x8599",
"value": "0x0",
"data": "0xafe29f710000000000000000000000000000000000000000000000000000000000000001",
"nonce": "0x5",
"accessList": []
}
}
],
"receipts": [
{
"transactionHash": "0x481dc86e40bba90403c76f8e144aa9ff04c1da2164299d0298573835f0991181",
"transactionIndex": "0x0",
"blockHash": "0xef0730448490304e5403be0fa8f8ce64f118e9adcca60c07a2ae1ab921d748af",
"blockNumber": "0x1",
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"to": null,
"cumulativeGasUsed": "0x13f3a",
"gasUsed": "0x13f3a",
"contractAddress": "0x5fbdb2315678afecb367f032d93f642f64180aa3",
"logs": [],
"status": "0x1",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"effectiveGasPrice": "0xee6b2800"
},
{
"transactionHash": "0x6a187183545b8a9e7f1790e847139379bf5622baff2cb43acf3f5c79470af782",
"transactionIndex": "0x0",
"blockHash": "0xf3acb96a90071640c2a8c067ae4e16aad87e634ea8d8bbbb5b352fba86ba0148",
"blockNumber": "0x2",
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"to": null,
"cumulativeGasUsed": "0x45d80",
"gasUsed": "0x45d80",
"contractAddress": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512",
"logs": [],
"status": "0x1",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"effectiveGasPrice": "0xee6b2800"
},
{
"transactionHash": "0x064ad173b4867bdef2fb60060bbdaf01735fbf10414541ea857772974e74ea9d",
"transactionIndex": "0x0",
"blockHash": "0x8373d02109d3ee06a0225f23da4c161c656ccc48fe0fcee931d325508ae73e58",
"blockNumber": "0x3",
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"to": "0x4e59b44847b379578588920ca78fbf26c0b4956c",
"cumulativeGasUsed": "0x45feb",
"gasUsed": "0x45feb",
"contractAddress": null,
"logs": [],
"status": "0x1",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"effectiveGasPrice": "0xee6b2800"
},
{
"transactionHash": "0xc6006863c267735a11476b7f15b15bc718e117e2da114a2be815dd651e1a509f",
"transactionIndex": "0x0",
"blockHash": "0x16712fae5c0e18f75045f84363fb6b4d9a9fe25e660c4ce286833a533c97f629",
"blockNumber": "0x4",
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512",
"cumulativeGasUsed": "0x5905",
"gasUsed": "0x5905",
"contractAddress": null,
"logs": [],
"status": "0x1",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"effectiveGasPrice": "0xee6b2800"
},
{
"transactionHash": "0xedf2b38d8d896519a947a1acf720f859bb35c0c5ecb8dd7511995b67b9853298",
"transactionIndex": "0x0",
"blockHash": "0x156b88c3eb9a1244ba00a1834f3f70de735b39e3e59006dd03af4fe7d5480c11",
"blockNumber": "0x5",
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"to": "0xe7f1725e7734ce288f8367e1bb143e90bb3f0512",
"cumulativeGasUsed": "0xa9c4",
"gasUsed": "0xa9c4",
"contractAddress": null,
"logs": [],
"status": "0x1",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"effectiveGasPrice": "0xee6b2800"
},
{
"transactionHash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c",
"transactionIndex": "0x0",
"blockHash": "0xcf61faca67dbb2c28952b0b8a379e53b1505ae0821e84779679390cb8571cadb",
"blockNumber": "0x6",
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"to": "0x7c6b4bbe207d642d98d5c537142d85209e585087",
"cumulativeGasUsed": "0x66c5",
"gasUsed": "0x66c5",
"contractAddress": null,
"logs": [
{
"address": "0x7c6b4bbe207d642d98d5c537142d85209e585087",
"topics": [
"0x0b2e13ff20ac7b474198655583edf70dedd2c1dc980e329c4fbb2fc0748b796b"
],
"data": "0x000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000000046865726500000000000000000000000000000000000000000000000000000000",
"blockHash": "0xcf61faca67dbb2c28952b0b8a379e53b1505ae0821e84779679390cb8571cadb",
"blockNumber": "0x6",
"transactionHash": "0xa57e8e3981a6c861442e46c9471bd19cb3e21f9a8a6c63a72e7b5c47c6675a7c",
"transactionIndex": "0x1",
"logIndex": "0x0",
"transactionLogIndex": "0x0",
"removed": false
}
],
"status": "0x1",
"logsBloom": "0x00000000000800000000000000000010000000000000000000000000000180000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100",
"effectiveGasPrice": "0xee6b2800"
},
{
"transactionHash": "0x11fbb10230c168ca1e36a7e5c69a6dbcd04fd9e64ede39d10a83e36ee8065c16",
"transactionIndex": "0x0",
"blockHash": "0xf1e0ed2eda4e923626ec74621006ed50b3fc27580dc7b4cf68a07ca77420e29c",
"blockNumber": "0x7",
"from": "0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266",
"to": "0x0000000000000000000000000000000000001337",
"cumulativeGasUsed": "0x5208",
"gasUsed": "0x5208",
"contractAddress": null,
"logs": [],
"status": "0x1",
"logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"effectiveGasPrice": "0xee6b2800"
}
],
"libraries": [
"src/Broadcast.t.sol:F:0x5fbdb2315678afecb367f032d93f642f64180aa3"
],
"pending": [],
"path": "broadcast/Broadcast.t.sol/31337/run-latest.json",
"returns": {},
"timestamp": 1655140035
}

81
lib/forge-std/test/fixtures/config.toml vendored Normal file
View File

@@ -0,0 +1,81 @@
# ------------------------------------------------
# EXAMPLE DEPLOYMENT CONFIG
# ------------------------------------------------
# -- MAINNET -------------------------------------
[mainnet]
endpoint_url = "${MAINNET_RPC}"
[mainnet.bool]
is_live = true
bool_array = [true, false]
[mainnet.address]
weth = "${WETH_MAINNET}"
deps = [
"0x0000000000000000000000000000000000000000",
"0x1111111111111111111111111111111111111111",
]
[mainnet.uint]
number = 1234
number_array = [5678, 9999]
[mainnet.int]
signed_number = -1234
signed_number_array = [-5678, 9999]
[mainnet.bytes32]
word = "0x00000000000000000000000000000000000000000000000000000000000004d2" # 1234
word_array = [
"0x000000000000000000000000000000000000000000000000000000000000162e", # 5678
"0x000000000000000000000000000000000000000000000000000000000000270f", # 9999
]
[mainnet.bytes]
b = "0xabcd"
b_array = ["0xdead", "0xbeef"]
[mainnet.string]
str = "foo"
str_array = ["bar", "baz"]
# -- OPTIMISM ------------------------------------
[optimism]
endpoint_url = "${OPTIMISM_RPC}"
[optimism.bool]
is_live = false
bool_array = [false, true]
[optimism.address]
weth = "${WETH_OPTIMISM}"
deps = [
"0x2222222222222222222222222222222222222222",
"0x3333333333333333333333333333333333333333",
]
[optimism.uint]
number = 9999
number_array = [1234, 5678]
[optimism.int]
signed_number = 9999
signed_number_array = [-1234, -5678]
[optimism.bytes32]
word = "0x000000000000000000000000000000000000000000000000000000000000270f" # 9999
word_array = [
"0x00000000000000000000000000000000000000000000000000000000000004d2", # 1234
"0x000000000000000000000000000000000000000000000000000000000000162e", # 5678
]
[optimism.bytes]
b = "0xdcba"
b_array = ["0xc0ffee", "0xbabe"]
[optimism.string]
str = "alice"
str_array = ["bob", "charlie"]

8
lib/forge-std/test/fixtures/test.json vendored Normal file
View File

@@ -0,0 +1,8 @@
{
"a": 123,
"b": "test",
"c": {
"a": 123,
"b": "test"
}
}

6
lib/forge-std/test/fixtures/test.toml vendored Normal file
View File

@@ -0,0 +1,6 @@
a = 123
b = "test"
[c]
a = 123
b = "test"