feat: Add foundry environment and CI

This commit is contained in:
Diana Carvalho
2025-01-16 18:20:13 +00:00
parent 3127c0e9bb
commit e16d7ccb8e
64 changed files with 27898 additions and 1 deletions

View File

@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
import {StdStorage} from "./StdStorage.sol";
import {Vm, VmSafe} from "./Vm.sol";
abstract contract CommonBase {
// Cheat code address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D.
address internal constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code"))));
// console.sol and console2.sol work by executing a staticcall to this address.
address internal constant CONSOLE = 0x000000000000000000636F6e736F6c652e6c6f67;
// Used when deploying with create2, https://github.com/Arachnid/deterministic-deployment-proxy.
address internal constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
// Default address for tx.origin and msg.sender, 0x1804c8AB1F12E6bbf3894d4083f33e07309d1f38.
address internal constant DEFAULT_SENDER = address(uint160(uint256(keccak256("foundry default caller"))));
// Address of the test contract, deployed by the DEFAULT_SENDER.
address internal constant DEFAULT_TEST_CONTRACT = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f;
// Deterministic deployment address of the Multicall3 contract.
address internal constant MULTICALL3_ADDRESS = 0xcA11bde05977b3631167028862bE2a173976CA11;
// The order of the secp256k1 curve.
uint256 internal constant SECP256K1_ORDER =
115792089237316195423570985008687907852837564279074904382605163141518161494337;
uint256 internal constant UINT256_MAX =
115792089237316195423570985008687907853269984665640564039457584007913129639935;
Vm internal constant vm = Vm(VM_ADDRESS);
StdStorage internal stdstore;
}
abstract contract TestBase is CommonBase {}
abstract contract ScriptBase is CommonBase {
VmSafe internal constant vmSafe = VmSafe(VM_ADDRESS);
}

View File

@@ -0,0 +1,27 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
// 💬 ABOUT
// Forge Std's default Script.
// 🧩 MODULES
import {console} from "./console.sol";
import {console2} from "./console2.sol";
import {safeconsole} from "./safeconsole.sol";
import {StdChains} from "./StdChains.sol";
import {StdCheatsSafe} from "./StdCheats.sol";
import {stdJson} from "./StdJson.sol";
import {stdMath} from "./StdMath.sol";
import {StdStorage, stdStorageSafe} from "./StdStorage.sol";
import {StdStyle} from "./StdStyle.sol";
import {StdUtils} from "./StdUtils.sol";
import {VmSafe} from "./Vm.sol";
// 📦 BOILERPLATE
import {ScriptBase} from "./Base.sol";
// ⭐️ SCRIPT
abstract contract Script is ScriptBase, StdChains, StdCheatsSafe, StdUtils {
// Note: IS_SCRIPT() must return true.
bool public IS_SCRIPT = true;
}

View File

@@ -0,0 +1,669 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
pragma experimental ABIEncoderV2;
import {Vm} from "./Vm.sol";
abstract contract StdAssertions {
Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
event log(string);
event logs(bytes);
event log_address(address);
event log_bytes32(bytes32);
event log_int(int256);
event log_uint(uint256);
event log_bytes(bytes);
event log_string(string);
event log_named_address(string key, address val);
event log_named_bytes32(string key, bytes32 val);
event log_named_decimal_int(string key, int256 val, uint256 decimals);
event log_named_decimal_uint(string key, uint256 val, uint256 decimals);
event log_named_int(string key, int256 val);
event log_named_uint(string key, uint256 val);
event log_named_bytes(string key, bytes val);
event log_named_string(string key, string val);
event log_array(uint256[] val);
event log_array(int256[] val);
event log_array(address[] val);
event log_named_array(string key, uint256[] val);
event log_named_array(string key, int256[] val);
event log_named_array(string key, address[] val);
bool private _failed;
function failed() public view returns (bool) {
if (_failed) {
return _failed;
} else {
return vm.load(address(vm), bytes32("failed")) != bytes32(0);
}
}
function fail() internal virtual {
vm.store(address(vm), bytes32("failed"), bytes32(uint256(1)));
_failed = true;
}
function assertTrue(bool data) internal pure virtual {
vm.assertTrue(data);
}
function assertTrue(bool data, string memory err) internal pure virtual {
vm.assertTrue(data, err);
}
function assertFalse(bool data) internal pure virtual {
vm.assertFalse(data);
}
function assertFalse(bool data, string memory err) internal pure virtual {
vm.assertFalse(data, err);
}
function assertEq(bool left, bool right) internal pure virtual {
vm.assertEq(left, right);
}
function assertEq(bool left, bool right, string memory err) internal pure virtual {
vm.assertEq(left, right, err);
}
function assertEq(uint256 left, uint256 right) internal pure virtual {
vm.assertEq(left, right);
}
function assertEq(uint256 left, uint256 right, string memory err) internal pure virtual {
vm.assertEq(left, right, err);
}
function assertEqDecimal(uint256 left, uint256 right, uint256 decimals) internal pure virtual {
vm.assertEqDecimal(left, right, decimals);
}
function assertEqDecimal(uint256 left, uint256 right, uint256 decimals, string memory err) internal pure virtual {
vm.assertEqDecimal(left, right, decimals, err);
}
function assertEq(int256 left, int256 right) internal pure virtual {
vm.assertEq(left, right);
}
function assertEq(int256 left, int256 right, string memory err) internal pure virtual {
vm.assertEq(left, right, err);
}
function assertEqDecimal(int256 left, int256 right, uint256 decimals) internal pure virtual {
vm.assertEqDecimal(left, right, decimals);
}
function assertEqDecimal(int256 left, int256 right, uint256 decimals, string memory err) internal pure virtual {
vm.assertEqDecimal(left, right, decimals, err);
}
function assertEq(address left, address right) internal pure virtual {
vm.assertEq(left, right);
}
function assertEq(address left, address right, string memory err) internal pure virtual {
vm.assertEq(left, right, err);
}
function assertEq(bytes32 left, bytes32 right) internal pure virtual {
vm.assertEq(left, right);
}
function assertEq(bytes32 left, bytes32 right, string memory err) internal pure virtual {
vm.assertEq(left, right, err);
}
function assertEq32(bytes32 left, bytes32 right) internal pure virtual {
assertEq(left, right);
}
function assertEq32(bytes32 left, bytes32 right, string memory err) internal pure virtual {
assertEq(left, right, err);
}
function assertEq(string memory left, string memory right) internal pure virtual {
vm.assertEq(left, right);
}
function assertEq(string memory left, string memory right, string memory err) internal pure virtual {
vm.assertEq(left, right, err);
}
function assertEq(bytes memory left, bytes memory right) internal pure virtual {
vm.assertEq(left, right);
}
function assertEq(bytes memory left, bytes memory right, string memory err) internal pure virtual {
vm.assertEq(left, right, err);
}
function assertEq(bool[] memory left, bool[] memory right) internal pure virtual {
vm.assertEq(left, right);
}
function assertEq(bool[] memory left, bool[] memory right, string memory err) internal pure virtual {
vm.assertEq(left, right, err);
}
function assertEq(uint256[] memory left, uint256[] memory right) internal pure virtual {
vm.assertEq(left, right);
}
function assertEq(uint256[] memory left, uint256[] memory right, string memory err) internal pure virtual {
vm.assertEq(left, right, err);
}
function assertEq(int256[] memory left, int256[] memory right) internal pure virtual {
vm.assertEq(left, right);
}
function assertEq(int256[] memory left, int256[] memory right, string memory err) internal pure virtual {
vm.assertEq(left, right, err);
}
function assertEq(address[] memory left, address[] memory right) internal pure virtual {
vm.assertEq(left, right);
}
function assertEq(address[] memory left, address[] memory right, string memory err) internal pure virtual {
vm.assertEq(left, right, err);
}
function assertEq(bytes32[] memory left, bytes32[] memory right) internal pure virtual {
vm.assertEq(left, right);
}
function assertEq(bytes32[] memory left, bytes32[] memory right, string memory err) internal pure virtual {
vm.assertEq(left, right, err);
}
function assertEq(string[] memory left, string[] memory right) internal pure virtual {
vm.assertEq(left, right);
}
function assertEq(string[] memory left, string[] memory right, string memory err) internal pure virtual {
vm.assertEq(left, right, err);
}
function assertEq(bytes[] memory left, bytes[] memory right) internal pure virtual {
vm.assertEq(left, right);
}
function assertEq(bytes[] memory left, bytes[] memory right, string memory err) internal pure virtual {
vm.assertEq(left, right, err);
}
// Legacy helper
function assertEqUint(uint256 left, uint256 right) internal pure virtual {
assertEq(left, right);
}
function assertNotEq(bool left, bool right) internal pure virtual {
vm.assertNotEq(left, right);
}
function assertNotEq(bool left, bool right, string memory err) internal pure virtual {
vm.assertNotEq(left, right, err);
}
function assertNotEq(uint256 left, uint256 right) internal pure virtual {
vm.assertNotEq(left, right);
}
function assertNotEq(uint256 left, uint256 right, string memory err) internal pure virtual {
vm.assertNotEq(left, right, err);
}
function assertNotEqDecimal(uint256 left, uint256 right, uint256 decimals) internal pure virtual {
vm.assertNotEqDecimal(left, right, decimals);
}
function assertNotEqDecimal(uint256 left, uint256 right, uint256 decimals, string memory err)
internal
pure
virtual
{
vm.assertNotEqDecimal(left, right, decimals, err);
}
function assertNotEq(int256 left, int256 right) internal pure virtual {
vm.assertNotEq(left, right);
}
function assertNotEq(int256 left, int256 right, string memory err) internal pure virtual {
vm.assertNotEq(left, right, err);
}
function assertNotEqDecimal(int256 left, int256 right, uint256 decimals) internal pure virtual {
vm.assertNotEqDecimal(left, right, decimals);
}
function assertNotEqDecimal(int256 left, int256 right, uint256 decimals, string memory err) internal pure virtual {
vm.assertNotEqDecimal(left, right, decimals, err);
}
function assertNotEq(address left, address right) internal pure virtual {
vm.assertNotEq(left, right);
}
function assertNotEq(address left, address right, string memory err) internal pure virtual {
vm.assertNotEq(left, right, err);
}
function assertNotEq(bytes32 left, bytes32 right) internal pure virtual {
vm.assertNotEq(left, right);
}
function assertNotEq(bytes32 left, bytes32 right, string memory err) internal pure virtual {
vm.assertNotEq(left, right, err);
}
function assertNotEq32(bytes32 left, bytes32 right) internal pure virtual {
assertNotEq(left, right);
}
function assertNotEq32(bytes32 left, bytes32 right, string memory err) internal pure virtual {
assertNotEq(left, right, err);
}
function assertNotEq(string memory left, string memory right) internal pure virtual {
vm.assertNotEq(left, right);
}
function assertNotEq(string memory left, string memory right, string memory err) internal pure virtual {
vm.assertNotEq(left, right, err);
}
function assertNotEq(bytes memory left, bytes memory right) internal pure virtual {
vm.assertNotEq(left, right);
}
function assertNotEq(bytes memory left, bytes memory right, string memory err) internal pure virtual {
vm.assertNotEq(left, right, err);
}
function assertNotEq(bool[] memory left, bool[] memory right) internal pure virtual {
vm.assertNotEq(left, right);
}
function assertNotEq(bool[] memory left, bool[] memory right, string memory err) internal pure virtual {
vm.assertNotEq(left, right, err);
}
function assertNotEq(uint256[] memory left, uint256[] memory right) internal pure virtual {
vm.assertNotEq(left, right);
}
function assertNotEq(uint256[] memory left, uint256[] memory right, string memory err) internal pure virtual {
vm.assertNotEq(left, right, err);
}
function assertNotEq(int256[] memory left, int256[] memory right) internal pure virtual {
vm.assertNotEq(left, right);
}
function assertNotEq(int256[] memory left, int256[] memory right, string memory err) internal pure virtual {
vm.assertNotEq(left, right, err);
}
function assertNotEq(address[] memory left, address[] memory right) internal pure virtual {
vm.assertNotEq(left, right);
}
function assertNotEq(address[] memory left, address[] memory right, string memory err) internal pure virtual {
vm.assertNotEq(left, right, err);
}
function assertNotEq(bytes32[] memory left, bytes32[] memory right) internal pure virtual {
vm.assertNotEq(left, right);
}
function assertNotEq(bytes32[] memory left, bytes32[] memory right, string memory err) internal pure virtual {
vm.assertNotEq(left, right, err);
}
function assertNotEq(string[] memory left, string[] memory right) internal pure virtual {
vm.assertNotEq(left, right);
}
function assertNotEq(string[] memory left, string[] memory right, string memory err) internal pure virtual {
vm.assertNotEq(left, right, err);
}
function assertNotEq(bytes[] memory left, bytes[] memory right) internal pure virtual {
vm.assertNotEq(left, right);
}
function assertNotEq(bytes[] memory left, bytes[] memory right, string memory err) internal pure virtual {
vm.assertNotEq(left, right, err);
}
function assertLt(uint256 left, uint256 right) internal pure virtual {
vm.assertLt(left, right);
}
function assertLt(uint256 left, uint256 right, string memory err) internal pure virtual {
vm.assertLt(left, right, err);
}
function assertLtDecimal(uint256 left, uint256 right, uint256 decimals) internal pure virtual {
vm.assertLtDecimal(left, right, decimals);
}
function assertLtDecimal(uint256 left, uint256 right, uint256 decimals, string memory err) internal pure virtual {
vm.assertLtDecimal(left, right, decimals, err);
}
function assertLt(int256 left, int256 right) internal pure virtual {
vm.assertLt(left, right);
}
function assertLt(int256 left, int256 right, string memory err) internal pure virtual {
vm.assertLt(left, right, err);
}
function assertLtDecimal(int256 left, int256 right, uint256 decimals) internal pure virtual {
vm.assertLtDecimal(left, right, decimals);
}
function assertLtDecimal(int256 left, int256 right, uint256 decimals, string memory err) internal pure virtual {
vm.assertLtDecimal(left, right, decimals, err);
}
function assertGt(uint256 left, uint256 right) internal pure virtual {
vm.assertGt(left, right);
}
function assertGt(uint256 left, uint256 right, string memory err) internal pure virtual {
vm.assertGt(left, right, err);
}
function assertGtDecimal(uint256 left, uint256 right, uint256 decimals) internal pure virtual {
vm.assertGtDecimal(left, right, decimals);
}
function assertGtDecimal(uint256 left, uint256 right, uint256 decimals, string memory err) internal pure virtual {
vm.assertGtDecimal(left, right, decimals, err);
}
function assertGt(int256 left, int256 right) internal pure virtual {
vm.assertGt(left, right);
}
function assertGt(int256 left, int256 right, string memory err) internal pure virtual {
vm.assertGt(left, right, err);
}
function assertGtDecimal(int256 left, int256 right, uint256 decimals) internal pure virtual {
vm.assertGtDecimal(left, right, decimals);
}
function assertGtDecimal(int256 left, int256 right, uint256 decimals, string memory err) internal pure virtual {
vm.assertGtDecimal(left, right, decimals, err);
}
function assertLe(uint256 left, uint256 right) internal pure virtual {
vm.assertLe(left, right);
}
function assertLe(uint256 left, uint256 right, string memory err) internal pure virtual {
vm.assertLe(left, right, err);
}
function assertLeDecimal(uint256 left, uint256 right, uint256 decimals) internal pure virtual {
vm.assertLeDecimal(left, right, decimals);
}
function assertLeDecimal(uint256 left, uint256 right, uint256 decimals, string memory err) internal pure virtual {
vm.assertLeDecimal(left, right, decimals, err);
}
function assertLe(int256 left, int256 right) internal pure virtual {
vm.assertLe(left, right);
}
function assertLe(int256 left, int256 right, string memory err) internal pure virtual {
vm.assertLe(left, right, err);
}
function assertLeDecimal(int256 left, int256 right, uint256 decimals) internal pure virtual {
vm.assertLeDecimal(left, right, decimals);
}
function assertLeDecimal(int256 left, int256 right, uint256 decimals, string memory err) internal pure virtual {
vm.assertLeDecimal(left, right, decimals, err);
}
function assertGe(uint256 left, uint256 right) internal pure virtual {
vm.assertGe(left, right);
}
function assertGe(uint256 left, uint256 right, string memory err) internal pure virtual {
vm.assertGe(left, right, err);
}
function assertGeDecimal(uint256 left, uint256 right, uint256 decimals) internal pure virtual {
vm.assertGeDecimal(left, right, decimals);
}
function assertGeDecimal(uint256 left, uint256 right, uint256 decimals, string memory err) internal pure virtual {
vm.assertGeDecimal(left, right, decimals, err);
}
function assertGe(int256 left, int256 right) internal pure virtual {
vm.assertGe(left, right);
}
function assertGe(int256 left, int256 right, string memory err) internal pure virtual {
vm.assertGe(left, right, err);
}
function assertGeDecimal(int256 left, int256 right, uint256 decimals) internal pure virtual {
vm.assertGeDecimal(left, right, decimals);
}
function assertGeDecimal(int256 left, int256 right, uint256 decimals, string memory err) internal pure virtual {
vm.assertGeDecimal(left, right, decimals, err);
}
function assertApproxEqAbs(uint256 left, uint256 right, uint256 maxDelta) internal pure virtual {
vm.assertApproxEqAbs(left, right, maxDelta);
}
function assertApproxEqAbs(uint256 left, uint256 right, uint256 maxDelta, string memory err)
internal
pure
virtual
{
vm.assertApproxEqAbs(left, right, maxDelta, err);
}
function assertApproxEqAbsDecimal(uint256 left, uint256 right, uint256 maxDelta, uint256 decimals)
internal
pure
virtual
{
vm.assertApproxEqAbsDecimal(left, right, maxDelta, decimals);
}
function assertApproxEqAbsDecimal(
uint256 left,
uint256 right,
uint256 maxDelta,
uint256 decimals,
string memory err
) internal pure virtual {
vm.assertApproxEqAbsDecimal(left, right, maxDelta, decimals, err);
}
function assertApproxEqAbs(int256 left, int256 right, uint256 maxDelta) internal pure virtual {
vm.assertApproxEqAbs(left, right, maxDelta);
}
function assertApproxEqAbs(int256 left, int256 right, uint256 maxDelta, string memory err) internal pure virtual {
vm.assertApproxEqAbs(left, right, maxDelta, err);
}
function assertApproxEqAbsDecimal(int256 left, int256 right, uint256 maxDelta, uint256 decimals)
internal
pure
virtual
{
vm.assertApproxEqAbsDecimal(left, right, maxDelta, decimals);
}
function assertApproxEqAbsDecimal(int256 left, int256 right, uint256 maxDelta, uint256 decimals, string memory err)
internal
pure
virtual
{
vm.assertApproxEqAbsDecimal(left, right, maxDelta, decimals, err);
}
function assertApproxEqRel(
uint256 left,
uint256 right,
uint256 maxPercentDelta // An 18 decimal fixed point number, where 1e18 == 100%
) internal pure virtual {
vm.assertApproxEqRel(left, right, maxPercentDelta);
}
function assertApproxEqRel(
uint256 left,
uint256 right,
uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100%
string memory err
) internal pure virtual {
vm.assertApproxEqRel(left, right, maxPercentDelta, err);
}
function assertApproxEqRelDecimal(
uint256 left,
uint256 right,
uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100%
uint256 decimals
) internal pure virtual {
vm.assertApproxEqRelDecimal(left, right, maxPercentDelta, decimals);
}
function assertApproxEqRelDecimal(
uint256 left,
uint256 right,
uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100%
uint256 decimals,
string memory err
) internal pure virtual {
vm.assertApproxEqRelDecimal(left, right, maxPercentDelta, decimals, err);
}
function assertApproxEqRel(int256 left, int256 right, uint256 maxPercentDelta) internal pure virtual {
vm.assertApproxEqRel(left, right, maxPercentDelta);
}
function assertApproxEqRel(
int256 left,
int256 right,
uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100%
string memory err
) internal pure virtual {
vm.assertApproxEqRel(left, right, maxPercentDelta, err);
}
function assertApproxEqRelDecimal(
int256 left,
int256 right,
uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100%
uint256 decimals
) internal pure virtual {
vm.assertApproxEqRelDecimal(left, right, maxPercentDelta, decimals);
}
function assertApproxEqRelDecimal(
int256 left,
int256 right,
uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100%
uint256 decimals,
string memory err
) internal pure virtual {
vm.assertApproxEqRelDecimal(left, right, maxPercentDelta, decimals, err);
}
// Inherited from DSTest, not used but kept for backwards-compatibility
function checkEq0(bytes memory left, bytes memory right) internal pure returns (bool) {
return keccak256(left) == keccak256(right);
}
function assertEq0(bytes memory left, bytes memory right) internal pure virtual {
assertEq(left, right);
}
function assertEq0(bytes memory left, bytes memory right, string memory err) internal pure virtual {
assertEq(left, right, err);
}
function assertNotEq0(bytes memory left, bytes memory right) internal pure virtual {
assertNotEq(left, right);
}
function assertNotEq0(bytes memory left, bytes memory right, string memory err) internal pure virtual {
assertNotEq(left, right, err);
}
function assertEqCall(address target, bytes memory callDataA, bytes memory callDataB) internal virtual {
assertEqCall(target, callDataA, target, callDataB, true);
}
function assertEqCall(address targetA, bytes memory callDataA, address targetB, bytes memory callDataB)
internal
virtual
{
assertEqCall(targetA, callDataA, targetB, callDataB, true);
}
function assertEqCall(address target, bytes memory callDataA, bytes memory callDataB, bool strictRevertData)
internal
virtual
{
assertEqCall(target, callDataA, target, callDataB, strictRevertData);
}
function assertEqCall(
address targetA,
bytes memory callDataA,
address targetB,
bytes memory callDataB,
bool strictRevertData
) internal virtual {
(bool successA, bytes memory returnDataA) = address(targetA).call(callDataA);
(bool successB, bytes memory returnDataB) = address(targetB).call(callDataB);
if (successA && successB) {
assertEq(returnDataA, returnDataB, "Call return data does not match");
}
if (!successA && !successB && strictRevertData) {
assertEq(returnDataA, returnDataB, "Call revert data does not match");
}
if (!successA && successB) {
emit log("Error: Calls were not equal");
emit log_named_bytes(" Left call revert data", returnDataA);
emit log_named_bytes(" Right call return data", returnDataB);
revert("assertion failed");
}
if (successA && !successB) {
emit log("Error: Calls were not equal");
emit log_named_bytes(" Left call return data", returnDataA);
emit log_named_bytes(" Right call revert data", returnDataB);
revert("assertion failed");
}
}
}

