// SPDX-License-Identifier: MIT pragma solidity >=0.6.0 <0.9.0; pragma experimental ABIEncoderV2; import "./Script.sol"; import "ds-test/test.sol"; // Wrappers around Cheatcodes to avoid footguns abstract contract Test is DSTest, Script { using stdStorage for StdStorage; uint256 internal constant UINT256_MAX = 115792089237316195423570985008687907853269984665640564039457584007913129639935; StdStorage internal stdstore; /*////////////////////////////////////////////////////////////////////////// STD-LOGS //////////////////////////////////////////////////////////////////////////*/ 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); /*////////////////////////////////////////////////////////////////////////// STD-CHEATS //////////////////////////////////////////////////////////////////////////*/ // Skip forward or rewind time by the specified number of seconds function skip(uint256 time) internal { vm.warp(block.timestamp + time); } function rewind(uint256 time) internal { vm.warp(block.timestamp - time); } // Setup a prank from an address that has some ether function hoax(address who) internal { vm.deal(who, 1 << 128); vm.prank(who); } function hoax(address who, uint256 give) internal { vm.deal(who, give); vm.prank(who); } function hoax(address who, address origin) internal { vm.deal(who, 1 << 128); vm.prank(who, origin); } function hoax(address who, address origin, uint256 give) internal { vm.deal(who, give); vm.prank(who, origin); } // Start perpetual prank from an address that has some ether function startHoax(address who) internal { vm.deal(who, 1 << 128); vm.startPrank(who); } function startHoax(address who, uint256 give) internal { 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 { vm.deal(who, 1 << 128); vm.startPrank(who, origin); } function startHoax(address who, address origin, uint256 give) internal { vm.deal(who, give); vm.startPrank(who, origin); } function changePrank(address who) internal { vm.stopPrank(); vm.startPrank(who); } // creates a labeled address and the corresponding private key function makeAddrAndKey(string memory name) internal 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 returns(address addr) { (addr,) = makeAddrAndKey(name); } // DEPRECATED: Use `deal` instead function tip(address token, address to, uint256 give) internal { emit log_named_string("WARNING", "Test tip(address,address,uint256): The `tip` stdcheat has been deprecated. Use `deal` instead."); stdstore .target(token) .sig(0x70a08231) .with_key(to) .checked_write(give); } // The same as Vm's `deal` // Use the alternative signature for ERC20 tokens function deal(address to, uint256 give) internal { 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 { deal(token, to, give, false); } function deal(address token, address to, uint256 give, bool adjust) internal { // 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); } } function bound(uint256 x, uint256 min, uint256 max) internal virtual returns (uint256 result) { require(min <= max, "Test bound(uint256,uint256,uint256): Max is less than min."); uint256 size = max - min; if (size == 0) { result = min; } else if (size == UINT256_MAX) { result = x; } else { ++size; // make `max` inclusive uint256 mod = x % size; result = min + mod; } emit log_named_uint("Bound Result", result); } // 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 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), "Test deployCode(string,bytes): Deployment failed." ); } function deployCode(string memory what) internal 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), "Test deployCode(string): Deployment failed." ); } /// deploy contract with value on construction function deployCode(string memory what, bytes memory args, uint256 val) internal 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), "Test deployCode(string,bytes,uint256): Deployment failed." ); } function deployCode(string memory what, uint256 val) internal 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), "Test deployCode(string,uint256): Deployment failed." ); } /*////////////////////////////////////////////////////////////////////////// STD-ASSERTIONS //////////////////////////////////////////////////////////////////////////*/ function fail(string memory err) internal virtual { emit log_named_string("Error", err); fail(); } function assertFalse(bool data) internal virtual { assertTrue(!data); } function assertFalse(bool data, string memory err) internal virtual { assertTrue(!data, err); } function assertEq(bool a, bool b) internal { if (a != b) { emit log ("Error: a == b not satisfied [bool]"); emit log_named_string (" Expected", b ? "true" : "false"); emit log_named_string (" Actual", a ? "true" : "false"); fail(); } } function assertEq(bool a, bool b, string memory err) internal { if (a != b) { emit log_named_string("Error", err); assertEq(a, b); } } function assertEq(bytes memory a, bytes memory b) internal { assertEq0(a, b); } function assertEq(bytes memory a, bytes memory b, string memory err) internal { assertEq0(a, b, err); } function assertEq(uint256[] memory a, uint256[] memory b) internal { if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { emit log("Error: a == b not satisfied [uint[]]"); emit log_named_array(" Expected", b); emit log_named_array(" Actual", a); fail(); } } function assertEq(int256[] memory a, int256[] memory b) internal { if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { emit log("Error: a == b not satisfied [int[]]"); emit log_named_array(" Expected", b); emit log_named_array(" Actual", a); fail(); } } function assertEq(address[] memory a, address[] memory b) internal { if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { emit log("Error: a == b not satisfied [address[]]"); emit log_named_array(" Expected", b); emit log_named_array(" Actual", a); fail(); } } function assertEq(uint256[] memory a, uint256[] memory b, string memory err) internal { if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { emit log_named_string("Error", err); assertEq(a, b); } } function assertEq(int256[] memory a, int256[] memory b, string memory err) internal { if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { emit log_named_string("Error", err); assertEq(a, b); } } function assertEq(address[] memory a, address[] memory b, string memory err) internal { if (keccak256(abi.encode(a)) != keccak256(abi.encode(b))) { emit log_named_string("Error", err); assertEq(a, b); } } function assertEqUint(uint256 a, uint256 b) internal { assertEq(uint256(a), uint256(b)); } function assertApproxEqAbs( uint256 a, uint256 b, uint256 maxDelta ) internal virtual { uint256 delta = stdMath.delta(a, b); if (delta > maxDelta) { emit log ("Error: a ~= b not satisfied [uint]"); emit log_named_uint (" Expected", b); emit log_named_uint (" Actual", a); emit log_named_uint (" Max Delta", maxDelta); emit log_named_uint (" Delta", delta); fail(); } } function assertApproxEqAbs( uint256 a, uint256 b, uint256 maxDelta, string memory err ) internal virtual { uint256 delta = stdMath.delta(a, b); if (delta > maxDelta) { emit log_named_string ("Error", err); assertApproxEqAbs(a, b, maxDelta); } } function assertApproxEqAbs( int256 a, int256 b, uint256 maxDelta ) internal virtual { uint256 delta = stdMath.delta(a, b); if (delta > maxDelta) { emit log ("Error: a ~= b not satisfied [int]"); emit log_named_int (" Expected", b); emit log_named_int (" Actual", a); emit log_named_uint (" Max Delta", maxDelta); emit log_named_uint (" Delta", delta); fail(); } } function assertApproxEqAbs( int256 a, int256 b, uint256 maxDelta, string memory err ) internal virtual { uint256 delta = stdMath.delta(a, b); if (delta > maxDelta) { emit log_named_string ("Error", err); assertApproxEqAbs(a, b, maxDelta); } } function assertApproxEqRel( uint256 a, uint256 b, uint256 maxPercentDelta // An 18 decimal fixed point number, where 1e18 == 100% ) internal virtual { if (b == 0) return assertEq(a, b); // If the expected is 0, actual must be too. uint256 percentDelta = stdMath.percentDelta(a, b); if (percentDelta > maxPercentDelta) { emit log ("Error: a ~= b not satisfied [uint]"); emit log_named_uint (" Expected", b); emit log_named_uint (" Actual", a); emit log_named_decimal_uint (" Max % Delta", maxPercentDelta, 18); emit log_named_decimal_uint (" % Delta", percentDelta, 18); fail(); } } function assertApproxEqRel( uint256 a, uint256 b, uint256 maxPercentDelta, // An 18 decimal fixed point number, where 1e18 == 100% string memory err ) internal virtual { if (b == 0) return assertEq(a, b, err); // If the expected is 0, actual must be too. uint256 percentDelta = stdMath.percentDelta(a, b); if (percentDelta > maxPercentDelta) { emit log_named_string ("Error", err); assertApproxEqRel(a, b, maxPercentDelta); } } function assertApproxEqRel( int256 a, int256 b, uint256 maxPercentDelta ) internal virtual { if (b == 0) return assertEq(a, b); // If the expected is 0, actual must be too. uint256 percentDelta = stdMath.percentDelta(a, b); if (percentDelta > maxPercentDelta) { emit log ("Error: a ~= b not satisfied [int]"); emit log_named_int (" Expected", b); emit log_named_int (" Actual", a); emit log_named_decimal_uint(" Max % Delta", maxPercentDelta, 18); emit log_named_decimal_uint(" % Delta", percentDelta, 18); fail(); } } function assertApproxEqRel( int256 a, int256 b, uint256 maxPercentDelta, string memory err ) internal virtual { if (b == 0) return assertEq(a, b); // If the expected is 0, actual must be too. uint256 percentDelta = stdMath.percentDelta(a, b); if (percentDelta > maxPercentDelta) { emit log_named_string ("Error", err); assertApproxEqRel(a, b, maxPercentDelta); } } /*////////////////////////////////////////////////////////////// JSON PARSING //////////////////////////////////////////////////////////////*/ // 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 readEIP1559ScriptArtifact(string memory path) internal 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 returns (Tx1559[] memory) { Tx1559[] memory txs = new Tx1559[](rawTxs.length); for (uint i; i < rawTxs.length; i++) { txs[i] = rawToConvertedEIPTx1559(rawTxs[i]); } return txs; } function rawToConvertedEIPTx1559(RawTx1559 memory rawTx) internal pure 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 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 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 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 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, uint index) internal 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 returns(Receipt[] memory) { Receipt[] memory receipts = new Receipt[](rawReceipts.length); for (uint i; i < rawReceipts.length; i++) { receipts[i] = rawToConvertedReceipt(rawReceipts[i]); } return receipts; } function rawToConvertedReceipt(RawReceipt memory rawReceipt) internal pure 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 returns (ReceiptLog[] memory) { ReceiptLog[] memory logs = new ReceiptLog[](rawLogs.length); for (uint 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; } function bytesToUint(bytes memory b) internal pure returns (uint256){ uint256 number; for (uint i=0; i < b.length; i++) { number = number + uint(uint8(b[i]))*(2**(8*(b.length-(i+1)))); } return number; } } /*////////////////////////////////////////////////////////////////////////// STD-ERRORS //////////////////////////////////////////////////////////////////////////*/ 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); // DEPRECATED: Use Vm's `expectRevert` without any arguments instead bytes public constant lowLevelError = bytes(""); // `0x` } /*////////////////////////////////////////////////////////////////////////// STD-STORAGE //////////////////////////////////////////////////////////////////////////*/ struct StdStorage { mapping (address => mapping(bytes4 => mapping(bytes32 => uint256))) slots; mapping (address => mapping(bytes4 => mapping(bytes32 => bool))) finds; bytes32[] _keys; bytes4 _sig; uint256 _depth; address _target; bytes32 _set; } library stdStorage { event SlotFound(address who, bytes4 fsig, bytes32 keysHash, uint slot); event WARNING_UninitedSlot(address who, uint slot); uint256 private constant UINT256_MAX = 115792089237316195423570985008687907853269984665640564039457584007913129639935; int256 private constant INT256_MAX = 57896044618658097711785492504343953926634992332820282019728792003956564819967; Vm private constant vm_std_store = Vm(address(uint160(uint256(keccak256('hevm cheat code'))))); function sigs( string memory sigStr ) internal pure returns (bytes4) { return bytes4(keccak256(bytes(sigStr))); } /// @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 ) internal returns (uint256) { address who = self._target; bytes4 fsig = self._sig; uint256 field_depth = self._depth; bytes32[] memory ins = self._keys; // calldata to test against if (self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]) { return self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]; } bytes memory cald = abi.encodePacked(fsig, flatten(ins)); vm_std_store.record(); bytes32 fdat; { (, bytes memory rdat) = who.staticcall(cald); fdat = bytesToBytes32(rdat, 32*field_depth); } (bytes32[] memory reads, ) = vm_std_store.accesses(address(who)); if (reads.length == 1) { bytes32 curr = vm_std_store.load(who, reads[0]); if (curr == bytes32(0)) { emit WARNING_UninitedSlot(who, uint256(reads[0])); } if (fdat != curr) { require(false, "stdStorage find(StdStorage): Packed slot. This would cause dangerous overwriting and currently isn't supported."); } emit SlotFound(who, fsig, keccak256(abi.encodePacked(ins, field_depth)), uint256(reads[0])); self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))] = uint256(reads[0]); self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))] = true; } else if (reads.length > 1) { for (uint256 i = 0; i < reads.length; i++) { bytes32 prev = vm_std_store.load(who, reads[i]); if (prev == bytes32(0)) { emit WARNING_UninitedSlot(who, uint256(reads[i])); } // store vm_std_store.store(who, reads[i], bytes32(hex"1337")); bool success; bytes memory rdat; { (success, rdat) = who.staticcall(cald); fdat = bytesToBytes32(rdat, 32*field_depth); } if (success && fdat == bytes32(hex"1337")) { // we found which of the slots is the actual one emit SlotFound(who, fsig, keccak256(abi.encodePacked(ins, field_depth)), uint256(reads[i])); self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))] = uint256(reads[i]); self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))] = true; vm_std_store.store(who, reads[i], prev); break; } vm_std_store.store(who, reads[i], prev); } } else { require(false, "stdStorage find(StdStorage): No storage use detected for target."); } require(self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))], "stdStorage find(StdStorage): Slot(s) not found."); delete self._target; delete self._sig; delete self._keys; delete self._depth; return self.slots[who][fsig][keccak256(abi.encodePacked(ins, 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_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 depth(StdStorage storage self, uint256 _depth) internal returns (StdStorage storage) { self._depth = _depth; return 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(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; bytes32[] memory ins = self._keys; bytes memory cald = abi.encodePacked(fsig, flatten(ins)); if (!self.finds[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]) { find(self); } bytes32 slot = bytes32(self.slots[who][fsig][keccak256(abi.encodePacked(ins, field_depth))]); bytes32 fdat; { (, bytes memory rdat) = who.staticcall(cald); fdat = bytesToBytes32(rdat, 32*field_depth); } bytes32 curr = vm_std_store.load(who, slot); if (fdat != curr) { require(false, "stdStorage find(StdStorage): Packed slot. This would cause dangerous overwriting and currently isn't supported."); } vm_std_store.store(who, slot, set); delete self._target; delete self._sig; delete self._keys; delete self._depth; } function read(StdStorage storage self) private returns (bytes memory) { address t = self._target; uint256 s = find(self); return abi.encode(vm_std_store.load(t, bytes32(s))); } 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 bytesToBytes32(bytes memory b, uint offset) public pure returns (bytes32) { bytes32 out; uint256 max = b.length > 32 ? 32 : b.length; for (uint 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; } } /*////////////////////////////////////////////////////////////////////////// STD-MATH //////////////////////////////////////////////////////////////////////////*/ 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; } }