// SPDX-License-Identifier: MIT pragma solidity >=0.6.2 <0.9.0; pragma experimental ABIEncoderV2; import "./StdStorage.sol"; import "./Vm.sol"; abstract contract StdCheatsSafe { VmSafe private constant vm = VmSafe(address(uint160(uint256(keccak256("hevm cheat code"))))); /// @dev To hide constructor warnings across solc versions due to different constructor visibility requirements and /// syntaxes, we put the constructor in a private method and assign an unused return value to a variable. This /// forces the method to run during construction, but without declaring an explicit constructor. uint256 private CONSTRUCTOR = _constructor(); struct Chain { // The chain name, using underscores as the separator to match `foundry.toml` conventions. string name; // The chain's Chain ID. uint256 chainId; // 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; } struct Chains { Chain Anvil; Chain Hardhat; Chain Mainnet; Chain Goerli; Chain Sepolia; Chain Optimism; Chain OptimismGoerli; Chain ArbitrumOne; Chain ArbitrumOneGoerli; Chain ArbitrumNova; Chain Polygon; Chain PolygonMumbai; Chain Avalanche; Chain AvalancheFuji; Chain BnbSmartChain; Chain BnbSmartChainTestnet; Chain GnosisChain; } Chains stdChains; // 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; } function _constructor() private returns (uint256) { // Initialize `stdChains` with the defaults. stdChains = Chains({ Anvil: Chain("Anvil", 31337, "http://127.0.0.1:8545"), Hardhat: Chain("Hardhat", 31337, "http://127.0.0.1:8545"), Mainnet: Chain("Mainnet", 1, "https://api.mycryptoapi.com/eth"), Goerli: Chain("Goerli", 5, "https://goerli.infura.io/v3/84842078b09946638c03157f83405213"), // Default Infura key from ethers.js: https://github.com/ethers-io/ethers.js/blob/c80fcddf50a9023486e9f9acb1848aba4c19f7b6/packages/providers/src.ts/infura-provider.ts Sepolia: Chain("Sepolia", 11155111, "https://rpc.sepolia.dev"), Optimism: Chain("Optimism", 10, "https://mainnet.optimism.io"), OptimismGoerli: Chain("OptimismGoerli", 420, "https://goerli.optimism.io"), ArbitrumOne: Chain("ArbitrumOne", 42161, "https://arb1.arbitrum.io/rpc"), ArbitrumOneGoerli: Chain("ArbitrumOneGoerli", 421613, "https://goerli-rollup.arbitrum.io/rpc"), ArbitrumNova: Chain("ArbitrumNova", 42170, "https://nova.arbitrum.io/rpc"), Polygon: Chain("Polygon", 137, "https://polygon-rpc.com"), PolygonMumbai: Chain("PolygonMumbai", 80001, "https://rpc-mumbai.matic.today"), Avalanche: Chain("Avalanche", 43114, "https://api.avax.network/ext/bc/C/rpc"), AvalancheFuji: Chain("AvalancheFuji", 43113, "https://api.avax-test.network/ext/bc/C/rpc"), BnbSmartChain: Chain("BnbSmartChain", 56, "https://bsc-dataseed1.binance.org"), BnbSmartChainTestnet: Chain("BnbSmartChainTestnet", 97, "https://data-seed-prebsc-1-s1.binance.org:8545"), GnosisChain: Chain("GnosisChain", 100, "https://rpc.gnosischain.com") }); // Loop over RPC URLs in the config file to replace the default RPC URLs (string[2][] memory rpcs) = vm.rpcUrls(); for (uint256 i = 0; i < rpcs.length; i++) { (string memory name, string memory rpcUrl) = (rpcs[i][0], rpcs[i][1]); // forgefmt: disable-start if (isEqual(name, "anvil")) stdChains.Anvil.rpcUrl = rpcUrl; else if (isEqual(name, "hardhat")) stdChains.Hardhat.rpcUrl = rpcUrl; else if (isEqual(name, "mainnet")) stdChains.Mainnet.rpcUrl = rpcUrl; else if (isEqual(name, "goerli")) stdChains.Goerli.rpcUrl = rpcUrl; else if (isEqual(name, "sepolia")) stdChains.Sepolia.rpcUrl = rpcUrl; else if (isEqual(name, "optimism")) stdChains.Optimism.rpcUrl = rpcUrl; else if (isEqual(name, "optimism_goerli", "optimism-goerli")) stdChains.OptimismGoerli.rpcUrl = rpcUrl; else if (isEqual(name, "arbitrum_one", "arbitrum-one")) stdChains.ArbitrumOne.rpcUrl = rpcUrl; else if (isEqual(name, "arbitrum_one_goerli", "arbitrum-one-goerli")) stdChains.ArbitrumOneGoerli.rpcUrl = rpcUrl; else if (isEqual(name, "arbitrum_nova", "arbitrum-nova")) stdChains.ArbitrumNova.rpcUrl = rpcUrl; else if (isEqual(name, "polygon")) stdChains.Polygon.rpcUrl = rpcUrl; else if (isEqual(name, "polygon_mumbai", "polygon-mumbai")) stdChains.PolygonMumbai.rpcUrl = rpcUrl; else if (isEqual(name, "avalanche")) stdChains.Avalanche.rpcUrl = rpcUrl; else if (isEqual(name, "avalanche_fuji", "avalanche-fuji")) stdChains.AvalancheFuji.rpcUrl = rpcUrl; else if (isEqual(name, "bnb_smart_chain", "bnb-smart-chain")) stdChains.BnbSmartChain.rpcUrl = rpcUrl; else if (isEqual(name, "bnb_smart_chain_testnet", "bnb-smart-chain-testnet")) stdChains.BnbSmartChainTestnet.rpcUrl = rpcUrl; else if (isEqual(name, "gnosis_chain", "gnosis-chain")) stdChains.GnosisChain.rpcUrl = rpcUrl; // forgefmt: disable-end } return 0; } function isEqual(string memory a, string memory b) private pure returns (bool) { return keccak256(abi.encode(a)) == keccak256(abi.encode(b)); } function isEqual(string memory a, string memory b, string memory c) private pure returns (bool) { return keccak256(abi.encode(a)) == keccak256(abi.encode(b)) || keccak256(abi.encode(a)) == keccak256(abi.encode(c)); } function assumeNoPrecompiles(address addr) internal virtual { // Assembly required since `block.chainid` was introduced in 0.8.0. uint256 chainId; assembly { chainId := chainid() } assumeNoPrecompiles(addr, chainId); } function assumeNoPrecompiles(address addr, uint256 chainId) internal 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 should be present on all EVM-compatible chains. vm.assume(addr < address(0x1) || addr > address(0x9)); // forgefmt: disable-start if (chainId == stdChains.Optimism.chainId || chainId == stdChains.OptimismGoerli.chainId) { // 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 == stdChains.ArbitrumOne.chainId || chainId == stdChains.ArbitrumOneGoerli.chainId) { // 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 == stdChains.Avalanche.chainId || chainId == stdChains.AvalancheFuji.chainId) { // 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 readEIP1559ScriptArtifact(string memory path) internal 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 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 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 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 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); } 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) { uint256 number; for (uint256 i = 0; i < b.length; i++) { number = number + uint256(uint8(b[i])) * (2 ** (8 * (b.length - (i + 1)))); } return number; } } // 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"))))); // Skip forward or rewind time by the specified number of seconds function skip(uint256 time) internal virtual { vm.warp(block.timestamp + time); } function rewind(uint256 time) internal virtual { vm.warp(block.timestamp - time); } // Setup a prank from an address that has some ether function hoax(address who) internal virtual { vm.deal(who, 1 << 128); vm.prank(who); } function hoax(address who, uint256 give) internal virtual { vm.deal(who, give); vm.prank(who); } function hoax(address who, address origin) internal virtual { vm.deal(who, 1 << 128); vm.prank(who, origin); } function hoax(address who, address origin, uint256 give) internal virtual { vm.deal(who, give); vm.prank(who, origin); } // Start perpetual prank from an address that has some ether function startHoax(address who) internal virtual { vm.deal(who, 1 << 128); vm.startPrank(who); } function startHoax(address who, uint256 give) internal virtual { vm.deal(who, give); vm.startPrank(who); } // Start perpetual prank from an address that has some ether // tx.origin is set to the origin parameter function startHoax(address who, address origin) internal virtual { vm.deal(who, 1 << 128); vm.startPrank(who, origin); } function startHoax(address who, address origin, uint256 give) internal virtual { vm.deal(who, give); vm.startPrank(who, origin); } function changePrank(address who) internal virtual { vm.stopPrank(); vm.startPrank(who); } // 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); } function deal(address token, address to, uint256 give, bool adjust) internal virtual { // get current balance (, bytes memory balData) = token.call(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.call(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); } } }