View File

@@ -0,0 +1,263 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
import {VmSafe} from "./Vm.sol";
/**
* StdChains provides information about EVM compatible chains that can be used in scripts/tests.
* For each chain, the chain's name, chain ID, and a default RPC URL are provided. Chains are
* identified by their alias, which is the same as the alias in the `[rpc_endpoints]` section of
* the `foundry.toml` file. For best UX, ensure the alias in the `foundry.toml` file match the
* alias used in this contract, which can be found as the first argument to the
* `setChainWithDefaultRpcUrl` call in the `initializeStdChains` function.
*
* There are two main ways to use this contract:
* 1. Set a chain with `setChain(string memory chainAlias, ChainData memory chain)` or
* `setChain(string memory chainAlias, Chain memory chain)`
* 2. Get a chain with `getChain(string memory chainAlias)` or `getChain(uint256 chainId)`.
*
* The first time either of those are used, chains are initialized with the default set of RPC URLs.
* This is done in `initializeStdChains`, which uses `setChainWithDefaultRpcUrl`. Defaults are recorded in
* `defaultRpcUrls`.
*
* The `setChain` function is straightforward, and it simply saves off the given chain data.
*
* The `getChain` methods use `getChainWithUpdatedRpcUrl` to return a chain. For example, let's say
* we want to retrieve the RPC URL for `mainnet`:
* - If you have specified data with `setChain`, it will return that.
* - If you have configured a mainnet RPC URL in `foundry.toml`, it will return the URL, provided it
* is valid (e.g. a URL is specified, or an environment variable is given and exists).
* - If neither of the above conditions is met, the default data is returned.
*
* Summarizing the above, the prioritization hierarchy is `setChain` -> `foundry.toml` -> environment variable -> defaults.
*/
abstract contract StdChains {
VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code")))));
bool private stdChainsInitialized;
struct ChainData {
string name;
uint256 chainId;
string rpcUrl;
}
struct Chain {
// The chain name.
string name;
// The chain's Chain ID.
uint256 chainId;
// The chain's alias. (i.e. what gets specified in `foundry.toml`).
string chainAlias;
// A default RPC endpoint for this chain.
// NOTE: This default RPC URL is included for convenience to facilitate quick tests and
// experimentation. Do not use this RPC URL for production test suites, CI, or other heavy
// usage as you will be throttled and this is a disservice to others who need this endpoint.
string rpcUrl;
}
// Maps from the chain's alias (matching the alias in the `foundry.toml` file) to chain data.
mapping(string => Chain) private chains;
// Maps from the chain's alias to it's default RPC URL.
mapping(string => string) private defaultRpcUrls;
// Maps from a chain ID to it's alias.
mapping(uint256 => string) private idToAlias;
bool private fallbackToDefaultRpcUrls = true;
// The RPC URL will be fetched from config or defaultRpcUrls if possible.
function getChain(string memory chainAlias) internal virtual returns (Chain memory chain) {
require(bytes(chainAlias).length != 0, "StdChains getChain(string): Chain alias cannot be the empty string.");
initializeStdChains();
chain = chains[chainAlias];
require(
chain.chainId != 0,
string(abi.encodePacked("StdChains getChain(string): Chain with alias \"", chainAlias, "\" not found."))
);
chain = getChainWithUpdatedRpcUrl(chainAlias, chain);
}
function getChain(uint256 chainId) internal virtual returns (Chain memory chain) {
require(chainId != 0, "StdChains getChain(uint256): Chain ID cannot be 0.");
initializeStdChains();
string memory chainAlias = idToAlias[chainId];
chain = chains[chainAlias];
require(
chain.chainId != 0,
string(abi.encodePacked("StdChains getChain(uint256): Chain with ID ", vm.toString(chainId), " not found."))
);
chain = getChainWithUpdatedRpcUrl(chainAlias, chain);
}
// set chain info, with priority to argument's rpcUrl field.
function setChain(string memory chainAlias, ChainData memory chain) internal virtual {
require(
bytes(chainAlias).length != 0,
"StdChains setChain(string,ChainData): Chain alias cannot be the empty string."
);
require(chain.chainId != 0, "StdChains setChain(string,ChainData): Chain ID cannot be 0.");
initializeStdChains();
string memory foundAlias = idToAlias[chain.chainId];
require(
bytes(foundAlias).length == 0 || keccak256(bytes(foundAlias)) == keccak256(bytes(chainAlias)),
string(
abi.encodePacked(
"StdChains setChain(string,ChainData): Chain ID ",
vm.toString(chain.chainId),
" already used by \"",
foundAlias,
"\"."
)
)
);
uint256 oldChainId = chains[chainAlias].chainId;
delete idToAlias[oldChainId];
chains[chainAlias] =
Chain({name: chain.name, chainId: chain.chainId, chainAlias: chainAlias, rpcUrl: chain.rpcUrl});
idToAlias[chain.chainId] = chainAlias;
}
// set chain info, with priority to argument's rpcUrl field.
function setChain(string memory chainAlias, Chain memory chain) internal virtual {
setChain(chainAlias, ChainData({name: chain.name, chainId: chain.chainId, rpcUrl: chain.rpcUrl}));
}
function _toUpper(string memory str) private pure returns (string memory) {
bytes memory strb = bytes(str);
bytes memory copy = new bytes(strb.length);
for (uint256 i = 0; i < strb.length; i++) {
bytes1 b = strb[i];
if (b >= 0x61 && b <= 0x7A) {
copy[i] = bytes1(uint8(b) - 32);
} else {
copy[i] = b;
}
}
return string(copy);
}
// lookup rpcUrl, in descending order of priority:
// current -> config (foundry.toml) -> environment variable -> default
function getChainWithUpdatedRpcUrl(string memory chainAlias, Chain memory chain)
private
view
returns (Chain memory)
{
if (bytes(chain.rpcUrl).length == 0) {
try vm.rpcUrl(chainAlias) returns (string memory configRpcUrl) {
chain.rpcUrl = configRpcUrl;
} catch (bytes memory err) {
string memory envName = string(abi.encodePacked(_toUpper(chainAlias), "_RPC_URL"));
if (fallbackToDefaultRpcUrls) {
chain.rpcUrl = vm.envOr(envName, defaultRpcUrls[chainAlias]);
} else {
chain.rpcUrl = vm.envString(envName);
}
// Distinguish 'not found' from 'cannot read'
// The upstream error thrown by forge for failing cheats changed so we check both the old and new versions
bytes memory oldNotFoundError =
abi.encodeWithSignature("CheatCodeError", string(abi.encodePacked("invalid rpc url ", chainAlias)));
bytes memory newNotFoundError = abi.encodeWithSignature(
"CheatcodeError(string)", string(abi.encodePacked("invalid rpc url: ", chainAlias))
);
bytes32 errHash = keccak256(err);
if (
(errHash != keccak256(oldNotFoundError) && errHash != keccak256(newNotFoundError))
|| bytes(chain.rpcUrl).length == 0
) {
/// @solidity memory-safe-assembly
assembly {
revert(add(32, err), mload(err))
}
}
}
}
return chain;
}
function setFallbackToDefaultRpcUrls(bool useDefault) internal {
fallbackToDefaultRpcUrls = useDefault;
}
function initializeStdChains() private {
if (stdChainsInitialized) return;
stdChainsInitialized = true;
// If adding an RPC here, make sure to test the default RPC URL in `test_Rpcs` in `StdChains.t.sol`
setChainWithDefaultRpcUrl("anvil", ChainData("Anvil", 31337, "http://127.0.0.1:8545"));
setChainWithDefaultRpcUrl(
"mainnet", ChainData("Mainnet", 1, "https://eth-mainnet.alchemyapi.io/v2/pwc5rmJhrdoaSEfimoKEmsvOjKSmPDrP")
);
setChainWithDefaultRpcUrl(
"sepolia", ChainData("Sepolia", 11155111, "https://sepolia.infura.io/v3/b9794ad1ddf84dfb8c34d6bb5dca2001")
);
setChainWithDefaultRpcUrl("holesky", ChainData("Holesky", 17000, "https://rpc.holesky.ethpandaops.io"));
setChainWithDefaultRpcUrl("optimism", ChainData("Optimism", 10, "https://mainnet.optimism.io"));
setChainWithDefaultRpcUrl(
"optimism_sepolia", ChainData("Optimism Sepolia", 11155420, "https://sepolia.optimism.io")
);
setChainWithDefaultRpcUrl("arbitrum_one", ChainData("Arbitrum One", 42161, "https://arb1.arbitrum.io/rpc"));
setChainWithDefaultRpcUrl(
"arbitrum_one_sepolia", ChainData("Arbitrum One Sepolia", 421614, "https://sepolia-rollup.arbitrum.io/rpc")
);
setChainWithDefaultRpcUrl("arbitrum_nova", ChainData("Arbitrum Nova", 42170, "https://nova.arbitrum.io/rpc"));
setChainWithDefaultRpcUrl("polygon", ChainData("Polygon", 137, "https://polygon-rpc.com"));
setChainWithDefaultRpcUrl(
"polygon_amoy", ChainData("Polygon Amoy", 80002, "https://rpc-amoy.polygon.technology")
);
setChainWithDefaultRpcUrl("avalanche", ChainData("Avalanche", 43114, "https://api.avax.network/ext/bc/C/rpc"));
setChainWithDefaultRpcUrl(
"avalanche_fuji", ChainData("Avalanche Fuji", 43113, "https://api.avax-test.network/ext/bc/C/rpc")
);
setChainWithDefaultRpcUrl(
"bnb_smart_chain", ChainData("BNB Smart Chain", 56, "https://bsc-dataseed1.binance.org")
);
setChainWithDefaultRpcUrl(
"bnb_smart_chain_testnet",
ChainData("BNB Smart Chain Testnet", 97, "https://rpc.ankr.com/bsc_testnet_chapel")
);
setChainWithDefaultRpcUrl("gnosis_chain", ChainData("Gnosis Chain", 100, "https://rpc.gnosischain.com"));
setChainWithDefaultRpcUrl("moonbeam", ChainData("Moonbeam", 1284, "https://rpc.api.moonbeam.network"));
setChainWithDefaultRpcUrl(
"moonriver", ChainData("Moonriver", 1285, "https://rpc.api.moonriver.moonbeam.network")
);
setChainWithDefaultRpcUrl("moonbase", ChainData("Moonbase", 1287, "https://rpc.testnet.moonbeam.network"));
setChainWithDefaultRpcUrl("base_sepolia", ChainData("Base Sepolia", 84532, "https://sepolia.base.org"));
setChainWithDefaultRpcUrl("base", ChainData("Base", 8453, "https://mainnet.base.org"));
setChainWithDefaultRpcUrl("blast_sepolia", ChainData("Blast Sepolia", 168587773, "https://sepolia.blast.io"));
setChainWithDefaultRpcUrl("blast", ChainData("Blast", 81457, "https://rpc.blast.io"));
setChainWithDefaultRpcUrl("fantom_opera", ChainData("Fantom Opera", 250, "https://rpc.ankr.com/fantom/"));
setChainWithDefaultRpcUrl(
"fantom_opera_testnet", ChainData("Fantom Opera Testnet", 4002, "https://rpc.ankr.com/fantom_testnet/")
);
setChainWithDefaultRpcUrl("fraxtal", ChainData("Fraxtal", 252, "https://rpc.frax.com"));
setChainWithDefaultRpcUrl("fraxtal_testnet", ChainData("Fraxtal Testnet", 2522, "https://rpc.testnet.frax.com"));
setChainWithDefaultRpcUrl(
"berachain_bartio_testnet", ChainData("Berachain bArtio Testnet", 80084, "https://bartio.rpc.berachain.com")
);
setChainWithDefaultRpcUrl("flare", ChainData("Flare", 14, "https://flare-api.flare.network/ext/C/rpc"));
setChainWithDefaultRpcUrl(
"flare_coston2", ChainData("Flare Coston2", 114, "https://coston2-api.flare.network/ext/C/rpc")
);
}
// set chain info, with priority to chainAlias' rpc url in foundry.toml
function setChainWithDefaultRpcUrl(string memory chainAlias, ChainData memory chain) private {
string memory rpcUrl = chain.rpcUrl;
defaultRpcUrls[chainAlias] = rpcUrl;
chain.rpcUrl = "";
setChain(chainAlias, chain);
chain.rpcUrl = rpcUrl; // restore argument
}
}

View File

@@ -0,0 +1,829 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
pragma experimental ABIEncoderV2;
import {StdStorage, stdStorage} from "./StdStorage.sol";
import {console2} from "./console2.sol";
import {Vm} from "./Vm.sol";
abstract contract StdCheatsSafe {
Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
uint256 private constant UINT256_MAX =
115792089237316195423570985008687907853269984665640564039457584007913129639935;
bool private gasMeteringOff;
// Data structures to parse Transaction objects from the broadcast artifact
// that conform to EIP1559. The Raw structs is what is parsed from the JSON
// and then converted to the one that is used by the user for better UX.
struct RawTx1559 {
string[] arguments;
address contractAddress;
string contractName;
// json value name = function
string functionSig;
bytes32 hash;
// json value name = tx
RawTx1559Detail txDetail;
// json value name = type
string opcode;
}
struct RawTx1559Detail {
AccessList[] accessList;
bytes data;
address from;
bytes gas;
bytes nonce;
address to;
bytes txType;
bytes value;
}
struct Tx1559 {
string[] arguments;
address contractAddress;
string contractName;
string functionSig;
bytes32 hash;
Tx1559Detail txDetail;
string opcode;
}
struct Tx1559Detail {
AccessList[] accessList;
bytes data;
address from;
uint256 gas;
uint256 nonce;
address to;
uint256 txType;
uint256 value;
}
// Data structures to parse Transaction objects from the broadcast artifact
// that DO NOT conform to EIP1559. The Raw structs is what is parsed from the JSON
// and then converted to the one that is used by the user for better UX.
struct TxLegacy {
string[] arguments;
address contractAddress;
string contractName;
string functionSig;
string hash;
string opcode;
TxDetailLegacy transaction;
}
struct TxDetailLegacy {
AccessList[] accessList;
uint256 chainId;
bytes data;
address from;
uint256 gas;
uint256 gasPrice;
bytes32 hash;
uint256 nonce;
bytes1 opcode;
bytes32 r;
bytes32 s;
uint256 txType;
address to;
uint8 v;
uint256 value;
}
struct AccessList {
address accessAddress;
bytes32[] storageKeys;
}
// Data structures to parse Receipt objects from the broadcast artifact.
// The Raw structs is what is parsed from the JSON
// and then converted to the one that is used by the user for better UX.
struct RawReceipt {
bytes32 blockHash;
bytes blockNumber;
address contractAddress;
bytes cumulativeGasUsed;
bytes effectiveGasPrice;
address from;
bytes gasUsed;
RawReceiptLog[] logs;
bytes logsBloom;
bytes status;
address to;
bytes32 transactionHash;
bytes transactionIndex;
}
struct Receipt {
bytes32 blockHash;
uint256 blockNumber;
address contractAddress;
uint256 cumulativeGasUsed;
uint256 effectiveGasPrice;
address from;
uint256 gasUsed;
ReceiptLog[] logs;
bytes logsBloom;
uint256 status;
address to;
bytes32 transactionHash;
uint256 transactionIndex;
}
// Data structures to parse the entire broadcast artifact, assuming the
// transactions conform to EIP1559.
struct EIP1559ScriptArtifact {
string[] libraries;
string path;
string[] pending;
Receipt[] receipts;
uint256 timestamp;
Tx1559[] transactions;
TxReturn[] txReturns;
}
struct RawEIP1559ScriptArtifact {
string[] libraries;
string path;
string[] pending;
RawReceipt[] receipts;
TxReturn[] txReturns;
uint256 timestamp;
RawTx1559[] transactions;
}
struct RawReceiptLog {
// json value = address
address logAddress;
bytes32 blockHash;
bytes blockNumber;
bytes data;
bytes logIndex;
bool removed;
bytes32[] topics;
bytes32 transactionHash;
bytes transactionIndex;
bytes transactionLogIndex;
}
struct ReceiptLog {
// json value = address
address logAddress;
bytes32 blockHash;
uint256 blockNumber;
bytes data;
uint256 logIndex;
bytes32[] topics;
uint256 transactionIndex;
uint256 transactionLogIndex;
bool removed;
}
struct TxReturn {
string internalType;
string value;
}
struct Account {
address addr;
uint256 key;
}
enum AddressType {
Payable,
NonPayable,
ZeroAddress,
Precompile,
ForgeAddress
}
// Checks that `addr` is not blacklisted by token contracts that have a blacklist.
function assumeNotBlacklisted(address token, address addr) internal view virtual {
// Nothing to check if `token` is not a contract.
uint256 tokenCodeSize;
assembly {
tokenCodeSize := extcodesize(token)
}
require(tokenCodeSize > 0, "StdCheats assumeNotBlacklisted(address,address): Token address is not a contract.");
bool success;
bytes memory returnData;
// 4-byte selector for `isBlacklisted(address)`, used by USDC.
(success, returnData) = token.staticcall(abi.encodeWithSelector(0xfe575a87, addr));
vm.assume(!success || abi.decode(returnData, (bool)) == false);
// 4-byte selector for `isBlackListed(address)`, used by USDT.
(success, returnData) = token.staticcall(abi.encodeWithSelector(0xe47d6060, addr));
vm.assume(!success || abi.decode(returnData, (bool)) == false);
}
// Checks that `addr` is not blacklisted by token contracts that have a blacklist.
// This is identical to `assumeNotBlacklisted(address,address)` but with a different name, for
// backwards compatibility, since this name was used in the original PR which already has
// a release. This function can be removed in a future release once we want a breaking change.
function assumeNoBlacklisted(address token, address addr) internal view virtual {
assumeNotBlacklisted(token, addr);
}
function assumeAddressIsNot(address addr, AddressType addressType) internal virtual {
if (addressType == AddressType.Payable) {
assumeNotPayable(addr);
} else if (addressType == AddressType.NonPayable) {
assumePayable(addr);
} else if (addressType == AddressType.ZeroAddress) {
assumeNotZeroAddress(addr);
} else if (addressType == AddressType.Precompile) {
assumeNotPrecompile(addr);
} else if (addressType == AddressType.ForgeAddress) {
assumeNotForgeAddress(addr);
}
}
function assumeAddressIsNot(address addr, AddressType addressType1, AddressType addressType2) internal virtual {
assumeAddressIsNot(addr, addressType1);
assumeAddressIsNot(addr, addressType2);
}
function assumeAddressIsNot(
address addr,
AddressType addressType1,
AddressType addressType2,
AddressType addressType3
) internal virtual {
assumeAddressIsNot(addr, addressType1);
assumeAddressIsNot(addr, addressType2);
assumeAddressIsNot(addr, addressType3);
}
function assumeAddressIsNot(
address addr,
AddressType addressType1,
AddressType addressType2,
AddressType addressType3,
AddressType addressType4
) internal virtual {
assumeAddressIsNot(addr, addressType1);
assumeAddressIsNot(addr, addressType2);
assumeAddressIsNot(addr, addressType3);
assumeAddressIsNot(addr, addressType4);
}
// This function checks whether an address, `addr`, is payable. It works by sending 1 wei to
// `addr` and checking the `success` return value.
// NOTE: This function may result in state changes depending on the fallback/receive logic
// implemented by `addr`, which should be taken into account when this function is used.
function _isPayable(address addr) private returns (bool) {
require(
addr.balance < UINT256_MAX,
"StdCheats _isPayable(address): Balance equals max uint256, so it cannot receive any more funds"
);
uint256 origBalanceTest = address(this).balance;
uint256 origBalanceAddr = address(addr).balance;
vm.deal(address(this), 1);
(bool success,) = payable(addr).call{value: 1}("");
// reset balances
vm.deal(address(this), origBalanceTest);
vm.deal(addr, origBalanceAddr);
return success;
}
// NOTE: This function may result in state changes depending on the fallback/receive logic
// implemented by `addr`, which should be taken into account when this function is used. See the
// `_isPayable` method for more information.
function assumePayable(address addr) internal virtual {
vm.assume(_isPayable(addr));
}
function assumeNotPayable(address addr) internal virtual {
vm.assume(!_isPayable(addr));
}
function assumeNotZeroAddress(address addr) internal pure virtual {
vm.assume(addr != address(0));
}
function assumeNotPrecompile(address addr) internal pure virtual {
assumeNotPrecompile(addr, _pureChainId());
}
function assumeNotPrecompile(address addr, uint256 chainId) internal pure virtual {
// Note: For some chains like Optimism these are technically predeploys (i.e. bytecode placed at a specific
// address), but the same rationale for excluding them applies so we include those too.
// These are reserved by Ethereum and may be on all EVM-compatible chains.
vm.assume(addr < address(0x1) || addr > address(0xff));
// forgefmt: disable-start
if (chainId == 10 || chainId == 420) {
// https://github.com/ethereum-optimism/optimism/blob/eaa371a0184b56b7ca6d9eb9cb0a2b78b2ccd864/op-bindings/predeploys/addresses.go#L6-L21
vm.assume(addr < address(0x4200000000000000000000000000000000000000) || addr > address(0x4200000000000000000000000000000000000800));
} else if (chainId == 42161 || chainId == 421613) {
// https://developer.arbitrum.io/useful-addresses#arbitrum-precompiles-l2-same-on-all-arb-chains
vm.assume(addr < address(0x0000000000000000000000000000000000000064) || addr > address(0x0000000000000000000000000000000000000068));
} else if (chainId == 43114 || chainId == 43113) {
// https://github.com/ava-labs/subnet-evm/blob/47c03fd007ecaa6de2c52ea081596e0a88401f58/precompile/params.go#L18-L59
vm.assume(addr < address(0x0100000000000000000000000000000000000000) || addr > address(0x01000000000000000000000000000000000000ff));
vm.assume(addr < address(0x0200000000000000000000000000000000000000) || addr > address(0x02000000000000000000000000000000000000FF));
vm.assume(addr < address(0x0300000000000000000000000000000000000000) || addr > address(0x03000000000000000000000000000000000000Ff));
}
// forgefmt: disable-end
}
function assumeNotForgeAddress(address addr) internal pure virtual {
// vm, console, and Create2Deployer addresses
vm.assume(
addr != address(vm) && addr != 0x000000000000000000636F6e736F6c652e6c6f67
&& addr != 0x4e59b44847b379578588920cA78FbF26c0B4956C
);
}
function assumeUnusedAddress(address addr) internal view virtual {
uint256 size;
assembly {
size := extcodesize(addr)
}
vm.assume(size == 0);
assumeNotPrecompile(addr);
assumeNotZeroAddress(addr);
assumeNotForgeAddress(addr);
}
function readEIP1559ScriptArtifact(string memory path)
internal
view
virtual
returns (EIP1559ScriptArtifact memory)
{
string memory data = vm.readFile(path);
bytes memory parsedData = vm.parseJson(data);
RawEIP1559ScriptArtifact memory rawArtifact = abi.decode(parsedData, (RawEIP1559ScriptArtifact));
EIP1559ScriptArtifact memory artifact;
artifact.libraries = rawArtifact.libraries;
artifact.path = rawArtifact.path;
artifact.timestamp = rawArtifact.timestamp;
artifact.pending = rawArtifact.pending;
artifact.txReturns = rawArtifact.txReturns;
artifact.receipts = rawToConvertedReceipts(rawArtifact.receipts);
artifact.transactions = rawToConvertedEIPTx1559s(rawArtifact.transactions);
return artifact;
}
function rawToConvertedEIPTx1559s(RawTx1559[] memory rawTxs) internal pure virtual returns (Tx1559[] memory) {
Tx1559[] memory txs = new Tx1559[](rawTxs.length);
for (uint256 i; i < rawTxs.length; i++) {
txs[i] = rawToConvertedEIPTx1559(rawTxs[i]);
}
return txs;
}
function rawToConvertedEIPTx1559(RawTx1559 memory rawTx) internal pure virtual returns (Tx1559 memory) {
Tx1559 memory transaction;
transaction.arguments = rawTx.arguments;
transaction.contractName = rawTx.contractName;
transaction.functionSig = rawTx.functionSig;
transaction.hash = rawTx.hash;
transaction.txDetail = rawToConvertedEIP1559Detail(rawTx.txDetail);
transaction.opcode = rawTx.opcode;
return transaction;
}
function rawToConvertedEIP1559Detail(RawTx1559Detail memory rawDetail)
internal
pure
virtual
returns (Tx1559Detail memory)
{
Tx1559Detail memory txDetail;
txDetail.data = rawDetail.data;
txDetail.from = rawDetail.from;
txDetail.to = rawDetail.to;
txDetail.nonce = _bytesToUint(rawDetail.nonce);
txDetail.txType = _bytesToUint(rawDetail.txType);
txDetail.value = _bytesToUint(rawDetail.value);
txDetail.gas = _bytesToUint(rawDetail.gas);
txDetail.accessList = rawDetail.accessList;
return txDetail;
}
function readTx1559s(string memory path) internal view virtual returns (Tx1559[] memory) {
string memory deployData = vm.readFile(path);
bytes memory parsedDeployData = vm.parseJson(deployData, ".transactions");
RawTx1559[] memory rawTxs = abi.decode(parsedDeployData, (RawTx1559[]));
return rawToConvertedEIPTx1559s(rawTxs);
}
function readTx1559(string memory path, uint256 index) internal view virtual returns (Tx1559 memory) {
string memory deployData = vm.readFile(path);
string memory key = string(abi.encodePacked(".transactions[", vm.toString(index), "]"));
bytes memory parsedDeployData = vm.parseJson(deployData, key);
RawTx1559 memory rawTx = abi.decode(parsedDeployData, (RawTx1559));
return rawToConvertedEIPTx1559(rawTx);
}
// Analogous to readTransactions, but for receipts.
function readReceipts(string memory path) internal view virtual returns (Receipt[] memory) {
string memory deployData = vm.readFile(path);
bytes memory parsedDeployData = vm.parseJson(deployData, ".receipts");
RawReceipt[] memory rawReceipts = abi.decode(parsedDeployData, (RawReceipt[]));
return rawToConvertedReceipts(rawReceipts);
}
function readReceipt(string memory path, uint256 index) internal view virtual returns (Receipt memory) {
string memory deployData = vm.readFile(path);
string memory key = string(abi.encodePacked(".receipts[", vm.toString(index), "]"));
bytes memory parsedDeployData = vm.parseJson(deployData, key);
RawReceipt memory rawReceipt = abi.decode(parsedDeployData, (RawReceipt));
return rawToConvertedReceipt(rawReceipt);
}
function rawToConvertedReceipts(RawReceipt[] memory rawReceipts) internal pure virtual returns (Receipt[] memory) {
Receipt[] memory receipts = new Receipt[](rawReceipts.length);
for (uint256 i; i < rawReceipts.length; i++) {
receipts[i] = rawToConvertedReceipt(rawReceipts[i]);
}
return receipts;
}
function rawToConvertedReceipt(RawReceipt memory rawReceipt) internal pure virtual returns (Receipt memory) {
Receipt memory receipt;
receipt.blockHash = rawReceipt.blockHash;
receipt.to = rawReceipt.to;
receipt.from = rawReceipt.from;
receipt.contractAddress = rawReceipt.contractAddress;
receipt.effectiveGasPrice = _bytesToUint(rawReceipt.effectiveGasPrice);
receipt.cumulativeGasUsed = _bytesToUint(rawReceipt.cumulativeGasUsed);
receipt.gasUsed = _bytesToUint(rawReceipt.gasUsed);
receipt.status = _bytesToUint(rawReceipt.status);
receipt.transactionIndex = _bytesToUint(rawReceipt.transactionIndex);
receipt.blockNumber = _bytesToUint(rawReceipt.blockNumber);
receipt.logs = rawToConvertedReceiptLogs(rawReceipt.logs);
receipt.logsBloom = rawReceipt.logsBloom;
receipt.transactionHash = rawReceipt.transactionHash;
return receipt;
}
function rawToConvertedReceiptLogs(RawReceiptLog[] memory rawLogs)
internal
pure
virtual
returns (ReceiptLog[] memory)
{
ReceiptLog[] memory logs = new ReceiptLog[](rawLogs.length);
for (uint256 i; i < rawLogs.length; i++) {
logs[i].logAddress = rawLogs[i].logAddress;
logs[i].blockHash = rawLogs[i].blockHash;
logs[i].blockNumber = _bytesToUint(rawLogs[i].blockNumber);
logs[i].data = rawLogs[i].data;
logs[i].logIndex = _bytesToUint(rawLogs[i].logIndex);
logs[i].topics = rawLogs[i].topics;
logs[i].transactionIndex = _bytesToUint(rawLogs[i].transactionIndex);
logs[i].transactionLogIndex = _bytesToUint(rawLogs[i].transactionLogIndex);
logs[i].removed = rawLogs[i].removed;
}
return logs;
}
// Deploy a contract by fetching the contract bytecode from
// the artifacts directory
// e.g. `deployCode(code, abi.encode(arg1,arg2,arg3))`
function deployCode(string memory what, bytes memory args) internal virtual returns (address addr) {
bytes memory bytecode = abi.encodePacked(vm.getCode(what), args);
/// @solidity memory-safe-assembly
assembly {
addr := create(0, add(bytecode, 0x20), mload(bytecode))
}
require(addr != address(0), "StdCheats deployCode(string,bytes): Deployment failed.");
}
function deployCode(string memory what) internal virtual returns (address addr) {
bytes memory bytecode = vm.getCode(what);
/// @solidity memory-safe-assembly
assembly {
addr := create(0, add(bytecode, 0x20), mload(bytecode))
}
require(addr != address(0), "StdCheats deployCode(string): Deployment failed.");
}
/// @dev deploy contract with value on construction
function deployCode(string memory what, bytes memory args, uint256 val) internal virtual returns (address addr) {
bytes memory bytecode = abi.encodePacked(vm.getCode(what), args);
/// @solidity memory-safe-assembly
assembly {
addr := create(val, add(bytecode, 0x20), mload(bytecode))
}
require(addr != address(0), "StdCheats deployCode(string,bytes,uint256): Deployment failed.");
}
function deployCode(string memory what, uint256 val) internal virtual returns (address addr) {
bytes memory bytecode = vm.getCode(what);
/// @solidity memory-safe-assembly
assembly {
addr := create(val, add(bytecode, 0x20), mload(bytecode))
}
require(addr != address(0), "StdCheats deployCode(string,uint256): Deployment failed.");
}
// creates a labeled address and the corresponding private key
function makeAddrAndKey(string memory name) internal virtual returns (address addr, uint256 privateKey) {
privateKey = uint256(keccak256(abi.encodePacked(name)));
addr = vm.addr(privateKey);
vm.label(addr, name);
}
// creates a labeled address
function makeAddr(string memory name) internal virtual returns (address addr) {
(addr,) = makeAddrAndKey(name);
}
// Destroys an account immediately, sending the balance to beneficiary.
// Destroying means: balance will be zero, code will be empty, and nonce will be 0
// This is similar to selfdestruct but not identical: selfdestruct destroys code and nonce
// only after tx ends, this will run immediately.
function destroyAccount(address who, address beneficiary) internal virtual {
uint256 currBalance = who.balance;
vm.etch(who, abi.encode());
vm.deal(who, 0);
vm.resetNonce(who);
uint256 beneficiaryBalance = beneficiary.balance;
vm.deal(beneficiary, currBalance + beneficiaryBalance);
}
// creates a struct containing both a labeled address and the corresponding private key
function makeAccount(string memory name) internal virtual returns (Account memory account) {
(account.addr, account.key) = makeAddrAndKey(name);
}
function deriveRememberKey(string memory mnemonic, uint32 index)
internal
virtual
returns (address who, uint256 privateKey)
{
privateKey = vm.deriveKey(mnemonic, index);
who = vm.rememberKey(privateKey);
}
function _bytesToUint(bytes memory b) private pure returns (uint256) {
require(b.length <= 32, "StdCheats _bytesToUint(bytes): Bytes length exceeds 32.");
return abi.decode(abi.encodePacked(new bytes(32 - b.length), b), (uint256));
}
function isFork() internal view virtual returns (bool status) {
try vm.activeFork() {
status = true;
} catch (bytes memory) {}
}
modifier skipWhenForking() {
if (!isFork()) {
_;
}
}
modifier skipWhenNotForking() {
if (isFork()) {
_;
}
}
modifier noGasMetering() {
vm.pauseGasMetering();
// To prevent turning gas monitoring back on with nested functions that use this modifier,
// we check if gasMetering started in the off position. If it did, we don't want to turn
// it back on until we exit the top level function that used the modifier
//
// i.e. funcA() noGasMetering { funcB() }, where funcB has noGasMetering as well.
// funcA will have `gasStartedOff` as false, funcB will have it as true,
// so we only turn metering back on at the end of the funcA
bool gasStartedOff = gasMeteringOff;
gasMeteringOff = true;
_;
// if gas metering was on when this modifier was called, turn it back on at the end
if (!gasStartedOff) {
gasMeteringOff = false;
vm.resumeGasMetering();
}
}
// We use this complex approach of `_viewChainId` and `_pureChainId` to ensure there are no
// compiler warnings when accessing chain ID in any solidity version supported by forge-std. We
// can't simply access the chain ID in a normal view or pure function because the solc View Pure
// Checker changed `chainid` from pure to view in 0.8.0.
function _viewChainId() private view returns (uint256 chainId) {
// Assembly required since `block.chainid` was introduced in 0.8.0.
assembly {
chainId := chainid()
}
address(this); // Silence warnings in older Solc versions.
}
function _pureChainId() private pure returns (uint256 chainId) {
function() internal view returns (uint256) fnIn = _viewChainId;
function() internal pure returns (uint256) pureChainId;
assembly {
pureChainId := fnIn
}
chainId = pureChainId();
}
}
// Wrappers around cheatcodes to avoid footguns
abstract contract StdCheats is StdCheatsSafe {
using stdStorage for StdStorage;
StdStorage private stdstore;
Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
address private constant CONSOLE2_ADDRESS = 0x000000000000000000636F6e736F6c652e6c6f67;
// Skip forward or rewind time by the specified number of seconds
function skip(uint256 time) internal virtual {
vm.warp(vm.getBlockTimestamp() + time);
}
function rewind(uint256 time) internal virtual {
vm.warp(vm.getBlockTimestamp() - time);
}
// Setup a prank from an address that has some ether
function hoax(address msgSender) internal virtual {
vm.deal(msgSender, 1 << 128);
vm.prank(msgSender);
}
function hoax(address msgSender, uint256 give) internal virtual {
vm.deal(msgSender, give);
vm.prank(msgSender);
}
function hoax(address msgSender, address origin) internal virtual {
vm.deal(msgSender, 1 << 128);
vm.prank(msgSender, origin);
}
function hoax(address msgSender, address origin, uint256 give) internal virtual {
vm.deal(msgSender, give);
vm.prank(msgSender, origin);
}
// Start perpetual prank from an address that has some ether
function startHoax(address msgSender) internal virtual {
vm.deal(msgSender, 1 << 128);
vm.startPrank(msgSender);
}
function startHoax(address msgSender, uint256 give) internal virtual {
vm.deal(msgSender, give);
vm.startPrank(msgSender);
}
// Start perpetual prank from an address that has some ether
// tx.origin is set to the origin parameter
function startHoax(address msgSender, address origin) internal virtual {
vm.deal(msgSender, 1 << 128);
vm.startPrank(msgSender, origin);
}
function startHoax(address msgSender, address origin, uint256 give) internal virtual {
vm.deal(msgSender, give);
vm.startPrank(msgSender, origin);
}
function changePrank(address msgSender) internal virtual {
console2_log_StdCheats("changePrank is deprecated. Please use vm.startPrank instead.");
vm.stopPrank();
vm.startPrank(msgSender);
}
function changePrank(address msgSender, address txOrigin) internal virtual {
vm.stopPrank();
vm.startPrank(msgSender, txOrigin);
}
// The same as Vm's `deal`
// Use the alternative signature for ERC20 tokens
function deal(address to, uint256 give) internal virtual {
vm.deal(to, give);
}
// Set the balance of an account for any ERC20 token
// Use the alternative signature to update `totalSupply`
function deal(address token, address to, uint256 give) internal virtual {
deal(token, to, give, false);
}
// Set the balance of an account for any ERC1155 token
// Use the alternative signature to update `totalSupply`
function dealERC1155(address token, address to, uint256 id, uint256 give) internal virtual {
dealERC1155(token, to, id, give, false);
}
function deal(address token, address to, uint256 give, bool adjust) internal virtual {
// get current balance
(, bytes memory balData) = token.staticcall(abi.encodeWithSelector(0x70a08231, to));
uint256 prevBal = abi.decode(balData, (uint256));
// update balance
stdstore.target(token).sig(0x70a08231).with_key(to).checked_write(give);
// update total supply
if (adjust) {
(, bytes memory totSupData) = token.staticcall(abi.encodeWithSelector(0x18160ddd));
uint256 totSup = abi.decode(totSupData, (uint256));
if (give < prevBal) {
totSup -= (prevBal - give);
} else {
totSup += (give - prevBal);
}
stdstore.target(token).sig(0x18160ddd).checked_write(totSup);
}
}
function dealERC1155(address token, address to, uint256 id, uint256 give, bool adjust) internal virtual {
// get current balance
(, bytes memory balData) = token.staticcall(abi.encodeWithSelector(0x00fdd58e, to, id));
uint256 prevBal = abi.decode(balData, (uint256));
// update balance
stdstore.target(token).sig(0x00fdd58e).with_key(to).with_key(id).checked_write(give);
// update total supply
if (adjust) {
(, bytes memory totSupData) = token.staticcall(abi.encodeWithSelector(0xbd85b039, id));
require(
totSupData.length != 0,
"StdCheats deal(address,address,uint,uint,bool): target contract is not ERC1155Supply."
);
uint256 totSup = abi.decode(totSupData, (uint256));
if (give < prevBal) {
totSup -= (prevBal - give);
} else {
totSup += (give - prevBal);
}
stdstore.target(token).sig(0xbd85b039).with_key(id).checked_write(totSup);
}
}
function dealERC721(address token, address to, uint256 id) internal virtual {
// check if token id is already minted and the actual owner.
(bool successMinted, bytes memory ownerData) = token.staticcall(abi.encodeWithSelector(0x6352211e, id));
require(successMinted, "StdCheats deal(address,address,uint,bool): id not minted.");
// get owner current balance
(, bytes memory fromBalData) =
token.staticcall(abi.encodeWithSelector(0x70a08231, abi.decode(ownerData, (address))));
uint256 fromPrevBal = abi.decode(fromBalData, (uint256));
// get new user current balance
(, bytes memory toBalData) = token.staticcall(abi.encodeWithSelector(0x70a08231, to));
uint256 toPrevBal = abi.decode(toBalData, (uint256));
// update balances
stdstore.target(token).sig(0x70a08231).with_key(abi.decode(ownerData, (address))).checked_write(--fromPrevBal);
stdstore.target(token).sig(0x70a08231).with_key(to).checked_write(++toPrevBal);
// update owner
stdstore.target(token).sig(0x6352211e).with_key(id).checked_write(to);
}
function deployCodeTo(string memory what, address where) internal virtual {
deployCodeTo(what, "", 0, where);
}
function deployCodeTo(string memory what, bytes memory args, address where) internal virtual {
deployCodeTo(what, args, 0, where);
}
function deployCodeTo(string memory what, bytes memory args, uint256 value, address where) internal virtual {
bytes memory creationCode = vm.getCode(what);
vm.etch(where, abi.encodePacked(creationCode, args));
(bool success, bytes memory runtimeBytecode) = where.call{value: value}("");
require(success, "StdCheats deployCodeTo(string,bytes,uint256,address): Failed to create runtime bytecode.");
vm.etch(where, runtimeBytecode);
}
// Used to prevent the compilation of console, which shortens the compilation time when console is not used elsewhere.
function console2_log_StdCheats(string memory p0) private view {
(bool status,) = address(CONSOLE2_ADDRESS).staticcall(abi.encodeWithSignature("log(string)", p0));
status;
}
}

View File

@@ -0,0 +1,15 @@
// SPDX-License-Identifier: MIT
// Panics work for versions >=0.8.0, but we lowered the pragma to make this compatible with Test
pragma solidity >=0.6.2 <0.9.0;
library stdError {
bytes public constant assertionError = abi.encodeWithSignature("Panic(uint256)", 0x01);
bytes public constant arithmeticError = abi.encodeWithSignature("Panic(uint256)", 0x11);
bytes public constant divisionError = abi.encodeWithSignature("Panic(uint256)", 0x12);
bytes public constant enumConversionError = abi.encodeWithSignature("Panic(uint256)", 0x21);
bytes public constant encodeStorageError = abi.encodeWithSignature("Panic(uint256)", 0x22);
bytes public constant popError = abi.encodeWithSignature("Panic(uint256)", 0x31);
bytes public constant indexOOBError = abi.encodeWithSignature("Panic(uint256)", 0x32);
bytes public constant memOverflowError = abi.encodeWithSignature("Panic(uint256)", 0x41);
bytes public constant zeroVarError = abi.encodeWithSignature("Panic(uint256)", 0x51);
}

View File

@@ -0,0 +1,122 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
pragma experimental ABIEncoderV2;
abstract contract StdInvariant {
struct FuzzSelector {
address addr;
bytes4[] selectors;
}
struct FuzzArtifactSelector {
string artifact;
bytes4[] selectors;
}
struct FuzzInterface {
address addr;
string[] artifacts;
}
address[] private _excludedContracts;
address[] private _excludedSenders;
address[] private _targetedContracts;
address[] private _targetedSenders;
string[] private _excludedArtifacts;
string[] private _targetedArtifacts;
FuzzArtifactSelector[] private _targetedArtifactSelectors;
FuzzSelector[] private _excludedSelectors;
FuzzSelector[] private _targetedSelectors;
FuzzInterface[] private _targetedInterfaces;
// Functions for users:
// These are intended to be called in tests.
function excludeContract(address newExcludedContract_) internal {
_excludedContracts.push(newExcludedContract_);
}
function excludeSelector(FuzzSelector memory newExcludedSelector_) internal {
_excludedSelectors.push(newExcludedSelector_);
}
function excludeSender(address newExcludedSender_) internal {
_excludedSenders.push(newExcludedSender_);
}
function excludeArtifact(string memory newExcludedArtifact_) internal {
_excludedArtifacts.push(newExcludedArtifact_);
}
function targetArtifact(string memory newTargetedArtifact_) internal {
_targetedArtifacts.push(newTargetedArtifact_);
}
function targetArtifactSelector(FuzzArtifactSelector memory newTargetedArtifactSelector_) internal {
_targetedArtifactSelectors.push(newTargetedArtifactSelector_);
}
function targetContract(address newTargetedContract_) internal {
_targetedContracts.push(newTargetedContract_);
}
function targetSelector(FuzzSelector memory newTargetedSelector_) internal {
_targetedSelectors.push(newTargetedSelector_);
}
function targetSender(address newTargetedSender_) internal {
_targetedSenders.push(newTargetedSender_);
}
function targetInterface(FuzzInterface memory newTargetedInterface_) internal {
_targetedInterfaces.push(newTargetedInterface_);
}
// Functions for forge:
// These are called by forge to run invariant tests and don't need to be called in tests.
function excludeArtifacts() public view returns (string[] memory excludedArtifacts_) {
excludedArtifacts_ = _excludedArtifacts;
}
function excludeContracts() public view returns (address[] memory excludedContracts_) {
excludedContracts_ = _excludedContracts;
}
function excludeSelectors() public view returns (FuzzSelector[] memory excludedSelectors_) {
excludedSelectors_ = _excludedSelectors;
}
function excludeSenders() public view returns (address[] memory excludedSenders_) {
excludedSenders_ = _excludedSenders;
}
function targetArtifacts() public view returns (string[] memory targetedArtifacts_) {
targetedArtifacts_ = _targetedArtifacts;
}
function targetArtifactSelectors() public view returns (FuzzArtifactSelector[] memory targetedArtifactSelectors_) {
targetedArtifactSelectors_ = _targetedArtifactSelectors;
}
function targetContracts() public view returns (address[] memory targetedContracts_) {
targetedContracts_ = _targetedContracts;
}
function targetSelectors() public view returns (FuzzSelector[] memory targetedSelectors_) {
targetedSelectors_ = _targetedSelectors;
}
function targetSenders() public view returns (address[] memory targetedSenders_) {
targetedSenders_ = _targetedSenders;
}
function targetInterfaces() public view returns (FuzzInterface[] memory targetedInterfaces_) {
targetedInterfaces_ = _targetedInterfaces;
}
}

View File

@@ -0,0 +1,283 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.9.0;
pragma experimental ABIEncoderV2;
import {VmSafe} from "./Vm.sol";
// Helpers for parsing and writing JSON files
// To parse:
// ```
// using stdJson for string;
// string memory json = vm.readFile("<some_path>");
// json.readUint("<json_path>");
// ```
// To write:
// ```
// using stdJson for string;
// 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("<some_path>");
// ```
library stdJson {
VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code")))));
function keyExists(string memory json, string memory key) internal view returns (bool) {
return vm.keyExistsJson(json, key);
}
function parseRaw(string memory json, string memory key) internal pure returns (bytes memory) {
return vm.parseJson(json, key);
}
function readUint(string memory json, string memory key) internal pure returns (uint256) {
return vm.parseJsonUint(json, key);
}
function readUintArray(string memory json, string memory key) internal pure returns (uint256[] memory) {
return vm.parseJsonUintArray(json, key);
}
function readInt(string memory json, string memory key) internal pure returns (int256) {
return vm.parseJsonInt(json, key);
}
function readIntArray(string memory json, string memory key) internal pure returns (int256[] memory) {
return vm.parseJsonIntArray(json, key);
}
function readBytes32(string memory json, string memory key) internal pure returns (bytes32) {
return vm.parseJsonBytes32(json, key);
}
function readBytes32Array(string memory json, string memory key) internal pure returns (bytes32[] memory) {
return vm.parseJsonBytes32Array(json, key);
}
function readString(string memory json, string memory key) internal pure returns (string memory) {
return vm.parseJsonString(json, key);
}
function readStringArray(string memory json, string memory key) internal pure returns (string[] memory) {
return vm.parseJsonStringArray(json, key);
}
function readAddress(string memory json, string memory key) internal pure returns (address) {
return vm.parseJsonAddress(json, key);
}
function readAddressArray(string memory json, string memory key) internal pure returns (address[] memory) {
return vm.parseJsonAddressArray(json, key);
}
function readBool(string memory json, string memory key) internal pure returns (bool) {
return vm.parseJsonBool(json, key);
}
function readBoolArray(string memory json, string memory key) internal pure returns (bool[] memory) {
return vm.parseJsonBoolArray(json, key);
}
function readBytes(string memory json, string memory key) internal pure returns (bytes memory) {
return vm.parseJsonBytes(json, key);
}
function readBytesArray(string memory json, string memory key) internal pure returns (bytes[] memory) {
return vm.parseJsonBytesArray(json, key);
}
function readUintOr(string memory json, string memory key, uint256 defaultValue) internal view returns (uint256) {
return keyExists(json, key) ? readUint(json, key) : defaultValue;
}
function readUintArrayOr(string memory json, string memory key, uint256[] memory defaultValue)
internal
view
returns (uint256[] memory)
{
return keyExists(json, key) ? readUintArray(json, key) : defaultValue;
}
function readIntOr(string memory json, string memory key, int256 defaultValue) internal view returns (int256) {
return keyExists(json, key) ? readInt(json, key) : defaultValue;
}
function readIntArrayOr(string memory json, string memory key, int256[] memory defaultValue)
internal
view
returns (int256[] memory)
{
return keyExists(json, key) ? readIntArray(json, key) : defaultValue;
}
function readBytes32Or(string memory json, string memory key, bytes32 defaultValue)
internal
view
returns (bytes32)
{
return keyExists(json, key) ? readBytes32(json, key) : defaultValue;
}
function readBytes32ArrayOr(string memory json, string memory key, bytes32[] memory defaultValue)
internal
view
returns (bytes32[] memory)
{
return keyExists(json, key) ? readBytes32Array(json, key) : defaultValue;
}
function readStringOr(string memory json, string memory key, string memory defaultValue)
internal
view
returns (string memory)
{
return keyExists(json, key) ? readString(json, key) : defaultValue;
}
function readStringArrayOr(string memory json, string memory key, string[] memory defaultValue)
internal
view
returns (string[] memory)
{
return keyExists(json, key) ? readStringArray(json, key) : defaultValue;
}
function readAddressOr(string memory json, string memory key, address defaultValue)
internal
view
returns (address)
{
return keyExists(json, key) ? readAddress(json, key) : defaultValue;
}
function readAddressArrayOr(string memory json, string memory key, address[] memory defaultValue)
internal
view
returns (address[] memory)
{
return keyExists(json, key) ? readAddressArray(json, key) : defaultValue;
}
function readBoolOr(string memory json, string memory key, bool defaultValue) internal view returns (bool) {
return keyExists(json, key) ? readBool(json, key) : defaultValue;
}
function readBoolArrayOr(string memory json, string memory key, bool[] memory defaultValue)
internal
view
returns (bool[] memory)
{
return keyExists(json, key) ? readBoolArray(json, key) : defaultValue;
}
function readBytesOr(string memory json, string memory key, bytes memory defaultValue)
internal
view
returns (bytes memory)
{
return keyExists(json, key) ? readBytes(json, key) : defaultValue;
}
function readBytesArrayOr(string memory json, string memory key, bytes[] memory defaultValue)
internal
view
returns (bytes[] memory)
{
return keyExists(json, key) ? readBytesArray(json, key) : defaultValue;
}
function serialize(string memory jsonKey, string memory rootObject) internal returns (string memory) {
return vm.serializeJson(jsonKey, rootObject);
}
function serialize(string memory jsonKey, string memory key, bool value) internal returns (string memory) {
return vm.serializeBool(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, bool[] memory value)
internal
returns (string memory)
{
return vm.serializeBool(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, uint256 value) internal returns (string memory) {
return vm.serializeUint(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, uint256[] memory value)
internal
returns (string memory)
{
return vm.serializeUint(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, int256 value) internal returns (string memory) {
return vm.serializeInt(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, int256[] memory value)
internal
returns (string memory)
{
return vm.serializeInt(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, address value) internal returns (string memory) {
return vm.serializeAddress(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, address[] memory value)
internal
returns (string memory)
{
return vm.serializeAddress(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, bytes32 value) internal returns (string memory) {
return vm.serializeBytes32(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, bytes32[] memory value)
internal
returns (string memory)
{
return vm.serializeBytes32(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, bytes memory value) internal returns (string memory) {
return vm.serializeBytes(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, bytes[] memory value)
internal
returns (string memory)
{
return vm.serializeBytes(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, string memory value)
internal
returns (string memory)
{
return vm.serializeString(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, string[] memory value)
internal
returns (string memory)
{
return vm.serializeString(jsonKey, key, value);
}
function write(string memory jsonKey, string memory path) internal {
vm.writeJson(jsonKey, path);
}
function write(string memory jsonKey, string memory path, string memory valueKey) internal {
vm.writeJson(jsonKey, path, valueKey);
}
}

View File

@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
library stdMath {
int256 private constant INT256_MIN = -57896044618658097711785492504343953926634992332820282019728792003956564819968;
function abs(int256 a) internal pure returns (uint256) {
// Required or it will fail when `a = type(int256).min`
if (a == INT256_MIN) {
return 57896044618658097711785492504343953926634992332820282019728792003956564819968;
}
return uint256(a > 0 ? a : -a);
}
function delta(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a - b : b - a;
}
function delta(int256 a, int256 b) internal pure returns (uint256) {
// a and b are of the same sign
// this works thanks to two's complement, the left-most bit is the sign bit
if ((a ^ b) > -1) {
return delta(abs(a), abs(b));
}
// a and b are of opposite signs
return abs(a) + abs(b);
}
function percentDelta(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 absDelta = delta(a, b);
return absDelta * 1e18 / b;
}
function percentDelta(int256 a, int256 b) internal pure returns (uint256) {
uint256 absDelta = delta(a, b);
uint256 absB = abs(b);
return absDelta * 1e18 / absB;
}
}

View File

@@ -0,0 +1,473 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
import {Vm} from "./Vm.sol";
struct FindData {
uint256 slot;
uint256 offsetLeft;
uint256 offsetRight;
bool found;
}
struct StdStorage {
mapping(address => mapping(bytes4 => mapping(bytes32 => FindData))) finds;
bytes32[] _keys;
bytes4 _sig;
uint256 _depth;
address _target;
bytes32 _set;
bool _enable_packed_slots;
bytes _calldata;
}
library stdStorageSafe {
event SlotFound(address who, bytes4 fsig, bytes32 keysHash, uint256 slot);
event WARNING_UninitedSlot(address who, uint256 slot);
Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
uint256 constant UINT256_MAX = 115792089237316195423570985008687907853269984665640564039457584007913129639935;
function sigs(string memory sigStr) internal pure returns (bytes4) {
return bytes4(keccak256(bytes(sigStr)));
}
function getCallParams(StdStorage storage self) internal view returns (bytes memory) {
if (self._calldata.length == 0) {
return flatten(self._keys);
} else {
return self._calldata;
}
}
// Calls target contract with configured parameters
function callTarget(StdStorage storage self) internal view returns (bool, bytes32) {
bytes memory cald = abi.encodePacked(self._sig, getCallParams(self));
(bool success, bytes memory rdat) = self._target.staticcall(cald);
bytes32 result = bytesToBytes32(rdat, 32 * self._depth);
return (success, result);
}
// Tries mutating slot value to determine if the targeted value is stored in it.
// If current value is 0, then we are setting slot value to type(uint256).max
// Otherwise, we set it to 0. That way, return value should always be affected.
function checkSlotMutatesCall(StdStorage storage self, bytes32 slot) internal returns (bool) {
bytes32 prevSlotValue = vm.load(self._target, slot);
(bool success, bytes32 prevReturnValue) = callTarget(self);
bytes32 testVal = prevReturnValue == bytes32(0) ? bytes32(UINT256_MAX) : bytes32(0);
vm.store(self._target, slot, testVal);
(, bytes32 newReturnValue) = callTarget(self);
vm.store(self._target, slot, prevSlotValue);
return (success && (prevReturnValue != newReturnValue));
}
// Tries setting one of the bits in slot to 1 until return value changes.
// Index of resulted bit is an offset packed slot has from left/right side
function findOffset(StdStorage storage self, bytes32 slot, bool left) internal returns (bool, uint256) {
for (uint256 offset = 0; offset < 256; offset++) {
uint256 valueToPut = left ? (1 << (255 - offset)) : (1 << offset);
vm.store(self._target, slot, bytes32(valueToPut));
(bool success, bytes32 data) = callTarget(self);
if (success && (uint256(data) > 0)) {
return (true, offset);
}
}
return (false, 0);
}
function findOffsets(StdStorage storage self, bytes32 slot) internal returns (bool, uint256, uint256) {
bytes32 prevSlotValue = vm.load(self._target, slot);
(bool foundLeft, uint256 offsetLeft) = findOffset(self, slot, true);
(bool foundRight, uint256 offsetRight) = findOffset(self, slot, false);
// `findOffset` may mutate slot value, so we are setting it to initial value
vm.store(self._target, slot, prevSlotValue);
return (foundLeft && foundRight, offsetLeft, offsetRight);
}
function find(StdStorage storage self) internal returns (FindData storage) {
return find(self, true);
}
/// @notice find an arbitrary storage slot given a function sig, input data, address of the contract and a value to check against
// slot complexity:
// if flat, will be bytes32(uint256(uint));
// if map, will be keccak256(abi.encode(key, uint(slot)));
// if deep map, will be keccak256(abi.encode(key1, keccak256(abi.encode(key0, uint(slot)))));
// if map struct, will be bytes32(uint256(keccak256(abi.encode(key1, keccak256(abi.encode(key0, uint(slot)))))) + structFieldDepth);
function find(StdStorage storage self, bool _clear) internal returns (FindData storage) {
address who = self._target;
bytes4 fsig = self._sig;
uint256 field_depth = self._depth;
bytes memory params = getCallParams(self);
// calldata to test against
if (self.finds[who][fsig][keccak256(abi.encodePacked(params, field_depth))].found) {
if (_clear) {
clear(self);
}
return self.finds[who][fsig][keccak256(abi.encodePacked(params, field_depth))];
}
vm.record();
(, bytes32 callResult) = callTarget(self);
(bytes32[] memory reads,) = vm.accesses(address(who));
if (reads.length == 0) {
revert("stdStorage find(StdStorage): No storage use detected for target.");
} else {
for (uint256 i = reads.length; --i >= 0;) {
bytes32 prev = vm.load(who, reads[i]);
if (prev == bytes32(0)) {
emit WARNING_UninitedSlot(who, uint256(reads[i]));
}
if (!checkSlotMutatesCall(self, reads[i])) {
continue;
}
(uint256 offsetLeft, uint256 offsetRight) = (0, 0);
if (self._enable_packed_slots) {
bool found;
(found, offsetLeft, offsetRight) = findOffsets(self, reads[i]);
if (!found) {
continue;
}
}
// Check that value between found offsets is equal to the current call result
uint256 curVal = (uint256(prev) & getMaskByOffsets(offsetLeft, offsetRight)) >> offsetRight;
if (uint256(callResult) != curVal) {
continue;
}
emit SlotFound(who, fsig, keccak256(abi.encodePacked(params, field_depth)), uint256(reads[i]));
self.finds[who][fsig][keccak256(abi.encodePacked(params, field_depth))] =
FindData(uint256(reads[i]), offsetLeft, offsetRight, true);
break;
}
}
require(
self.finds[who][fsig][keccak256(abi.encodePacked(params, field_depth))].found,
"stdStorage find(StdStorage): Slot(s) not found."
);
if (_clear) {
clear(self);
}
return self.finds[who][fsig][keccak256(abi.encodePacked(params, field_depth))];
}
function target(StdStorage storage self, address _target) internal returns (StdStorage storage) {
self._target = _target;
return self;
}
function sig(StdStorage storage self, bytes4 _sig) internal returns (StdStorage storage) {
self._sig = _sig;
return self;
}
function sig(StdStorage storage self, string memory _sig) internal returns (StdStorage storage) {
self._sig = sigs(_sig);
return self;
}
function with_calldata(StdStorage storage self, bytes memory _calldata) internal returns (StdStorage storage) {
self._calldata = _calldata;
return self;
}
function with_key(StdStorage storage self, address who) internal returns (StdStorage storage) {
self._keys.push(bytes32(uint256(uint160(who))));
return self;
}
function with_key(StdStorage storage self, uint256 amt) internal returns (StdStorage storage) {
self._keys.push(bytes32(amt));
return self;
}
function with_key(StdStorage storage self, bytes32 key) internal returns (StdStorage storage) {
self._keys.push(key);
return self;
}
function enable_packed_slots(StdStorage storage self) internal returns (StdStorage storage) {
self._enable_packed_slots = true;
return self;
}
function depth(StdStorage storage self, uint256 _depth) internal returns (StdStorage storage) {
self._depth = _depth;
return self;
}
function read(StdStorage storage self) private returns (bytes memory) {
FindData storage data = find(self, false);
uint256 mask = getMaskByOffsets(data.offsetLeft, data.offsetRight);
uint256 value = (uint256(vm.load(self._target, bytes32(data.slot))) & mask) >> data.offsetRight;
clear(self);
return abi.encode(value);
}
function read_bytes32(StdStorage storage self) internal returns (bytes32) {
return abi.decode(read(self), (bytes32));
}
function read_bool(StdStorage storage self) internal returns (bool) {
int256 v = read_int(self);
if (v == 0) return false;
if (v == 1) return true;
revert("stdStorage read_bool(StdStorage): Cannot decode. Make sure you are reading a bool.");
}
function read_address(StdStorage storage self) internal returns (address) {
return abi.decode(read(self), (address));
}
function read_uint(StdStorage storage self) internal returns (uint256) {
return abi.decode(read(self), (uint256));
}
function read_int(StdStorage storage self) internal returns (int256) {
return abi.decode(read(self), (int256));
}
function parent(StdStorage storage self) internal returns (uint256, bytes32) {
address who = self._target;
uint256 field_depth = self._depth;
vm.startMappingRecording();
uint256 child = find(self, true).slot - field_depth;
(bool found, bytes32 key, bytes32 parent_slot) = vm.getMappingKeyAndParentOf(who, bytes32(child));
if (!found) {
revert(
"stdStorage read_bool(StdStorage): Cannot find parent. Make sure you give a slot and startMappingRecording() has been called."
);
}
return (uint256(parent_slot), key);
}
function root(StdStorage storage self) internal returns (uint256) {
address who = self._target;
uint256 field_depth = self._depth;
vm.startMappingRecording();
uint256 child = find(self, true).slot - field_depth;
bool found;
bytes32 root_slot;
bytes32 parent_slot;
(found,, parent_slot) = vm.getMappingKeyAndParentOf(who, bytes32(child));
if (!found) {
revert(
"stdStorage read_bool(StdStorage): Cannot find parent. Make sure you give a slot and startMappingRecording() has been called."
);
}
while (found) {
root_slot = parent_slot;
(found,, parent_slot) = vm.getMappingKeyAndParentOf(who, bytes32(root_slot));
}
return uint256(root_slot);
}
function bytesToBytes32(bytes memory b, uint256 offset) private pure returns (bytes32) {
bytes32 out;
uint256 max = b.length > 32 ? 32 : b.length;
for (uint256 i = 0; i < max; i++) {
out |= bytes32(b[offset + i] & 0xFF) >> (i * 8);
}
return out;
}
function flatten(bytes32[] memory b) private pure returns (bytes memory) {
bytes memory result = new bytes(b.length * 32);
for (uint256 i = 0; i < b.length; i++) {
bytes32 k = b[i];
/// @solidity memory-safe-assembly
assembly {
mstore(add(result, add(32, mul(32, i))), k)
}
}
return result;
}
function clear(StdStorage storage self) internal {
delete self._target;
delete self._sig;
delete self._keys;
delete self._depth;
delete self._enable_packed_slots;
delete self._calldata;
}
// Returns mask which contains non-zero bits for values between `offsetLeft` and `offsetRight`
// (slotValue & mask) >> offsetRight will be the value of the given packed variable
function getMaskByOffsets(uint256 offsetLeft, uint256 offsetRight) internal pure returns (uint256 mask) {
// mask = ((1 << (256 - (offsetRight + offsetLeft))) - 1) << offsetRight;
// using assembly because (1 << 256) causes overflow
assembly {
mask := shl(offsetRight, sub(shl(sub(256, add(offsetRight, offsetLeft)), 1), 1))
}
}
// Returns slot value with updated packed variable.
function getUpdatedSlotValue(bytes32 curValue, uint256 varValue, uint256 offsetLeft, uint256 offsetRight)
internal
pure
returns (bytes32 newValue)
{
return bytes32((uint256(curValue) & ~getMaskByOffsets(offsetLeft, offsetRight)) | (varValue << offsetRight));
}
}
library stdStorage {
Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code")))));
function sigs(string memory sigStr) internal pure returns (bytes4) {
return stdStorageSafe.sigs(sigStr);
}
function find(StdStorage storage self) internal returns (uint256) {
return find(self, true);
}
function find(StdStorage storage self, bool _clear) internal returns (uint256) {
return stdStorageSafe.find(self, _clear).slot;
}
function target(StdStorage storage self, address _target) internal returns (StdStorage storage) {
return stdStorageSafe.target(self, _target);
}
function sig(StdStorage storage self, bytes4 _sig) internal returns (StdStorage storage) {
return stdStorageSafe.sig(self, _sig);
}
function sig(StdStorage storage self, string memory _sig) internal returns (StdStorage storage) {
return stdStorageSafe.sig(self, _sig);
}
function with_key(StdStorage storage self, address who) internal returns (StdStorage storage) {
return stdStorageSafe.with_key(self, who);
}
function with_key(StdStorage storage self, uint256 amt) internal returns (StdStorage storage) {
return stdStorageSafe.with_key(self, amt);
}
function with_key(StdStorage storage self, bytes32 key) internal returns (StdStorage storage) {
return stdStorageSafe.with_key(self, key);
}
function with_calldata(StdStorage storage self, bytes memory _calldata) internal returns (StdStorage storage) {
return stdStorageSafe.with_calldata(self, _calldata);
}
function enable_packed_slots(StdStorage storage self) internal returns (StdStorage storage) {
return stdStorageSafe.enable_packed_slots(self);
}
function depth(StdStorage storage self, uint256 _depth) internal returns (StdStorage storage) {
return stdStorageSafe.depth(self, _depth);
}
function clear(StdStorage storage self) internal {
stdStorageSafe.clear(self);
}
function checked_write(StdStorage storage self, address who) internal {
checked_write(self, bytes32(uint256(uint160(who))));
}
function checked_write(StdStorage storage self, uint256 amt) internal {
checked_write(self, bytes32(amt));
}
function checked_write_int(StdStorage storage self, int256 val) internal {
checked_write(self, bytes32(uint256(val)));
}
function checked_write(StdStorage storage self, bool write) internal {
bytes32 t;
/// @solidity memory-safe-assembly
assembly {
t := write
}
checked_write(self, t);
}
function checked_write(StdStorage storage self, bytes32 set) internal {
address who = self._target;
bytes4 fsig = self._sig;
uint256 field_depth = self._depth;
bytes memory params = stdStorageSafe.getCallParams(self);
if (!self.finds[who][fsig][keccak256(abi.encodePacked(params, field_depth))].found) {
find(self, false);
}
FindData storage data = self.finds[who][fsig][keccak256(abi.encodePacked(params, field_depth))];
if ((data.offsetLeft + data.offsetRight) > 0) {
uint256 maxVal = 2 ** (256 - (data.offsetLeft + data.offsetRight));
require(
uint256(set) < maxVal,
string(
abi.encodePacked(
"stdStorage find(StdStorage): Packed slot. We can't fit value greater than ",
vm.toString(maxVal)
)
)
);
}
bytes32 curVal = vm.load(who, bytes32(data.slot));
bytes32 valToSet = stdStorageSafe.getUpdatedSlotValue(curVal, uint256(set), data.offsetLeft, data.offsetRight);
vm.store(who, bytes32(data.slot), valToSet);
(bool success, bytes32 callResult) = stdStorageSafe.callTarget(self);
if (!success || callResult != set) {
vm.store(who, bytes32(data.slot), curVal);
revert("stdStorage find(StdStorage): Failed to write value.");
}
clear(self);
}
function read_bytes32(StdStorage storage self) internal returns (bytes32) {
return stdStorageSafe.read_bytes32(self);
}
function read_bool(StdStorage storage self) internal returns (bool) {
return stdStorageSafe.read_bool(self);
}
function read_address(StdStorage storage self) internal returns (address) {
return stdStorageSafe.read_address(self);
}
function read_uint(StdStorage storage self) internal returns (uint256) {
return stdStorageSafe.read_uint(self);
}
function read_int(StdStorage storage self) internal returns (int256) {
return stdStorageSafe.read_int(self);
}
function parent(StdStorage storage self) internal returns (uint256, bytes32) {
return stdStorageSafe.parent(self);
}
function root(StdStorage storage self) internal returns (uint256) {
return stdStorageSafe.root(self);
}
}

View File

@@ -0,0 +1,333 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
import {VmSafe} from "./Vm.sol";
library StdStyle {
VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code")))));
string constant RED = "\u001b[91m";
string constant GREEN = "\u001b[92m";
string constant YELLOW = "\u001b[93m";
string constant BLUE = "\u001b[94m";
string constant MAGENTA = "\u001b[95m";
string constant CYAN = "\u001b[96m";
string constant BOLD = "\u001b[1m";
string constant DIM = "\u001b[2m";
string constant ITALIC = "\u001b[3m";
string constant UNDERLINE = "\u001b[4m";
string constant INVERSE = "\u001b[7m";
string constant RESET = "\u001b[0m";
function styleConcat(string memory style, string memory self) private pure returns (string memory) {
return string(abi.encodePacked(style, self, RESET));
}
function red(string memory self) internal pure returns (string memory) {
return styleConcat(RED, self);
}
function red(uint256 self) internal pure returns (string memory) {
return red(vm.toString(self));
}
function red(int256 self) internal pure returns (string memory) {
return red(vm.toString(self));
}
function red(address self) internal pure returns (string memory) {
return red(vm.toString(self));
}
function red(bool self) internal pure returns (string memory) {
return red(vm.toString(self));
}
function redBytes(bytes memory self) internal pure returns (string memory) {
return red(vm.toString(self));
}
function redBytes32(bytes32 self) internal pure returns (string memory) {
return red(vm.toString(self));
}
function green(string memory self) internal pure returns (string memory) {
return styleConcat(GREEN, self);
}
function green(uint256 self) internal pure returns (string memory) {
return green(vm.toString(self));
}
function green(int256 self) internal pure returns (string memory) {
return green(vm.toString(self));
}
function green(address self) internal pure returns (string memory) {
return green(vm.toString(self));
}
function green(bool self) internal pure returns (string memory) {
return green(vm.toString(self));
}
function greenBytes(bytes memory self) internal pure returns (string memory) {
return green(vm.toString(self));
}
function greenBytes32(bytes32 self) internal pure returns (string memory) {
return green(vm.toString(self));
}
function yellow(string memory self) internal pure returns (string memory) {
return styleConcat(YELLOW, self);
}
function yellow(uint256 self) internal pure returns (string memory) {
return yellow(vm.toString(self));
}
function yellow(int256 self) internal pure returns (string memory) {
return yellow(vm.toString(self));
}
function yellow(address self) internal pure returns (string memory) {
return yellow(vm.toString(self));
}
function yellow(bool self) internal pure returns (string memory) {
return yellow(vm.toString(self));
}
function yellowBytes(bytes memory self) internal pure returns (string memory) {
return yellow(vm.toString(self));
}
function yellowBytes32(bytes32 self) internal pure returns (string memory) {
return yellow(vm.toString(self));
}
function blue(string memory self) internal pure returns (string memory) {
return styleConcat(BLUE, self);
}
function blue(uint256 self) internal pure returns (string memory) {
return blue(vm.toString(self));
}
function blue(int256 self) internal pure returns (string memory) {
return blue(vm.toString(self));
}
function blue(address self) internal pure returns (string memory) {
return blue(vm.toString(self));
}
function blue(bool self) internal pure returns (string memory) {
return blue(vm.toString(self));
}
function blueBytes(bytes memory self) internal pure returns (string memory) {
return blue(vm.toString(self));
}
function blueBytes32(bytes32 self) internal pure returns (string memory) {
return blue(vm.toString(self));
}
function magenta(string memory self) internal pure returns (string memory) {
return styleConcat(MAGENTA, self);
}
function magenta(uint256 self) internal pure returns (string memory) {
return magenta(vm.toString(self));
}
function magenta(int256 self) internal pure returns (string memory) {
return magenta(vm.toString(self));
}
function magenta(address self) internal pure returns (string memory) {
return magenta(vm.toString(self));
}
function magenta(bool self) internal pure returns (string memory) {
return magenta(vm.toString(self));
}
function magentaBytes(bytes memory self) internal pure returns (string memory) {
return magenta(vm.toString(self));
}
function magentaBytes32(bytes32 self) internal pure returns (string memory) {
return magenta(vm.toString(self));
}
function cyan(string memory self) internal pure returns (string memory) {
return styleConcat(CYAN, self);
}
function cyan(uint256 self) internal pure returns (string memory) {
return cyan(vm.toString(self));
}
function cyan(int256 self) internal pure returns (string memory) {
return cyan(vm.toString(self));
}
function cyan(address self) internal pure returns (string memory) {
return cyan(vm.toString(self));
}
function cyan(bool self) internal pure returns (string memory) {
return cyan(vm.toString(self));
}
function cyanBytes(bytes memory self) internal pure returns (string memory) {
return cyan(vm.toString(self));
}
function cyanBytes32(bytes32 self) internal pure returns (string memory) {
return cyan(vm.toString(self));
}
function bold(string memory self) internal pure returns (string memory) {
return styleConcat(BOLD, self);
}
function bold(uint256 self) internal pure returns (string memory) {
return bold(vm.toString(self));
}
function bold(int256 self) internal pure returns (string memory) {
return bold(vm.toString(self));
}
function bold(address self) internal pure returns (string memory) {
return bold(vm.toString(self));
}
function bold(bool self) internal pure returns (string memory) {
return bold(vm.toString(self));
}
function boldBytes(bytes memory self) internal pure returns (string memory) {
return bold(vm.toString(self));
}
function boldBytes32(bytes32 self) internal pure returns (string memory) {
return bold(vm.toString(self));
}
function dim(string memory self) internal pure returns (string memory) {
return styleConcat(DIM, self);
}
function dim(uint256 self) internal pure returns (string memory) {
return dim(vm.toString(self));
}
function dim(int256 self) internal pure returns (string memory) {
return dim(vm.toString(self));
}
function dim(address self) internal pure returns (string memory) {
return dim(vm.toString(self));
}
function dim(bool self) internal pure returns (string memory) {
return dim(vm.toString(self));
}
function dimBytes(bytes memory self) internal pure returns (string memory) {
return dim(vm.toString(self));
}
function dimBytes32(bytes32 self) internal pure returns (string memory) {
return dim(vm.toString(self));
}
function italic(string memory self) internal pure returns (string memory) {
return styleConcat(ITALIC, self);
}
function italic(uint256 self) internal pure returns (string memory) {
return italic(vm.toString(self));
}
function italic(int256 self) internal pure returns (string memory) {
return italic(vm.toString(self));
}
function italic(address self) internal pure returns (string memory) {
return italic(vm.toString(self));
}
function italic(bool self) internal pure returns (string memory) {
return italic(vm.toString(self));
}
function italicBytes(bytes memory self) internal pure returns (string memory) {
return italic(vm.toString(self));
}
function italicBytes32(bytes32 self) internal pure returns (string memory) {
return italic(vm.toString(self));
}
function underline(string memory self) internal pure returns (string memory) {
return styleConcat(UNDERLINE, self);
}
function underline(uint256 self) internal pure returns (string memory) {
return underline(vm.toString(self));
}
function underline(int256 self) internal pure returns (string memory) {
return underline(vm.toString(self));
}
function underline(address self) internal pure returns (string memory) {
return underline(vm.toString(self));
}
function underline(bool self) internal pure returns (string memory) {
return underline(vm.toString(self));
}
function underlineBytes(bytes memory self) internal pure returns (string memory) {
return underline(vm.toString(self));
}
function underlineBytes32(bytes32 self) internal pure returns (string memory) {
return underline(vm.toString(self));
}
function inverse(string memory self) internal pure returns (string memory) {
return styleConcat(INVERSE, self);
}
function inverse(uint256 self) internal pure returns (string memory) {
return inverse(vm.toString(self));
}
function inverse(int256 self) internal pure returns (string memory) {
return inverse(vm.toString(self));
}
function inverse(address self) internal pure returns (string memory) {
return inverse(vm.toString(self));
}
function inverse(bool self) internal pure returns (string memory) {
return inverse(vm.toString(self));
}
function inverseBytes(bytes memory self) internal pure returns (string memory) {
return inverse(vm.toString(self));
}
function inverseBytes32(bytes32 self) internal pure returns (string memory) {
return inverse(vm.toString(self));
}
}

View File

@@ -0,0 +1,283 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.0 <0.9.0;
pragma experimental ABIEncoderV2;
import {VmSafe} from "./Vm.sol";
// Helpers for parsing and writing TOML files
// To parse:
// ```
// using stdToml for string;
// string memory toml = vm.readFile("<some_path>");
// toml.readUint("<json_path>");
// ```
// To write:
// ```
// using stdToml for string;
// 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("<some_path>");
// ```
library stdToml {
VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code")))));
function keyExists(string memory toml, string memory key) internal view returns (bool) {
return vm.keyExistsToml(toml, key);
}
function parseRaw(string memory toml, string memory key) internal pure returns (bytes memory) {
return vm.parseToml(toml, key);
}
function readUint(string memory toml, string memory key) internal pure returns (uint256) {
return vm.parseTomlUint(toml, key);
}
function readUintArray(string memory toml, string memory key) internal pure returns (uint256[] memory) {
return vm.parseTomlUintArray(toml, key);
}
function readInt(string memory toml, string memory key) internal pure returns (int256) {
return vm.parseTomlInt(toml, key);
}
function readIntArray(string memory toml, string memory key) internal pure returns (int256[] memory) {
return vm.parseTomlIntArray(toml, key);
}
function readBytes32(string memory toml, string memory key) internal pure returns (bytes32) {
return vm.parseTomlBytes32(toml, key);
}
function readBytes32Array(string memory toml, string memory key) internal pure returns (bytes32[] memory) {
return vm.parseTomlBytes32Array(toml, key);
}
function readString(string memory toml, string memory key) internal pure returns (string memory) {
return vm.parseTomlString(toml, key);
}
function readStringArray(string memory toml, string memory key) internal pure returns (string[] memory) {
return vm.parseTomlStringArray(toml, key);
}
function readAddress(string memory toml, string memory key) internal pure returns (address) {
return vm.parseTomlAddress(toml, key);
}
function readAddressArray(string memory toml, string memory key) internal pure returns (address[] memory) {
return vm.parseTomlAddressArray(toml, key);
}
function readBool(string memory toml, string memory key) internal pure returns (bool) {
return vm.parseTomlBool(toml, key);
}
function readBoolArray(string memory toml, string memory key) internal pure returns (bool[] memory) {
return vm.parseTomlBoolArray(toml, key);
}
function readBytes(string memory toml, string memory key) internal pure returns (bytes memory) {
return vm.parseTomlBytes(toml, key);
}
function readBytesArray(string memory toml, string memory key) internal pure returns (bytes[] memory) {
return vm.parseTomlBytesArray(toml, key);
}
function readUintOr(string memory toml, string memory key, uint256 defaultValue) internal view returns (uint256) {
return keyExists(toml, key) ? readUint(toml, key) : defaultValue;
}
function readUintArrayOr(string memory toml, string memory key, uint256[] memory defaultValue)
internal
view
returns (uint256[] memory)
{
return keyExists(toml, key) ? readUintArray(toml, key) : defaultValue;
}
function readIntOr(string memory toml, string memory key, int256 defaultValue) internal view returns (int256) {
return keyExists(toml, key) ? readInt(toml, key) : defaultValue;
}
function readIntArrayOr(string memory toml, string memory key, int256[] memory defaultValue)
internal
view
returns (int256[] memory)
{
return keyExists(toml, key) ? readIntArray(toml, key) : defaultValue;
}
function readBytes32Or(string memory toml, string memory key, bytes32 defaultValue)
internal
view
returns (bytes32)
{
return keyExists(toml, key) ? readBytes32(toml, key) : defaultValue;
}
function readBytes32ArrayOr(string memory toml, string memory key, bytes32[] memory defaultValue)
internal
view
returns (bytes32[] memory)
{
return keyExists(toml, key) ? readBytes32Array(toml, key) : defaultValue;
}
function readStringOr(string memory toml, string memory key, string memory defaultValue)
internal
view
returns (string memory)
{
return keyExists(toml, key) ? readString(toml, key) : defaultValue;
}
function readStringArrayOr(string memory toml, string memory key, string[] memory defaultValue)
internal
view
returns (string[] memory)
{
return keyExists(toml, key) ? readStringArray(toml, key) : defaultValue;
}
function readAddressOr(string memory toml, string memory key, address defaultValue)
internal
view
returns (address)
{
return keyExists(toml, key) ? readAddress(toml, key) : defaultValue;
}
function readAddressArrayOr(string memory toml, string memory key, address[] memory defaultValue)
internal
view
returns (address[] memory)
{
return keyExists(toml, key) ? readAddressArray(toml, key) : defaultValue;
}
function readBoolOr(string memory toml, string memory key, bool defaultValue) internal view returns (bool) {
return keyExists(toml, key) ? readBool(toml, key) : defaultValue;
}
function readBoolArrayOr(string memory toml, string memory key, bool[] memory defaultValue)
internal
view
returns (bool[] memory)
{
return keyExists(toml, key) ? readBoolArray(toml, key) : defaultValue;
}
function readBytesOr(string memory toml, string memory key, bytes memory defaultValue)
internal
view
returns (bytes memory)
{
return keyExists(toml, key) ? readBytes(toml, key) : defaultValue;
}
function readBytesArrayOr(string memory toml, string memory key, bytes[] memory defaultValue)
internal
view
returns (bytes[] memory)
{
return keyExists(toml, key) ? readBytesArray(toml, key) : defaultValue;
}
function serialize(string memory jsonKey, string memory rootObject) internal returns (string memory) {
return vm.serializeJson(jsonKey, rootObject);
}
function serialize(string memory jsonKey, string memory key, bool value) internal returns (string memory) {
return vm.serializeBool(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, bool[] memory value)
internal
returns (string memory)
{
return vm.serializeBool(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, uint256 value) internal returns (string memory) {
return vm.serializeUint(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, uint256[] memory value)
internal
returns (string memory)
{
return vm.serializeUint(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, int256 value) internal returns (string memory) {
return vm.serializeInt(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, int256[] memory value)
internal
returns (string memory)
{
return vm.serializeInt(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, address value) internal returns (string memory) {
return vm.serializeAddress(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, address[] memory value)
internal
returns (string memory)
{
return vm.serializeAddress(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, bytes32 value) internal returns (string memory) {
return vm.serializeBytes32(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, bytes32[] memory value)
internal
returns (string memory)
{
return vm.serializeBytes32(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, bytes memory value) internal returns (string memory) {
return vm.serializeBytes(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, bytes[] memory value)
internal
returns (string memory)
{
return vm.serializeBytes(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, string memory value)
internal
returns (string memory)
{
return vm.serializeString(jsonKey, key, value);
}
function serialize(string memory jsonKey, string memory key, string[] memory value)
internal
returns (string memory)
{
return vm.serializeString(jsonKey, key, value);
}
function write(string memory jsonKey, string memory path) internal {
vm.writeToml(jsonKey, path);
}
function write(string memory jsonKey, string memory path, string memory valueKey) internal {
vm.writeToml(jsonKey, path, valueKey);
}
}

View File

@@ -0,0 +1,226 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
pragma experimental ABIEncoderV2;
import {IMulticall3} from "./interfaces/IMulticall3.sol";
import {MockERC20} from "./mocks/MockERC20.sol";
import {MockERC721} from "./mocks/MockERC721.sol";
import {VmSafe} from "./Vm.sol";
abstract contract StdUtils {
/*//////////////////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////////////////*/
IMulticall3 private constant multicall = IMulticall3(0xcA11bde05977b3631167028862bE2a173976CA11);
VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code")))));
address private constant CONSOLE2_ADDRESS = 0x000000000000000000636F6e736F6c652e6c6f67;
uint256 private constant INT256_MIN_ABS =
57896044618658097711785492504343953926634992332820282019728792003956564819968;
uint256 private constant SECP256K1_ORDER =
115792089237316195423570985008687907852837564279074904382605163141518161494337;
uint256 private constant UINT256_MAX =
115792089237316195423570985008687907853269984665640564039457584007913129639935;
// Used by default when deploying with create2, https://github.com/Arachnid/deterministic-deployment-proxy.
address private constant CREATE2_FACTORY = 0x4e59b44847b379578588920cA78FbF26c0B4956C;
/*//////////////////////////////////////////////////////////////////////////
INTERNAL FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
function _bound(uint256 x, uint256 min, uint256 max) internal pure virtual returns (uint256 result) {
require(min <= max, "StdUtils bound(uint256,uint256,uint256): Max is less than min.");
// If x is between min and max, return x directly. This is to ensure that dictionary values
// do not get shifted if the min is nonzero. More info: https://github.com/foundry-rs/forge-std/issues/188
if (x >= min && x <= max) return x;
uint256 size = max - min + 1;
// If the value is 0, 1, 2, 3, wrap that to min, min+1, min+2, min+3. Similarly for the UINT256_MAX side.
// This helps ensure coverage of the min/max values.
if (x <= 3 && size > x) return min + x;
if (x >= UINT256_MAX - 3 && size > UINT256_MAX - x) return max - (UINT256_MAX - x);
// Otherwise, wrap x into the range [min, max], i.e. the range is inclusive.
if (x > max) {
uint256 diff = x - max;
uint256 rem = diff % size;
if (rem == 0) return max;
result = min + rem - 1;
} else if (x < min) {
uint256 diff = min - x;
uint256 rem = diff % size;
if (rem == 0) return min;
result = max - rem + 1;
}
}
function bound(uint256 x, uint256 min, uint256 max) internal pure virtual returns (uint256 result) {
result = _bound(x, min, max);
console2_log_StdUtils("Bound result", result);
}
function _bound(int256 x, int256 min, int256 max) internal pure virtual returns (int256 result) {
require(min <= max, "StdUtils bound(int256,int256,int256): Max is less than min.");
// Shifting all int256 values to uint256 to use _bound function. The range of two types are:
// int256 : -(2**255) ~ (2**255 - 1)
// uint256: 0 ~ (2**256 - 1)
// So, add 2**255, INT256_MIN_ABS to the integer values.
//
// If the given integer value is -2**255, we cannot use `-uint256(-x)` because of the overflow.
// So, use `~uint256(x) + 1` instead.
uint256 _x = x < 0 ? (INT256_MIN_ABS - ~uint256(x) - 1) : (uint256(x) + INT256_MIN_ABS);
uint256 _min = min < 0 ? (INT256_MIN_ABS - ~uint256(min) - 1) : (uint256(min) + INT256_MIN_ABS);
uint256 _max = max < 0 ? (INT256_MIN_ABS - ~uint256(max) - 1) : (uint256(max) + INT256_MIN_ABS);
uint256 y = _bound(_x, _min, _max);
// To move it back to int256 value, subtract INT256_MIN_ABS at here.
result = y < INT256_MIN_ABS ? int256(~(INT256_MIN_ABS - y) + 1) : int256(y - INT256_MIN_ABS);
}
function bound(int256 x, int256 min, int256 max) internal pure virtual returns (int256 result) {
result = _bound(x, min, max);
console2_log_StdUtils("Bound result", vm.toString(result));
}
function boundPrivateKey(uint256 privateKey) internal pure virtual returns (uint256 result) {
result = _bound(privateKey, 1, SECP256K1_ORDER - 1);
}
function bytesToUint(bytes memory b) internal pure virtual returns (uint256) {
require(b.length <= 32, "StdUtils bytesToUint(bytes): Bytes length exceeds 32.");
return abi.decode(abi.encodePacked(new bytes(32 - b.length), b), (uint256));
}
/// @dev Compute the address a contract will be deployed at for a given deployer address and nonce
/// @notice adapted from Solmate implementation (https://github.com/Rari-Capital/solmate/blob/main/src/utils/LibRLP.sol)
function computeCreateAddress(address deployer, uint256 nonce) internal pure virtual returns (address) {
console2_log_StdUtils("computeCreateAddress is deprecated. Please use vm.computeCreateAddress instead.");
return vm.computeCreateAddress(deployer, nonce);
}
function computeCreate2Address(bytes32 salt, bytes32 initcodeHash, address deployer)
internal
pure
virtual
returns (address)
{
console2_log_StdUtils("computeCreate2Address is deprecated. Please use vm.computeCreate2Address instead.");
return vm.computeCreate2Address(salt, initcodeHash, deployer);
}
/// @dev returns the address of a contract created with CREATE2 using the default CREATE2 deployer
function computeCreate2Address(bytes32 salt, bytes32 initCodeHash) internal pure returns (address) {
console2_log_StdUtils("computeCreate2Address is deprecated. Please use vm.computeCreate2Address instead.");
return vm.computeCreate2Address(salt, initCodeHash);
}
/// @dev returns an initialized mock ERC20 contract
function deployMockERC20(string memory name, string memory symbol, uint8 decimals)
internal
returns (MockERC20 mock)
{
mock = new MockERC20();
mock.initialize(name, symbol, decimals);
}
/// @dev returns an initialized mock ERC721 contract
function deployMockERC721(string memory name, string memory symbol) internal returns (MockERC721 mock) {
mock = new MockERC721();
mock.initialize(name, symbol);
}
/// @dev returns the hash of the init code (creation code + no args) used in CREATE2 with no constructor arguments
/// @param creationCode the creation code of a contract C, as returned by type(C).creationCode
function hashInitCode(bytes memory creationCode) internal pure returns (bytes32) {
return hashInitCode(creationCode, "");
}
/// @dev returns the hash of the init code (creation code + ABI-encoded args) used in CREATE2
/// @param creationCode the creation code of a contract C, as returned by type(C).creationCode
/// @param args the ABI-encoded arguments to the constructor of C
function hashInitCode(bytes memory creationCode, bytes memory args) internal pure returns (bytes32) {
return keccak256(abi.encodePacked(creationCode, args));
}
// Performs a single call with Multicall3 to query the ERC-20 token balances of the given addresses.
function getTokenBalances(address token, address[] memory addresses)
internal
virtual
returns (uint256[] memory balances)
{
uint256 tokenCodeSize;
assembly {
tokenCodeSize := extcodesize(token)
}
require(tokenCodeSize > 0, "StdUtils getTokenBalances(address,address[]): Token address is not a contract.");
// ABI encode the aggregate call to Multicall3.
uint256 length = addresses.length;
IMulticall3.Call[] memory calls = new IMulticall3.Call[](length);
for (uint256 i = 0; i < length; ++i) {
// 0x70a08231 = bytes4("balanceOf(address)"))
calls[i] = IMulticall3.Call({target: token, callData: abi.encodeWithSelector(0x70a08231, (addresses[i]))});
}
// Make the aggregate call.
(, bytes[] memory returnData) = multicall.aggregate(calls);
// ABI decode the return data and return the balances.
balances = new uint256[](length);
for (uint256 i = 0; i < length; ++i) {
balances[i] = abi.decode(returnData[i], (uint256));
}
}
/*//////////////////////////////////////////////////////////////////////////
PRIVATE FUNCTIONS
//////////////////////////////////////////////////////////////////////////*/
function addressFromLast20Bytes(bytes32 bytesValue) private pure returns (address) {
return address(uint160(uint256(bytesValue)));
}
// This section is used to prevent the compilation of console, which shortens the compilation time when console is
// not used elsewhere. We also trick the compiler into letting us make the console log methods as `pure` to avoid
// any breaking changes to function signatures.
function _castLogPayloadViewToPure(function(bytes memory) internal view fnIn)
internal
pure
returns (function(bytes memory) internal pure fnOut)
{
assembly {
fnOut := fnIn
}
}
function _sendLogPayload(bytes memory payload) internal pure {
_castLogPayloadViewToPure(_sendLogPayloadView)(payload);
}
function _sendLogPayloadView(bytes memory payload) private view {
uint256 payloadLength = payload.length;
address consoleAddress = CONSOLE2_ADDRESS;
/// @solidity memory-safe-assembly
assembly {
let payloadStart := add(payload, 32)
let r := staticcall(gas(), consoleAddress, payloadStart, payloadLength, 0, 0)
}
}
function console2_log_StdUtils(string memory p0) private pure {
_sendLogPayload(abi.encodeWithSignature("log(string)", p0));
}
function console2_log_StdUtils(string memory p0, uint256 p1) private pure {
_sendLogPayload(abi.encodeWithSignature("log(string,uint256)", p0, p1));
}
function console2_log_StdUtils(string memory p0, string memory p1) private pure {
_sendLogPayload(abi.encodeWithSignature("log(string,string)", p0, p1));
}
}

View File

@@ -0,0 +1,33 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
pragma experimental ABIEncoderV2;
// 💬 ABOUT
// Forge Std's default Test.
// 🧩 MODULES
import {console} from "./console.sol";
import {console2} from "./console2.sol";
import {safeconsole} from "./safeconsole.sol";
import {StdAssertions} from "./StdAssertions.sol";
import {StdChains} from "./StdChains.sol";
import {StdCheats} from "./StdCheats.sol";
import {stdError} from "./StdError.sol";
import {StdInvariant} from "./StdInvariant.sol";
import {stdJson} from "./StdJson.sol";
import {stdMath} from "./StdMath.sol";
import {StdStorage, stdStorage} from "./StdStorage.sol";
import {StdStyle} from "./StdStyle.sol";
import {stdToml} from "./StdToml.sol";
import {StdUtils} from "./StdUtils.sol";
import {Vm} from "./Vm.sol";
// 📦 BOILERPLATE
import {TestBase} from "./Base.sol";
// ⭐️ TEST
abstract contract Test is TestBase, StdAssertions, StdChains, StdCheats, StdInvariant, StdUtils {
// Note: IS_TEST() must return true.
bool public IS_TEST = true;
}

2245
foundry/lib/forge-std/src/Vm.sol generated Normal file

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.4.22 <0.9.0;
import {console as console2} from "./console.sol";

View File

@@ -0,0 +1,105 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;
import "./IERC165.sol";
/// @title ERC-1155 Multi Token Standard
/// @dev See https://eips.ethereum.org/EIPS/eip-1155
/// Note: The ERC-165 identifier for this interface is 0xd9b67a26.
interface IERC1155 is IERC165 {
/// @dev
/// - Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard).
/// - The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).
/// - The `_from` argument MUST be the address of the holder whose balance is decreased.
/// - The `_to` argument MUST be the address of the recipient whose balance is increased.
/// - The `_id` argument MUST be the token type being transferred.
/// - The `_value` argument MUST be the number of tokens the holder balance is decreased by and match what the recipient balance is increased by.
/// - When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
/// - When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
event TransferSingle(
address indexed _operator, address indexed _from, address indexed _to, uint256 _id, uint256 _value
);
/// @dev
/// - Either `TransferSingle` or `TransferBatch` MUST emit when tokens are transferred, including zero value transfers as well as minting or burning (see "Safe Transfer Rules" section of the standard).
/// - The `_operator` argument MUST be the address of an account/contract that is approved to make the transfer (SHOULD be msg.sender).
/// - The `_from` argument MUST be the address of the holder whose balance is decreased.
/// - The `_to` argument MUST be the address of the recipient whose balance is increased.
/// - The `_ids` argument MUST be the list of tokens being transferred.
/// - The `_values` argument MUST be the list of number of tokens (matching the list and order of tokens specified in _ids) the holder balance is decreased by and match what the recipient balance is increased by.
/// - When minting/creating tokens, the `_from` argument MUST be set to `0x0` (i.e. zero address).
/// - When burning/destroying tokens, the `_to` argument MUST be set to `0x0` (i.e. zero address).
event TransferBatch(
address indexed _operator, address indexed _from, address indexed _to, uint256[] _ids, uint256[] _values
);
/// @dev MUST emit when approval for a second party/operator address to manage all tokens for an owner address is enabled or disabled (absence of an event assumes disabled).
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
/// @dev MUST emit when the URI is updated for a token ID. URIs are defined in RFC 3986.
/// The URI MUST point to a JSON file that conforms to the "ERC-1155 Metadata URI JSON Schema".
event URI(string _value, uint256 indexed _id);
/// @notice Transfers `_value` amount of an `_id` from the `_from` address to the `_to` address specified (with safety call).
/// @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
/// - MUST revert if `_to` is the zero address.
/// - MUST revert if balance of holder for token `_id` is lower than the `_value` sent.
/// - MUST revert on any other error.
/// - MUST emit the `TransferSingle` event to reflect the balance change (see "Safe Transfer Rules" section of the standard).
/// - After the above conditions are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call `onERC1155Received` on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
/// @param _from Source address
/// @param _to Target address
/// @param _id ID of the token type
/// @param _value Transfer amount
/// @param _data Additional data with no specified format, MUST be sent unaltered in call to `onERC1155Received` on `_to`
function safeTransferFrom(address _from, address _to, uint256 _id, uint256 _value, bytes calldata _data) external;
/// @notice Transfers `_values` amount(s) of `_ids` from the `_from` address to the `_to` address specified (with safety call).
/// @dev Caller must be approved to manage the tokens being transferred out of the `_from` account (see "Approval" section of the standard).
/// - MUST revert if `_to` is the zero address.
/// - MUST revert if length of `_ids` is not the same as length of `_values`.
/// - MUST revert if any of the balance(s) of the holder(s) for token(s) in `_ids` is lower than the respective amount(s) in `_values` sent to the recipient.
/// - MUST revert on any other error.
/// - MUST emit `TransferSingle` or `TransferBatch` event(s) such that all the balance changes are reflected (see "Safe Transfer Rules" section of the standard).
/// - Balance changes and events MUST follow the ordering of the arrays (_ids[0]/_values[0] before _ids[1]/_values[1], etc).
/// - After the above conditions for the transfer(s) in the batch are met, this function MUST check if `_to` is a smart contract (e.g. code size > 0). If so, it MUST call the relevant `ERC1155TokenReceiver` hook(s) on `_to` and act appropriately (see "Safe Transfer Rules" section of the standard).
/// @param _from Source address
/// @param _to Target address
/// @param _ids IDs of each token type (order and length must match _values array)
/// @param _values Transfer amounts per token type (order and length must match _ids array)
/// @param _data Additional data with no specified format, MUST be sent unaltered in call to the `ERC1155TokenReceiver` hook(s) on `_to`
function safeBatchTransferFrom(
address _from,
address _to,
uint256[] calldata _ids,
uint256[] calldata _values,
bytes calldata _data
) external;
/// @notice Get the balance of an account's tokens.
/// @param _owner The address of the token holder
/// @param _id ID of the token
/// @return The _owner's balance of the token type requested
function balanceOf(address _owner, uint256 _id) external view returns (uint256);
/// @notice Get the balance of multiple account/token pairs
/// @param _owners The addresses of the token holders
/// @param _ids ID of the tokens
/// @return The _owner's balance of the token types requested (i.e. balance for each (owner, id) pair)
function balanceOfBatch(address[] calldata _owners, uint256[] calldata _ids)
external
view
returns (uint256[] memory);
/// @notice Enable or disable approval for a third party ("operator") to manage all of the caller's tokens.
/// @dev MUST emit the ApprovalForAll event on success.
/// @param _operator Address to add to the set of authorized operators
/// @param _approved True if the operator is approved, false to revoke approval
function setApprovalForAll(address _operator, bool _approved) external;
/// @notice Queries the approval status of an operator for a given owner.
/// @param _owner The owner of the tokens
/// @param _operator Address of authorized operator
/// @return True if the operator is approved, false if not
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

View File

@@ -0,0 +1,12 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;
interface IERC165 {
/// @notice Query if a contract implements an interface
/// @param interfaceID The interface identifier, as specified in ERC-165
/// @dev Interface identification is specified in ERC-165. This function
/// uses less than 30,000 gas.
/// @return `true` if the contract implements `interfaceID` and
/// `interfaceID` is not 0xffffffff, `false` otherwise
function supportsInterface(bytes4 interfaceID) external view returns (bool);
}

View File

@@ -0,0 +1,43 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;
/// @dev Interface of the ERC20 standard as defined in the EIP.
/// @dev This includes the optional name, symbol, and decimals metadata.
interface IERC20 {
/// @dev Emitted when `value` tokens are moved from one account (`from`) to another (`to`).
event Transfer(address indexed from, address indexed to, uint256 value);
/// @dev Emitted when the allowance of a `spender` for an `owner` is set, where `value`
/// is the new allowance.
event Approval(address indexed owner, address indexed spender, uint256 value);
/// @notice Returns the amount of tokens in existence.
function totalSupply() external view returns (uint256);
/// @notice Returns the amount of tokens owned by `account`.
function balanceOf(address account) external view returns (uint256);
/// @notice Moves `amount` tokens from the caller's account to `to`.
function transfer(address to, uint256 amount) external returns (bool);
/// @notice Returns the remaining number of tokens that `spender` is allowed
/// to spend on behalf of `owner`
function allowance(address owner, address spender) external view returns (uint256);
/// @notice Sets `amount` as the allowance of `spender` over the caller's tokens.
/// @dev Be aware of front-running risks: https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
function approve(address spender, uint256 amount) external returns (bool);
/// @notice Moves `amount` tokens from `from` to `to` using the allowance mechanism.
/// `amount` is then deducted from the caller's allowance.
function transferFrom(address from, address to, uint256 amount) external returns (bool);
/// @notice Returns the name of the token.
function name() external view returns (string memory);
/// @notice Returns the symbol of the token.
function symbol() external view returns (string memory);
/// @notice Returns the decimals places of the token.
function decimals() external view returns (uint8);
}

View File

@@ -0,0 +1,190 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;
import "./IERC20.sol";
/// @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
/// https://eips.ethereum.org/EIPS/eip-4626
interface IERC4626 is IERC20 {
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares
);
/// @notice Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
/// @dev
/// - MUST be an ERC-20 token contract.
/// - MUST NOT revert.
function asset() external view returns (address assetTokenAddress);
/// @notice Returns the total amount of the underlying asset that is “managed” by Vault.
/// @dev
/// - SHOULD include any compounding that occurs from yield.
/// - MUST be inclusive of any fees that are charged against assets in the Vault.
/// - MUST NOT revert.
function totalAssets() external view returns (uint256 totalManagedAssets);
/// @notice Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
/// scenario where all the conditions are met.
/// @dev
/// - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
/// - MUST NOT show any variations depending on the caller.
/// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
/// - MUST NOT revert.
///
/// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
/// “average-users” price-per-share, meaning what the average user should expect to see when exchanging to and
/// from.
function convertToShares(uint256 assets) external view returns (uint256 shares);
/// @notice Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
/// scenario where all the conditions are met.
/// @dev
/// - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
/// - MUST NOT show any variations depending on the caller.
/// - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
/// - MUST NOT revert.
///
/// NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
/// “average-users” price-per-share, meaning what the average user should expect to see when exchanging to and
/// from.
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/// @notice Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
/// through a deposit call.
/// @dev
/// - MUST return a limited value if receiver is subject to some deposit limit.
/// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
/// - MUST NOT revert.
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
/// @notice Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
/// current on-chain conditions.
/// @dev
/// - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
/// call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
/// in the same transaction.
/// - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
/// deposit would be accepted, regardless if the user has enough tokens approved, etc.
/// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
/// - MUST NOT revert.
///
/// NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
/// share price or some other type of condition, meaning the depositor will lose assets by depositing.
function previewDeposit(uint256 assets) external view returns (uint256 shares);
/// @notice Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
/// @dev
/// - MUST emit the Deposit event.
/// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
/// deposit execution, and are accounted for during deposit.
/// - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
/// approving enough underlying tokens to the Vault contract, etc).
///
/// NOTE: most implementations will require pre-approval of the Vault with the Vaults underlying asset token.
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
/// @notice Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
/// @dev
/// - MUST return a limited value if receiver is subject to some mint limit.
/// - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
/// - MUST NOT revert.
function maxMint(address receiver) external view returns (uint256 maxShares);
/// @notice Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
/// current on-chain conditions.
/// @dev
/// - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
/// in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
/// same transaction.
/// - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
/// would be accepted, regardless if the user has enough tokens approved, etc.
/// - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
/// - MUST NOT revert.
///
/// NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
/// share price or some other type of condition, meaning the depositor will lose assets by minting.
function previewMint(uint256 shares) external view returns (uint256 assets);
/// @notice Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
/// @dev
/// - MUST emit the Deposit event.
/// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
/// execution, and are accounted for during mint.
/// - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
/// approving enough underlying tokens to the Vault contract, etc).
///
/// NOTE: most implementations will require pre-approval of the Vault with the Vaults underlying asset token.
function mint(uint256 shares, address receiver) external returns (uint256 assets);
/// @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
/// Vault, through a withdrawal call.
/// @dev
/// - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
/// - MUST NOT revert.
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
/// @notice Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
/// given current on-chain conditions.
/// @dev
/// - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
/// call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
/// called
/// in the same transaction.
/// - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
/// the withdrawal would be accepted, regardless if the user has enough shares, etc.
/// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
/// - MUST NOT revert.
///
/// NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
/// share price or some other type of condition, meaning the depositor will lose assets by depositing.
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
/// @notice Burns shares from owner and sends exactly assets of underlying tokens to receiver.
/// @dev
/// - MUST emit the Withdraw event.
/// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
/// withdraw execution, and are accounted for during withdrawal.
/// - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
/// not having enough shares, etc).
///
/// Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
/// Those methods should be performed separately.
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
/// @notice Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
/// through a redeem call.
/// @dev
/// - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
/// - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
/// - MUST NOT revert.
function maxRedeem(address owner) external view returns (uint256 maxShares);
/// @notice Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
/// given current on-chain conditions.
/// @dev
/// - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
/// in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
/// same transaction.
/// - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
/// redemption would be accepted, regardless if the user has enough shares, etc.
/// - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
/// - MUST NOT revert.
///
/// NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
/// share price or some other type of condition, meaning the depositor will lose assets by redeeming.
function previewRedeem(uint256 shares) external view returns (uint256 assets);
/// @notice Burns exactly shares from owner and sends assets of underlying tokens to receiver.
/// @dev
/// - MUST emit the Withdraw event.
/// - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
/// redeem execution, and are accounted for during redeem.
/// - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
/// not having enough shares, etc).
///
/// NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
/// Those methods should be performed separately.
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}

View File

@@ -0,0 +1,164 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2;
import "./IERC165.sol";
/// @title ERC-721 Non-Fungible Token Standard
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// Note: the ERC-165 identifier for this interface is 0x80ac58cd.
interface IERC721 is IERC165 {
/// @dev This emits when ownership of any NFT changes by any mechanism.
/// This event emits when NFTs are created (`from` == 0) and destroyed
/// (`to` == 0). Exception: during contract creation, any number of NFTs
/// may be created and assigned without emitting Transfer. At the time of
/// any transfer, the approved address for that NFT (if any) is reset to none.
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
/// @dev This emits when the approved address for an NFT is changed or
/// reaffirmed. The zero address indicates there is no approved address.
/// When a Transfer event emits, this also indicates that the approved
/// address for that NFT (if any) is reset to none.
event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
/// @dev This emits when an operator is enabled or disabled for an owner.
/// The operator can manage all NFTs of the owner.
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
/// @notice Count all NFTs assigned to an owner
/// @dev NFTs assigned to the zero address are considered invalid, and this
/// function throws for queries about the zero address.
/// @param _owner An address for whom to query the balance
/// @return The number of NFTs owned by `_owner`, possibly zero
function balanceOf(address _owner) external view returns (uint256);
/// @notice Find the owner of an NFT
/// @dev NFTs assigned to zero address are considered invalid, and queries
/// about them do throw.
/// @param _tokenId The identifier for an NFT
/// @return The address of the owner of the NFT
function ownerOf(uint256 _tokenId) external view returns (address);
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT. When transfer is complete, this function
/// checks if `_to` is a smart contract (code size > 0). If so, it calls
/// `onERC721Received` on `_to` and throws if the return value is not
/// `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
/// @param data Additional data with no specified format, sent in call to `_to`
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes calldata data) external payable;
/// @notice Transfers the ownership of an NFT from one address to another address
/// @dev This works identically to the other function with an extra data parameter,
/// except this function just sets data to "".
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
/// @notice Transfer ownership of an NFT -- THE CALLER IS RESPONSIBLE
/// TO CONFIRM THAT `_to` IS CAPABLE OF RECEIVING NFTS OR ELSE
/// THEY MAY BE PERMANENTLY LOST
/// @dev Throws unless `msg.sender` is the current owner, an authorized
/// operator, or the approved address for this NFT. Throws if `_from` is
/// not the current owner. Throws if `_to` is the zero address. Throws if
/// `_tokenId` is not a valid NFT.
/// @param _from The current owner of the NFT
/// @param _to The new owner
/// @param _tokenId The NFT to transfer
function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
/// @notice Change or reaffirm the approved address for an NFT
/// @dev The zero address indicates there is no approved address.
/// Throws unless `msg.sender` is the current NFT owner, or an authorized
/// operator of the current owner.
/// @param _approved The new approved NFT controller
/// @param _tokenId The NFT to approve
function approve(address _approved, uint256 _tokenId) external payable;
/// @notice Enable or disable approval for a third party ("operator") to manage
/// all of `msg.sender`'s assets
/// @dev Emits the ApprovalForAll event. The contract MUST allow
/// multiple operators per owner.
/// @param _operator Address to add to the set of authorized operators
/// @param _approved True if the operator is approved, false to revoke approval
function setApprovalForAll(address _operator, bool _approved) external;
/// @notice Get the approved address for a single NFT
/// @dev Throws if `_tokenId` is not a valid NFT.
/// @param _tokenId The NFT to find the approved address for
/// @return The approved address for this NFT, or the zero address if there is none
function getApproved(uint256 _tokenId) external view returns (address);
/// @notice Query if an address is an authorized operator for another address
/// @param _owner The address that owns the NFTs
/// @param _operator The address that acts on behalf of the owner
/// @return True if `_operator` is an approved operator for `_owner`, false otherwise
function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}
/// @dev Note: the ERC-165 identifier for this interface is 0x150b7a02.
interface IERC721TokenReceiver {
/// @notice Handle the receipt of an NFT
/// @dev The ERC721 smart contract calls this function on the recipient
/// after a `transfer`. This function MAY throw to revert and reject the
/// transfer. Return of other than the magic value MUST result in the
/// transaction being reverted.
/// Note: the contract address is always the message sender.
/// @param _operator The address which called `safeTransferFrom` function
/// @param _from The address which previously owned the token
/// @param _tokenId The NFT identifier which is being transferred
/// @param _data Additional data with no specified format
/// @return `bytes4(keccak256("onERC721Received(address,address,uint256,bytes)"))`
/// unless throwing
function onERC721Received(address _operator, address _from, uint256 _tokenId, bytes calldata _data)
external
returns (bytes4);
}
/// @title ERC-721 Non-Fungible Token Standard, optional metadata extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// Note: the ERC-165 identifier for this interface is 0x5b5e139f.
interface IERC721Metadata is IERC721 {
/// @notice A descriptive name for a collection of NFTs in this contract
function name() external view returns (string memory _name);
/// @notice An abbreviated name for NFTs in this contract
function symbol() external view returns (string memory _symbol);
/// @notice A distinct Uniform Resource Identifier (URI) for a given asset.
/// @dev Throws if `_tokenId` is not a valid NFT. URIs are defined in RFC
/// 3986. The URI may point to a JSON file that conforms to the "ERC721
/// Metadata JSON Schema".
function tokenURI(uint256 _tokenId) external view returns (string memory);
}
/// @title ERC-721 Non-Fungible Token Standard, optional enumeration extension
/// @dev See https://eips.ethereum.org/EIPS/eip-721
/// Note: the ERC-165 identifier for this interface is 0x780e9d63.
interface IERC721Enumerable is IERC721 {
/// @notice Count NFTs tracked by this contract
/// @return A count of valid NFTs tracked by this contract, where each one of
/// them has an assigned and queryable owner not equal to the zero address
function totalSupply() external view returns (uint256);
/// @notice Enumerate valid NFTs
/// @dev Throws if `_index` >= `totalSupply()`.
/// @param _index A counter less than `totalSupply()`
/// @return The token identifier for the `_index`th NFT,
/// (sort order not specified)
function tokenByIndex(uint256 _index) external view returns (uint256);
/// @notice Enumerate NFTs assigned to an owner
/// @dev Throws if `_index` >= `balanceOf(_owner)` or if
/// `_owner` is the zero address, representing invalid NFTs.
/// @param _owner An address where we are interested in NFTs owned by them
/// @param _index A counter less than `balanceOf(_owner)`
/// @return The token identifier for the `_index`th NFT assigned to `_owner`,
/// (sort order not specified)
function tokenOfOwnerByIndex(address _owner, uint256 _index) external view returns (uint256);
}

View File

@@ -0,0 +1,73 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
pragma experimental ABIEncoderV2;
interface IMulticall3 {
struct Call {
address target;
bytes callData;
}
struct Call3 {
address target;
bool allowFailure;
bytes callData;
}
struct Call3Value {
address target;
bool allowFailure;
uint256 value;
bytes callData;
}
struct Result {
bool success;
bytes returnData;
}
function aggregate(Call[] calldata calls)
external
payable
returns (uint256 blockNumber, bytes[] memory returnData);
function aggregate3(Call3[] calldata calls) external payable returns (Result[] memory returnData);
function aggregate3Value(Call3Value[] calldata calls) external payable returns (Result[] memory returnData);
function blockAndAggregate(Call[] calldata calls)
external
payable
returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData);
function getBasefee() external view returns (uint256 basefee);
function getBlockHash(uint256 blockNumber) external view returns (bytes32 blockHash);
function getBlockNumber() external view returns (uint256 blockNumber);
function getChainId() external view returns (uint256 chainid);
function getCurrentBlockCoinbase() external view returns (address coinbase);
function getCurrentBlockDifficulty() external view returns (uint256 difficulty);
function getCurrentBlockGasLimit() external view returns (uint256 gaslimit);
function getCurrentBlockTimestamp() external view returns (uint256 timestamp);
function getEthBalance(address addr) external view returns (uint256 balance);
function getLastBlockHash() external view returns (bytes32 blockHash);
function tryAggregate(bool requireSuccess, Call[] calldata calls)
external
payable
returns (Result[] memory returnData);
function tryBlockAndAggregate(bool requireSuccess, Call[] calldata calls)
external
payable
returns (uint256 blockNumber, bytes32 blockHash, Result[] memory returnData);
}

View File

@@ -0,0 +1,234 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
import {IERC20} from "../interfaces/IERC20.sol";
/// @notice This is a mock contract of the ERC20 standard for testing purposes only, it SHOULD NOT be used in production.
/// @dev Forked from: https://github.com/transmissions11/solmate/blob/0384dbaaa4fcb5715738a9254a7c0a4cb62cf458/src/tokens/ERC20.sol
contract MockERC20 is IERC20 {
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/
string internal _name;
string internal _symbol;
uint8 internal _decimals;
function name() external view override returns (string memory) {
return _name;
}
function symbol() external view override returns (string memory) {
return _symbol;
}
function decimals() external view override returns (uint8) {
return _decimals;
}
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal _totalSupply;
mapping(address => uint256) internal _balanceOf;
mapping(address => mapping(address => uint256)) internal _allowance;
function totalSupply() external view override returns (uint256) {
return _totalSupply;
}
function balanceOf(address owner) external view override returns (uint256) {
return _balanceOf[owner];
}
function allowance(address owner, address spender) external view override returns (uint256) {
return _allowance[owner][spender];
}
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/
uint256 internal INITIAL_CHAIN_ID;
bytes32 internal INITIAL_DOMAIN_SEPARATOR;
mapping(address => uint256) public nonces;
/*//////////////////////////////////////////////////////////////
INITIALIZE
//////////////////////////////////////////////////////////////*/
/// @dev A bool to track whether the contract has been initialized.
bool private initialized;
/// @dev To hide constructor warnings across solc versions due to different constructor visibility requirements and
/// syntaxes, we add an initialization function that can be called only once.
function initialize(string memory name_, string memory symbol_, uint8 decimals_) public {
require(!initialized, "ALREADY_INITIALIZED");
_name = name_;
_symbol = symbol_;
_decimals = decimals_;
INITIAL_CHAIN_ID = _pureChainId();
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
initialized = true;
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
_allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
return true;
}
function transfer(address to, uint256 amount) public virtual override returns (bool) {
_balanceOf[msg.sender] = _sub(_balanceOf[msg.sender], amount);
_balanceOf[to] = _add(_balanceOf[to], amount);
emit Transfer(msg.sender, to, amount);
return true;
}
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
uint256 allowed = _allowance[from][msg.sender]; // Saves gas for limited approvals.
if (allowed != ~uint256(0)) _allowance[from][msg.sender] = _sub(allowed, amount);
_balanceOf[from] = _sub(_balanceOf[from], amount);
_balanceOf[to] = _add(_balanceOf[to], amount);
emit Transfer(from, to, amount);
return true;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/
function permit(address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
public
virtual
{
require(deadline >= block.timestamp, "PERMIT_DEADLINE_EXPIRED");
address recoveredAddress = ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress != address(0) && recoveredAddress == owner, "INVALID_SIGNER");
_allowance[recoveredAddress][spender] = value;
emit Approval(owner, spender, value);
}
function DOMAIN_SEPARATOR() public view virtual returns (bytes32) {
return _pureChainId() == INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
function computeDomainSeparator() internal view virtual returns (bytes32) {
return keccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(_name)),
keccak256("1"),
_pureChainId(),
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 amount) internal virtual {
_totalSupply = _add(_totalSupply, amount);
_balanceOf[to] = _add(_balanceOf[to], amount);
emit Transfer(address(0), to, amount);
}
function _burn(address from, uint256 amount) internal virtual {
_balanceOf[from] = _sub(_balanceOf[from], amount);
_totalSupply = _sub(_totalSupply, amount);
emit Transfer(from, address(0), amount);
}
/*//////////////////////////////////////////////////////////////
INTERNAL SAFE MATH LOGIC
//////////////////////////////////////////////////////////////*/
function _add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
require(c >= a, "ERC20: addition overflow");
return c;
}
function _sub(uint256 a, uint256 b) internal pure returns (uint256) {
require(a >= b, "ERC20: subtraction underflow");
return a - b;
}
/*//////////////////////////////////////////////////////////////
HELPERS
//////////////////////////////////////////////////////////////*/
// We use this complex approach of `_viewChainId` and `_pureChainId` to ensure there are no
// compiler warnings when accessing chain ID in any solidity version supported by forge-std. We
// can't simply access the chain ID in a normal view or pure function because the solc View Pure
// Checker changed `chainid` from pure to view in 0.8.0.
function _viewChainId() private view returns (uint256 chainId) {
// Assembly required since `block.chainid` was introduced in 0.8.0.
assembly {
chainId := chainid()
}
address(this); // Silence warnings in older Solc versions.
}
function _pureChainId() private pure returns (uint256 chainId) {
function() internal view returns (uint256) fnIn = _viewChainId;
function() internal pure returns (uint256) pureChainId;
assembly {
pureChainId := fnIn
}
chainId = pureChainId();
}
}

View File

@@ -0,0 +1,231 @@
// SPDX-License-Identifier: MIT
pragma solidity >=0.6.2 <0.9.0;
import {IERC721Metadata, IERC721TokenReceiver} from "../interfaces/IERC721.sol";
/// @notice This is a mock contract of the ERC721 standard for testing purposes only, it SHOULD NOT be used in production.
/// @dev Forked from: https://github.com/transmissions11/solmate/blob/0384dbaaa4fcb5715738a9254a7c0a4cb62cf458/src/tokens/ERC721.sol
contract MockERC721 is IERC721Metadata {
/*//////////////////////////////////////////////////////////////
METADATA STORAGE/LOGIC
//////////////////////////////////////////////////////////////*/
string internal _name;
string internal _symbol;
function name() external view override returns (string memory) {
return _name;
}
function symbol() external view override returns (string memory) {
return _symbol;
}
function tokenURI(uint256 id) public view virtual override returns (string memory) {}
/*//////////////////////////////////////////////////////////////
ERC721 BALANCE/OWNER STORAGE
//////////////////////////////////////////////////////////////*/
mapping(uint256 => address) internal _ownerOf;
mapping(address => uint256) internal _balanceOf;
function ownerOf(uint256 id) public view virtual override returns (address owner) {
require((owner = _ownerOf[id]) != address(0), "NOT_MINTED");
}
function balanceOf(address owner) public view virtual override returns (uint256) {
require(owner != address(0), "ZERO_ADDRESS");
return _balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
ERC721 APPROVAL STORAGE
//////////////////////////////////////////////////////////////*/
mapping(uint256 => address) internal _getApproved;
mapping(address => mapping(address => bool)) internal _isApprovedForAll;
function getApproved(uint256 id) public view virtual override returns (address) {
return _getApproved[id];
}
function isApprovedForAll(address owner, address operator) public view virtual override returns (bool) {
return _isApprovedForAll[owner][operator];
}
/*//////////////////////////////////////////////////////////////
INITIALIZE
//////////////////////////////////////////////////////////////*/
/// @dev A bool to track whether the contract has been initialized.
bool private initialized;
/// @dev To hide constructor warnings across solc versions due to different constructor visibility requirements and
/// syntaxes, we add an initialization function that can be called only once.
function initialize(string memory name_, string memory symbol_) public {
require(!initialized, "ALREADY_INITIALIZED");
_name = name_;
_symbol = symbol_;
initialized = true;
}
/*//////////////////////////////////////////////////////////////
ERC721 LOGIC
//////////////////////////////////////////////////////////////*/
function approve(address spender, uint256 id) public payable virtual override {
address owner = _ownerOf[id];
require(msg.sender == owner || _isApprovedForAll[owner][msg.sender], "NOT_AUTHORIZED");
_getApproved[id] = spender;
emit Approval(owner, spender, id);
}
function setApprovalForAll(address operator, bool approved) public virtual override {
_isApprovedForAll[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function transferFrom(address from, address to, uint256 id) public payable virtual override {
require(from == _ownerOf[id], "WRONG_FROM");
require(to != address(0), "INVALID_RECIPIENT");
require(
msg.sender == from || _isApprovedForAll[from][msg.sender] || msg.sender == _getApproved[id],
"NOT_AUTHORIZED"
);
// Underflow of the sender's balance is impossible because we check for
// ownership above and the recipient's balance can't realistically overflow.
_balanceOf[from]--;
_balanceOf[to]++;
_ownerOf[id] = to;
delete _getApproved[id];
emit Transfer(from, to, id);
}
function safeTransferFrom(address from, address to, uint256 id) public payable virtual override {
transferFrom(from, to, id);
require(
!_isContract(to)
|| IERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, "")
== IERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function safeTransferFrom(address from, address to, uint256 id, bytes memory data)
public
payable
virtual
override
{
transferFrom(from, to, id);
require(
!_isContract(to)
|| IERC721TokenReceiver(to).onERC721Received(msg.sender, from, id, data)
== IERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
/*//////////////////////////////////////////////////////////////
ERC165 LOGIC
//////////////////////////////////////////////////////////////*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == 0x01ffc9a7 // ERC165 Interface ID for ERC165
|| interfaceId == 0x80ac58cd // ERC165 Interface ID for ERC721
|| interfaceId == 0x5b5e139f; // ERC165 Interface ID for ERC721Metadata
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/
function _mint(address to, uint256 id) internal virtual {
require(to != address(0), "INVALID_RECIPIENT");
require(_ownerOf[id] == address(0), "ALREADY_MINTED");
// Counter overflow is incredibly unrealistic.
_balanceOf[to]++;
_ownerOf[id] = to;
emit Transfer(address(0), to, id);
}
function _burn(uint256 id) internal virtual {
address owner = _ownerOf[id];
require(owner != address(0), "NOT_MINTED");
_balanceOf[owner]--;
delete _ownerOf[id];
delete _getApproved[id];
emit Transfer(owner, address(0), id);
}
/*//////////////////////////////////////////////////////////////
INTERNAL SAFE MINT LOGIC
//////////////////////////////////////////////////////////////*/
function _safeMint(address to, uint256 id) internal virtual {
_mint(to, id);
require(
!_isContract(to)
|| IERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, "")
== IERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
function _safeMint(address to, uint256 id, bytes memory data) internal virtual {
_mint(to, id);
require(
!_isContract(to)
|| IERC721TokenReceiver(to).onERC721Received(msg.sender, address(0), id, data)
== IERC721TokenReceiver.onERC721Received.selector,
"UNSAFE_RECIPIENT"
);
}
/*//////////////////////////////////////////////////////////////
HELPERS
//////////////////////////////////////////////////////////////*/
function _isContract(address _addr) private view returns (bool) {
uint256 codeLength;
// Assembly required for versions < 0.8.0 to check extcodesize.
assembly {
codeLength := extcodesize(_addr)
}
return codeLength > 0;
}
}

File diff suppressed because it is too large Load Diff