From 73236b461b9d93de3d54eef010c98fef1d4ac205 Mon Sep 17 00:00:00 2001 From: teddy Date: Thu, 8 Aug 2024 14:38:56 -0300 Subject: [PATCH 01/48] chore: configure medusa with basic supERC20 self-bridging - used --foundry-compile-all to ensure the test contract under `test/properties` is compiled (otherwise it is not compiled and medusa crashes when it can't find it's compiled representation) - set src,test,script to test/properties/medusa to not waste time compiling contracts that are not required for the medusa campaign - used an atomic bridge, which doesnt allow for testing of several of the proposed invariants --- packages/contracts-bedrock/.gitignore | 1 + packages/contracts-bedrock/foundry.toml | 5 + packages/contracts-bedrock/justfile | 3 + packages/contracts-bedrock/medusa.json | 82 ++++++++++ .../test/properties/PROPERTIES.md | 71 ++++++++ .../test/properties/SUMMARY.md | 43 +++++ .../test/properties/helpers/Utils.sol | 14 ++ .../properties/medusa/ProtocolAtomic.t.sol | 151 ++++++++++++++++++ 8 files changed, 370 insertions(+) create mode 100644 packages/contracts-bedrock/medusa.json create mode 100644 packages/contracts-bedrock/test/properties/PROPERTIES.md create mode 100644 packages/contracts-bedrock/test/properties/SUMMARY.md create mode 100644 packages/contracts-bedrock/test/properties/helpers/Utils.sol create mode 100644 packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol diff --git a/packages/contracts-bedrock/.gitignore b/packages/contracts-bedrock/.gitignore index 96e09c8c7190..396c03d4458d 100644 --- a/packages/contracts-bedrock/.gitignore +++ b/packages/contracts-bedrock/.gitignore @@ -6,6 +6,7 @@ broadcast kout-deployment kout-proofs test/kontrol/logs +test/properties/medusa/corpus/ # Metrics coverage.out diff --git a/packages/contracts-bedrock/foundry.toml b/packages/contracts-bedrock/foundry.toml index 9edf752f983d..3b69c67412f6 100644 --- a/packages/contracts-bedrock/foundry.toml +++ b/packages/contracts-bedrock/foundry.toml @@ -96,3 +96,8 @@ src = 'test/kontrol/proofs' out = 'kout-proofs' test = 'test/kontrol/proofs' script = 'test/kontrol/proofs' + +[profile.medusa] +src = 'test/properties/medusa/' +test = 'test/properties/medusa/' +script = 'test/properties/medusa/' diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index 45cdd14cfae0..ebb8d9db6d47 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -22,6 +22,9 @@ test: build-go-ffi test-kontrol: ./test/kontrol/scripts/run-kontrol.sh script +test-medusa: + FOUNDRY_PROFILE=medusa medusa fuzz + test-rerun: build-go-ffi forge test --rerun -vvv diff --git a/packages/contracts-bedrock/medusa.json b/packages/contracts-bedrock/medusa.json new file mode 100644 index 000000000000..76592655ffa1 --- /dev/null +++ b/packages/contracts-bedrock/medusa.json @@ -0,0 +1,82 @@ +{ + "fuzzing": { + "workers": 10, + "workerResetLimit": 50, + "timeout": 0, + "testLimit": 500000, + "callSequenceLength": 100, + "corpusDirectory": "test/properties/medusa/corpus/", + "coverageEnabled": true, + "targetContracts": ["ProtocolAtomicFuzz"], + "targetContractsBalances": [], + "constructorArgs": {}, + "deployerAddress": "0x30000", + "senderAddresses": [ + "0x10000", + "0x20000", + "0x30000" + ], + "blockNumberDelayMax": 60480, + "blockTimestampDelayMax": 604800, + "blockGasLimit": 125000000, + "transactionGasLimit": 12500000, + "testing": { + "stopOnFailedTest": true, + "stopOnFailedContractMatching": false, + "stopOnNoTests": true, + "testAllContracts": false, + "traceAll": true, + "assertionTesting": { + "enabled": true, + "testViewMethods": false, + "panicCodeConfig": { + "failOnCompilerInsertedPanic": false, + "failOnAssertion": true, + "failOnArithmeticUnderflow": false, + "failOnDivideByZero": false, + "failOnEnumTypeConversionOutOfBounds": false, + "failOnIncorrectStorageAccess": false, + "failOnPopEmptyArray": false, + "failOnOutOfBoundsArrayAccess": false, + "failOnAllocateTooMuchMemory": false, + "failOnCallUninitializedVariable": false + } + }, + "propertyTesting": { + "enabled": false, + "testPrefixes": [ + "property_" + ] + }, + "optimizationTesting": { + "enabled": false, + "testPrefixes": [ + "optimize_" + ] + }, + "targetFunctionSignatures": [], + "excludeFunctionSignatures": [] + }, + "chainConfig": { + "codeSizeCheckDisabled": true, + "cheatCodes": { + "cheatCodesEnabled": true, + "enableFFI": false + } + } + }, + "compilation": { + "platform": "crytic-compile", + "platformConfig": { + "target": ".", + "solcVersion": "", + "exportDirectory": "", + "args": ["--foundry-out-directory", "artifacts","--foundry-compile-all"] + } + }, + "logging": { + "level": "info", + "logDirectory": "", + "noColor": false + } +} diff --git a/packages/contracts-bedrock/test/properties/PROPERTIES.md b/packages/contracts-bedrock/test/properties/PROPERTIES.md new file mode 100644 index 000000000000..b3a18514d254 --- /dev/null +++ b/packages/contracts-bedrock/test/properties/PROPERTIES.md @@ -0,0 +1,71 @@ +# supertoken properties + +legend: +- `[ ]`: property not yet tested +- `**[ ]**`: property not yet tested, dev/research team has asked for extra focus on it +- `[X]`: tested/proven property +- `:(`: property won't be tested due to some limitation + +## Unit test + +| id | description | halmos | medusa | +| --- | --- | --- | --- | +| 0 | supertoken token address does not depend on the executing chain’s chainID | [ ] | [x] | +| 1 | supertoken token address depends on name, remote token, address and decimals | [ ] | [x] | +| 2 | convert() should only allow converting legacy tokens to supertoken and viceversa | [ ] | [ ] | +| 3 | convert() only allows migrations between tokens representing the same remote asset | [ ] | [ ] | +| 4 | convert() only allows migrations from tokens with the same decimals | [ ] | [ ] | +| 5 | convert() burns the same amount of one token that it mints of the other | [ ] | [ ] | + +## Valid state + +| id | description | halmos | medusa | +| --- | --- | --- | --- | +| 6 | calls to sendERC20 succeed as long as caller has enough balance | [ ] | [ ] | +| 7 | calls to relayERC20 always succeed as long as the cross-domain caller is valid | **[ ]** | [ ] | + +## Variable transition + +| id | description | halmos | medusa | +| --- | --- | --- | --- | +| 8 | sendERC20 with a value of zero does not modify accounting | [ ] | [ ] | +| 9 | relayERC20 with a value of zero does not modify accounting | [ ] | [ ] | +| 10 | sendERC20 decreases the token's totalSupply in the source chain exactly by the input amount | [ ] | [ ] | +| 11 | relayERC20 increases the token's totalSupply in the destination chain exactly by the input amount | [ ] | [ ] | +| 12 | supertoken total supply only increases on calls to mint() by the L2toL2StandardBridge | [ ] | [ ] | +| 13 | supertoken total supply only decreases on calls to burn() by the L2toL2StandardBridge | [ ] | [ ] | +| 14 | supertoken total supply starts at zero | [ ] | [ ] | +| 15 | deploying a supertoken registers its remote token in the factory | [ ] | [ ] | +| 16 | deploying an OptimismMintableERC20 registers its remote token in the factory | [ ] | [ ] | + +## High level + +| id | description | halmos | medusa | +| --- | --- | --- | --- | +| 17 | only calls to convert(legacy, super) can increase a supertoken’s total supply across chains | [ ] | [ ] | +| 18 | only calls to convert(super, legacy) can decrease a supertoken’s total supply across chains | [ ] | [ ] | +| 19 | sum of total supply across all chains is always <= to convert(legacy, super)- convert(super, legacy) | [ ] | [ ] | +| 20 | tokens sendERC20-ed on a source chain to a destination chain can be relayERC20-ed on it as long as the source chain is in the dependency set of the destination chain | [ ] | [ ] | +| 21 | sum of supertoken total supply across all chains is = to convert(legacy, super)- convert(super, legacy) when all cross-chain messages are processed | [ ] | [ ] | + +## Atomic bridging pseudo-properties + +As another layer of defense, the following properties are defined which assume bridging operations to be atomic (that is, the sequencer and L2Inbox and CrossDomainMessenger contracts are fully abstracted away, `sendERC20` triggering the `relayERC20` call on the same transaction) +It’s worth noting that these properties will not hold for a live system + +| id | description | halmos | echidna | +| --- | --- | --- | --- | +| 20 | sendERC20 decreases sender balance in source chain and increases receiver balance in destination chain exactly by the input amount | [ ] | [ ] | +| 21 | sendERC20 decreases total supply in source chain and increases it in destination chain exactly by the input amount | [ ] | [ ] | +| 22 | sum of supertoken total supply across all chains is always equal to convert(legacy, super)- convert(super, legacy) | [ ] | [ ] | + +# Expected external interactions + +- regular ERC20 operations between any accounts on the same chain, provided by [crytic ERC20 properties](https://github.com/crytic/properties?tab=readme-ov-file#erc20-tests) + +# Invariant-breaking candidates (brain dump) + +here we’ll list possible interactions that we intend the fuzzing campaign to support in order to help break invariants + +- [ ] changing the decimals of tokens after deployment +- [ ] `convert()` ing between multiple (3+) representations of the same remote token, by having different names/symbols diff --git a/packages/contracts-bedrock/test/properties/SUMMARY.md b/packages/contracts-bedrock/test/properties/SUMMARY.md new file mode 100644 index 000000000000..ed6c286556fd --- /dev/null +++ b/packages/contracts-bedrock/test/properties/SUMMARY.md @@ -0,0 +1,43 @@ +# SupERC20 advanced testing + +# Overview + +This document defines a set of properties global to the supertoken ecosystem, for which we will: + +- run a [Medusa](https://github.com/crytic/medusa) fuzzing campaign, trying to break system invariants +- formally prove with [Halmos](https://github.com/ethereum-optimism/optimism) whenever possible + +## Where to place the testing campaign + +Given the [OP monorepo](https://github.com/ethereum-optimism/optimism) already has invariant testing provided by foundry, it's not a trivial matter where to place this advanced testing campaign. Two alternatives are proposed: + +- including it in the mainline OP monorepo, in a subdirectory of the existing test contracts such as `test/invariants/medusa/superc20/` +- creating a separate (potentially private) repository for this testing campaign, in which case the deliverable would consist primarily of: + - a summary of the results, extending this document + - PRs with extra unit tests replicating found issues to the main repo where applicable + +## Contracts in scope + +- [ ] [OptimismSuperchainERC20](https://github.com/defi-wonderland/optimism/pull/9/files#diff-810060510a8a9c06dc60cdce6782e5cafd93b638e2557307a68abe694ee86aee) +- [ ] [OptimismMintableERC20Factory](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol) +- [ ] [SuperchsupERC20ainERC20](https://github.com/defi-wonderland/optimism/pull/8/files#diff-603fd7d5a0b2c403c0d1eee21d0ee60fb8eb72430169eaac5ec7081e01de96b8) (not yet merged) +- [ ] [SuperchainERC20Factory](https://github.com/defi-wonderland/optimism/pull/8/files#diff-09838f5703c353d0f7c5ff395acc04c1768ef58becac67404bc17e1fb0018517) (not yet merged) +- [ ] [L2StandardBridgeInterop](https://github.com/defi-wonderland/optimism/pull/10/files#diff-56cf869412631eac0a04a03f7d026596f64a1e00fcffa713bc770d67c6856c2f) (not yet merged) + +## Behavior assumed correct + +- [ ] inclusion of relay transactions +- [ ] sequencer implementation +- [ ] [OptimismMintableERC20](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC20.sol) +- [ ] [L2ToL2CrossDomainMessenger](https://www.notion.so/defi-wonderland/src/L2/L2CrossDomainMessenger.sol) +- [ ] [CrossL2Inbox](https://www.notion.so/defi-wonderland/src/L2/CrossL2Inbox.sol) + +## Pain points + +- existing fuzzing tools use the same EVM to run the tested contracts as they do for asserting invariants, tracking ghost variables and everything else necessary to provision a fuzzing campaign. While this is usually very convenient, it means that we can’t assert on the behaviour/state of *different* EVMs from within a fuzzing campaign. This means we will have to walk around the requirement of supertokens having the same address across all chains, and implement a way to mock tokens existing in different chains. We will strive to formally prove it in a unitary fashion to mitigate this in properties 13 and 14 +- a buffer to represent 'in transit' messages should be implemented to assert on invariants relating to the non-atomicity of bridging from one chain to another. It is yet to be determined if it’ll be a FIFO queue (assuming ideal message ordering by sequencers) or it’ll have random-access capability to simulate messages arriving out of order + +## Definitions + +- *legacy token:* an OptimismMintableERC20 or L2StandardERC20 token on the suprechain that has either been deployed by the factory after the liquidity migration upgrade to the latter, or has been deployed before it **but** added to factory’s `deployments` mapping as part of the upgrade. This testing campaign is not concerned with tokens on L1 or not listed in the factory’s `deployments` mapping. +- *supertoken:* a SuperchainERC20 contract deployed on the Superchain diff --git a/packages/contracts-bedrock/test/properties/helpers/Utils.sol b/packages/contracts-bedrock/test/properties/helpers/Utils.sol new file mode 100644 index 000000000000..eabf7b483f18 --- /dev/null +++ b/packages/contracts-bedrock/test/properties/helpers/Utils.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { MockERC20 } from "forge-std/mocks/MockERC20.sol"; + +contract FuzzERC20 is MockERC20 { + function mint(address _to, uint256 _amount) public { + _mint(_to, _amount); + } + + function burn(address _from, uint256 _amount) public { + _burn(_from, _amount); + } +} diff --git a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol b/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol new file mode 100644 index 000000000000..55e63b42963e --- /dev/null +++ b/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import "forge-std/console.sol"; + +import { Test } from "forge-std/Test.sol"; + +import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol"; +import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; +import { Predeploys } from "src/libraries/Predeploys.sol"; +import { SafeCall } from "src/libraries/SafeCall.sol"; +import { FuzzERC20 } from "../helpers/Utils.sol"; + +contract MockCrossDomainMessenger { + address public crossDomainMessageSender; + address public crossDomainMessageSource; + mapping(uint256 chainId => mapping(bytes32 reayDeployData => address)) internal superTokenAddresses; + mapping(address => bytes32) internal superTokenInitDeploySalts; + // test-specific functions + + function crossChainMessageReceiver(address sender, uint256 destinationChainId) external returns (OptimismSuperchainERC20) { + return OptimismSuperchainERC20(superTokenAddresses[destinationChainId][superTokenInitDeploySalts[sender]]); + } + + function registerSupertoken(bytes32 deploySalt, uint256 chainId, address token) external { + superTokenAddresses[chainId][deploySalt] = token; + superTokenInitDeploySalts[token] = deploySalt; + } + // mocked functions + + function sendMessage(uint256 chainId, address recipient, bytes memory message) external returns (address) { + address crossChainRecipient = superTokenAddresses[chainId][superTokenInitDeploySalts[msg.sender]]; + if (crossChainRecipient == msg.sender) { + require(false, "same chain"); + } + crossDomainMessageSender = crossChainRecipient; + crossDomainMessageSource = msg.sender; + SafeCall.call(crossDomainMessageSender, 0, message); + crossDomainMessageSender = address(0); + } +} + +contract ProtocolAtomicFuzz is Test { + uint8 internal constant MAX_CHAINS = 4; + address internal constant BRIDGE = Predeploys.L2_STANDARD_BRIDGE; + MockCrossDomainMessenger internal constant MESSENGER = + MockCrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); + OptimismSuperchainERC20 internal superchainERC20Impl; + string[] internal WORDS = ["FANCY", "TOKENS"]; + uint8[] internal DECIMALS = [0, 6, 18, 36]; + + struct TokenDeployParams { + uint8 remoteTokenIndex; + uint8 name; + uint8 symbol; + uint8 decimals; + } + + address[] internal remoteTokens; + address[] internal allSuperTokens; + mapping(bytes32 => uint256) internal superTokenTotalSupply; + mapping(bytes32 => uint256) internal superTokensTotalSupply; + + constructor() { + vm.etch(address(MESSENGER), address(new MockCrossDomainMessenger()).code); + superchainERC20Impl = new OptimismSuperchainERC20(); + } + + modifier validateTokenDeployParams(TokenDeployParams memory params) { + params.remoteTokenIndex = uint8(bound(params.remoteTokenIndex, 0, remoteTokens.length - 1)); + params.name = uint8(bound(params.name, 0, WORDS.length - 1)); + params.symbol = uint8(bound(params.symbol, 0, WORDS.length - 1)); + params.decimals = uint8(bound(params.decimals, 0, DECIMALS.length - 1)); + _; + } + + function fuzz_DeployNewSupertoken( + TokenDeployParams memory params, + uint256 chainId + ) + external + validateTokenDeployParams(params) + { + chainId = bound(chainId, 0, MAX_CHAINS - 1); + _deploySupertoken( + remoteTokens[params.remoteTokenIndex], + WORDS[params.name], + WORDS[params.symbol], + DECIMALS[params.decimals], + chainId + ); + } + + function fuzz_SelfBridgeSupertoken(uint256 fromIndex, uint256 destinationChainId, uint256 amount) external { + destinationChainId = bound(destinationChainId, 0, MAX_CHAINS - 1); + fromIndex = bound(fromIndex, 0, allSuperTokens.length - 1); + OptimismSuperchainERC20 sourceToken = OptimismSuperchainERC20(allSuperTokens[fromIndex]); + OptimismSuperchainERC20 destinationToken = MESSENGER.crossChainMessageReceiver(address(sourceToken), destinationChainId); + // TODO: when implementing non-atomic bridging, allow for the token to + // not yet be deployed and funds be recovered afterwards. + require(address(destinationToken) != address(0)); + uint256 balanceFromBefore = sourceToken.balanceOf(msg.sender); + uint256 balanceToBefore = destinationToken.balanceOf(msg.sender); + vm.prank(msg.sender); + try sourceToken.sendERC20(msg.sender, amount, destinationChainId) { + uint256 balanceFromAfter = sourceToken.balanceOf(msg.sender); + uint256 balanceToAfter = destinationToken.balanceOf(msg.sender); + assert(balanceFromBefore + balanceToBefore == balanceFromAfter + balanceToAfter); + } catch { + assert(balanceFromBefore < amount || address(destinationToken) == address(sourceToken)); + } + } + + // TODO: track total supply for invariant checking + function fuzz_MintSupertoken(uint256 index, uint96 amount) external { + index = bound(index, 0, allSuperTokens.length - 1); + address addr = allSuperTokens[index]; + vm.prank(BRIDGE); + // medusa calls with different senders by default + OptimismSuperchainERC20(addr).mint(msg.sender, amount); + } + + function fuzz_MockNewRemoteToken() external { + // make sure they don't conflict with predeploys/preinstalls/precompiles/other tokens + remoteTokens.push(address(uint160(1000 + remoteTokens.length))); + } + + function _deploySupertoken( + address remoteToken, + string memory name, + string memory symbol, + uint8 decimals, + uint256 chainId + ) + internal + { + bytes32 realSalt = keccak256(abi.encode(remoteToken, name, symbol, decimals)); + bytes32 hackySalt = keccak256(abi.encode(remoteToken, name, symbol, decimals, chainId)); + OptimismSuperchainERC20 localToken = OptimismSuperchainERC20( + address( + // TODO: Use the SuperchainERC20 Beacon Proxy + new ERC1967Proxy{ salt: hackySalt }( + address(superchainERC20Impl), + abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, name, symbol, decimals)) + ) + ) + ); + MESSENGER.registerSupertoken(realSalt, chainId, address(localToken)); + allSuperTokens.push(address(localToken)); + } +} From 6f386a560b7112954b6105269edd131a360fe57e Mon Sep 17 00:00:00 2001 From: teddy Date: Tue, 13 Aug 2024 17:51:45 -0300 Subject: [PATCH 02/48] fix: delete dead code --- .../test/properties/helpers/Utils.sol | 14 -------------- .../test/properties/medusa/ProtocolAtomic.t.sol | 3 +-- 2 files changed, 1 insertion(+), 16 deletions(-) delete mode 100644 packages/contracts-bedrock/test/properties/helpers/Utils.sol diff --git a/packages/contracts-bedrock/test/properties/helpers/Utils.sol b/packages/contracts-bedrock/test/properties/helpers/Utils.sol deleted file mode 100644 index eabf7b483f18..000000000000 --- a/packages/contracts-bedrock/test/properties/helpers/Utils.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.25; - -import { MockERC20 } from "forge-std/mocks/MockERC20.sol"; - -contract FuzzERC20 is MockERC20 { - function mint(address _to, uint256 _amount) public { - _mint(_to, _amount); - } - - function burn(address _from, uint256 _amount) public { - _burn(_from, _amount); - } -} diff --git a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol b/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol index 55e63b42963e..ef795e3d29aa 100644 --- a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol +++ b/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol @@ -9,7 +9,6 @@ import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Pr import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; -import { FuzzERC20 } from "../helpers/Utils.sol"; contract MockCrossDomainMessenger { address public crossDomainMessageSender; @@ -28,7 +27,7 @@ contract MockCrossDomainMessenger { } // mocked functions - function sendMessage(uint256 chainId, address recipient, bytes memory message) external returns (address) { + function sendMessage(uint256 chainId, address /*recipient*/, bytes memory message) external { address crossChainRecipient = superTokenAddresses[chainId][superTokenInitDeploySalts[msg.sender]]; if (crossChainRecipient == msg.sender) { require(false, "same chain"); From e8d42b8a7224b850c44ba4100f0db55e891b91d7 Mon Sep 17 00:00:00 2001 From: teddy Date: Tue, 13 Aug 2024 17:53:39 -0300 Subject: [PATCH 03/48] test: give the fuzzer a head start --- .../properties/medusa/ProtocolAtomic.t.sol | 43 +++++++++++++++---- 1 file changed, 34 insertions(+), 9 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol b/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol index ef795e3d29aa..d16b79610737 100644 --- a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol +++ b/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol @@ -17,7 +17,13 @@ contract MockCrossDomainMessenger { mapping(address => bytes32) internal superTokenInitDeploySalts; // test-specific functions - function crossChainMessageReceiver(address sender, uint256 destinationChainId) external returns (OptimismSuperchainERC20) { + function crossChainMessageReceiver( + address sender, + uint256 destinationChainId + ) + external + returns (OptimismSuperchainERC20) + { return OptimismSuperchainERC20(superTokenAddresses[destinationChainId][superTokenInitDeploySalts[sender]]); } @@ -27,7 +33,7 @@ contract MockCrossDomainMessenger { } // mocked functions - function sendMessage(uint256 chainId, address /*recipient*/, bytes memory message) external { + function sendMessage(uint256 chainId, address, /*recipient*/ bytes memory message) external { address crossChainRecipient = superTokenAddresses[chainId][superTokenInitDeploySalts[msg.sender]]; if (crossChainRecipient == msg.sender) { require(false, "same chain"); @@ -41,12 +47,16 @@ contract MockCrossDomainMessenger { contract ProtocolAtomicFuzz is Test { uint8 internal constant MAX_CHAINS = 4; + uint8 internal constant INITIAL_TOKENS = 2; + uint8 internal constant INITIAL_SUPERTOKENS = 2; + uint8 internal constant SUPERTOKEN_INITIAL_MINT = 100; address internal constant BRIDGE = Predeploys.L2_STANDARD_BRIDGE; MockCrossDomainMessenger internal constant MESSENGER = MockCrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); OptimismSuperchainERC20 internal superchainERC20Impl; - string[] internal WORDS = ["FANCY", "TOKENS"]; - uint8[] internal DECIMALS = [0, 6, 18, 36]; + // NOTE: having more options for this enables the fuzzer to configure different supertokens for the same + string[] internal WORDS = ["TOKENS"]; + uint8[] internal DECIMALS = [6, 18]; struct TokenDeployParams { uint8 remoteTokenIndex; @@ -63,6 +73,12 @@ contract ProtocolAtomicFuzz is Test { constructor() { vm.etch(address(MESSENGER), address(new MockCrossDomainMessenger()).code); superchainERC20Impl = new OptimismSuperchainERC20(); + for (uint256 i = 0; i < INITIAL_TOKENS; i++) { + _deployRemoteToken(); + for (uint256 j = 0; j < INITIAL_SUPERTOKENS ; j++){ + _deploySupertoken(remoteTokens[i], WORDS[0], WORDS[0], DECIMALS[0], j); + } + } } modifier validateTokenDeployParams(TokenDeployParams memory params) { @@ -94,11 +110,14 @@ contract ProtocolAtomicFuzz is Test { destinationChainId = bound(destinationChainId, 0, MAX_CHAINS - 1); fromIndex = bound(fromIndex, 0, allSuperTokens.length - 1); OptimismSuperchainERC20 sourceToken = OptimismSuperchainERC20(allSuperTokens[fromIndex]); - OptimismSuperchainERC20 destinationToken = MESSENGER.crossChainMessageReceiver(address(sourceToken), destinationChainId); + OptimismSuperchainERC20 destinationToken = + MESSENGER.crossChainMessageReceiver(address(sourceToken), destinationChainId); // TODO: when implementing non-atomic bridging, allow for the token to // not yet be deployed and funds be recovered afterwards. require(address(destinationToken) != address(0)); uint256 balanceFromBefore = sourceToken.balanceOf(msg.sender); + // NOTE: lift this requirement to allow one more failure mode + amount = bound(amount, 0, balanceFromBefore); uint256 balanceToBefore = destinationToken.balanceOf(msg.sender); vm.prank(msg.sender); try sourceToken.sendERC20(msg.sender, amount, destinationChainId) { @@ -106,7 +125,7 @@ contract ProtocolAtomicFuzz is Test { uint256 balanceToAfter = destinationToken.balanceOf(msg.sender); assert(balanceFromBefore + balanceToBefore == balanceFromAfter + balanceToAfter); } catch { - assert(balanceFromBefore < amount || address(destinationToken) == address(sourceToken)); + assert(address(destinationToken) == address(sourceToken)); } } @@ -120,6 +139,10 @@ contract ProtocolAtomicFuzz is Test { } function fuzz_MockNewRemoteToken() external { + _deployRemoteToken(); + } + + function _deployRemoteToken() internal { // make sure they don't conflict with predeploys/preinstalls/precompiles/other tokens remoteTokens.push(address(uint160(1000 + remoteTokens.length))); } @@ -135,7 +158,7 @@ contract ProtocolAtomicFuzz is Test { { bytes32 realSalt = keccak256(abi.encode(remoteToken, name, symbol, decimals)); bytes32 hackySalt = keccak256(abi.encode(remoteToken, name, symbol, decimals, chainId)); - OptimismSuperchainERC20 localToken = OptimismSuperchainERC20( + OptimismSuperchainERC20 token = OptimismSuperchainERC20( address( // TODO: Use the SuperchainERC20 Beacon Proxy new ERC1967Proxy{ salt: hackySalt }( @@ -144,7 +167,9 @@ contract ProtocolAtomicFuzz is Test { ) ) ); - MESSENGER.registerSupertoken(realSalt, chainId, address(localToken)); - allSuperTokens.push(address(localToken)); + MESSENGER.registerSupertoken(realSalt, chainId, address(token)); + allSuperTokens.push(address(token)); + vm.prank(BRIDGE); + token.mint(msg.sender, INITIAL_TOKENS * 10 ** decimals); } } From 326366b1d51f3548bbb48f56d82b79ddd52a60de Mon Sep 17 00:00:00 2001 From: teddy Date: Wed, 14 Aug 2024 11:21:20 -0300 Subject: [PATCH 04/48] docs: fix properties order --- packages/contracts-bedrock/test/properties/PROPERTIES.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/PROPERTIES.md b/packages/contracts-bedrock/test/properties/PROPERTIES.md index b3a18514d254..152232a8473b 100644 --- a/packages/contracts-bedrock/test/properties/PROPERTIES.md +++ b/packages/contracts-bedrock/test/properties/PROPERTIES.md @@ -55,9 +55,9 @@ It’s worth noting that these properties will not hold for a live system | id | description | halmos | echidna | | --- | --- | --- | --- | -| 20 | sendERC20 decreases sender balance in source chain and increases receiver balance in destination chain exactly by the input amount | [ ] | [ ] | -| 21 | sendERC20 decreases total supply in source chain and increases it in destination chain exactly by the input amount | [ ] | [ ] | -| 22 | sum of supertoken total supply across all chains is always equal to convert(legacy, super)- convert(super, legacy) | [ ] | [ ] | +| 22 | sendERC20 decreases sender balance in source chain and increases receiver balance in destination chain exactly by the input amount | [ ] | [ ] | +| 23 | sendERC20 decreases total supply in source chain and increases it in destination chain exactly by the input amount | [ ] | [ ] | +| 24 | sum of supertoken total supply across all chains is always equal to convert(legacy, super)- convert(super, legacy) | [ ] | [ ] | # Expected external interactions From 7a66ae6743d0407709fdfa7676617c4c340949af Mon Sep 17 00:00:00 2001 From: teddy Date: Tue, 13 Aug 2024 18:56:45 -0300 Subject: [PATCH 05/48] test: document & implement assertions 22, 23 and 24 --- .../properties/medusa/ProtocolAtomic.t.sol | 75 ++++++++++++++----- 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol b/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol index d16b79610737..e1a84c80c40f 100644 --- a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol +++ b/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol @@ -1,11 +1,10 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.25; -import "forge-std/console.sol"; - import { Test } from "forge-std/Test.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol"; +import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; @@ -13,8 +12,8 @@ import { SafeCall } from "src/libraries/SafeCall.sol"; contract MockCrossDomainMessenger { address public crossDomainMessageSender; address public crossDomainMessageSource; - mapping(uint256 chainId => mapping(bytes32 reayDeployData => address)) internal superTokenAddresses; - mapping(address => bytes32) internal superTokenInitDeploySalts; + mapping(address => bytes32) public superTokenInitDeploySalts; + mapping(uint256 chainId => mapping(bytes32 reayDeployData => address)) public superTokenAddresses; // test-specific functions function crossChainMessageReceiver( @@ -46,15 +45,18 @@ contract MockCrossDomainMessenger { } contract ProtocolAtomicFuzz is Test { + using EnumerableMap for EnumerableMap.Bytes32ToUintMap; + uint8 internal constant MAX_CHAINS = 4; - uint8 internal constant INITIAL_TOKENS = 2; - uint8 internal constant INITIAL_SUPERTOKENS = 2; + uint8 internal constant INITIAL_TOKENS = 1; + uint8 internal constant INITIAL_SUPERTOKENS = 1; uint8 internal constant SUPERTOKEN_INITIAL_MINT = 100; address internal constant BRIDGE = Predeploys.L2_STANDARD_BRIDGE; MockCrossDomainMessenger internal constant MESSENGER = MockCrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); OptimismSuperchainERC20 internal superchainERC20Impl; - // NOTE: having more options for this enables the fuzzer to configure different supertokens for the same + // NOTE: having more options for this enables the fuzzer to configure + // different supertokens for the same remote token string[] internal WORDS = ["TOKENS"]; uint8[] internal DECIMALS = [6, 18]; @@ -67,15 +69,16 @@ contract ProtocolAtomicFuzz is Test { address[] internal remoteTokens; address[] internal allSuperTokens; - mapping(bytes32 => uint256) internal superTokenTotalSupply; - mapping(bytes32 => uint256) internal superTokensTotalSupply; + + // deploy salt => total supply sum across chains + EnumerableMap.Bytes32ToUintMap internal ghost_totalSupplyAcrossChains; constructor() { vm.etch(address(MESSENGER), address(new MockCrossDomainMessenger()).code); superchainERC20Impl = new OptimismSuperchainERC20(); for (uint256 i = 0; i < INITIAL_TOKENS; i++) { _deployRemoteToken(); - for (uint256 j = 0; j < INITIAL_SUPERTOKENS ; j++){ + for (uint256 j = 0; j < INITIAL_SUPERTOKENS; j++) { _deploySupertoken(remoteTokens[i], WORDS[0], WORDS[0], DECIMALS[0], j); } } @@ -106,6 +109,8 @@ contract ProtocolAtomicFuzz is Test { ); } + /// @custom:property-id 22 + /// @custom:property-id 23 function fuzz_SelfBridgeSupertoken(uint256 fromIndex, uint256 destinationChainId, uint256 amount) external { destinationChainId = bound(destinationChainId, 0, MAX_CHAINS - 1); fromIndex = bound(fromIndex, 0, allSuperTokens.length - 1); @@ -115,27 +120,58 @@ contract ProtocolAtomicFuzz is Test { // TODO: when implementing non-atomic bridging, allow for the token to // not yet be deployed and funds be recovered afterwards. require(address(destinationToken) != address(0)); - uint256 balanceFromBefore = sourceToken.balanceOf(msg.sender); + uint256 sourceBalanceBefore = sourceToken.balanceOf(msg.sender); + uint256 sourceSupplyBefore = sourceToken.totalSupply(); // NOTE: lift this requirement to allow one more failure mode - amount = bound(amount, 0, balanceFromBefore); - uint256 balanceToBefore = destinationToken.balanceOf(msg.sender); + uint256 destinationBalanceBefore = destinationToken.balanceOf(msg.sender); + uint256 destinationSupplyBefore = destinationToken.totalSupply(); + + amount = bound(amount, 0, sourceBalanceBefore); vm.prank(msg.sender); try sourceToken.sendERC20(msg.sender, amount, destinationChainId) { - uint256 balanceFromAfter = sourceToken.balanceOf(msg.sender); - uint256 balanceToAfter = destinationToken.balanceOf(msg.sender); - assert(balanceFromBefore + balanceToBefore == balanceFromAfter + balanceToAfter); + uint256 sourceBalanceAfter = sourceToken.balanceOf(msg.sender); + uint256 destinationBalanceAfter = destinationToken.balanceOf(msg.sender); + // no free mint + assert(sourceBalanceBefore + destinationBalanceBefore == sourceBalanceAfter + destinationBalanceAfter); + // 22 + assert(sourceBalanceBefore - amount == sourceBalanceAfter); + assert(destinationBalanceBefore + amount == destinationBalanceAfter); + uint256 sourceSupplyAfter = sourceToken.totalSupply(); + uint256 destinationSupplyAfter = destinationToken.totalSupply(); + // 23 + assert(sourceSupplyBefore - amount == sourceSupplyAfter); + assert(destinationSupplyBefore + amount == destinationSupplyAfter); } catch { assert(address(destinationToken) == address(sourceToken)); } } - // TODO: track total supply for invariant checking function fuzz_MintSupertoken(uint256 index, uint96 amount) external { index = bound(index, 0, allSuperTokens.length - 1); address addr = allSuperTokens[index]; vm.prank(BRIDGE); // medusa calls with different senders by default OptimismSuperchainERC20(addr).mint(msg.sender, amount); + uint256 currentValue = ghost_totalSupplyAcrossChains.get(MESSENGER.superTokenInitDeploySalts(addr)); + ghost_totalSupplyAcrossChains.set(MESSENGER.superTokenInitDeploySalts(addr), currentValue + amount); + } + + // TODO: will need rework after + // - non-atomic bridge + // - `convert` + /// @custom:property-id 24 + function property_totalSupplyAcrossChainsEqualsMints() external { + for (uint256 i = 0; i < ghost_totalSupplyAcrossChains.length(); i++) { + uint256 totalSupply = 0; + (bytes32 currentSalt, uint256 trackedSupply) = ghost_totalSupplyAcrossChains.at(i); + for (uint256 j = 0; j < MAX_CHAINS; j++) { + address supertoken = MESSENGER.superTokenAddresses(j, currentSalt); + if (supertoken != address(0)) { + totalSupply += OptimismSuperchainERC20(supertoken).totalSupply(); + } + } + assert(trackedSupply == totalSupply); + } } function fuzz_MockNewRemoteToken() external { @@ -169,7 +205,10 @@ contract ProtocolAtomicFuzz is Test { ); MESSENGER.registerSupertoken(realSalt, chainId, address(token)); allSuperTokens.push(address(token)); + uint256 mintAmount = INITIAL_TOKENS * 10 ** decimals; vm.prank(BRIDGE); - token.mint(msg.sender, INITIAL_TOKENS * 10 ** decimals); + token.mint(msg.sender, mintAmount); + (,uint256 curr) = ghost_totalSupplyAcrossChains.tryGet(realSalt); + ghost_totalSupplyAcrossChains.set(realSalt, curr + mintAmount); } } From fa4cf29a5f41b4e334d81fcce1aa5704c9fa7604 Mon Sep 17 00:00:00 2001 From: teddy Date: Wed, 14 Aug 2024 12:26:16 -0300 Subject: [PATCH 06/48] fix: fixes from self-review --- .../test/properties/PROPERTIES.md | 13 ++-- .../test/properties/SUMMARY.md | 8 +- .../helpers/MockCrossDomainMessenger.t.sol | 52 +++++++++++++ .../properties/medusa/ProtocolAtomic.t.sol | 78 ++++++------------- 4 files changed, 88 insertions(+), 63 deletions(-) create mode 100644 packages/contracts-bedrock/test/properties/helpers/MockCrossDomainMessenger.t.sol diff --git a/packages/contracts-bedrock/test/properties/PROPERTIES.md b/packages/contracts-bedrock/test/properties/PROPERTIES.md index 152232a8473b..540d591f77de 100644 --- a/packages/contracts-bedrock/test/properties/PROPERTIES.md +++ b/packages/contracts-bedrock/test/properties/PROPERTIES.md @@ -4,14 +4,15 @@ legend: - `[ ]`: property not yet tested - `**[ ]**`: property not yet tested, dev/research team has asked for extra focus on it - `[X]`: tested/proven property +- `[~]`: partially tested/proven property - `:(`: property won't be tested due to some limitation ## Unit test | id | description | halmos | medusa | | --- | --- | --- | --- | -| 0 | supertoken token address does not depend on the executing chain’s chainID | [ ] | [x] | -| 1 | supertoken token address depends on name, remote token, address and decimals | [ ] | [x] | +| 0 | supertoken token address does not depend on the executing chain’s chainID | [ ] | [ ] | +| 1 | supertoken token address depends on name, remote token, address and decimals | [ ] | [ ] | | 2 | convert() should only allow converting legacy tokens to supertoken and viceversa | [ ] | [ ] | | 3 | convert() only allows migrations between tokens representing the same remote asset | [ ] | [ ] | | 4 | convert() only allows migrations from tokens with the same decimals | [ ] | [ ] | @@ -53,11 +54,11 @@ legend: As another layer of defense, the following properties are defined which assume bridging operations to be atomic (that is, the sequencer and L2Inbox and CrossDomainMessenger contracts are fully abstracted away, `sendERC20` triggering the `relayERC20` call on the same transaction) It’s worth noting that these properties will not hold for a live system -| id | description | halmos | echidna | +| id | description | halmos | medusa | | --- | --- | --- | --- | -| 22 | sendERC20 decreases sender balance in source chain and increases receiver balance in destination chain exactly by the input amount | [ ] | [ ] | -| 23 | sendERC20 decreases total supply in source chain and increases it in destination chain exactly by the input amount | [ ] | [ ] | -| 24 | sum of supertoken total supply across all chains is always equal to convert(legacy, super)- convert(super, legacy) | [ ] | [ ] | +| 22 | sendERC20 decreases sender balance in source chain and increases receiver balance in destination chain exactly by the input amount | [ ] | [x] | +| 23 | sendERC20 decreases total supply in source chain and increases it in destination chain exactly by the input amount | [ ] | [x] | +| 24 | sum of supertoken total supply across all chains is always equal to convert(legacy, super)- convert(super, legacy) | [ ] | [~] | # Expected external interactions diff --git a/packages/contracts-bedrock/test/properties/SUMMARY.md b/packages/contracts-bedrock/test/properties/SUMMARY.md index ed6c286556fd..cf9c8a93afa5 100644 --- a/packages/contracts-bedrock/test/properties/SUMMARY.md +++ b/packages/contracts-bedrock/test/properties/SUMMARY.md @@ -5,7 +5,7 @@ This document defines a set of properties global to the supertoken ecosystem, for which we will: - run a [Medusa](https://github.com/crytic/medusa) fuzzing campaign, trying to break system invariants -- formally prove with [Halmos](https://github.com/ethereum-optimism/optimism) whenever possible +- formally prove with [Halmos](https://github.com/a16z/halmos) whenever possible ## Where to place the testing campaign @@ -29,12 +29,12 @@ Given the [OP monorepo](https://github.com/ethereum-optimism/optimism) already h - [ ] inclusion of relay transactions - [ ] sequencer implementation - [ ] [OptimismMintableERC20](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC20.sol) -- [ ] [L2ToL2CrossDomainMessenger](https://www.notion.so/defi-wonderland/src/L2/L2CrossDomainMessenger.sol) -- [ ] [CrossL2Inbox](https://www.notion.so/defi-wonderland/src/L2/CrossL2Inbox.sol) +- [ ] [L2ToL2CrossDomainMessenger](https://github.com/defi-wonderland/src/L2/L2CrossDomainMessenger.sol) +- [ ] [CrossL2Inbox](https://github.com/defi-wonderland/src/L2/CrossL2Inbox.sol) ## Pain points -- existing fuzzing tools use the same EVM to run the tested contracts as they do for asserting invariants, tracking ghost variables and everything else necessary to provision a fuzzing campaign. While this is usually very convenient, it means that we can’t assert on the behaviour/state of *different* EVMs from within a fuzzing campaign. This means we will have to walk around the requirement of supertokens having the same address across all chains, and implement a way to mock tokens existing in different chains. We will strive to formally prove it in a unitary fashion to mitigate this in properties 13 and 14 +- existing fuzzing tools use the same EVM to run the tested contracts as they do for asserting invariants, tracking ghost variables and everything else necessary to provision a fuzzing campaign. While this is usually very convenient, it means that we can’t assert on the behaviour/state of *different* EVMs from within a fuzzing campaign. This means we will have to walk around the requirement of supertokens having the same address across all chains, and implement a way to mock tokens existing in different chains. We will strive to formally prove it in a unitary fashion to mitigate this in properties 0 and 1 - a buffer to represent 'in transit' messages should be implemented to assert on invariants relating to the non-atomicity of bridging from one chain to another. It is yet to be determined if it’ll be a FIFO queue (assuming ideal message ordering by sequencers) or it’ll have random-access capability to simulate messages arriving out of order ## Definitions diff --git a/packages/contracts-bedrock/test/properties/helpers/MockCrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/properties/helpers/MockCrossDomainMessenger.t.sol new file mode 100644 index 000000000000..4ad4d474b738 --- /dev/null +++ b/packages/contracts-bedrock/test/properties/helpers/MockCrossDomainMessenger.t.sol @@ -0,0 +1,52 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; +import { SafeCall } from "src/libraries/SafeCall.sol"; + +contract MockCrossDomainMessenger { + ///////////////////////////////////////////////////////// + // State vars mocking the L2toL2CrossDomainMessenger // + ///////////////////////////////////////////////////////// + address public crossDomainMessageSender; + address public crossDomainMessageSource; + + /////////////////////////////////////////////////// + // Helpers for cross-chain interaction mocking // + /////////////////////////////////////////////////// + mapping(address => bytes32) public superTokenInitDeploySalts; + mapping(uint256 chainId => mapping(bytes32 reayDeployData => address)) public superTokenAddresses; + + function crossChainMessageReceiver( + address sender, + uint256 destinationChainId + ) + external + returns (OptimismSuperchainERC20) + { + return OptimismSuperchainERC20(superTokenAddresses[destinationChainId][superTokenInitDeploySalts[sender]]); + } + + function registerSupertoken(bytes32 deploySalt, uint256 chainId, address token) external { + superTokenAddresses[chainId][deploySalt] = token; + superTokenInitDeploySalts[token] = deploySalt; + } + + //////////////////////////////////////////////////////// + // Functions mocking the L2toL2CrossDomainMessenger // + //////////////////////////////////////////////////////// + + /// @dev recipient will not be used since in normal execution it's the same + /// address on a different chain, but here we have to compute it to mock + /// cross-chain messaging + function sendMessage(uint256 chainId, address, /*recipient*/ bytes memory message) external { + address crossChainRecipient = superTokenAddresses[chainId][superTokenInitDeploySalts[msg.sender]]; + if (crossChainRecipient == msg.sender) { + require(false, "same chain"); + } + crossDomainMessageSender = crossChainRecipient; + crossDomainMessageSource = msg.sender; + SafeCall.call(crossDomainMessageSender, 0, message); + crossDomainMessageSender = address(0); + } +} diff --git a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol b/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol index e1a84c80c40f..a7ffa6229039 100644 --- a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol +++ b/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol @@ -7,42 +7,7 @@ import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Pr import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { SafeCall } from "src/libraries/SafeCall.sol"; - -contract MockCrossDomainMessenger { - address public crossDomainMessageSender; - address public crossDomainMessageSource; - mapping(address => bytes32) public superTokenInitDeploySalts; - mapping(uint256 chainId => mapping(bytes32 reayDeployData => address)) public superTokenAddresses; - // test-specific functions - - function crossChainMessageReceiver( - address sender, - uint256 destinationChainId - ) - external - returns (OptimismSuperchainERC20) - { - return OptimismSuperchainERC20(superTokenAddresses[destinationChainId][superTokenInitDeploySalts[sender]]); - } - - function registerSupertoken(bytes32 deploySalt, uint256 chainId, address token) external { - superTokenAddresses[chainId][deploySalt] = token; - superTokenInitDeploySalts[token] = deploySalt; - } - // mocked functions - - function sendMessage(uint256 chainId, address, /*recipient*/ bytes memory message) external { - address crossChainRecipient = superTokenAddresses[chainId][superTokenInitDeploySalts[msg.sender]]; - if (crossChainRecipient == msg.sender) { - require(false, "same chain"); - } - crossDomainMessageSender = crossChainRecipient; - crossDomainMessageSource = msg.sender; - SafeCall.call(crossDomainMessageSender, 0, message); - crossDomainMessageSender = address(0); - } -} +import { MockCrossDomainMessenger } from "../helpers/MockCrossDomainMessenger.t.sol"; contract ProtocolAtomicFuzz is Test { using EnumerableMap for EnumerableMap.Bytes32ToUintMap; @@ -62,15 +27,15 @@ contract ProtocolAtomicFuzz is Test { struct TokenDeployParams { uint8 remoteTokenIndex; - uint8 name; - uint8 symbol; - uint8 decimals; + uint8 nameIndex; + uint8 symbolIndex; + uint8 decimalsIndex; } address[] internal remoteTokens; address[] internal allSuperTokens; - // deploy salt => total supply sum across chains + //@dev 'real' deploy salt => total supply sum across chains EnumerableMap.Bytes32ToUintMap internal ghost_totalSupplyAcrossChains; constructor() { @@ -86,9 +51,9 @@ contract ProtocolAtomicFuzz is Test { modifier validateTokenDeployParams(TokenDeployParams memory params) { params.remoteTokenIndex = uint8(bound(params.remoteTokenIndex, 0, remoteTokens.length - 1)); - params.name = uint8(bound(params.name, 0, WORDS.length - 1)); - params.symbol = uint8(bound(params.symbol, 0, WORDS.length - 1)); - params.decimals = uint8(bound(params.decimals, 0, DECIMALS.length - 1)); + params.nameIndex = uint8(bound(params.nameIndex, 0, WORDS.length - 1)); + params.symbolIndex = uint8(bound(params.symbolIndex, 0, WORDS.length - 1)); + params.decimalsIndex = uint8(bound(params.decimalsIndex, 0, DECIMALS.length - 1)); _; } @@ -102,15 +67,19 @@ contract ProtocolAtomicFuzz is Test { chainId = bound(chainId, 0, MAX_CHAINS - 1); _deploySupertoken( remoteTokens[params.remoteTokenIndex], - WORDS[params.name], - WORDS[params.symbol], - DECIMALS[params.decimals], + WORDS[params.nameIndex], + WORDS[params.symbolIndex], + DECIMALS[params.decimalsIndex], chainId ); } /// @custom:property-id 22 + /// @custom:property sendERC20 decreases sender balance in source chain and increases receiver balance in + /// destination chain exactly by the input amount /// @custom:property-id 23 + /// @custom:property sendERC20 decreases total supply in source chain and increases it in destination chain exactly + /// by the input amount function fuzz_SelfBridgeSupertoken(uint256 fromIndex, uint256 destinationChainId, uint256 amount) external { destinationChainId = bound(destinationChainId, 0, MAX_CHAINS - 1); fromIndex = bound(fromIndex, 0, allSuperTokens.length - 1); @@ -122,10 +91,10 @@ contract ProtocolAtomicFuzz is Test { require(address(destinationToken) != address(0)); uint256 sourceBalanceBefore = sourceToken.balanceOf(msg.sender); uint256 sourceSupplyBefore = sourceToken.totalSupply(); - // NOTE: lift this requirement to allow one more failure mode uint256 destinationBalanceBefore = destinationToken.balanceOf(msg.sender); uint256 destinationSupplyBefore = destinationToken.totalSupply(); + // NOTE: lift this requirement to allow one more failure mode amount = bound(amount, 0, sourceBalanceBefore); vm.prank(msg.sender); try sourceToken.sendERC20(msg.sender, amount, destinationChainId) { @@ -160,6 +129,9 @@ contract ProtocolAtomicFuzz is Test { // - non-atomic bridge // - `convert` /// @custom:property-id 24 + /// @custom:property sum of supertoken total supply across all chains is always equal to convert(legacy, super)- + /// convert(super, legacy) + /// @dev deliberately not a view method so medusa runs it but not the view methods defined by Test function property_totalSupplyAcrossChainsEqualsMints() external { for (uint256 i = 0; i < ghost_totalSupplyAcrossChains.length(); i++) { uint256 totalSupply = 0; @@ -185,21 +157,21 @@ contract ProtocolAtomicFuzz is Test { function _deploySupertoken( address remoteToken, - string memory name, - string memory symbol, + string memory nameIndex, + string memory symbolIndex, uint8 decimals, uint256 chainId ) internal { - bytes32 realSalt = keccak256(abi.encode(remoteToken, name, symbol, decimals)); - bytes32 hackySalt = keccak256(abi.encode(remoteToken, name, symbol, decimals, chainId)); + bytes32 realSalt = keccak256(abi.encode(remoteToken, nameIndex, symbolIndex, decimals)); + bytes32 hackySalt = keccak256(abi.encode(remoteToken, nameIndex, symbolIndex, decimals, chainId)); OptimismSuperchainERC20 token = OptimismSuperchainERC20( address( // TODO: Use the SuperchainERC20 Beacon Proxy new ERC1967Proxy{ salt: hackySalt }( address(superchainERC20Impl), - abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, name, symbol, decimals)) + abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, nameIndex, symbolIndex, decimals)) ) ) ); @@ -208,7 +180,7 @@ contract ProtocolAtomicFuzz is Test { uint256 mintAmount = INITIAL_TOKENS * 10 ** decimals; vm.prank(BRIDGE); token.mint(msg.sender, mintAmount); - (,uint256 curr) = ghost_totalSupplyAcrossChains.tryGet(realSalt); + (, uint256 curr) = ghost_totalSupplyAcrossChains.tryGet(realSalt); ghost_totalSupplyAcrossChains.set(realSalt, curr + mintAmount); } } From 8173ee7515543f1fa726d202a5b64ad4476b944b Mon Sep 17 00:00:00 2001 From: teddy Date: Wed, 14 Aug 2024 12:47:03 -0300 Subject: [PATCH 07/48] test: guide the fuzzer a little bit less previously: initial mint, bound on transfer amount: 146625 calls in 200s now: no initial mint, no bound on transfer amount: 176835 calls in 200s it doesn't seem to slow the fuzzer down --- .../test/properties/medusa/ProtocolAtomic.t.sol | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol b/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol index a7ffa6229039..9c3acd1daa21 100644 --- a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol +++ b/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol @@ -94,8 +94,6 @@ contract ProtocolAtomicFuzz is Test { uint256 destinationBalanceBefore = destinationToken.balanceOf(msg.sender); uint256 destinationSupplyBefore = destinationToken.totalSupply(); - // NOTE: lift this requirement to allow one more failure mode - amount = bound(amount, 0, sourceBalanceBefore); vm.prank(msg.sender); try sourceToken.sendERC20(msg.sender, amount, destinationChainId) { uint256 sourceBalanceAfter = sourceToken.balanceOf(msg.sender); @@ -111,7 +109,7 @@ contract ProtocolAtomicFuzz is Test { assert(sourceSupplyBefore - amount == sourceSupplyAfter); assert(destinationSupplyBefore + amount == destinationSupplyAfter); } catch { - assert(address(destinationToken) == address(sourceToken)); + assert(address(destinationToken) == address(sourceToken) || sourceBalanceBefore < amount); } } @@ -121,7 +119,8 @@ contract ProtocolAtomicFuzz is Test { vm.prank(BRIDGE); // medusa calls with different senders by default OptimismSuperchainERC20(addr).mint(msg.sender, amount); - uint256 currentValue = ghost_totalSupplyAcrossChains.get(MESSENGER.superTokenInitDeploySalts(addr)); + // currentValue will be zero if key is not present + (,uint256 currentValue) = ghost_totalSupplyAcrossChains.tryGet(MESSENGER.superTokenInitDeploySalts(addr)); ghost_totalSupplyAcrossChains.set(MESSENGER.superTokenInitDeploySalts(addr), currentValue + amount); } @@ -177,10 +176,5 @@ contract ProtocolAtomicFuzz is Test { ); MESSENGER.registerSupertoken(realSalt, chainId, address(token)); allSuperTokens.push(address(token)); - uint256 mintAmount = INITIAL_TOKENS * 10 ** decimals; - vm.prank(BRIDGE); - token.mint(msg.sender, mintAmount); - (, uint256 curr) = ghost_totalSupplyAcrossChains.tryGet(realSalt); - ghost_totalSupplyAcrossChains.set(realSalt, curr + mintAmount); } } From ec29415b14a53b23d743d143b4f7420528133664 Mon Sep 17 00:00:00 2001 From: teddy Date: Thu, 15 Aug 2024 13:48:38 -0300 Subject: [PATCH 08/48] fix: fixes after lovely feedback by disco --- packages/contracts-bedrock/medusa.json | 2 +- .../test/properties/SUMMARY.md | 7 ++-- ...l => MockL2ToL2CrossDomainMessenger.t.sol} | 9 +++-- .../properties/medusa/ProtocolAtomic.t.sol | 39 ++++++++++++++----- 4 files changed, 38 insertions(+), 19 deletions(-) rename packages/contracts-bedrock/test/properties/helpers/{MockCrossDomainMessenger.t.sol => MockL2ToL2CrossDomainMessenger.t.sol} (85%) diff --git a/packages/contracts-bedrock/medusa.json b/packages/contracts-bedrock/medusa.json index 76592655ffa1..88b940370834 100644 --- a/packages/contracts-bedrock/medusa.json +++ b/packages/contracts-bedrock/medusa.json @@ -18,7 +18,7 @@ ], "blockNumberDelayMax": 60480, "blockTimestampDelayMax": 604800, - "blockGasLimit": 125000000, + "blockGasLimit": 30000000, "transactionGasLimit": 12500000, "testing": { "stopOnFailedTest": true, diff --git a/packages/contracts-bedrock/test/properties/SUMMARY.md b/packages/contracts-bedrock/test/properties/SUMMARY.md index cf9c8a93afa5..31598c1cdd28 100644 --- a/packages/contracts-bedrock/test/properties/SUMMARY.md +++ b/packages/contracts-bedrock/test/properties/SUMMARY.md @@ -18,9 +18,8 @@ Given the [OP monorepo](https://github.com/ethereum-optimism/optimism) already h ## Contracts in scope -- [ ] [OptimismSuperchainERC20](https://github.com/defi-wonderland/optimism/pull/9/files#diff-810060510a8a9c06dc60cdce6782e5cafd93b638e2557307a68abe694ee86aee) -- [ ] [OptimismMintableERC20Factory](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol) -- [ ] [SuperchsupERC20ainERC20](https://github.com/defi-wonderland/optimism/pull/8/files#diff-603fd7d5a0b2c403c0d1eee21d0ee60fb8eb72430169eaac5ec7081e01de96b8) (not yet merged) +- [ ] [OptimismMintableERC20Factory](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol) (modifications to enable `convert` not yet merged) +- [ ] [OptimismSuperchainERC20](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol1) - [ ] [SuperchainERC20Factory](https://github.com/defi-wonderland/optimism/pull/8/files#diff-09838f5703c353d0f7c5ff395acc04c1768ef58becac67404bc17e1fb0018517) (not yet merged) - [ ] [L2StandardBridgeInterop](https://github.com/defi-wonderland/optimism/pull/10/files#diff-56cf869412631eac0a04a03f7d026596f64a1e00fcffa713bc770d67c6856c2f) (not yet merged) @@ -29,7 +28,7 @@ Given the [OP monorepo](https://github.com/ethereum-optimism/optimism) already h - [ ] inclusion of relay transactions - [ ] sequencer implementation - [ ] [OptimismMintableERC20](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC20.sol) -- [ ] [L2ToL2CrossDomainMessenger](https://github.com/defi-wonderland/src/L2/L2CrossDomainMessenger.sol) +- [ ] [L2ToL2CrossDomainMessenger](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2CrossDomainMessenger.sol) - [ ] [CrossL2Inbox](https://github.com/defi-wonderland/src/L2/CrossL2Inbox.sol) ## Pain points diff --git a/packages/contracts-bedrock/test/properties/helpers/MockCrossDomainMessenger.t.sol b/packages/contracts-bedrock/test/properties/helpers/MockL2ToL2CrossDomainMessenger.t.sol similarity index 85% rename from packages/contracts-bedrock/test/properties/helpers/MockCrossDomainMessenger.t.sol rename to packages/contracts-bedrock/test/properties/helpers/MockL2ToL2CrossDomainMessenger.t.sol index 4ad4d474b738..0e3f819a6f06 100644 --- a/packages/contracts-bedrock/test/properties/helpers/MockCrossDomainMessenger.t.sol +++ b/packages/contracts-bedrock/test/properties/helpers/MockL2ToL2CrossDomainMessenger.t.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.25; import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; -contract MockCrossDomainMessenger { +contract MockL2ToL2CrossDomainMessenger { ///////////////////////////////////////////////////////// // State vars mocking the L2toL2CrossDomainMessenger // ///////////////////////////////////////////////////////// @@ -14,14 +14,15 @@ contract MockCrossDomainMessenger { /////////////////////////////////////////////////// // Helpers for cross-chain interaction mocking // /////////////////////////////////////////////////// - mapping(address => bytes32) public superTokenInitDeploySalts; - mapping(uint256 chainId => mapping(bytes32 reayDeployData => address)) public superTokenAddresses; + mapping(address supertoken => bytes32 deploySalt) public superTokenInitDeploySalts; + mapping(uint256 chainId => mapping(bytes32 deploySalt => address supertoken)) public superTokenAddresses; function crossChainMessageReceiver( address sender, uint256 destinationChainId ) external + view returns (OptimismSuperchainERC20) { return OptimismSuperchainERC20(superTokenAddresses[destinationChainId][superTokenInitDeploySalts[sender]]); @@ -36,7 +37,7 @@ contract MockCrossDomainMessenger { // Functions mocking the L2toL2CrossDomainMessenger // //////////////////////////////////////////////////////// - /// @dev recipient will not be used since in normal execution it's the same + /// @notice recipient will not be used since in normal execution it's the same /// address on a different chain, but here we have to compute it to mock /// cross-chain messaging function sendMessage(uint256 chainId, address, /*recipient*/ bytes memory message) external { diff --git a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol b/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol index 9c3acd1daa21..0368abb5f70f 100644 --- a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol +++ b/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol @@ -7,7 +7,7 @@ import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Pr import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { MockCrossDomainMessenger } from "../helpers/MockCrossDomainMessenger.t.sol"; +import { MockL2ToL2CrossDomainMessenger } from "../helpers/MockL2ToL2CrossDomainMessenger.t.sol"; contract ProtocolAtomicFuzz is Test { using EnumerableMap for EnumerableMap.Bytes32ToUintMap; @@ -17,8 +17,8 @@ contract ProtocolAtomicFuzz is Test { uint8 internal constant INITIAL_SUPERTOKENS = 1; uint8 internal constant SUPERTOKEN_INITIAL_MINT = 100; address internal constant BRIDGE = Predeploys.L2_STANDARD_BRIDGE; - MockCrossDomainMessenger internal constant MESSENGER = - MockCrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); + MockL2ToL2CrossDomainMessenger internal constant MESSENGER = + MockL2ToL2CrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); OptimismSuperchainERC20 internal superchainERC20Impl; // NOTE: having more options for this enables the fuzzer to configure // different supertokens for the same remote token @@ -35,11 +35,11 @@ contract ProtocolAtomicFuzz is Test { address[] internal remoteTokens; address[] internal allSuperTokens; - //@dev 'real' deploy salt => total supply sum across chains + //@notice 'real' deploy salt => total supply sum across chains EnumerableMap.Bytes32ToUintMap internal ghost_totalSupplyAcrossChains; constructor() { - vm.etch(address(MESSENGER), address(new MockCrossDomainMessenger()).code); + vm.etch(address(MESSENGER), address(new MockL2ToL2CrossDomainMessenger()).code); superchainERC20Impl = new OptimismSuperchainERC20(); for (uint256 i = 0; i < INITIAL_TOKENS; i++) { _deployRemoteToken(); @@ -49,6 +49,10 @@ contract ProtocolAtomicFuzz is Test { } } + /// @notice the deploy params are _indexes_ to pick from a pre-defined array of options and limit + /// the amount of supertokens for a given remoteAsset that are incompatible between them, as + /// two supertokens have to share decimals, name, symbol and remoteAsset to be considered + /// the same asset, and therefore bridgable. modifier validateTokenDeployParams(TokenDeployParams memory params) { params.remoteTokenIndex = uint8(bound(params.remoteTokenIndex, 0, remoteTokens.length - 1)); params.nameIndex = uint8(bound(params.nameIndex, 0, WORDS.length - 1)); @@ -57,6 +61,7 @@ contract ProtocolAtomicFuzz is Test { _; } + /// @notice deploy a new supertoken with deploy salt determined by params, to the given (of course mocked) chainId function fuzz_DeployNewSupertoken( TokenDeployParams memory params, uint256 chainId @@ -113,6 +118,9 @@ contract ProtocolAtomicFuzz is Test { } } + /// @notice pick one already-deployed supertoken and mint an arbitrary amount of it + /// necessary so there is something to be bridged :D + /// TODO: will be replaced when testing the factories and `convert()` function fuzz_MintSupertoken(uint256 index, uint96 amount) external { index = bound(index, 0, allSuperTokens.length - 1); address addr = allSuperTokens[index]; @@ -130,13 +138,15 @@ contract ProtocolAtomicFuzz is Test { /// @custom:property-id 24 /// @custom:property sum of supertoken total supply across all chains is always equal to convert(legacy, super)- /// convert(super, legacy) - /// @dev deliberately not a view method so medusa runs it but not the view methods defined by Test + /// @notice deliberately not a view method so medusa runs it but not the view methods defined by Test function property_totalSupplyAcrossChainsEqualsMints() external { - for (uint256 i = 0; i < ghost_totalSupplyAcrossChains.length(); i++) { + // iterate over unique deploy salts aka supertokens that are supposed to be compatible with each other + for (uint256 deploySaltIndex = 0; deploySaltIndex < ghost_totalSupplyAcrossChains.length(); deploySaltIndex++) { uint256 totalSupply = 0; - (bytes32 currentSalt, uint256 trackedSupply) = ghost_totalSupplyAcrossChains.at(i); - for (uint256 j = 0; j < MAX_CHAINS; j++) { - address supertoken = MESSENGER.superTokenAddresses(j, currentSalt); + (bytes32 currentSalt, uint256 trackedSupply) = ghost_totalSupplyAcrossChains.at(deploySaltIndex); + // and then over all the (mocked) chain ids where that supertoken could be deployed + for (uint256 validChainId = 0; validChainId < MAX_CHAINS; validChainId++) { + address supertoken = MESSENGER.superTokenAddresses(validChainId, currentSalt); if (supertoken != address(0)) { totalSupply += OptimismSuperchainERC20(supertoken).totalSupply(); } @@ -149,11 +159,15 @@ contract ProtocolAtomicFuzz is Test { _deployRemoteToken(); } + /// @notice deploy a remote token, that supertokens will be a representation of. They are never called, so there + /// is no need to actually deploy a contract for them function _deployRemoteToken() internal { // make sure they don't conflict with predeploys/preinstalls/precompiles/other tokens remoteTokens.push(address(uint160(1000 + remoteTokens.length))); } + /// @custom:property-id 14 + /// @custom:property supertoken total supply starts at zero function _deploySupertoken( address remoteToken, string memory nameIndex, @@ -163,7 +177,10 @@ contract ProtocolAtomicFuzz is Test { ) internal { + // this salt would be used in production. Tokens sharing it will be bridgable with each other bytes32 realSalt = keccak256(abi.encode(remoteToken, nameIndex, symbolIndex, decimals)); + // what we use in the tests to walk around two contracts needing two different addresses + // tbf we could be using CREATE1, but this feels more verbose bytes32 hackySalt = keccak256(abi.encode(remoteToken, nameIndex, symbolIndex, decimals, chainId)); OptimismSuperchainERC20 token = OptimismSuperchainERC20( address( @@ -174,6 +191,8 @@ contract ProtocolAtomicFuzz is Test { ) ) ); + // 14 + assert(token.totalSupply() == 0); MESSENGER.registerSupertoken(realSalt, chainId, address(token)); allSuperTokens.push(address(token)); } From 75f6977d02fa1000ae6fc4c5ef817fc6f6cbdff9 Mon Sep 17 00:00:00 2001 From: teddy Date: Thu, 15 Aug 2024 16:03:22 -0300 Subject: [PATCH 09/48] docs: merge both documents and categorized properties by their milestone --- .../test/properties/PROPERTIES.md | 125 +++++++++++++----- .../test/properties/SUMMARY.md | 42 ------ 2 files changed, 89 insertions(+), 78 deletions(-) delete mode 100644 packages/contracts-bedrock/test/properties/SUMMARY.md diff --git a/packages/contracts-bedrock/test/properties/PROPERTIES.md b/packages/contracts-bedrock/test/properties/PROPERTIES.md index 540d591f77de..78b9de501962 100644 --- a/packages/contracts-bedrock/test/properties/PROPERTIES.md +++ b/packages/contracts-bedrock/test/properties/PROPERTIES.md @@ -1,4 +1,57 @@ -# supertoken properties +# Supertoken advanced testing + +## Overview + +This document defines a set of properties global to the supertoken ecosystem, for which we will: + +- run a [Medusa](https://github.com/crytic/medusa) fuzzing campaign, trying to break system invariants +- formally prove with [Halmos](https://github.com/a16z/halmos) whenever possible + +## Milestones + +The supertoken ecosystem consists of not just the supertoken contract, but the required changes to other contracts for liquidity to reach the former. + +Considering only the supertoken contract is merged into the `develop` branch, and work for the other components is still in progress, we define three milestones for the testing campaign: + +- SupERC20: concerned with only the supertoken contract, the first one to be implemented +- Factories: covers the above + the development of `OptimismSuperchainERC20Factory` and required changes to `OptimismMintableERC20Factory` +- Liquidity Migration: includes the `convert` function on the `L2StandardBridgeInterop` to migrate liquidity from legacy tokens into supertokens + +## Where to place the testing campaign + +Given the [OP monorepo](https://github.com/ethereum-optimism/optimism) already has invariant testing provided by foundry, it's not a trivial matter where to place this advanced testing campaign. Two alternatives are proposed: + +- including it in the mainline OP monorepo, in a subdirectory of the existing test contracts such as `test/invariants/medusa/superc20/` +- keep the campaign in wonderland's fork of the repository, in its own feature branch, in which case the deliverable would consist primarily of: + - a summary of the results, extending this document + - PRs with extra unit tests replicating found issues to the main repo where applicable + +## Contracts in scope + +- [ ] [OptimismMintableERC20Factory](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol) (modifications to enable `convert` not yet merged) +- [ ] [OptimismSuperchainERC20](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol1) +- [ ] [SuperchainERC20Factory](https://github.com/defi-wonderland/optimism/pull/8/files#diff-09838f5703c353d0f7c5ff395acc04c1768ef58becac67404bc17e1fb0018517) (not yet merged) +- [ ] [L2StandardBridgeInterop](https://github.com/defi-wonderland/optimism/pull/10/files#diff-56cf869412631eac0a04a03f7d026596f64a1e00fcffa713bc770d67c6856c2f) (not yet merged) + +## Behavior assumed correct + +- [ ] inclusion of relay transactions +- [ ] sequencer implementation +- [ ] [OptimismMintableERC20](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC20.sol) +- [ ] [L2ToL2CrossDomainMessenger](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2CrossDomainMessenger.sol) +- [ ] [CrossL2Inbox](https://github.com/defi-wonderland/src/L2/CrossL2Inbox.sol) + +## Pain points + +- existing fuzzing tools use the same EVM to run the tested contracts as they do for asserting invariants, tracking ghost variables and everything else necessary to provision a fuzzing campaign. While this is usually very convenient, it means that we can’t assert on the behaviour/state of *different* EVMs from within a fuzzing campaign. This means we will have to walk around the requirement of supertokens having the same address across all chains, and implement a way to mock tokens existing in different chains. We will strive to formally prove it in a unitary fashion to mitigate this in properties 0 and 1 +- a buffer to represent 'in transit' messages should be implemented to assert on invariants relating to the non-atomicity of bridging from one chain to another. It is yet to be determined if it’ll be a FIFO queue (assuming ideal message ordering by sequencers) or it’ll have random-access capability to simulate messages arriving out of order + +## Definitions + +- *legacy token:* an OptimismMintableERC20 or L2StandardERC20 token on the suprechain that has either been deployed by the factory after the liquidity migration upgrade to the latter, or has been deployed before it **but** added to factory’s `deployments` mapping as part of the upgrade. This testing campaign is not concerned with tokens on L1 or not listed in the factory’s `deployments` mapping. +- *supertoken:* a SuperchainERC20 contract deployed on the Superchain + +# Ecosystem properties legend: - `[ ]`: property not yet tested @@ -9,56 +62,56 @@ legend: ## Unit test -| id | description | halmos | medusa | -| --- | --- | --- | --- | -| 0 | supertoken token address does not depend on the executing chain’s chainID | [ ] | [ ] | -| 1 | supertoken token address depends on name, remote token, address and decimals | [ ] | [ ] | -| 2 | convert() should only allow converting legacy tokens to supertoken and viceversa | [ ] | [ ] | -| 3 | convert() only allows migrations between tokens representing the same remote asset | [ ] | [ ] | -| 4 | convert() only allows migrations from tokens with the same decimals | [ ] | [ ] | -| 5 | convert() burns the same amount of one token that it mints of the other | [ ] | [ ] | +| id | milestone | description | halmos | medusa | +| --- | --- | --- | --- | --- | +| 0 | Factories | supertoken token address does not depend on the executing chain’s chainID | [ ] | [ ] | +| 1 | Factories | supertoken token address depends on name, remote token, address and decimals | [ ] | [ ] | +| 2 | Liquidity Migration | convert() should only allow converting legacy tokens to supertoken and viceversa | [ ] | [ ] | +| 3 | Liquidity Migration | convert() only allows migrations between tokens representing the same remote asset | [ ] | [ ] | +| 4 | Liquidity Migration | convert() only allows migrations from tokens with the same decimals | [ ] | [ ] | +| 5 | Liquidity Migration | convert() burns the same amount of one token that it mints of the other | [ ] | [ ] | ## Valid state -| id | description | halmos | medusa | -| --- | --- | --- | --- | -| 6 | calls to sendERC20 succeed as long as caller has enough balance | [ ] | [ ] | -| 7 | calls to relayERC20 always succeed as long as the cross-domain caller is valid | **[ ]** | [ ] | +| id | milestone | description | halmos | medusa | +| --- | --- | --- | --- | --- | +| 6 | SupERC20 | calls to sendERC20 succeed as long as caller has enough balance | [ ] | [ ] | +| 7 | SupERC20 | calls to relayERC20 always succeed as long as the cross-domain caller is valid | **[ ]** | [ ] | ## Variable transition -| id | description | halmos | medusa | -| --- | --- | --- | --- | -| 8 | sendERC20 with a value of zero does not modify accounting | [ ] | [ ] | -| 9 | relayERC20 with a value of zero does not modify accounting | [ ] | [ ] | -| 10 | sendERC20 decreases the token's totalSupply in the source chain exactly by the input amount | [ ] | [ ] | -| 11 | relayERC20 increases the token's totalSupply in the destination chain exactly by the input amount | [ ] | [ ] | -| 12 | supertoken total supply only increases on calls to mint() by the L2toL2StandardBridge | [ ] | [ ] | -| 13 | supertoken total supply only decreases on calls to burn() by the L2toL2StandardBridge | [ ] | [ ] | -| 14 | supertoken total supply starts at zero | [ ] | [ ] | -| 15 | deploying a supertoken registers its remote token in the factory | [ ] | [ ] | -| 16 | deploying an OptimismMintableERC20 registers its remote token in the factory | [ ] | [ ] | +| id | milestone | description | halmos | medusa | +| --- | --- | --- | --- | --- | +| 8 | SupERC20 | sendERC20 with a value of zero does not modify accounting | [ ] | [ ] | +| 9 | SupERC20 | relayERC20 with a value of zero does not modify accounting | [ ] | [ ] | +| 10 | SupERC20 | sendERC20 decreases the token's totalSupply in the source chain exactly by the input amount | [ ] | [ ] | +| 11 | SupERC20 | relayERC20 increases the token's totalSupply in the destination chain exactly by the input amount | [ ] | [ ] | +| 12 | Liquidity Migration | supertoken total supply only increases on calls to mint() by the L2toL2StandardBridge | [ ] | [ ] | +| 13 | Liquidity Migration | supertoken total supply only decreases on calls to burn() by the L2toL2StandardBridge | [ ] | [ ] | +| 14 | SupERC20 | supertoken total supply starts at zero | [ ] | [ ] | +| 15 | Factories | deploying a supertoken registers its remote token in the factory | [ ] | [ ] | +| 16 | Factories | deploying an OptimismMintableERC20 registers its remote token in the factory | [ ] | [ ] | ## High level -| id | description | halmos | medusa | -| --- | --- | --- | --- | -| 17 | only calls to convert(legacy, super) can increase a supertoken’s total supply across chains | [ ] | [ ] | -| 18 | only calls to convert(super, legacy) can decrease a supertoken’s total supply across chains | [ ] | [ ] | -| 19 | sum of total supply across all chains is always <= to convert(legacy, super)- convert(super, legacy) | [ ] | [ ] | -| 20 | tokens sendERC20-ed on a source chain to a destination chain can be relayERC20-ed on it as long as the source chain is in the dependency set of the destination chain | [ ] | [ ] | -| 21 | sum of supertoken total supply across all chains is = to convert(legacy, super)- convert(super, legacy) when all cross-chain messages are processed | [ ] | [ ] | +| id | milestone | description | halmos | medusa | +| --- | --- | --- | --- | --- | +| 17 | Liquidity Migration | only calls to convert(legacy, super) can increase a supertoken’s total supply across chains | [ ] | [ ] | +| 18 | Liquidity Migration | only calls to convert(super, legacy) can decrease a supertoken’s total supply across chains | [ ] | [ ] | +| 19 | Liquidity Migration | sum of total supply across all chains is always <= to convert(legacy, super)- convert(super, legacy) | [ ] | [ ] | +| 20 | SupERC20 | tokens sendERC20-ed on a source chain to a destination chain can be relayERC20-ed on it as long as the source chain is in the dependency set of the destination chain | [ ] | [ ] | +| 21 | Liquidity Migration | sum of supertoken total supply across all chains is = to convert(legacy, super)- convert(super, legacy) when all cross-chain messages are processed | [ ] | [ ] | ## Atomic bridging pseudo-properties As another layer of defense, the following properties are defined which assume bridging operations to be atomic (that is, the sequencer and L2Inbox and CrossDomainMessenger contracts are fully abstracted away, `sendERC20` triggering the `relayERC20` call on the same transaction) It’s worth noting that these properties will not hold for a live system -| id | description | halmos | medusa | -| --- | --- | --- | --- | -| 22 | sendERC20 decreases sender balance in source chain and increases receiver balance in destination chain exactly by the input amount | [ ] | [x] | -| 23 | sendERC20 decreases total supply in source chain and increases it in destination chain exactly by the input amount | [ ] | [x] | -| 24 | sum of supertoken total supply across all chains is always equal to convert(legacy, super)- convert(super, legacy) | [ ] | [~] | +| id | milestone | description | halmos | medusa | +| --- | --- | --- | --- | --- | +| 22 | SupERC20 | sendERC20 decreases sender balance in source chain and increases receiver balance in destination chain exactly by the input amount | [ ] | [x] | +| 23 | SupERC20 | sendERC20 decreases total supply in source chain and increases it in destination chain exactly by the input amount | [ ] | [x] | +| 24 | Liquidity Migration | sum of supertoken total supply across all chains is always equal to convert(legacy, super)- convert(super, legacy) | [ ] | [~] | # Expected external interactions diff --git a/packages/contracts-bedrock/test/properties/SUMMARY.md b/packages/contracts-bedrock/test/properties/SUMMARY.md deleted file mode 100644 index 31598c1cdd28..000000000000 --- a/packages/contracts-bedrock/test/properties/SUMMARY.md +++ /dev/null @@ -1,42 +0,0 @@ -# SupERC20 advanced testing - -# Overview - -This document defines a set of properties global to the supertoken ecosystem, for which we will: - -- run a [Medusa](https://github.com/crytic/medusa) fuzzing campaign, trying to break system invariants -- formally prove with [Halmos](https://github.com/a16z/halmos) whenever possible - -## Where to place the testing campaign - -Given the [OP monorepo](https://github.com/ethereum-optimism/optimism) already has invariant testing provided by foundry, it's not a trivial matter where to place this advanced testing campaign. Two alternatives are proposed: - -- including it in the mainline OP monorepo, in a subdirectory of the existing test contracts such as `test/invariants/medusa/superc20/` -- creating a separate (potentially private) repository for this testing campaign, in which case the deliverable would consist primarily of: - - a summary of the results, extending this document - - PRs with extra unit tests replicating found issues to the main repo where applicable - -## Contracts in scope - -- [ ] [OptimismMintableERC20Factory](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol) (modifications to enable `convert` not yet merged) -- [ ] [OptimismSuperchainERC20](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol1) -- [ ] [SuperchainERC20Factory](https://github.com/defi-wonderland/optimism/pull/8/files#diff-09838f5703c353d0f7c5ff395acc04c1768ef58becac67404bc17e1fb0018517) (not yet merged) -- [ ] [L2StandardBridgeInterop](https://github.com/defi-wonderland/optimism/pull/10/files#diff-56cf869412631eac0a04a03f7d026596f64a1e00fcffa713bc770d67c6856c2f) (not yet merged) - -## Behavior assumed correct - -- [ ] inclusion of relay transactions -- [ ] sequencer implementation -- [ ] [OptimismMintableERC20](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC20.sol) -- [ ] [L2ToL2CrossDomainMessenger](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2CrossDomainMessenger.sol) -- [ ] [CrossL2Inbox](https://github.com/defi-wonderland/src/L2/CrossL2Inbox.sol) - -## Pain points - -- existing fuzzing tools use the same EVM to run the tested contracts as they do for asserting invariants, tracking ghost variables and everything else necessary to provision a fuzzing campaign. While this is usually very convenient, it means that we can’t assert on the behaviour/state of *different* EVMs from within a fuzzing campaign. This means we will have to walk around the requirement of supertokens having the same address across all chains, and implement a way to mock tokens existing in different chains. We will strive to formally prove it in a unitary fashion to mitigate this in properties 0 and 1 -- a buffer to represent 'in transit' messages should be implemented to assert on invariants relating to the non-atomicity of bridging from one chain to another. It is yet to be determined if it’ll be a FIFO queue (assuming ideal message ordering by sequencers) or it’ll have random-access capability to simulate messages arriving out of order - -## Definitions - -- *legacy token:* an OptimismMintableERC20 or L2StandardERC20 token on the suprechain that has either been deployed by the factory after the liquidity migration upgrade to the latter, or has been deployed before it **but** added to factory’s `deployments` mapping as part of the upgrade. This testing campaign is not concerned with tokens on L1 or not listed in the factory’s `deployments` mapping. -- *supertoken:* a SuperchainERC20 contract deployed on the Superchain From d761fa9f4fd4ead0781abfa961bb9c26af136c10 Mon Sep 17 00:00:00 2001 From: teddy Date: Thu, 15 Aug 2024 16:45:05 -0300 Subject: [PATCH 10/48] fix: fixes from parti's review --- .../test/properties/PROPERTIES.md | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/PROPERTIES.md b/packages/contracts-bedrock/test/properties/PROPERTIES.md index 78b9de501962..0262ecd23b52 100644 --- a/packages/contracts-bedrock/test/properties/PROPERTIES.md +++ b/packages/contracts-bedrock/test/properties/PROPERTIES.md @@ -30,7 +30,7 @@ Given the [OP monorepo](https://github.com/ethereum-optimism/optimism) already h - [ ] [OptimismMintableERC20Factory](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol) (modifications to enable `convert` not yet merged) - [ ] [OptimismSuperchainERC20](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol1) -- [ ] [SuperchainERC20Factory](https://github.com/defi-wonderland/optimism/pull/8/files#diff-09838f5703c353d0f7c5ff395acc04c1768ef58becac67404bc17e1fb0018517) (not yet merged) +- [ ] [OptimismSuperchainERC20Factory](https://github.com/defi-wonderland/optimism/pull/8/files#diff-09838f5703c353d0f7c5ff395acc04c1768ef58becac67404bc17e1fb0018517) (not yet merged) - [ ] [L2StandardBridgeInterop](https://github.com/defi-wonderland/optimism/pull/10/files#diff-56cf869412631eac0a04a03f7d026596f64a1e00fcffa713bc770d67c6856c2f) (not yet merged) ## Behavior assumed correct @@ -49,7 +49,7 @@ Given the [OP monorepo](https://github.com/ethereum-optimism/optimism) already h ## Definitions - *legacy token:* an OptimismMintableERC20 or L2StandardERC20 token on the suprechain that has either been deployed by the factory after the liquidity migration upgrade to the latter, or has been deployed before it **but** added to factory’s `deployments` mapping as part of the upgrade. This testing campaign is not concerned with tokens on L1 or not listed in the factory’s `deployments` mapping. -- *supertoken:* a SuperchainERC20 contract deployed on the Superchain +- *supertoken:* a SuperchainERC20 contract deployed by the `OptimismSuperchainERC20Factory` # Ecosystem properties @@ -62,14 +62,14 @@ legend: ## Unit test -| id | milestone | description | halmos | medusa | -| --- | --- | --- | --- | --- | -| 0 | Factories | supertoken token address does not depend on the executing chain’s chainID | [ ] | [ ] | -| 1 | Factories | supertoken token address depends on name, remote token, address and decimals | [ ] | [ ] | -| 2 | Liquidity Migration | convert() should only allow converting legacy tokens to supertoken and viceversa | [ ] | [ ] | -| 3 | Liquidity Migration | convert() only allows migrations between tokens representing the same remote asset | [ ] | [ ] | -| 4 | Liquidity Migration | convert() only allows migrations from tokens with the same decimals | [ ] | [ ] | -| 5 | Liquidity Migration | convert() burns the same amount of one token that it mints of the other | [ ] | [ ] | +| id | milestone | description | halmos | medusa | +| --- | --- | --- | --- | --- | +| 0 | Factories | supertoken token address does not depend on the executing chain’s chainID | [ ] | [ ] | +| 1 | Factories | supertoken token address depends on remote token, name, symbol and decimals | [ ] | [ ] | +| 2 | Liquidity Migration | convert() should only allow converting legacy tokens to supertoken and viceversa | [ ] | [ ] | +| 3 | Liquidity Migration | convert() only allows migrations between tokens representing the same remote asset | [ ] | [ ] | +| 4 | Liquidity Migration | convert() only allows migrations from tokens with the same decimals | [ ] | [ ] | +| 5 | Liquidity Migration | convert() burns the same amount of legacy token that it mints of supertoken, and viceversa | [ ] | [ ] | ## Valid state @@ -98,7 +98,7 @@ legend: | --- | --- | --- | --- | --- | | 17 | Liquidity Migration | only calls to convert(legacy, super) can increase a supertoken’s total supply across chains | [ ] | [ ] | | 18 | Liquidity Migration | only calls to convert(super, legacy) can decrease a supertoken’s total supply across chains | [ ] | [ ] | -| 19 | Liquidity Migration | sum of total supply across all chains is always <= to convert(legacy, super)- convert(super, legacy) | [ ] | [ ] | +| 19 | Liquidity Migration | sum of supertoken total supply across all chains is always <= to convert(legacy, super)- convert(super, legacy) | [ ] | [ ] | | 20 | SupERC20 | tokens sendERC20-ed on a source chain to a destination chain can be relayERC20-ed on it as long as the source chain is in the dependency set of the destination chain | [ ] | [ ] | | 21 | Liquidity Migration | sum of supertoken total supply across all chains is = to convert(legacy, super)- convert(super, legacy) when all cross-chain messages are processed | [ ] | [ ] | From 51a72e3d7276b4ae7818c0afa9df49ae2e05fedc Mon Sep 17 00:00:00 2001 From: teddy Date: Fri, 16 Aug 2024 15:24:08 -0300 Subject: [PATCH 11/48] fix: feedback from disco --- .../properties/medusa/ProtocolAtomic.t.sol | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol b/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol index 0368abb5f70f..b4f44d2bcf67 100644 --- a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol +++ b/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol @@ -41,10 +41,10 @@ contract ProtocolAtomicFuzz is Test { constructor() { vm.etch(address(MESSENGER), address(new MockL2ToL2CrossDomainMessenger()).code); superchainERC20Impl = new OptimismSuperchainERC20(); - for (uint256 i = 0; i < INITIAL_TOKENS; i++) { + for (uint256 remoteTokenIndex = 0; remoteTokenIndex < INITIAL_TOKENS; remoteTokenIndex++) { _deployRemoteToken(); - for (uint256 j = 0; j < INITIAL_SUPERTOKENS; j++) { - _deploySupertoken(remoteTokens[i], WORDS[0], WORDS[0], DECIMALS[0], j); + for (uint256 supertokenChainId = 0; supertokenChainId < INITIAL_SUPERTOKENS; supertokenChainId++) { + _deploySupertoken(remoteTokens[remoteTokenIndex], WORDS[0], WORDS[0], DECIMALS[0], supertokenChainId); } } } @@ -62,6 +62,8 @@ contract ProtocolAtomicFuzz is Test { } /// @notice deploy a new supertoken with deploy salt determined by params, to the given (of course mocked) chainId + /// @custom:property-id 14 + /// @custom:property supertoken total supply starts at zero function fuzz_DeployNewSupertoken( TokenDeployParams memory params, uint256 chainId @@ -70,13 +72,15 @@ contract ProtocolAtomicFuzz is Test { validateTokenDeployParams(params) { chainId = bound(chainId, 0, MAX_CHAINS - 1); - _deploySupertoken( + OptimismSuperchainERC20 supertoken = _deploySupertoken( remoteTokens[params.remoteTokenIndex], WORDS[params.nameIndex], WORDS[params.symbolIndex], DECIMALS[params.decimalsIndex], chainId ); + // 14 + assert(supertoken.totalSupply() == 0); } /// @custom:property-id 22 @@ -166,34 +170,36 @@ contract ProtocolAtomicFuzz is Test { remoteTokens.push(address(uint160(1000 + remoteTokens.length))); } - /// @custom:property-id 14 - /// @custom:property supertoken total supply starts at zero + /// @notice deploy a new supertoken representing remoteToken + /// remoteToken, name, symbol and decimals determine the 'real' deploy salt + /// and supertokens sharing it are interoperable between them + /// we however use the chainId as part of the deploy salt to mock the ability of + /// supertokens to exist on different chains on a single EVM. function _deploySupertoken( address remoteToken, - string memory nameIndex, - string memory symbolIndex, + string memory name, + string memory symbol, uint8 decimals, uint256 chainId ) internal + returns(OptimismSuperchainERC20 supertoken) { // this salt would be used in production. Tokens sharing it will be bridgable with each other - bytes32 realSalt = keccak256(abi.encode(remoteToken, nameIndex, symbolIndex, decimals)); + bytes32 realSalt = keccak256(abi.encode(remoteToken, name, symbol, decimals)); // what we use in the tests to walk around two contracts needing two different addresses // tbf we could be using CREATE1, but this feels more verbose - bytes32 hackySalt = keccak256(abi.encode(remoteToken, nameIndex, symbolIndex, decimals, chainId)); - OptimismSuperchainERC20 token = OptimismSuperchainERC20( + bytes32 hackySalt = keccak256(abi.encode(remoteToken, name, symbol, decimals, chainId)); + supertoken = OptimismSuperchainERC20( address( // TODO: Use the SuperchainERC20 Beacon Proxy new ERC1967Proxy{ salt: hackySalt }( address(superchainERC20Impl), - abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, nameIndex, symbolIndex, decimals)) + abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, name, symbol, decimals)) ) ) ); - // 14 - assert(token.totalSupply() == 0); - MESSENGER.registerSupertoken(realSalt, chainId, address(token)); - allSuperTokens.push(address(token)); + MESSENGER.registerSupertoken(realSalt, chainId, address(supertoken)); + allSuperTokens.push(address(supertoken)); } } From 745dcbc90b8940b97492b5fba2eb400cba507051 Mon Sep 17 00:00:00 2001 From: teddy Date: Fri, 16 Aug 2024 16:58:58 -0300 Subject: [PATCH 12/48] fix: feedback from doc --- packages/contracts-bedrock/test/properties/PROPERTIES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts-bedrock/test/properties/PROPERTIES.md b/packages/contracts-bedrock/test/properties/PROPERTIES.md index 0262ecd23b52..4bf542771307 100644 --- a/packages/contracts-bedrock/test/properties/PROPERTIES.md +++ b/packages/contracts-bedrock/test/properties/PROPERTIES.md @@ -43,7 +43,7 @@ Given the [OP monorepo](https://github.com/ethereum-optimism/optimism) already h ## Pain points -- existing fuzzing tools use the same EVM to run the tested contracts as they do for asserting invariants, tracking ghost variables and everything else necessary to provision a fuzzing campaign. While this is usually very convenient, it means that we can’t assert on the behaviour/state of *different* EVMs from within a fuzzing campaign. This means we will have to walk around the requirement of supertokens having the same address across all chains, and implement a way to mock tokens existing in different chains. We will strive to formally prove it in a unitary fashion to mitigate this in properties 0 and 1 +- existing fuzzing tools use the same EVM to run the tested contracts as they do for asserting invariants, tracking ghost variables and everything else necessary to provision a fuzzing campaign. While this is usually very convenient, it means that we can’t assert on the behaviour/state of *different* chains from within a fuzzing campaign. This means we will have to walk around the requirement of supertokens having the same address across all chains, and implement a way to mock tokens existing in different chains. We will strive to formally prove it in a unitary fashion to mitigate this in properties 0 and 1 - a buffer to represent 'in transit' messages should be implemented to assert on invariants relating to the non-atomicity of bridging from one chain to another. It is yet to be determined if it’ll be a FIFO queue (assuming ideal message ordering by sequencers) or it’ll have random-access capability to simulate messages arriving out of order ## Definitions From 07ca17ec5dc36395379943e79e0e93d3ee2a9ea9 Mon Sep 17 00:00:00 2001 From: teddy Date: Mon, 19 Aug 2024 13:12:19 -0300 Subject: [PATCH 13/48] refactor: separate state transitions from pure properties --- packages/contracts-bedrock/medusa.json | 4 +-- .../medusa/Protocol.properties.t.sol | 35 +++++++++++++++++++ .../Protocol.handler.t.sol} | 31 +++------------- 3 files changed, 41 insertions(+), 29 deletions(-) create mode 100644 packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol rename packages/contracts-bedrock/test/properties/medusa/{ProtocolAtomic.t.sol => handlers/Protocol.handler.t.sol} (84%) diff --git a/packages/contracts-bedrock/medusa.json b/packages/contracts-bedrock/medusa.json index 88b940370834..c91f69be607e 100644 --- a/packages/contracts-bedrock/medusa.json +++ b/packages/contracts-bedrock/medusa.json @@ -7,7 +7,7 @@ "callSequenceLength": 100, "corpusDirectory": "test/properties/medusa/corpus/", "coverageEnabled": true, - "targetContracts": ["ProtocolAtomicFuzz"], + "targetContracts": ["ProtocolProperties"], "targetContractsBalances": [], "constructorArgs": {}, "deployerAddress": "0x30000", @@ -43,7 +43,7 @@ } }, "propertyTesting": { - "enabled": false, + "enabled": true, "testPrefixes": [ "property_" ] diff --git a/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol b/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol new file mode 100644 index 000000000000..a6ed77dc6c6e --- /dev/null +++ b/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { ProtocolHandler } from "./handlers/Protocol.handler.t.sol"; +import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; +import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; + +contract ProtocolProperties is ProtocolHandler { + using EnumerableMap for EnumerableMap.Bytes32ToUintMap; + // TODO: will need rework after + // - non-atomic bridge + // - `convert` + /// @custom:property-id 24 + /// @custom:property sum of supertoken total supply across all chains is always equal to convert(legacy, super)- + /// convert(super, legacy) + + function property_totalSupplyAcrossChainsEqualsMints() external view returns (bool success) { + // iterate over unique deploy salts aka supertokens that are supposed to be compatible with each other + for (uint256 deploySaltIndex = 0; deploySaltIndex < ghost_totalSupplyAcrossChains.length(); deploySaltIndex++) { + uint256 totalSupply = 0; + (bytes32 currentSalt, uint256 trackedSupply) = ghost_totalSupplyAcrossChains.at(deploySaltIndex); + // and then over all the (mocked) chain ids where that supertoken could be deployed + for (uint256 validChainId = 0; validChainId < MAX_CHAINS; validChainId++) { + address supertoken = MESSENGER.superTokenAddresses(validChainId, currentSalt); + if (supertoken != address(0)) { + totalSupply += OptimismSuperchainERC20(supertoken).totalSupply(); + } + } + if (trackedSupply != totalSupply) { + return false; + } + } + return true; + } +} diff --git a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol b/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol similarity index 84% rename from packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol rename to packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol index b4f44d2bcf67..6d6209aca11b 100644 --- a/packages/contracts-bedrock/test/properties/medusa/ProtocolAtomic.t.sol +++ b/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol @@ -7,9 +7,9 @@ import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Pr import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { MockL2ToL2CrossDomainMessenger } from "../helpers/MockL2ToL2CrossDomainMessenger.t.sol"; +import { MockL2ToL2CrossDomainMessenger } from "../../helpers/MockL2ToL2CrossDomainMessenger.t.sol"; -contract ProtocolAtomicFuzz is Test { +contract ProtocolHandler is Test { using EnumerableMap for EnumerableMap.Bytes32ToUintMap; uint8 internal constant MAX_CHAINS = 4; @@ -132,33 +132,10 @@ contract ProtocolAtomicFuzz is Test { // medusa calls with different senders by default OptimismSuperchainERC20(addr).mint(msg.sender, amount); // currentValue will be zero if key is not present - (,uint256 currentValue) = ghost_totalSupplyAcrossChains.tryGet(MESSENGER.superTokenInitDeploySalts(addr)); + (, uint256 currentValue) = ghost_totalSupplyAcrossChains.tryGet(MESSENGER.superTokenInitDeploySalts(addr)); ghost_totalSupplyAcrossChains.set(MESSENGER.superTokenInitDeploySalts(addr), currentValue + amount); } - // TODO: will need rework after - // - non-atomic bridge - // - `convert` - /// @custom:property-id 24 - /// @custom:property sum of supertoken total supply across all chains is always equal to convert(legacy, super)- - /// convert(super, legacy) - /// @notice deliberately not a view method so medusa runs it but not the view methods defined by Test - function property_totalSupplyAcrossChainsEqualsMints() external { - // iterate over unique deploy salts aka supertokens that are supposed to be compatible with each other - for (uint256 deploySaltIndex = 0; deploySaltIndex < ghost_totalSupplyAcrossChains.length(); deploySaltIndex++) { - uint256 totalSupply = 0; - (bytes32 currentSalt, uint256 trackedSupply) = ghost_totalSupplyAcrossChains.at(deploySaltIndex); - // and then over all the (mocked) chain ids where that supertoken could be deployed - for (uint256 validChainId = 0; validChainId < MAX_CHAINS; validChainId++) { - address supertoken = MESSENGER.superTokenAddresses(validChainId, currentSalt); - if (supertoken != address(0)) { - totalSupply += OptimismSuperchainERC20(supertoken).totalSupply(); - } - } - assert(trackedSupply == totalSupply); - } - } - function fuzz_MockNewRemoteToken() external { _deployRemoteToken(); } @@ -183,7 +160,7 @@ contract ProtocolAtomicFuzz is Test { uint256 chainId ) internal - returns(OptimismSuperchainERC20 supertoken) + returns (OptimismSuperchainERC20 supertoken) { // this salt would be used in production. Tokens sharing it will be bridgable with each other bytes32 realSalt = keccak256(abi.encode(remoteToken, name, symbol, decimals)); From f23d8ff482ffa74086e2813b7d891c3d20be2d57 Mon Sep 17 00:00:00 2001 From: teddy Date: Mon, 19 Aug 2024 13:16:13 -0300 Subject: [PATCH 14/48] docs: update tested properties --- packages/contracts-bedrock/test/properties/PROPERTIES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/PROPERTIES.md b/packages/contracts-bedrock/test/properties/PROPERTIES.md index 4bf542771307..36b7b330ab0b 100644 --- a/packages/contracts-bedrock/test/properties/PROPERTIES.md +++ b/packages/contracts-bedrock/test/properties/PROPERTIES.md @@ -86,9 +86,9 @@ legend: | 9 | SupERC20 | relayERC20 with a value of zero does not modify accounting | [ ] | [ ] | | 10 | SupERC20 | sendERC20 decreases the token's totalSupply in the source chain exactly by the input amount | [ ] | [ ] | | 11 | SupERC20 | relayERC20 increases the token's totalSupply in the destination chain exactly by the input amount | [ ] | [ ] | -| 12 | Liquidity Migration | supertoken total supply only increases on calls to mint() by the L2toL2StandardBridge | [ ] | [ ] | +| 12 | Liquidity Migration | supertoken total supply only increases on calls to mint() by the L2toL2StandardBridge | [ ] | [~] | | 13 | Liquidity Migration | supertoken total supply only decreases on calls to burn() by the L2toL2StandardBridge | [ ] | [ ] | -| 14 | SupERC20 | supertoken total supply starts at zero | [ ] | [ ] | +| 14 | SupERC20 | supertoken total supply starts at zero | [ ] | [x] | | 15 | Factories | deploying a supertoken registers its remote token in the factory | [ ] | [ ] | | 16 | Factories | deploying an OptimismMintableERC20 registers its remote token in the factory | [ ] | [ ] | From 590a3ba550a5462b28a2ec2bfbef4286e1454174 Mon Sep 17 00:00:00 2001 From: teddy Date: Mon, 19 Aug 2024 15:33:20 -0300 Subject: [PATCH 15/48] refactor: move all assertions into properties contract --- .../medusa/Protocol.properties.t.sol | 77 ++++++++++++++++++- .../medusa/handlers/Protocol.handler.t.sol | 75 ------------------ 2 files changed, 76 insertions(+), 76 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol b/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol index a6ed77dc6c6e..36a696f6c442 100644 --- a/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol +++ b/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol @@ -7,13 +7,13 @@ import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableM contract ProtocolProperties is ProtocolHandler { using EnumerableMap for EnumerableMap.Bytes32ToUintMap; + // TODO: will need rework after // - non-atomic bridge // - `convert` /// @custom:property-id 24 /// @custom:property sum of supertoken total supply across all chains is always equal to convert(legacy, super)- /// convert(super, legacy) - function property_totalSupplyAcrossChainsEqualsMints() external view returns (bool success) { // iterate over unique deploy salts aka supertokens that are supposed to be compatible with each other for (uint256 deploySaltIndex = 0; deploySaltIndex < ghost_totalSupplyAcrossChains.length(); deploySaltIndex++) { @@ -32,4 +32,79 @@ contract ProtocolProperties is ProtocolHandler { } return true; } + + /// @notice deploy a new supertoken with deploy salt determined by params, to the given (of course mocked) chainId + /// @custom:property-id 14 + /// @custom:property supertoken total supply starts at zero + function fuzz_DeployNewSupertoken( + TokenDeployParams memory params, + uint256 chainId + ) + external + validateTokenDeployParams(params) + { + chainId = bound(chainId, 0, MAX_CHAINS - 1); + OptimismSuperchainERC20 supertoken = _deploySupertoken( + remoteTokens[params.remoteTokenIndex], + WORDS[params.nameIndex], + WORDS[params.symbolIndex], + DECIMALS[params.decimalsIndex], + chainId + ); + // 14 + assert(supertoken.totalSupply() == 0); + } + + /// @custom:property-id 22 + /// @custom:property sendERC20 decreases sender balance in source chain and increases receiver balance in + /// destination chain exactly by the input amount + /// @custom:property-id 23 + /// @custom:property sendERC20 decreases total supply in source chain and increases it in destination chain exactly + /// by the input amount + function fuzz_SelfBridgeSupertoken(uint256 fromIndex, uint256 destinationChainId, uint256 amount) external { + destinationChainId = bound(destinationChainId, 0, MAX_CHAINS - 1); + fromIndex = bound(fromIndex, 0, allSuperTokens.length - 1); + OptimismSuperchainERC20 sourceToken = OptimismSuperchainERC20(allSuperTokens[fromIndex]); + OptimismSuperchainERC20 destinationToken = + MESSENGER.crossChainMessageReceiver(address(sourceToken), destinationChainId); + // TODO: when implementing non-atomic bridging, allow for the token to + // not yet be deployed and funds be recovered afterwards. + require(address(destinationToken) != address(0)); + uint256 sourceBalanceBefore = sourceToken.balanceOf(msg.sender); + uint256 sourceSupplyBefore = sourceToken.totalSupply(); + uint256 destinationBalanceBefore = destinationToken.balanceOf(msg.sender); + uint256 destinationSupplyBefore = destinationToken.totalSupply(); + + vm.prank(msg.sender); + try sourceToken.sendERC20(msg.sender, amount, destinationChainId) { + uint256 sourceBalanceAfter = sourceToken.balanceOf(msg.sender); + uint256 destinationBalanceAfter = destinationToken.balanceOf(msg.sender); + // no free mint + assert(sourceBalanceBefore + destinationBalanceBefore == sourceBalanceAfter + destinationBalanceAfter); + // 22 + assert(sourceBalanceBefore - amount == sourceBalanceAfter); + assert(destinationBalanceBefore + amount == destinationBalanceAfter); + uint256 sourceSupplyAfter = sourceToken.totalSupply(); + uint256 destinationSupplyAfter = destinationToken.totalSupply(); + // 23 + assert(sourceSupplyBefore - amount == sourceSupplyAfter); + assert(destinationSupplyBefore + amount == destinationSupplyAfter); + } catch { + assert(address(destinationToken) == address(sourceToken) || sourceBalanceBefore < amount); + } + } + + /// @notice pick one already-deployed supertoken and mint an arbitrary amount of it + /// necessary so there is something to be bridged :D + /// TODO: will be replaced when testing the factories and `convert()` + function fuzz_MintSupertoken(uint256 index, uint96 amount) external { + index = bound(index, 0, allSuperTokens.length - 1); + address addr = allSuperTokens[index]; + vm.prank(BRIDGE); + // medusa calls with different senders by default + OptimismSuperchainERC20(addr).mint(msg.sender, amount); + // currentValue will be zero if key is not present + (, uint256 currentValue) = ghost_totalSupplyAcrossChains.tryGet(MESSENGER.superTokenInitDeploySalts(addr)); + ghost_totalSupplyAcrossChains.set(MESSENGER.superTokenInitDeploySalts(addr), currentValue + amount); + } } diff --git a/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol b/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol index 6d6209aca11b..523f67801edf 100644 --- a/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol +++ b/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol @@ -61,81 +61,6 @@ contract ProtocolHandler is Test { _; } - /// @notice deploy a new supertoken with deploy salt determined by params, to the given (of course mocked) chainId - /// @custom:property-id 14 - /// @custom:property supertoken total supply starts at zero - function fuzz_DeployNewSupertoken( - TokenDeployParams memory params, - uint256 chainId - ) - external - validateTokenDeployParams(params) - { - chainId = bound(chainId, 0, MAX_CHAINS - 1); - OptimismSuperchainERC20 supertoken = _deploySupertoken( - remoteTokens[params.remoteTokenIndex], - WORDS[params.nameIndex], - WORDS[params.symbolIndex], - DECIMALS[params.decimalsIndex], - chainId - ); - // 14 - assert(supertoken.totalSupply() == 0); - } - - /// @custom:property-id 22 - /// @custom:property sendERC20 decreases sender balance in source chain and increases receiver balance in - /// destination chain exactly by the input amount - /// @custom:property-id 23 - /// @custom:property sendERC20 decreases total supply in source chain and increases it in destination chain exactly - /// by the input amount - function fuzz_SelfBridgeSupertoken(uint256 fromIndex, uint256 destinationChainId, uint256 amount) external { - destinationChainId = bound(destinationChainId, 0, MAX_CHAINS - 1); - fromIndex = bound(fromIndex, 0, allSuperTokens.length - 1); - OptimismSuperchainERC20 sourceToken = OptimismSuperchainERC20(allSuperTokens[fromIndex]); - OptimismSuperchainERC20 destinationToken = - MESSENGER.crossChainMessageReceiver(address(sourceToken), destinationChainId); - // TODO: when implementing non-atomic bridging, allow for the token to - // not yet be deployed and funds be recovered afterwards. - require(address(destinationToken) != address(0)); - uint256 sourceBalanceBefore = sourceToken.balanceOf(msg.sender); - uint256 sourceSupplyBefore = sourceToken.totalSupply(); - uint256 destinationBalanceBefore = destinationToken.balanceOf(msg.sender); - uint256 destinationSupplyBefore = destinationToken.totalSupply(); - - vm.prank(msg.sender); - try sourceToken.sendERC20(msg.sender, amount, destinationChainId) { - uint256 sourceBalanceAfter = sourceToken.balanceOf(msg.sender); - uint256 destinationBalanceAfter = destinationToken.balanceOf(msg.sender); - // no free mint - assert(sourceBalanceBefore + destinationBalanceBefore == sourceBalanceAfter + destinationBalanceAfter); - // 22 - assert(sourceBalanceBefore - amount == sourceBalanceAfter); - assert(destinationBalanceBefore + amount == destinationBalanceAfter); - uint256 sourceSupplyAfter = sourceToken.totalSupply(); - uint256 destinationSupplyAfter = destinationToken.totalSupply(); - // 23 - assert(sourceSupplyBefore - amount == sourceSupplyAfter); - assert(destinationSupplyBefore + amount == destinationSupplyAfter); - } catch { - assert(address(destinationToken) == address(sourceToken) || sourceBalanceBefore < amount); - } - } - - /// @notice pick one already-deployed supertoken and mint an arbitrary amount of it - /// necessary so there is something to be bridged :D - /// TODO: will be replaced when testing the factories and `convert()` - function fuzz_MintSupertoken(uint256 index, uint96 amount) external { - index = bound(index, 0, allSuperTokens.length - 1); - address addr = allSuperTokens[index]; - vm.prank(BRIDGE); - // medusa calls with different senders by default - OptimismSuperchainERC20(addr).mint(msg.sender, amount); - // currentValue will be zero if key is not present - (, uint256 currentValue) = ghost_totalSupplyAcrossChains.tryGet(MESSENGER.superTokenInitDeploySalts(addr)); - ghost_totalSupplyAcrossChains.set(MESSENGER.superTokenInitDeploySalts(addr), currentValue + amount); - } - function fuzz_MockNewRemoteToken() external { _deployRemoteToken(); } From 51bf7fdc58653d4bab135aeeaff5cea49b02210e Mon Sep 17 00:00:00 2001 From: teddy Date: Mon, 19 Aug 2024 16:07:17 -0300 Subject: [PATCH 16/48] fix: move function without assertions back into handler --- .../properties/medusa/Protocol.properties.t.sol | 14 -------------- .../medusa/handlers/Protocol.handler.t.sol | 14 ++++++++++++++ 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol b/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol index 36a696f6c442..5b7090d6dc87 100644 --- a/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol +++ b/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol @@ -93,18 +93,4 @@ contract ProtocolProperties is ProtocolHandler { assert(address(destinationToken) == address(sourceToken) || sourceBalanceBefore < amount); } } - - /// @notice pick one already-deployed supertoken and mint an arbitrary amount of it - /// necessary so there is something to be bridged :D - /// TODO: will be replaced when testing the factories and `convert()` - function fuzz_MintSupertoken(uint256 index, uint96 amount) external { - index = bound(index, 0, allSuperTokens.length - 1); - address addr = allSuperTokens[index]; - vm.prank(BRIDGE); - // medusa calls with different senders by default - OptimismSuperchainERC20(addr).mint(msg.sender, amount); - // currentValue will be zero if key is not present - (, uint256 currentValue) = ghost_totalSupplyAcrossChains.tryGet(MESSENGER.superTokenInitDeploySalts(addr)); - ghost_totalSupplyAcrossChains.set(MESSENGER.superTokenInitDeploySalts(addr), currentValue + amount); - } } diff --git a/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol b/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol index 523f67801edf..db126425ab36 100644 --- a/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol +++ b/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol @@ -65,6 +65,20 @@ contract ProtocolHandler is Test { _deployRemoteToken(); } + /// @notice pick one already-deployed supertoken and mint an arbitrary amount of it + /// necessary so there is something to be bridged :D + /// TODO: will be replaced when testing the factories and `convert()` + function fuzz_MintSupertoken(uint256 index, uint96 amount) external { + index = bound(index, 0, allSuperTokens.length - 1); + address addr = allSuperTokens[index]; + vm.prank(BRIDGE); + // medusa calls with different senders by default + OptimismSuperchainERC20(addr).mint(msg.sender, amount); + // currentValue will be zero if key is not present + (, uint256 currentValue) = ghost_totalSupplyAcrossChains.tryGet(MESSENGER.superTokenInitDeploySalts(addr)); + ghost_totalSupplyAcrossChains.set(MESSENGER.superTokenInitDeploySalts(addr), currentValue + amount); + } + /// @notice deploy a remote token, that supertokens will be a representation of. They are never called, so there /// is no need to actually deploy a contract for them function _deployRemoteToken() internal { From 79bf5e54d03877647b1769c8077e0937b057ff26 Mon Sep 17 00:00:00 2001 From: teddy Date: Tue, 20 Aug 2024 15:26:28 -0300 Subject: [PATCH 17/48] test: only use assertion mode --- packages/contracts-bedrock/medusa.json | 4 ++-- .../test/properties/medusa/Protocol.properties.t.sol | 11 ++++------- .../properties/medusa/handlers/Protocol.handler.t.sol | 9 +++++---- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/packages/contracts-bedrock/medusa.json b/packages/contracts-bedrock/medusa.json index c91f69be607e..cb4737956fea 100644 --- a/packages/contracts-bedrock/medusa.json +++ b/packages/contracts-bedrock/medusa.json @@ -28,7 +28,7 @@ "traceAll": true, "assertionTesting": { "enabled": true, - "testViewMethods": false, + "testViewMethods": true, "panicCodeConfig": { "failOnCompilerInsertedPanic": false, "failOnAssertion": true, @@ -43,7 +43,7 @@ } }, "propertyTesting": { - "enabled": true, + "enabled": false, "testPrefixes": [ "property_" ] diff --git a/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol b/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol index 5b7090d6dc87..dd169dc5748d 100644 --- a/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol +++ b/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol @@ -14,7 +14,7 @@ contract ProtocolProperties is ProtocolHandler { /// @custom:property-id 24 /// @custom:property sum of supertoken total supply across all chains is always equal to convert(legacy, super)- /// convert(super, legacy) - function property_totalSupplyAcrossChainsEqualsMints() external view returns (bool success) { + function property_totalSupplyAcrossChainsEqualsMints() external view { // iterate over unique deploy salts aka supertokens that are supposed to be compatible with each other for (uint256 deploySaltIndex = 0; deploySaltIndex < ghost_totalSupplyAcrossChains.length(); deploySaltIndex++) { uint256 totalSupply = 0; @@ -26,17 +26,14 @@ contract ProtocolProperties is ProtocolHandler { totalSupply += OptimismSuperchainERC20(supertoken).totalSupply(); } } - if (trackedSupply != totalSupply) { - return false; - } + assert(trackedSupply == totalSupply); } - return true; } /// @notice deploy a new supertoken with deploy salt determined by params, to the given (of course mocked) chainId /// @custom:property-id 14 /// @custom:property supertoken total supply starts at zero - function fuzz_DeployNewSupertoken( + function property_DeployNewSupertoken( TokenDeployParams memory params, uint256 chainId ) @@ -61,7 +58,7 @@ contract ProtocolProperties is ProtocolHandler { /// @custom:property-id 23 /// @custom:property sendERC20 decreases total supply in source chain and increases it in destination chain exactly /// by the input amount - function fuzz_SelfBridgeSupertoken(uint256 fromIndex, uint256 destinationChainId, uint256 amount) external { + function property_SelfBridgeSupertoken(uint256 fromIndex, uint256 destinationChainId, uint256 amount) external { destinationChainId = bound(destinationChainId, 0, MAX_CHAINS - 1); fromIndex = bound(fromIndex, 0, allSuperTokens.length - 1); OptimismSuperchainERC20 sourceToken = OptimismSuperchainERC20(allSuperTokens[fromIndex]); diff --git a/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol b/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol index db126425ab36..266650486e34 100644 --- a/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol +++ b/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol @@ -1,7 +1,8 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.25; -import { Test } from "forge-std/Test.sol"; +import { TestBase } from "forge-std/Base.sol"; +import { StdUtils } from "forge-std/StdUtils.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol"; import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; @@ -9,7 +10,7 @@ import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { MockL2ToL2CrossDomainMessenger } from "../../helpers/MockL2ToL2CrossDomainMessenger.t.sol"; -contract ProtocolHandler is Test { +contract ProtocolHandler is TestBase, StdUtils { using EnumerableMap for EnumerableMap.Bytes32ToUintMap; uint8 internal constant MAX_CHAINS = 4; @@ -61,14 +62,14 @@ contract ProtocolHandler is Test { _; } - function fuzz_MockNewRemoteToken() external { + function handler_MockNewRemoteToken() external { _deployRemoteToken(); } /// @notice pick one already-deployed supertoken and mint an arbitrary amount of it /// necessary so there is something to be bridged :D /// TODO: will be replaced when testing the factories and `convert()` - function fuzz_MintSupertoken(uint256 index, uint96 amount) external { + function handler_MintSupertoken(uint256 index, uint96 amount) external { index = bound(index, 0, allSuperTokens.length - 1); address addr = allSuperTokens[index]; vm.prank(BRIDGE); From 6e13fd227f1a5348adc308268845796c2c8fecf9 Mon Sep 17 00:00:00 2001 From: teddy Date: Tue, 20 Aug 2024 15:26:53 -0300 Subject: [PATCH 18/48] fix: improve justfile recipie for medusa --- packages/contracts-bedrock/justfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index ebb8d9db6d47..290f01574ead 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -22,8 +22,8 @@ test: build-go-ffi test-kontrol: ./test/kontrol/scripts/run-kontrol.sh script -test-medusa: - FOUNDRY_PROFILE=medusa medusa fuzz +test-medusa timeout='100': + FOUNDRY_PROFILE=medusa medusa fuzz --timeout {{timeout}} test-rerun: build-go-ffi forge test --rerun -vvv From 7e6fdbc7eabeea9f41ab9517d85401a6861cc0e6 Mon Sep 17 00:00:00 2001 From: Disco <131301107+0xDiscotech@users.noreply.github.com> Date: Fri, 23 Aug 2024 13:22:01 -0300 Subject: [PATCH 19/48] feat: halmos symbolic tests (#21) * feat: introduce OptimismSuperchainERC20 * fix: contract fixes * feat: add snapshots and semver * test: add supports interface tests * test: add invariant test * feat: add parameters to the RelayERC20 event * fix: typo * fix: from param description * fix: event signature and interface pragma * feat: add initializer * feat: use unstructured storage and OZ v5 * feat: update superchain erc20 interfaces * fix: adapt storage to ERC7201 * test: add initializable OZ v5 test * fix: invariant docs * fix: ERC165 implementation * test: improve superc20 invariant (#11) * fix: gas snapshot * chore: configure medusa with basic supERC20 self-bridging - used --foundry-compile-all to ensure the test contract under `test/properties` is compiled (otherwise it is not compiled and medusa crashes when it can't find it's compiled representation) - set src,test,script to test/properties/medusa to not waste time compiling contracts that are not required for the medusa campaign - used an atomic bridge, which doesnt allow for testing of several of the proposed invariants * fix: delete dead code * test: give the fuzzer a head start * feat: create suite for sybolic tests with halmos * test: setup and 3 properties with symbolic tests * chore: remove todo comment * docs: fix properties order * test: document & implement assertions 22, 23 and 24 * fix: fixes from self-review * test: guide the fuzzer a little bit less previously: initial mint, bound on transfer amount: 146625 calls in 200s now: no initial mint, no bound on transfer amount: 176835 calls in 200s it doesn't seem to slow the fuzzer down * feat: add property for burn * refactor: remove symbolic address on mint property * refactor: order the tests based on the property id * feat: checkpoint * chore: set xdomain sender on failing test * chore: enhance mocks * Revert "Merge branch 'chore/setup-medusa' into feat/halmos-symbolic-tests" This reverts commit 945d6b6ad265ea5e3790d7ac9c5bf4d6586eb533, reversing changes made to 5dcb3a89252e9e8fa9b54ba9012e714f7cc96395. * refactor: remove symbolic addresses to make all of the test work * chore: remove console logs * feat: add properties file * chore: polish * refactor: enhance test on property 7 using direct try catch (now works) * fix: review comments * refactor: add symbolic addresses on test functions * feat: create halmos toml * chore: polish test contract and mock * chore: update property * refactor: move symbolic folder into properties one * feat: create advanced tests helper contract * refactor: enhance tests using symbolic addresses instead of concrete ones * chore: remove 0 property natspec * feat: add halmos profile and just script * chore: rename symbolic folder to halmos * feat: add halmos commands to justfile * chore: reorder assertions on one test * refactor: complete test property seven * chore: mark properties as completed * chore: add halmos-cheatcodes dependency * chore: rename advancedtest->halmosbase * chore: minimize mocked messenger * chore: delete empty halmos file * chore: revert changes to medusa.json * docs: update changes to PROPERTIES.md from base branch * test: sendERC20 destination fix * chore: natspec fixes --------- Co-authored-by: agusduha Co-authored-by: 0xng Co-authored-by: teddy --- .gitmodules | 3 + packages/contracts-bedrock/foundry.toml | 8 +- packages/contracts-bedrock/halmos.toml | 15 ++ packages/contracts-bedrock/justfile | 7 + .../contracts-bedrock/lib/halmos-cheatcodes | 1 + .../test/invariants/PROPERTIES.md | 73 ++++++ .../test/properties/PROPERTIES.md | 23 +- .../properties/halmos/MockL2ToL2Messenger.sol | 25 ++ .../halmos/OptimismSuperchainERC20.t.sol | 237 ++++++++++++++++++ .../test/properties/helpers/HalmosBase.sol | 18 ++ 10 files changed, 398 insertions(+), 12 deletions(-) create mode 100644 packages/contracts-bedrock/halmos.toml create mode 160000 packages/contracts-bedrock/lib/halmos-cheatcodes create mode 100644 packages/contracts-bedrock/test/invariants/PROPERTIES.md create mode 100644 packages/contracts-bedrock/test/properties/halmos/MockL2ToL2Messenger.sol create mode 100644 packages/contracts-bedrock/test/properties/halmos/OptimismSuperchainERC20.t.sol create mode 100644 packages/contracts-bedrock/test/properties/helpers/HalmosBase.sol diff --git a/.gitmodules b/.gitmodules index 21ecaedbb77a..222d45be7ccc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -29,3 +29,6 @@ [submodule "packages/contracts-bedrock/lib/openzeppelin-contracts-v5"] path = packages/contracts-bedrock/lib/openzeppelin-contracts-v5 url = https://github.com/OpenZeppelin/openzeppelin-contracts +[submodule "packages/contracts-bedrock/lib/halmos-cheatcodes"] + path = packages/contracts-bedrock/lib/halmos-cheatcodes + url = https://github.com/a16z/halmos-cheatcodes diff --git a/packages/contracts-bedrock/foundry.toml b/packages/contracts-bedrock/foundry.toml index 3b69c67412f6..69b1fde5c5eb 100644 --- a/packages/contracts-bedrock/foundry.toml +++ b/packages/contracts-bedrock/foundry.toml @@ -21,7 +21,8 @@ remappings = [ 'ds-test/=lib/forge-std/lib/ds-test/src', 'safe-contracts/=lib/safe-contracts/contracts', 'kontrol-cheatcodes/=lib/kontrol-cheatcodes/src', - 'gelato/=lib/automate/contracts' + 'gelato/=lib/automate/contracts', + 'halmos-cheatcodes/=lib/halmos-cheatcodes/' ] extra_output = ['devdoc', 'userdoc', 'metadata', 'storageLayout'] bytecode_hash = 'none' @@ -101,3 +102,8 @@ script = 'test/kontrol/proofs' src = 'test/properties/medusa/' test = 'test/properties/medusa/' script = 'test/properties/medusa/' + +[profile.halmos] +src = 'test/properties/halmos/' +test = 'test/properties/halmos/' +script = 'test/properties/halmos/' diff --git a/packages/contracts-bedrock/halmos.toml b/packages/contracts-bedrock/halmos.toml new file mode 100644 index 000000000000..c431c6c3a532 --- /dev/null +++ b/packages/contracts-bedrock/halmos.toml @@ -0,0 +1,15 @@ +# Halmos configuration file + +## The version needed is `halmos 0.1.15.dev2+gc3f45dd` +## Just running `halmos` will run the tests with the default configuration + +[global] +# Contract to test +match-contract = "SymTest_" + +# Path to the Forge artifacts directory +forge_build_out = "./forge-artifacts" + + +# Storage layout +storage_layout = "generic" \ No newline at end of file diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index 290f01574ead..a003558bb91e 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -25,6 +25,13 @@ test-kontrol: test-medusa timeout='100': FOUNDRY_PROFILE=medusa medusa fuzz --timeout {{timeout}} + +test-halmos-all VERBOSE="-v": + FOUNDRY_PROFILE=halmos halmos {{VERBOSE}} + +test-halmos TEST VERBOSE="-v": + FOUNDRY_PROFILE=halmos halmos --function {{TEST}} {{VERBOSE}} + test-rerun: build-go-ffi forge test --rerun -vvv diff --git a/packages/contracts-bedrock/lib/halmos-cheatcodes b/packages/contracts-bedrock/lib/halmos-cheatcodes new file mode 160000 index 000000000000..c0d865508c0f --- /dev/null +++ b/packages/contracts-bedrock/lib/halmos-cheatcodes @@ -0,0 +1 @@ +Subproject commit c0d865508c0fee0a11b97732c5e90f9cad6b65a5 diff --git a/packages/contracts-bedrock/test/invariants/PROPERTIES.md b/packages/contracts-bedrock/test/invariants/PROPERTIES.md new file mode 100644 index 000000000000..5a5cc71d73b5 --- /dev/null +++ b/packages/contracts-bedrock/test/invariants/PROPERTIES.md @@ -0,0 +1,73 @@ +# supertoken properties + +legend: + +- `[ ]`: property not yet tested +- `**[ ]**`: property not yet tested, dev/research team has asked for extra focus on it +- `[X]`: tested/proven property +- `[~]`: partially tested/proven property +- `:(`: property won't be tested due to some limitation + +## Unit test + +| id | description | halmos | medusa | +| --- | ---------------------------------------------------------------------------------- | ------ | ------ | +| 0 | supertoken token address does not depend on the executing chain’s chainID | [ ] | [ ] | +| 1 | supertoken token address depends on name, remote token, address and decimals | [ ] | [ ] | +| 2 | convert() should only allow converting legacy tokens to supertoken and viceversa | [ ] | [ ] | +| 3 | convert() only allows migrations between tokens representing the same remote asset | [ ] | [ ] | +| 4 | convert() only allows migrations from tokens with the same decimals | [ ] | [ ] | +| 5 | convert() burns the same amount of one token that it mints of the other | [ ] | [ ] | + +## Valid state + +| id | description | halmos | medusa | +| --- | ------------------------------------------------------------------------------------------ | ------- | ------ | +| 6 | calls to sendERC20 succeed as long as caller has enough balance | [x] | [ ] | +| 7 | calls to relayERC20 always succeed as long as the sender and cross-domain caller are valid | **[~]** | [ ] | + +## Variable transition + +| id | description | halmos | medusa | +| --- | ------------------------------------------------------------------------------------------------- | ------ | ------ | +| 8 | sendERC20 with a value of zero does not modify accounting | [x] | [ ] | +| 9 | relayERC20 with a value of zero does not modify accounting | [x] | [ ] | +| 10 | sendERC20 decreases the token's totalSupply in the source chain exactly by the input amount | [x] | [ ] | +| 11 | relayERC20 increases the token's totalSupply in the destination chain exactly by the input amount | [x] | [ ] | +| 12 | supertoken total supply only increases on calls to mint() by the L2toL2StandardBridge | [x] | [ ] | +| 13 | supertoken total supply only decreases on calls to burn() by the L2toL2StandardBridge | [x] | [ ] | +| 14 | supertoken total supply starts at zero | [x] | [ ] | +| 15 | deploying a supertoken registers its remote token in the factory | [ ] | [ ] | +| 16 | deploying an OptimismMintableERC20 registers its remote token in the factory | [ ] | [ ] | + +## High level + +| id | description | halmos | medusa | +| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------ | +| 17 | only calls to convert(legacy, super) can increase a supertoken’s total supply across chains | [ ] | [ ] | +| 18 | only calls to convert(super, legacy) can decrease a supertoken’s total supply across chains | [ ] | [ ] | +| 19 | sum of total supply across all chains is always <= to convert(legacy, super)- convert(super, legacy) | [ ] | [ ] | +| 20 | tokens sendERC20-ed on a source chain to a destination chain can be relayERC20-ed on it as long as the source chain is in the dependency set of the destination chain | [ ] | [ ] | +| 21 | sum of supertoken total supply across all chains is = to convert(legacy, super)- convert(super, legacy) when all cross-chain messages are processed | [ ] | [ ] | + +## Atomic bridging pseudo-properties + +As another layer of defense, the following properties are defined which assume bridging operations to be atomic (that is, the sequencer and L2Inbox and CrossDomainMessenger contracts are fully abstracted away, `sendERC20` triggering the `relayERC20` call on the same transaction) +It’s worth noting that these properties will not hold for a live system + +| id | description | halmos | medusa | +| --- | ---------------------------------------------------------------------------------------------------------------------------------- | ------ | ------ | +| 22 | sendERC20 decreases sender balance in source chain and increases receiver balance in destination chain exactly by the input amount | [ ] | [x] | +| 23 | sendERC20 decreases total supply in source chain and increases it in destination chain exactly by the input amount | [ ] | [x] | +| 24 | sum of supertoken total supply across all chains is always equal to convert(legacy, super)- convert(super, legacy) | [ ] | [~] | + +# Expected external interactions + +- regular ERC20 operations between any accounts on the same chain, provided by [crytic ERC20 properties](https://github.com/crytic/properties?tab=readme-ov-file#erc20-tests) + +# Invariant-breaking candidates (brain dump) + +here we’ll list possible interactions that we intend the fuzzing campaign to support in order to help break invariants + +- [ ] changing the decimals of tokens after deployment +- [ ] `convert()` ing between multiple (3+) representations of the same remote token, by having different names/symbols diff --git a/packages/contracts-bedrock/test/properties/PROPERTIES.md b/packages/contracts-bedrock/test/properties/PROPERTIES.md index 36b7b330ab0b..25dc9b036d3b 100644 --- a/packages/contracts-bedrock/test/properties/PROPERTIES.md +++ b/packages/contracts-bedrock/test/properties/PROPERTIES.md @@ -54,6 +54,7 @@ Given the [OP monorepo](https://github.com/ethereum-optimism/optimism) already h # Ecosystem properties legend: + - `[ ]`: property not yet tested - `**[ ]**`: property not yet tested, dev/research team has asked for extra focus on it - `[X]`: tested/proven property @@ -73,22 +74,22 @@ legend: ## Valid state -| id | milestone | description | halmos | medusa | -| --- | --- | --- | --- | --- | -| 6 | SupERC20 | calls to sendERC20 succeed as long as caller has enough balance | [ ] | [ ] | -| 7 | SupERC20 | calls to relayERC20 always succeed as long as the cross-domain caller is valid | **[ ]** | [ ] | +| id | milestone | description | halmos | medusa | +| --- | --- | --- | --- | --- | +| 6 | SupERC20 | calls to sendERC20 succeed as long as caller has enough balance | [x] | [ ] | +| 7 | SupERC20 | calls to relayERC20 always succeed as long as the sender and cross-domain caller are valid | **[~]** | [ ] | ## Variable transition | id | milestone | description | halmos | medusa | | --- | --- | --- | --- | --- | -| 8 | SupERC20 | sendERC20 with a value of zero does not modify accounting | [ ] | [ ] | -| 9 | SupERC20 | relayERC20 with a value of zero does not modify accounting | [ ] | [ ] | -| 10 | SupERC20 | sendERC20 decreases the token's totalSupply in the source chain exactly by the input amount | [ ] | [ ] | -| 11 | SupERC20 | relayERC20 increases the token's totalSupply in the destination chain exactly by the input amount | [ ] | [ ] | -| 12 | Liquidity Migration | supertoken total supply only increases on calls to mint() by the L2toL2StandardBridge | [ ] | [~] | -| 13 | Liquidity Migration | supertoken total supply only decreases on calls to burn() by the L2toL2StandardBridge | [ ] | [ ] | -| 14 | SupERC20 | supertoken total supply starts at zero | [ ] | [x] | +| 8 | SupERC20 | sendERC20 with a value of zero does not modify accounting | [x] | [ ] | +| 9 | SupERC20 | relayERC20 with a value of zero does not modify accounting | [x] | [ ] | +| 10 | SupERC20 | sendERC20 decreases the token's totalSupply in the source chain exactly by the input amount | [x] | [ ] | +| 11 | SupERC20 | relayERC20 increases the token's totalSupply in the destination chain exactly by the input amount | [x] | [ ] | +| 12 | Liquidity Migration | supertoken total supply only increases on calls to mint() by the L2toL2StandardBridge | [x] | [~] | +| 13 | Liquidity Migration | supertoken total supply only decreases on calls to burn() by the L2toL2StandardBridge | [x] | [ ] | +| 14 | SupERC20 | supertoken total supply starts at zero | [x] | [x] | | 15 | Factories | deploying a supertoken registers its remote token in the factory | [ ] | [ ] | | 16 | Factories | deploying an OptimismMintableERC20 registers its remote token in the factory | [ ] | [ ] | diff --git a/packages/contracts-bedrock/test/properties/halmos/MockL2ToL2Messenger.sol b/packages/contracts-bedrock/test/properties/halmos/MockL2ToL2Messenger.sol new file mode 100644 index 000000000000..fb8732f384f8 --- /dev/null +++ b/packages/contracts-bedrock/test/properties/halmos/MockL2ToL2Messenger.sol @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + + +// TODO: Try to merge to a single mocked contract used by fuzzing and symbolic invariant tests - only if possible +// and low priorty +contract MockL2ToL2Messenger { + // Setting the current cross domain sender for the check of sender address equals the supertoken address + address internal immutable CROSS_DOMAIN_SENDER; + + constructor(address _xDomainSender) { + CROSS_DOMAIN_SENDER = _xDomainSender; + } + + function sendMessage(uint256 , address , bytes calldata) external payable { + } + + function crossDomainMessageSource() external view returns (uint256 _source) { + _source = block.chainid + 1; + } + + function crossDomainMessageSender() external view returns (address _sender) { + _sender = CROSS_DOMAIN_SENDER; + } +} diff --git a/packages/contracts-bedrock/test/properties/halmos/OptimismSuperchainERC20.t.sol b/packages/contracts-bedrock/test/properties/halmos/OptimismSuperchainERC20.t.sol new file mode 100644 index 000000000000..a6285ac58e08 --- /dev/null +++ b/packages/contracts-bedrock/test/properties/halmos/OptimismSuperchainERC20.t.sol @@ -0,0 +1,237 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; +import { SymTest } from "halmos-cheatcodes/src/SymTest.sol"; +import { Predeploys } from "src/libraries/Predeploys.sol"; +import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol"; +import { MockL2ToL2Messenger } from "./MockL2ToL2Messenger.sol"; +import { HalmosBase } from "../helpers/HalmosBase.sol"; + +contract SymTest_OptimismSuperchainERC20 is SymTest, HalmosBase { + MockL2ToL2Messenger internal constant MESSENGER = MockL2ToL2Messenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); + + OptimismSuperchainERC20 public superchainERC20Impl; + OptimismSuperchainERC20 internal optimismSuperchainERC20; + + function setUp() public { + // Deploy the OptimismSuperchainERC20 contract implementation and the proxy to be used + superchainERC20Impl = new OptimismSuperchainERC20(); + optimismSuperchainERC20 = OptimismSuperchainERC20( + address( + // TODO: Update to beacon proxy + new ERC1967Proxy( + address(superchainERC20Impl), + abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, name, symbol, decimals)) + ) + ) + ); + + // Etch the mocked L2 to L2 Messenger since the messenger logic is out of scope for these test suite. Also, we + // avoid issues such as `TSTORE` opcode not being supported, or issues with `encodeVersionedNonce()` + address _mockL2ToL2CrossDomainMessenger = address(new MockL2ToL2Messenger(address(optimismSuperchainERC20))); + vm.etch(address(MESSENGER), _mockL2ToL2CrossDomainMessenger.code); + // NOTE: We need to set the crossDomainMessageSender as an immutable or otherwise storage vars and not taken + // into account when etching on halmos. Setting a constant slot with setters and getters didn't work neither. + } + + /// @notice Check setup works as expected + function check_setup() public view { + assert(optimismSuperchainERC20.remoteToken() == remoteToken); + assert(eqStrings(optimismSuperchainERC20.name(), name)); + assert(eqStrings(optimismSuperchainERC20.symbol(), symbol)); + assert(optimismSuperchainERC20.decimals() == decimals); + assert(MESSENGER.crossDomainMessageSender() == address(optimismSuperchainERC20)); + } + + /// @custom:property-id 6 + /// @custom:property Calls to sendERC20 succeed as long as caller has enough balance + function check_sendERC20SucceedsOnlyIfEnoughBalance( + uint256 _initialBalance, + address _from, + uint256 _amount, + address _to, + uint256 _chainId + ) + public + { + /* Preconditions */ + vm.assume(_chainId != CURRENT_CHAIN_ID); + vm.assume(_to != address(0)); + vm.assume(_from != address(0)); + + // Can't deal to unsupported cheatcode + vm.prank(Predeploys.L2_STANDARD_BRIDGE); + optimismSuperchainERC20.mint(_from, _initialBalance); + + vm.prank(_from); + /* Action */ + try optimismSuperchainERC20.sendERC20(_to, _amount, _chainId) { + /* Postcondition */ + assert(_initialBalance >= _amount); + } catch { + assert(_initialBalance < _amount); + } + } + + /// @custom:property-id 7 + /// @custom:property Calls to relayERC20 always succeed as long as the sender the cross-domain caller are valid + function check_relayERC20OnlyFromL2ToL2Messenger( + address _crossDomainSender, + address _sender, + address _from, + address _to, + uint256 _amount + ) + public + { + /* Precondition */ + vm.assume(_to != address(0)); + // Deploying a new messenger because of an issue of not being able to etch the storage layout of the mock + // contract. So needed to a new one setting the symbolic immutable variable for the crossDomainSender. + vm.etch(address(MESSENGER), address(new MockL2ToL2Messenger(_crossDomainSender)).code); + + vm.prank(_sender); + /* Action */ + try optimismSuperchainERC20.relayERC20(_from, _to, _amount) { + /* Postconditions */ + assert( + _sender == address(MESSENGER) + && MESSENGER.crossDomainMessageSender() == address(optimismSuperchainERC20) + ); + } catch { + assert( + _sender != address(MESSENGER) + || MESSENGER.crossDomainMessageSender() != address(optimismSuperchainERC20) + ); + } + } + + /// @custom:property-id 8 + /// @custom:property `sendERC20` with a value of zero does not modify accounting + function check_sendERC20ZeroCall(address _from, address _to, uint256 _chainId) public { + /* Preconditions */ + vm.assume(_to != address(0)); + vm.assume(_chainId != CURRENT_CHAIN_ID); + vm.assume(_to != address(Predeploys.CROSS_L2_INBOX) && _to != address(MESSENGER)); + + uint256 _totalSupplyBefore = optimismSuperchainERC20.totalSupply(); + uint256 _fromBalanceBefore = optimismSuperchainERC20.balanceOf(_from); + + vm.startPrank(_from); + /* Action */ + optimismSuperchainERC20.sendERC20(_to, ZERO_AMOUNT, _chainId); + + /* Postcondition */ + assert(optimismSuperchainERC20.totalSupply() == _totalSupplyBefore); + assert(optimismSuperchainERC20.balanceOf(_from) == _fromBalanceBefore); + } + + /// @custom:property-id 9 + /// @custom:property `relayERC20` with a value of zero does not modify accounting + function check_relayERC20ZeroCall(address _from, address _to) public { + uint256 _totalSupplyBefore = optimismSuperchainERC20.totalSupply(); + /* Preconditions */ + uint256 _fromBalanceBefore = optimismSuperchainERC20.balanceOf(_from); + uint256 _toBalanceBefore = optimismSuperchainERC20.balanceOf(_to); + vm.prank(address(MESSENGER)); + + /* Action */ + optimismSuperchainERC20.relayERC20(_from, _to, ZERO_AMOUNT); + + /* Postconditions */ + assert(optimismSuperchainERC20.totalSupply() == _totalSupplyBefore); + assert(optimismSuperchainERC20.balanceOf(_from) == _fromBalanceBefore); + assert(optimismSuperchainERC20.balanceOf(_to) == _toBalanceBefore); + } + + /// @custom:property-id 10 + /// @custom:property `sendERC20` decreases the token's totalSupply in the source chain exactly by the input amount + function check_sendERC20DecreasesTotalSupply( + address _sender, + address _to, + uint256 _amount, + uint256 _chainId + ) + public + { + /* Preconditions */ + vm.assume(_to != address(0)); + vm.assume(_chainId != CURRENT_CHAIN_ID); + + vm.prank(Predeploys.L2_STANDARD_BRIDGE); + optimismSuperchainERC20.mint(_sender, _amount); + + uint256 _totalSupplyBefore = optimismSuperchainERC20.totalSupply(); + uint256 _balanceBefore = optimismSuperchainERC20.balanceOf(_sender); + + vm.prank(_sender); + /* Action */ + optimismSuperchainERC20.sendERC20(_to, _amount, _chainId); + + /* Postconditions */ + assert(optimismSuperchainERC20.totalSupply() == _totalSupplyBefore - _amount); + assert(optimismSuperchainERC20.balanceOf(_sender) == _balanceBefore - _amount); + } + + /// @custom:property-id 11 + /// @custom:property `relayERC20` increases the token's totalSupply in the destination chain exactly by the input + /// amount + function check_relayERC20IncreasesTotalSupply(address _from, address _to, uint256 _amount) public { + vm.assume(_to != address(0)); + + /* Preconditions */ + uint256 _totalSupplyBefore = optimismSuperchainERC20.totalSupply(); + uint256 _toBalanceBefore = optimismSuperchainERC20.balanceOf(_to); + + vm.prank(address(MESSENGER)); + /* Action */ + optimismSuperchainERC20.relayERC20(_from, _to, _amount); + + /* Postconditions */ + assert(optimismSuperchainERC20.totalSupply() == _totalSupplyBefore + _amount); + assert(optimismSuperchainERC20.balanceOf(_to) == _toBalanceBefore + _amount); + } + + /// @custom:property-id 12 + /// @custom:property Increases the total supply on the amount minted by the bridge + function check_mint(address _from, uint256 _amount) public { + /* Preconditions */ + uint256 _totalSupplyBefore = optimismSuperchainERC20.totalSupply(); + uint256 _balanceBefore = optimismSuperchainERC20.balanceOf(_from); + + vm.startPrank(Predeploys.L2_STANDARD_BRIDGE); + /* Action */ + optimismSuperchainERC20.mint(_from, _amount); + + /* Postconditions */ + assert(optimismSuperchainERC20.totalSupply() == _totalSupplyBefore + _amount); + assert(optimismSuperchainERC20.balanceOf(_from) == _balanceBefore + _amount); + } + + /// @custom:property-id 13 + /// @custom:property Supertoken total supply only decreases on the amount burned by the bridge + function check_burn(address _from, uint256 _amount) public { + /* Preconditions */ + vm.prank(Predeploys.L2_STANDARD_BRIDGE); + optimismSuperchainERC20.mint(_from, _amount); + + uint256 _totalSupplyBefore = optimismSuperchainERC20.totalSupply(); + uint256 _balanceBefore = optimismSuperchainERC20.balanceOf(_from); + + vm.prank(Predeploys.L2_STANDARD_BRIDGE); + /* Action */ + optimismSuperchainERC20.burn(_from, _amount); + + /* Postconditions */ + assert(optimismSuperchainERC20.totalSupply() == _totalSupplyBefore - _amount); + assert(optimismSuperchainERC20.balanceOf(_from) == _balanceBefore - _amount); + } + + /// @custom:property-id 14 + /// @custom:property Supertoken total supply starts at zero + function check_totalSupplyStartsAtZero() public view { + /* Postconditions */ + assert(optimismSuperchainERC20.totalSupply() == 0); + } +} diff --git a/packages/contracts-bedrock/test/properties/helpers/HalmosBase.sol b/packages/contracts-bedrock/test/properties/helpers/HalmosBase.sol new file mode 100644 index 000000000000..75bfe2f7c9e1 --- /dev/null +++ b/packages/contracts-bedrock/test/properties/helpers/HalmosBase.sol @@ -0,0 +1,18 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { Test } from "forge-std/Test.sol"; + +contract HalmosBase is Test { + uint256 internal constant CURRENT_CHAIN_ID = 1; + uint256 internal constant ZERO_AMOUNT = 0; + + address internal remoteToken = address(bytes20(keccak256("remoteToken"))); + string internal name = "SuperchainERC20"; + string internal symbol = "SUPER"; + uint8 internal decimals = 18; + + function eqStrings(string memory a, string memory b) internal pure returns (bool) { + return keccak256(abi.encode(a)) == keccak256(abi.encode(b)); + } +} From 773a1d85ec170359fb39b481923b051c24320919 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Fri, 16 Aug 2024 23:00:53 -0300 Subject: [PATCH 20/48] feat: test properties 22 and 23 * refactor: allow for atomicity on mock messenger * refactor: cross domain sender logic on mock messenger * refactor: l2 to l2 caller and messenge sender logic * chore: remove assumes for removed checks on mock messenger --- .../properties/halmos/MockL2ToL2Messenger.sol | 56 +++++- .../halmos/OptimismSuperchainERC20.t.sol | 184 ++++++++++++------ .../test/properties/helpers/HalmosBase.sol | 1 + 3 files changed, 174 insertions(+), 67 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/halmos/MockL2ToL2Messenger.sol b/packages/contracts-bedrock/test/properties/halmos/MockL2ToL2Messenger.sol index fb8732f384f8..5f7aa3b301e7 100644 --- a/packages/contracts-bedrock/test/properties/halmos/MockL2ToL2Messenger.sol +++ b/packages/contracts-bedrock/test/properties/halmos/MockL2ToL2Messenger.sol @@ -1,25 +1,65 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; +import "src/L2/L2ToL2CrossDomainMessenger.sol"; +import { SafeCall } from "src/libraries/SafeCall.sol"; // TODO: Try to merge to a single mocked contract used by fuzzing and symbolic invariant tests - only if possible -// and low priorty -contract MockL2ToL2Messenger { - // Setting the current cross domain sender for the check of sender address equals the supertoken address - address internal immutable CROSS_DOMAIN_SENDER; +// and is a low priorty +contract MockL2ToL2Messenger is IL2ToL2CrossDomainMessenger { + /// NOTE: Setting everything as immutable bc the storage layour is ignored when etching. + address internal immutable SOURCE_TOKEN; + address internal immutable DESTINATION_TOKEN; + uint256 public immutable DESTINATION_CHAIN_ID; + // Custom cross domain sender to be used when neither the source nor destination token are the callers + address internal immutable CUSTOM_CROSS_DOMAIN_SENDER; - constructor(address _xDomainSender) { - CROSS_DOMAIN_SENDER = _xDomainSender; + constructor( + address _sourceToken, + address _destinationToken, + uint256 _destinationChainId, + address _customCrossDomainSender + ) { + SOURCE_TOKEN = _sourceToken; + DESTINATION_TOKEN = _destinationToken; + DESTINATION_CHAIN_ID = _destinationChainId; + CUSTOM_CROSS_DOMAIN_SENDER = _customCrossDomainSender; } - function sendMessage(uint256 , address , bytes calldata) external payable { + function sendMessage(uint256 _destination, address, bytes calldata _message) external payable { + // Mocking the environment to allow atomicity by executing the message call + if (_destination == DESTINATION_CHAIN_ID) { + (bool _success) = SafeCall.call(DESTINATION_TOKEN, 0, _message); + if (!_success) revert("MockL2ToL2Messenger: sendMessage failed"); + } + } + + function relayMessage( + uint256, + uint256, + uint256, + address, + address _target, + bytes calldata _message + ) + external + payable + { + (bool succ, bytes memory ret) = _target.call{ value: msg.value }(_message); + if (!succ) revert(string(ret)); + + // TODO: Add more logic? Like replacing the (unsupported) `TSTORE` updates with `SSTORE` - or add the checks } function crossDomainMessageSource() external view returns (uint256 _source) { _source = block.chainid + 1; } + // Mock this function so it just always returns the expected if called by the supertoken, or otherwise defaults to + // the custom cross domain sender function crossDomainMessageSender() external view returns (address _sender) { - _sender = CROSS_DOMAIN_SENDER; + if (msg.sender == SOURCE_TOKEN) _sender = SOURCE_TOKEN; + else if (msg.sender == DESTINATION_TOKEN) _sender = DESTINATION_TOKEN; + else _sender = CUSTOM_CROSS_DOMAIN_SENDER; } } diff --git a/packages/contracts-bedrock/test/properties/halmos/OptimismSuperchainERC20.t.sol b/packages/contracts-bedrock/test/properties/halmos/OptimismSuperchainERC20.t.sol index a6285ac58e08..cdbae879d0ff 100644 --- a/packages/contracts-bedrock/test/properties/halmos/OptimismSuperchainERC20.t.sol +++ b/packages/contracts-bedrock/test/properties/halmos/OptimismSuperchainERC20.t.sol @@ -12,12 +12,23 @@ contract SymTest_OptimismSuperchainERC20 is SymTest, HalmosBase { MockL2ToL2Messenger internal constant MESSENGER = MockL2ToL2Messenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); OptimismSuperchainERC20 public superchainERC20Impl; - OptimismSuperchainERC20 internal optimismSuperchainERC20; + OptimismSuperchainERC20 internal sourceToken; + OptimismSuperchainERC20 internal destToken; function setUp() public { // Deploy the OptimismSuperchainERC20 contract implementation and the proxy to be used superchainERC20Impl = new OptimismSuperchainERC20(); - optimismSuperchainERC20 = OptimismSuperchainERC20( + sourceToken = OptimismSuperchainERC20( + address( + // TODO: Update to beacon proxy + new ERC1967Proxy( + address(superchainERC20Impl), + abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, name, symbol, decimals)) + ) + ) + ); + + destToken = OptimismSuperchainERC20( address( // TODO: Update to beacon proxy new ERC1967Proxy( @@ -29,19 +40,34 @@ contract SymTest_OptimismSuperchainERC20 is SymTest, HalmosBase { // Etch the mocked L2 to L2 Messenger since the messenger logic is out of scope for these test suite. Also, we // avoid issues such as `TSTORE` opcode not being supported, or issues with `encodeVersionedNonce()` - address _mockL2ToL2CrossDomainMessenger = address(new MockL2ToL2Messenger(address(optimismSuperchainERC20))); + address _mockL2ToL2CrossDomainMessenger = + address(new MockL2ToL2Messenger(address(sourceToken), address(destToken), DESTINATION_CHAIN_ID, address(0))); vm.etch(address(MESSENGER), _mockL2ToL2CrossDomainMessenger.code); // NOTE: We need to set the crossDomainMessageSender as an immutable or otherwise storage vars and not taken // into account when etching on halmos. Setting a constant slot with setters and getters didn't work neither. } /// @notice Check setup works as expected - function check_setup() public view { - assert(optimismSuperchainERC20.remoteToken() == remoteToken); - assert(eqStrings(optimismSuperchainERC20.name(), name)); - assert(eqStrings(optimismSuperchainERC20.symbol(), symbol)); - assert(optimismSuperchainERC20.decimals() == decimals); - assert(MESSENGER.crossDomainMessageSender() == address(optimismSuperchainERC20)); + function check_setup() public { + // Source token + assert(sourceToken.remoteToken() == remoteToken); + assert(eqStrings(sourceToken.name(), name)); + assert(eqStrings(sourceToken.symbol(), symbol)); + assert(sourceToken.decimals() == decimals); + vm.prank(address(sourceToken)); + assert(MESSENGER.crossDomainMessageSender() == address(sourceToken)); + + // Destination token + assert(destToken.remoteToken() == remoteToken); + assert(eqStrings(destToken.name(), name)); + assert(eqStrings(destToken.symbol(), symbol)); + assert(destToken.decimals() == decimals); + assert(MESSENGER.DESTINATION_CHAIN_ID() == DESTINATION_CHAIN_ID); + vm.prank(address(destToken)); + assert(MESSENGER.crossDomainMessageSender() == address(destToken)); + + // Custom cross domain sender + assert(MESSENGER.crossDomainMessageSender() == address(0)); } /// @custom:property-id 6 @@ -56,17 +82,16 @@ contract SymTest_OptimismSuperchainERC20 is SymTest, HalmosBase { public { /* Preconditions */ - vm.assume(_chainId != CURRENT_CHAIN_ID); vm.assume(_to != address(0)); vm.assume(_from != address(0)); // Can't deal to unsupported cheatcode vm.prank(Predeploys.L2_STANDARD_BRIDGE); - optimismSuperchainERC20.mint(_from, _initialBalance); + sourceToken.mint(_from, _initialBalance); vm.prank(_from); /* Action */ - try optimismSuperchainERC20.sendERC20(_to, _amount, _chainId) { + try sourceToken.sendERC20(_to, _amount, _chainId) { /* Postcondition */ assert(_initialBalance >= _amount); } catch { @@ -89,21 +114,18 @@ contract SymTest_OptimismSuperchainERC20 is SymTest, HalmosBase { vm.assume(_to != address(0)); // Deploying a new messenger because of an issue of not being able to etch the storage layout of the mock // contract. So needed to a new one setting the symbolic immutable variable for the crossDomainSender. - vm.etch(address(MESSENGER), address(new MockL2ToL2Messenger(_crossDomainSender)).code); + // Used 0 address on source token so when the `soureToken` calls it if returns the symbolic `_crossDomainSender` + vm.etch( + address(MESSENGER), address(new MockL2ToL2Messenger(address(0), address(0), 0, _crossDomainSender)).code + ); vm.prank(_sender); /* Action */ - try optimismSuperchainERC20.relayERC20(_from, _to, _amount) { + try sourceToken.relayERC20(_from, _to, _amount) { /* Postconditions */ - assert( - _sender == address(MESSENGER) - && MESSENGER.crossDomainMessageSender() == address(optimismSuperchainERC20) - ); + assert(_sender == address(MESSENGER) && MESSENGER.crossDomainMessageSender() == address(sourceToken)); } catch { - assert( - _sender != address(MESSENGER) - || MESSENGER.crossDomainMessageSender() != address(optimismSuperchainERC20) - ); + assert(_sender != address(MESSENGER) || MESSENGER.crossDomainMessageSender() != address(sourceToken)); } } @@ -112,37 +134,38 @@ contract SymTest_OptimismSuperchainERC20 is SymTest, HalmosBase { function check_sendERC20ZeroCall(address _from, address _to, uint256 _chainId) public { /* Preconditions */ vm.assume(_to != address(0)); - vm.assume(_chainId != CURRENT_CHAIN_ID); vm.assume(_to != address(Predeploys.CROSS_L2_INBOX) && _to != address(MESSENGER)); - uint256 _totalSupplyBefore = optimismSuperchainERC20.totalSupply(); - uint256 _fromBalanceBefore = optimismSuperchainERC20.balanceOf(_from); + uint256 _totalSupplyBefore = sourceToken.totalSupply(); + uint256 _fromBalanceBefore = sourceToken.balanceOf(_from); + uint256 _toBalanceBefore = sourceToken.balanceOf(_to); vm.startPrank(_from); /* Action */ - optimismSuperchainERC20.sendERC20(_to, ZERO_AMOUNT, _chainId); + sourceToken.sendERC20(_to, ZERO_AMOUNT, _chainId); /* Postcondition */ - assert(optimismSuperchainERC20.totalSupply() == _totalSupplyBefore); - assert(optimismSuperchainERC20.balanceOf(_from) == _fromBalanceBefore); + assert(sourceToken.totalSupply() == _totalSupplyBefore); + assert(sourceToken.balanceOf(_from) == _fromBalanceBefore); + assert(sourceToken.balanceOf(_to) == _toBalanceBefore); } /// @custom:property-id 9 /// @custom:property `relayERC20` with a value of zero does not modify accounting function check_relayERC20ZeroCall(address _from, address _to) public { - uint256 _totalSupplyBefore = optimismSuperchainERC20.totalSupply(); + uint256 _totalSupplyBefore = sourceToken.totalSupply(); /* Preconditions */ - uint256 _fromBalanceBefore = optimismSuperchainERC20.balanceOf(_from); - uint256 _toBalanceBefore = optimismSuperchainERC20.balanceOf(_to); + uint256 _fromBalanceBefore = sourceToken.balanceOf(_from); + uint256 _toBalanceBefore = sourceToken.balanceOf(_to); vm.prank(address(MESSENGER)); /* Action */ - optimismSuperchainERC20.relayERC20(_from, _to, ZERO_AMOUNT); + sourceToken.relayERC20(_from, _to, ZERO_AMOUNT); /* Postconditions */ - assert(optimismSuperchainERC20.totalSupply() == _totalSupplyBefore); - assert(optimismSuperchainERC20.balanceOf(_from) == _fromBalanceBefore); - assert(optimismSuperchainERC20.balanceOf(_to) == _toBalanceBefore); + assert(sourceToken.totalSupply() == _totalSupplyBefore); + assert(sourceToken.balanceOf(_from) == _fromBalanceBefore); + assert(sourceToken.balanceOf(_to) == _toBalanceBefore); } /// @custom:property-id 10 @@ -157,21 +180,20 @@ contract SymTest_OptimismSuperchainERC20 is SymTest, HalmosBase { { /* Preconditions */ vm.assume(_to != address(0)); - vm.assume(_chainId != CURRENT_CHAIN_ID); vm.prank(Predeploys.L2_STANDARD_BRIDGE); - optimismSuperchainERC20.mint(_sender, _amount); + sourceToken.mint(_sender, _amount); - uint256 _totalSupplyBefore = optimismSuperchainERC20.totalSupply(); - uint256 _balanceBefore = optimismSuperchainERC20.balanceOf(_sender); + uint256 _totalSupplyBefore = sourceToken.totalSupply(); + uint256 _balanceBefore = sourceToken.balanceOf(_sender); vm.prank(_sender); /* Action */ - optimismSuperchainERC20.sendERC20(_to, _amount, _chainId); + sourceToken.sendERC20(Predeploys.CROSS_L2_INBOX, _amount, _chainId); /* Postconditions */ - assert(optimismSuperchainERC20.totalSupply() == _totalSupplyBefore - _amount); - assert(optimismSuperchainERC20.balanceOf(_sender) == _balanceBefore - _amount); + assert(sourceToken.totalSupply() == _totalSupplyBefore - _amount); + assert(sourceToken.balanceOf(_sender) == _balanceBefore - _amount); } /// @custom:property-id 11 @@ -181,32 +203,32 @@ contract SymTest_OptimismSuperchainERC20 is SymTest, HalmosBase { vm.assume(_to != address(0)); /* Preconditions */ - uint256 _totalSupplyBefore = optimismSuperchainERC20.totalSupply(); - uint256 _toBalanceBefore = optimismSuperchainERC20.balanceOf(_to); + uint256 _totalSupplyBefore = sourceToken.totalSupply(); + uint256 _toBalanceBefore = sourceToken.balanceOf(_to); vm.prank(address(MESSENGER)); /* Action */ - optimismSuperchainERC20.relayERC20(_from, _to, _amount); + sourceToken.relayERC20(_from, _to, _amount); /* Postconditions */ - assert(optimismSuperchainERC20.totalSupply() == _totalSupplyBefore + _amount); - assert(optimismSuperchainERC20.balanceOf(_to) == _toBalanceBefore + _amount); + assert(sourceToken.totalSupply() == _totalSupplyBefore + _amount); + assert(sourceToken.balanceOf(_to) == _toBalanceBefore + _amount); } /// @custom:property-id 12 /// @custom:property Increases the total supply on the amount minted by the bridge function check_mint(address _from, uint256 _amount) public { /* Preconditions */ - uint256 _totalSupplyBefore = optimismSuperchainERC20.totalSupply(); - uint256 _balanceBefore = optimismSuperchainERC20.balanceOf(_from); + uint256 _totalSupplyBefore = sourceToken.totalSupply(); + uint256 _balanceBefore = sourceToken.balanceOf(_from); vm.startPrank(Predeploys.L2_STANDARD_BRIDGE); /* Action */ - optimismSuperchainERC20.mint(_from, _amount); + sourceToken.mint(_from, _amount); /* Postconditions */ - assert(optimismSuperchainERC20.totalSupply() == _totalSupplyBefore + _amount); - assert(optimismSuperchainERC20.balanceOf(_from) == _balanceBefore + _amount); + assert(sourceToken.totalSupply() == _totalSupplyBefore + _amount); + assert(sourceToken.balanceOf(_from) == _balanceBefore + _amount); } /// @custom:property-id 13 @@ -214,24 +236,68 @@ contract SymTest_OptimismSuperchainERC20 is SymTest, HalmosBase { function check_burn(address _from, uint256 _amount) public { /* Preconditions */ vm.prank(Predeploys.L2_STANDARD_BRIDGE); - optimismSuperchainERC20.mint(_from, _amount); + sourceToken.mint(_from, _amount); - uint256 _totalSupplyBefore = optimismSuperchainERC20.totalSupply(); - uint256 _balanceBefore = optimismSuperchainERC20.balanceOf(_from); + uint256 _totalSupplyBefore = sourceToken.totalSupply(); + uint256 _balanceBefore = sourceToken.balanceOf(_from); vm.prank(Predeploys.L2_STANDARD_BRIDGE); /* Action */ - optimismSuperchainERC20.burn(_from, _amount); + sourceToken.burn(_from, _amount); /* Postconditions */ - assert(optimismSuperchainERC20.totalSupply() == _totalSupplyBefore - _amount); - assert(optimismSuperchainERC20.balanceOf(_from) == _balanceBefore - _amount); + assert(sourceToken.totalSupply() == _totalSupplyBefore - _amount); + assert(sourceToken.balanceOf(_from) == _balanceBefore - _amount); } /// @custom:property-id 14 /// @custom:property Supertoken total supply starts at zero function check_totalSupplyStartsAtZero() public view { /* Postconditions */ - assert(optimismSuperchainERC20.totalSupply() == 0); + assert(sourceToken.totalSupply() == 0); + } + + /// @custom:property-id 22 + /// @custom:property `sendERC20` decreases sender balance in source chain and increases receiver balance in + /// destination chain exactly by the input amount + /// @custom:property-id 23 + /// @custom:property `sendERC20` decreases total supply in source chain and increases it in destination chain + /// exactly by the input amount + function check_crossChainMintAndBurn(address _from, address _to, uint256 _amount, uint256 _chainId) public { + /* Preconditions */ + vm.assume(_to != address(0)); + vm.assume(_from != address(0)); + + // Mint the amount to send + vm.prank(Predeploys.L2_STANDARD_BRIDGE); + sourceToken.mint(_from, _amount); + + uint256 fromBalanceBefore = sourceToken.balanceOf(_from); + uint256 toBalanceBefore = destToken.balanceOf(_to); + uint256 sourceTotalSupplyBefore = sourceToken.totalSupply(); + uint256 destTotalSupplyBefore = destToken.totalSupply(); + + vm.prank(_from); + /* Action */ + try sourceToken.sendERC20(_to, _amount, _chainId) { + /* Postconditions */ + // Source + assert(sourceToken.balanceOf(_from) == fromBalanceBefore - _amount); + assert(sourceToken.totalSupply() == sourceTotalSupplyBefore - _amount); + + // Destination + if (_chainId == DESTINATION_CHAIN_ID) { + // If the destination chain matches the one of the dest token, check that the amount was minted + assert(destToken.balanceOf(_to) == toBalanceBefore + _amount); + assert(destToken.totalSupply() == destTotalSupplyBefore + _amount); + } else { + // Otherwise the balances should remain the same + assert(destToken.balanceOf(_to) == toBalanceBefore); + assert(destToken.totalSupply() == destTotalSupplyBefore); + } + } catch { + // Shouldn't fail + assert(false); + } } } diff --git a/packages/contracts-bedrock/test/properties/helpers/HalmosBase.sol b/packages/contracts-bedrock/test/properties/helpers/HalmosBase.sol index 75bfe2f7c9e1..5ffac1050d14 100644 --- a/packages/contracts-bedrock/test/properties/helpers/HalmosBase.sol +++ b/packages/contracts-bedrock/test/properties/helpers/HalmosBase.sol @@ -5,6 +5,7 @@ import { Test } from "forge-std/Test.sol"; contract HalmosBase is Test { uint256 internal constant CURRENT_CHAIN_ID = 1; + uint256 internal constant DESTINATION_CHAIN_ID = 2; uint256 internal constant ZERO_AMOUNT = 0; address internal remoteToken = address(bytes20(keccak256("remoteToken"))); From 8c4e7c672b1a3be43b36a942add8f1738424c0bf Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Thu, 29 Aug 2024 11:41:36 -0300 Subject: [PATCH 21/48] feat: checkpoint of superc20 kontrol tests --- .../kontrol/proofs/OptimismSuperchain.k.sol | 303 ++++++++++++++++++ .../test/kontrol/scripts/run-kontrol.sh | 3 +- 2 files changed, 305 insertions(+), 1 deletion(-) create mode 100644 packages/contracts-bedrock/test/kontrol/proofs/OptimismSuperchain.k.sol diff --git a/packages/contracts-bedrock/test/kontrol/proofs/OptimismSuperchain.k.sol b/packages/contracts-bedrock/test/kontrol/proofs/OptimismSuperchain.k.sol new file mode 100644 index 000000000000..1e78fb4e7c90 --- /dev/null +++ b/packages/contracts-bedrock/test/kontrol/proofs/OptimismSuperchain.k.sol @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; +import { SymTest } from "halmos-cheatcodes/src/SymTest.sol"; +import { Predeploys } from "src/libraries/Predeploys.sol"; +import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol"; +import { MockL2ToL2Messenger } from "test/properties/halmos/MockL2ToL2Messenger.sol"; +import { HalmosBase } from "test/properties/helpers/HalmosBase.sol"; + +contract OptimismSuperchainERC20Kontrol is SymTest, HalmosBase { + MockL2ToL2Messenger internal constant MESSENGER = MockL2ToL2Messenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); + + OptimismSuperchainERC20 public superchainERC20Impl; + OptimismSuperchainERC20 internal sourceToken; + OptimismSuperchainERC20 internal destToken; + + function setUp() public { + // Deploy the OptimismSuperchainERC20 contract implementation and the proxy to be used + superchainERC20Impl = new OptimismSuperchainERC20(); + sourceToken = OptimismSuperchainERC20( + address( + // TODO: Update to beacon proxy + new ERC1967Proxy( + address(superchainERC20Impl), + abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, name, symbol, decimals)) + ) + ) + ); + + destToken = OptimismSuperchainERC20( + address( + // TODO: Update to beacon proxy + new ERC1967Proxy( + address(superchainERC20Impl), + abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, name, symbol, decimals)) + ) + ) + ); + + // Etch the mocked L2 to L2 Messenger since the messenger logic is out of scope for these test suite. Also, we + // avoid issues such as `TSTORE` opcode not being supported, or issues with `encodeVersionedNonce()` + address _mockL2ToL2CrossDomainMessenger = + address(new MockL2ToL2Messenger(address(sourceToken), address(destToken), DESTINATION_CHAIN_ID, address(0))); + vm.etch(address(MESSENGER), _mockL2ToL2CrossDomainMessenger.code); + // NOTE: We need to set the crossDomainMessageSender as an immutable or otherwise storage vars and not taken + // into account when etching on halmos. Setting a constant slot with setters and getters didn't work neither. + } + + /// @notice Check setup works as expected + function prove_setup() public { + // Source token + assert(sourceToken.remoteToken() == remoteToken); + assert(eqStrings(sourceToken.name(), name)); + assert(eqStrings(sourceToken.symbol(), symbol)); + assert(sourceToken.decimals() == decimals); + vm.prank(address(sourceToken)); + assert(MESSENGER.crossDomainMessageSender() == address(sourceToken)); + + // Destination token + assert(destToken.remoteToken() == remoteToken); + assert(eqStrings(destToken.name(), name)); + assert(eqStrings(destToken.symbol(), symbol)); + assert(destToken.decimals() == decimals); + assert(MESSENGER.DESTINATION_CHAIN_ID() == DESTINATION_CHAIN_ID); + vm.prank(address(destToken)); + assert(MESSENGER.crossDomainMessageSender() == address(destToken)); + + // Custom cross domain sender + assert(MESSENGER.crossDomainMessageSender() == address(0)); + } + + /// @custom:property-id 6 + /// @custom:property Calls to sendERC20 succeed as long as caller has enough balance + function prove_sendERC20SucceedsOnlyIfEnoughBalance( + uint256 _initialBalance, + address _from, + uint256 _amount, + address _to, + uint256 _chainId + ) + public + { + /* Preconditions */ + vm.assume(_to != address(0)); + vm.assume(_from != address(0)); + + // Can't deal to unsupported cheatcode + vm.prank(Predeploys.L2_STANDARD_BRIDGE); + sourceToken.mint(_from, _initialBalance); + + vm.prank(_from); + /* Action */ + try sourceToken.sendERC20(_to, _amount, _chainId) { + /* Postcondition */ + assert(_initialBalance >= _amount); + } catch { + assert(_initialBalance < _amount); + } + } + + /// @custom:property-id 7 + /// @custom:property Calls to relayERC20 always succeed as long as the sender the cross-domain caller are valid + function prove_relayERC20OnlyFromL2ToL2Messenger( + address _crossDomainSender, + address _sender, + address _from, + address _to, + uint256 _amount + ) + public + { + /* Precondition */ + vm.assume(_to != address(0)); + // Deploying a new messenger because of an issue of not being able to etch the storage layout of the mock + // contract. So needed to a new one setting the symbolic immutable variable for the crossDomainSender. + // Used 0 address on source token so when the `soureToken` calls it if returns the symbolic `_crossDomainSender` + vm.etch( + address(MESSENGER), address(new MockL2ToL2Messenger(address(0), address(0), 0, _crossDomainSender)).code + ); + + vm.prank(_sender); + /* Action */ + try sourceToken.relayERC20(_from, _to, _amount) { + /* Postconditions */ + assert(_sender == address(MESSENGER) && MESSENGER.crossDomainMessageSender() == address(sourceToken)); + } catch { + assert(_sender != address(MESSENGER) || MESSENGER.crossDomainMessageSender() != address(sourceToken)); + } + } + + /// @custom:property-id 8 + /// @custom:property `sendERC20` with a value of zero does not modify accounting + function prove_sendERC20ZeroCall(address _from, address _to, uint256 _chainId) public { + /* Preconditions */ + vm.assume(_to != address(0)); + vm.assume(_to != address(Predeploys.CROSS_L2_INBOX) && _to != address(MESSENGER)); + + uint256 _totalSupplyBefore = sourceToken.totalSupply(); + uint256 _fromBalanceBefore = sourceToken.balanceOf(_from); + uint256 _toBalanceBefore = sourceToken.balanceOf(_to); + + vm.startPrank(_from); + /* Action */ + sourceToken.sendERC20(_to, ZERO_AMOUNT, _chainId); + + /* Postcondition */ + assert(sourceToken.totalSupply() == _totalSupplyBefore); + assert(sourceToken.balanceOf(_from) == _fromBalanceBefore); + assert(sourceToken.balanceOf(_to) == _toBalanceBefore); + } + + /// @custom:property-id 9 + /// @custom:property `relayERC20` with a value of zero does not modify accounting + function prove_relayERC20ZeroCall(address _from, address _to) public { + uint256 _totalSupplyBefore = sourceToken.totalSupply(); + /* Preconditions */ + uint256 _fromBalanceBefore = sourceToken.balanceOf(_from); + uint256 _toBalanceBefore = sourceToken.balanceOf(_to); + vm.prank(address(MESSENGER)); + + /* Action */ + sourceToken.relayERC20(_from, _to, ZERO_AMOUNT); + + /* Postconditions */ + assert(sourceToken.totalSupply() == _totalSupplyBefore); + assert(sourceToken.balanceOf(_from) == _fromBalanceBefore); + assert(sourceToken.balanceOf(_to) == _toBalanceBefore); + } + + /// @custom:property-id 10 + /// @custom:property `sendERC20` decreases the token's totalSupply in the source chain exactly by the input amount + function prove_sendERC20DecreasesTotalSupply( + address _sender, + address _to, + uint256 _amount, + uint256 _chainId + ) + public + { + /* Preconditions */ + vm.assume(_to != address(0)); + + vm.prank(Predeploys.L2_STANDARD_BRIDGE); + sourceToken.mint(_sender, _amount); + + uint256 _totalSupplyBefore = sourceToken.totalSupply(); + uint256 _balanceBefore = sourceToken.balanceOf(_sender); + + vm.prank(_sender); + /* Action */ + sourceToken.sendERC20(Predeploys.CROSS_L2_INBOX, _amount, _chainId); + + /* Postconditions */ + assert(sourceToken.totalSupply() == _totalSupplyBefore - _amount); + assert(sourceToken.balanceOf(_sender) == _balanceBefore - _amount); + } + + /// @custom:property-id 11 + /// @custom:property `relayERC20` increases the token's totalSupply in the destination chain exactly by the input + /// amount + function prove_relayERC20IncreasesTotalSupply(address _from, address _to, uint256 _amount) public { + vm.assume(_to != address(0)); + + /* Preconditions */ + uint256 _totalSupplyBefore = sourceToken.totalSupply(); + uint256 _toBalanceBefore = sourceToken.balanceOf(_to); + + vm.prank(address(MESSENGER)); + /* Action */ + sourceToken.relayERC20(_from, _to, _amount); + + /* Postconditions */ + assert(sourceToken.totalSupply() == _totalSupplyBefore + _amount); + assert(sourceToken.balanceOf(_to) == _toBalanceBefore + _amount); + } + + /// @custom:property-id 12 + /// @custom:property Increases the total supply on the amount minted by the bridge + function prove_mint(address _from, uint256 _amount) public { + /* Preconditions */ + uint256 _totalSupplyBefore = sourceToken.totalSupply(); + uint256 _balanceBefore = sourceToken.balanceOf(_from); + + vm.startPrank(Predeploys.L2_STANDARD_BRIDGE); + /* Action */ + sourceToken.mint(_from, _amount); + + /* Postconditions */ + assert(sourceToken.totalSupply() == _totalSupplyBefore + _amount); + assert(sourceToken.balanceOf(_from) == _balanceBefore + _amount); + } + + /// @custom:property-id 13 + /// @custom:property Supertoken total supply only decreases on the amount burned by the bridge + function prove_burn(address _from, uint256 _amount) public { + /* Preconditions */ + vm.prank(Predeploys.L2_STANDARD_BRIDGE); + sourceToken.mint(_from, _amount); + + uint256 _totalSupplyBefore = sourceToken.totalSupply(); + uint256 _balanceBefore = sourceToken.balanceOf(_from); + + vm.prank(Predeploys.L2_STANDARD_BRIDGE); + /* Action */ + sourceToken.burn(_from, _amount); + + /* Postconditions */ + assert(sourceToken.totalSupply() == _totalSupplyBefore - _amount); + assert(sourceToken.balanceOf(_from) == _balanceBefore - _amount); + } + + /// @custom:property-id 14 + /// @custom:property Supertoken total supply starts at zero + function prove_totalSupplyStartsAtZero() public view { + /* Postconditions */ + assert(sourceToken.totalSupply() == 0); + } + + /// @custom:property-id 22 + /// @custom:property `sendERC20` decreases sender balance in source chain and increases receiver balance in + /// destination chain exactly by the input amount + /// @custom:property-id 23 + /// @custom:property `sendERC20` decreases total supply in source chain and increases it in destination chain + /// exactly by the input amount + function prove_crossChainMintAndBurn(address _from, address _to, uint256 _amount, uint256 _chainId) public { + /* Preconditions */ + vm.assume(_to != address(0)); + vm.assume(_from != address(0)); + + // Mint the amount to send + vm.prank(Predeploys.L2_STANDARD_BRIDGE); + sourceToken.mint(_from, _amount); + + uint256 fromBalanceBefore = sourceToken.balanceOf(_from); + uint256 toBalanceBefore = destToken.balanceOf(_to); + uint256 sourceTotalSupplyBefore = sourceToken.totalSupply(); + uint256 destTotalSupplyBefore = destToken.totalSupply(); + + vm.prank(_from); + /* Action */ + try sourceToken.sendERC20(_to, _amount, _chainId) { + /* Postconditions */ + // Source + assert(sourceToken.balanceOf(_from) == fromBalanceBefore - _amount); + assert(sourceToken.totalSupply() == sourceTotalSupplyBefore - _amount); + + // Destination + if (_chainId == DESTINATION_CHAIN_ID) { + // If the destination chain matches the one of the dest token, check that the amount was minted + assert(destToken.balanceOf(_to) == toBalanceBefore + _amount); + assert(destToken.totalSupply() == destTotalSupplyBefore + _amount); + } else { + // Otherwise the balances should remain the same + assert(destToken.balanceOf(_to) == toBalanceBefore); + assert(destToken.totalSupply() == destTotalSupplyBefore); + } + } catch { + // Shouldn't fail + assert(false); + } + } +} diff --git a/packages/contracts-bedrock/test/kontrol/scripts/run-kontrol.sh b/packages/contracts-bedrock/test/kontrol/scripts/run-kontrol.sh index eeda15b92293..e87527086aeb 100755 --- a/packages/contracts-bedrock/test/kontrol/scripts/run-kontrol.sh +++ b/packages/contracts-bedrock/test/kontrol/scripts/run-kontrol.sh @@ -140,7 +140,8 @@ if [ "$SCRIPT_TESTS" == true ]; then "L1StandardBridgeKontrol.prove_finalizeBridgeERC20_paused" \ "L1StandardBridgeKontrol.prove_finalizeBridgeETH_paused" \ "L1ERC721BridgeKontrol.prove_finalizeBridgeERC721_paused" \ - "L1CrossDomainMessengerKontrol.prove_relayMessage_paused" + "L1CrossDomainMessengerKontrol.prove_relayMessage_paused" \ + "OptimismSuperchainERC20Kontrol.prove_setup" ) elif [ "$CUSTOM_TESTS" != 0 ]; then test_list=( "${@:${CUSTOM_TESTS}}" ) From 8b379dc8cb82bddf37974ef7da7d9ad97c027b88 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Thu, 29 Aug 2024 13:20:42 -0300 Subject: [PATCH 22/48] feat: setup kontrol --- packages/contracts-bedrock/.gitignore | 1 + packages/contracts-bedrock/foundry.toml | 6 ++++++ .../proofs => properties/kontrol}/OptimismSuperchain.k.sol | 4 ++-- 3 files changed, 9 insertions(+), 2 deletions(-) rename packages/contracts-bedrock/test/{kontrol/proofs => properties/kontrol}/OptimismSuperchain.k.sol (98%) diff --git a/packages/contracts-bedrock/.gitignore b/packages/contracts-bedrock/.gitignore index 396c03d4458d..6ecdd53bc7b6 100644 --- a/packages/contracts-bedrock/.gitignore +++ b/packages/contracts-bedrock/.gitignore @@ -7,6 +7,7 @@ kout-deployment kout-proofs test/kontrol/logs test/properties/medusa/corpus/ +out/ # Metrics coverage.out diff --git a/packages/contracts-bedrock/foundry.toml b/packages/contracts-bedrock/foundry.toml index 69b1fde5c5eb..0c82189413ce 100644 --- a/packages/contracts-bedrock/foundry.toml +++ b/packages/contracts-bedrock/foundry.toml @@ -107,3 +107,9 @@ script = 'test/properties/medusa/' src = 'test/properties/halmos/' test = 'test/properties/halmos/' script = 'test/properties/halmos/' + +[profile.kontrol-properties] +src = "test/properties/kontrol" +test = "test/properties/kontrol" +script = "test/properties/kontrol" +out = 'out' \ No newline at end of file diff --git a/packages/contracts-bedrock/test/kontrol/proofs/OptimismSuperchain.k.sol b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchain.k.sol similarity index 98% rename from packages/contracts-bedrock/test/kontrol/proofs/OptimismSuperchain.k.sol rename to packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchain.k.sol index 1e78fb4e7c90..94116cf536e2 100644 --- a/packages/contracts-bedrock/test/kontrol/proofs/OptimismSuperchain.k.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchain.k.sol @@ -2,13 +2,13 @@ pragma solidity 0.8.25; import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; -import { SymTest } from "halmos-cheatcodes/src/SymTest.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol"; import { MockL2ToL2Messenger } from "test/properties/halmos/MockL2ToL2Messenger.sol"; import { HalmosBase } from "test/properties/helpers/HalmosBase.sol"; +import { KontrolCheats } from "kontrol-cheatcodes/KontrolCheats.sol"; -contract OptimismSuperchainERC20Kontrol is SymTest, HalmosBase { +contract OptimismSuperchainERC20Kontrol is KontrolCheats, HalmosBase { MockL2ToL2Messenger internal constant MESSENGER = MockL2ToL2Messenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); OptimismSuperchainERC20 public superchainERC20Impl; From c29990e01457bd1d1d25b9e006635efa5cef1566 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Sun, 1 Sep 2024 14:06:21 -0300 Subject: [PATCH 23/48] feat: external computation diff state wip --- packages/contracts-bedrock/.gitignore | 3 + packages/contracts-bedrock/foundry.toml | 8 +- .../supererc20-state-diff/AddressNames.json | 6 + .../supererc20-state-diff/StateDiff.json | 585 ++++++++++++++++++ .../test/properties/kontrol/KontrolBase.sol | 65 ++ ...in.k.sol => OptimismSuperchainERC20.k.sol} | 62 +- .../kontrol/deployments/InitialState.sol | 90 +++ .../kontrol/deployments/InitialStateCode.sol | 15 + .../kontrol/helpers/LibStateDiff.sol | 136 ++++ .../kontrol/helpers/RecordStateDiff.sol | 74 +++ .../kontrol/optimism-superchain-lemmas.md | 33 + .../kontrol/utils/record-state-diff.sh | 55 ++ 12 files changed, 1090 insertions(+), 42 deletions(-) create mode 100644 packages/contracts-bedrock/supererc20-state-diff/AddressNames.json create mode 100644 packages/contracts-bedrock/supererc20-state-diff/StateDiff.json create mode 100644 packages/contracts-bedrock/test/properties/kontrol/KontrolBase.sol rename packages/contracts-bedrock/test/properties/kontrol/{OptimismSuperchain.k.sol => OptimismSuperchainERC20.k.sol} (82%) create mode 100644 packages/contracts-bedrock/test/properties/kontrol/deployments/InitialState.sol create mode 100644 packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol create mode 100644 packages/contracts-bedrock/test/properties/kontrol/helpers/LibStateDiff.sol create mode 100644 packages/contracts-bedrock/test/properties/kontrol/helpers/RecordStateDiff.sol create mode 100644 packages/contracts-bedrock/test/properties/kontrol/optimism-superchain-lemmas.md create mode 100644 packages/contracts-bedrock/test/properties/kontrol/utils/record-state-diff.sh diff --git a/packages/contracts-bedrock/.gitignore b/packages/contracts-bedrock/.gitignore index 6ecdd53bc7b6..e0676cfa0fd5 100644 --- a/packages/contracts-bedrock/.gitignore +++ b/packages/contracts-bedrock/.gitignore @@ -42,3 +42,6 @@ deploy-config/devnetL1.json # Getting Started guide deploy config deploy-config/getting-started.json + +# Supererc20 kontrol +out-kontrol-properties/ \ No newline at end of file diff --git a/packages/contracts-bedrock/foundry.toml b/packages/contracts-bedrock/foundry.toml index 0c82189413ce..db14a7f6bb8b 100644 --- a/packages/contracts-bedrock/foundry.toml +++ b/packages/contracts-bedrock/foundry.toml @@ -112,4 +112,10 @@ script = 'test/properties/halmos/' src = "test/properties/kontrol" test = "test/properties/kontrol" script = "test/properties/kontrol" -out = 'out' \ No newline at end of file +out = 'out-kontrol-properties' +# Make sure to export the variables $STATE_DIFF_DIR and $STATE_DIFF_NAME in your shell +fs_permissions = [ + { access = 'read', path = './supererc20-state-diff' }, + { access = 'read-write', path = './supererc20-state-diff/StateDiff.json' }, + { access = 'read-write', path = './supererc20-state-diff/AddressNames.json' } +] \ No newline at end of file diff --git a/packages/contracts-bedrock/supererc20-state-diff/AddressNames.json b/packages/contracts-bedrock/supererc20-state-diff/AddressNames.json new file mode 100644 index 000000000000..d94c12b6939f --- /dev/null +++ b/packages/contracts-bedrock/supererc20-state-diff/AddressNames.json @@ -0,0 +1,6 @@ +{ + "0x2e234DAe75C793f67A35089C9d99245E1C58470b": "sourceToken", + "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f": "superchainERC20Impl", + "0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9": "mockL2ToL2Messenger", + "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a": "destToken" +} \ No newline at end of file diff --git a/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json b/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json new file mode 100644 index 000000000000..f1dba0541bbf --- /dev/null +++ b/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json @@ -0,0 +1,585 @@ +{ + "accountAccesses": [ + { + "accessor": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "account": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0x6080604052348015600e575f80fd5b5060156019565b60c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff161560685760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b039081161460c65780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b6117bd806100d65f395ff3fe608060405234801561000f575f80fd5b5060043610610163575f3560e01c806378a3727b116100c7578063d505accf1161007d578063d9f5004611610063578063d9f5004614610356578063dd62ed3e14610369578063f6d2ee8614610391575f80fd5b8063d505accf146102fc578063d6c0b2c41461030f575f80fd5b806395d89b41116100ad57806395d89b41146102ce5780639dc29fac146102d6578063a9059cbb146102e9575f80fd5b806378a3727b146102965780637ecebe00146102a9575f80fd5b8063313ce5671161011c57806340c10f191161010257806340c10f191461022057806354fd4d501461023557806370a0823114610271575f80fd5b8063313ce567146101e45780633644e51514610218575f80fd5b8063095ea7b31161014c578063095ea7b3146101a457806318160ddd146101b757806323b872dd146101d1575f80fd5b806301ffc9a71461016757806306fdde031461018f575b5f80fd5b61017a610175366004611201565b6103a4565b60405190151581526020015b60405180910390f35b61019761043c565b6040516101869190611293565b61017a6101b23660046112c9565b6104ee565b6805345cdf77eb68f44c545b604051908152602001610186565b61017a6101df3660046112f3565b61053d565b7f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb035460405160ff9091168152602001610186565b6101c36105f7565b61023361022e3660046112c9565b610673565b005b6101976040518060400160405280600c81526020017f312e302e302d626574612e31000000000000000000000000000000000000000081525081565b6101c361027f366004611331565b6387a211a2600c9081525f91909152602090205490565b6102336102a436600461134c565b61076b565b6101c36102b7366004611331565b6338377508600c9081525f91909152602090205490565b610197610923565b6102336102e43660046112c9565b610954565b61017a6102f73660046112c9565b610a40565b61023361030a366004611393565b610ab7565b7f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610186565b6102336103643660046112f3565b610c4a565b6101c36103773660046113fc565b602052637f5e9f20600c9081525f91909152603490205490565b61023361039f366004611507565b610ebf565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f0bc3227100000000000000000000000000000000000000000000000000000000148061043657507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60607f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00600101805461046d90611589565b80601f016020809104026020016040519081016040528092919081815260200182805461049990611589565b80156104e45780601f106104bb576101008083540402835291602001916104e4565b820191905f5260205f20905b8154815290600101906020018083116104c757829003601f168201915b5050505050905090565b5f82602052637f5e9f20600c52335f52816034600c2055815f52602c5160601c337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560205fa350600192915050565b5f8360601b33602052637f5e9f208117600c526034600c208054600181011561057b5780851115610575576313be252b5f526004601cfd5b84810382555b50506387a211a28117600c526020600c208054808511156105a35763f4d678b85f526004601cfd5b84810382555050835f526020600c208381540181555082602052600c5160601c8160601c7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a3505060019392505050565b5f8061060161043c565b8051906020012090506040517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81528160208201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6604082015246606082015230608082015260a081209250505090565b33734200000000000000000000000000000000000010146106c0576040517f38da3b1500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821661070d576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107178282611104565b8173ffffffffffffffffffffffffffffffffffffffff167f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d41213968858260405161075f91815260200190565b60405180910390a25050565b73ffffffffffffffffffffffffffffffffffffffff83166107b8576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107c23383611180565b6040805133602482015273ffffffffffffffffffffffffffffffffffffffff85166044820152606480820185905282518083039091018152608490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fd9f500460000000000000000000000000000000000000000000000000000000017905290517f7056f41f00000000000000000000000000000000000000000000000000000000815273420000000000000000000000000000000000002390637056f41f9061089c908590309086906004016115da565b5f604051808303815f87803b1580156108b3575f80fd5b505af11580156108c5573d5f803e3d5ffd5b5050604080518681526020810186905273ffffffffffffffffffffffffffffffffffffffff881693503392507ffcea3600a13c757f2758710b089cc9752781c35d2a9d6804370ed18cd82f0bb691015b60405180910390a350505050565b60607f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00600201805461046d90611589565b33734200000000000000000000000000000000000010146109a1576040517f38da3b1500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166109ee576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109f88282611180565b8173ffffffffffffffffffffffffffffffffffffffff167fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca58260405161075f91815260200190565b5f6387a211a2600c52335f526020600c20805480841115610a685763f4d678b85f526004601cfd5b83810382555050825f526020600c208281540181555081602052600c5160601c337fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a350600192915050565b5f610ac061043c565b80519060200120905084421115610ade57631a15a3cc5f526004601cfd5b6040518860601b60601c98508760601b60601c975065383775081901600e52885f526020600c2080547f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f83528360208401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6604084015246606084015230608084015260a08320602e527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c983528a60208401528960408401528860608401528060808401528760a084015260c08320604e526042602c205f528660ff16602052856040528460605260208060805f60015afa8b3d5114610be65763ddafbaef5f526004601cfd5b019055777f5e9f20000000000000000000000000000000000000000088176040526034602c2087905587897f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925602060608501a360405250505f606052505050505050565b73ffffffffffffffffffffffffffffffffffffffff8216610c97576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373420000000000000000000000000000000000002314610ce4576040517f065d515000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff1673420000000000000000000000000000000000002373ffffffffffffffffffffffffffffffffffffffff166338ffde186040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d58573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d7c9190611617565b73ffffffffffffffffffffffffffffffffffffffff1614610dc9576040517fbc22e2aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f73420000000000000000000000000000000000002373ffffffffffffffffffffffffffffffffffffffff1663247944626040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e27573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e4b9190611632565b9050610e578383611104565b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fc75e22a0b57fb7740dbfc0caa5c6b7a82a2139964e7f1b7be7ac4e8be0f719ba8484604051610915929190918252602082015260400190565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff165f81158015610f095750825b90505f8267ffffffffffffffff166001148015610f255750303b155b905081158015610f33575080155b15610f6a576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610fcb5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b7f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb0080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8b161781557f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb016110558a82611694565b50600281016110648982611694565b5060030180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff881617905583156110f45784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b505050565b6805345cdf77eb68f44c54818101818110156111275763e5cfe9575f526004601cfd5b806805345cdf77eb68f44c5550506387a211a2600c52815f526020600c208181540181555080602052600c5160601c5f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a35050565b6387a211a2600c52815f526020600c208054808311156111a75763f4d678b85f526004601cfd5b82900390556805345cdf77eb68f44c805482900390555f81815273ffffffffffffffffffffffffffffffffffffffff83167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602083a35050565b5f60208284031215611211575f80fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114611240575f80fd5b9392505050565b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081525f6112406020830184611247565b73ffffffffffffffffffffffffffffffffffffffff811681146112c6575f80fd5b50565b5f80604083850312156112da575f80fd5b82356112e5816112a5565b946020939093013593505050565b5f805f60608486031215611305575f80fd5b8335611310816112a5565b92506020840135611320816112a5565b929592945050506040919091013590565b5f60208284031215611341575f80fd5b8135611240816112a5565b5f805f6060848603121561135e575f80fd5b8335611369816112a5565b95602085013595506040909401359392505050565b803560ff8116811461138e575f80fd5b919050565b5f805f805f805f60e0888a0312156113a9575f80fd5b87356113b4816112a5565b965060208801356113c4816112a5565b955060408801359450606088013593506113e06080890161137e565b925060a0880135915060c0880135905092959891949750929550565b5f806040838503121561140d575f80fd5b8235611418816112a5565b91506020830135611428816112a5565b809150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f82601f83011261146f575f80fd5b813567ffffffffffffffff8082111561148a5761148a611433565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156114d0576114d0611433565b816040528381528660208588010111156114e8575f80fd5b836020870160208301375f602085830101528094505050505092915050565b5f805f806080858703121561151a575f80fd5b8435611525816112a5565b9350602085013567ffffffffffffffff80821115611541575f80fd5b61154d88838901611460565b94506040870135915080821115611562575f80fd5b5061156f87828801611460565b92505061157e6060860161137e565b905092959194509250565b600181811c9082168061159d57607f821691505b6020821081036115d4577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b83815273ffffffffffffffffffffffffffffffffffffffff83166020820152606060408201525f61160e6060830184611247565b95945050505050565b5f60208284031215611627575f80fd5b8151611240816112a5565b5f60208284031215611642575f80fd5b5051919050565b601f8211156110ff57805f5260205f20601f840160051c8101602085101561166e5750805b601f840160051c820191505b8181101561168d575f815560010161167a565b5050505050565b815167ffffffffffffffff8111156116ae576116ae611433565b6116c2816116bc8454611589565b84611649565b602080601f831160018114611714575f84156116de5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556117a8565b5f858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561176057888601518255948401946001909101908401611741565b508582101561179c57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b505060018460011b0185555b50505050505056fea164736f6c6343000819000a", + "deployedCode": "0x608060405234801561000f575f80fd5b5060043610610163575f3560e01c806378a3727b116100c7578063d505accf1161007d578063d9f5004611610063578063d9f5004614610356578063dd62ed3e14610369578063f6d2ee8614610391575f80fd5b8063d505accf146102fc578063d6c0b2c41461030f575f80fd5b806395d89b41116100ad57806395d89b41146102ce5780639dc29fac146102d6578063a9059cbb146102e9575f80fd5b806378a3727b146102965780637ecebe00146102a9575f80fd5b8063313ce5671161011c57806340c10f191161010257806340c10f191461022057806354fd4d501461023557806370a0823114610271575f80fd5b8063313ce567146101e45780633644e51514610218575f80fd5b8063095ea7b31161014c578063095ea7b3146101a457806318160ddd146101b757806323b872dd146101d1575f80fd5b806301ffc9a71461016757806306fdde031461018f575b5f80fd5b61017a610175366004611201565b6103a4565b60405190151581526020015b60405180910390f35b61019761043c565b6040516101869190611293565b61017a6101b23660046112c9565b6104ee565b6805345cdf77eb68f44c545b604051908152602001610186565b61017a6101df3660046112f3565b61053d565b7f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb035460405160ff9091168152602001610186565b6101c36105f7565b61023361022e3660046112c9565b610673565b005b6101976040518060400160405280600c81526020017f312e302e302d626574612e31000000000000000000000000000000000000000081525081565b6101c361027f366004611331565b6387a211a2600c9081525f91909152602090205490565b6102336102a436600461134c565b61076b565b6101c36102b7366004611331565b6338377508600c9081525f91909152602090205490565b610197610923565b6102336102e43660046112c9565b610954565b61017a6102f73660046112c9565b610a40565b61023361030a366004611393565b610ab7565b7f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610186565b6102336103643660046112f3565b610c4a565b6101c36103773660046113fc565b602052637f5e9f20600c9081525f91909152603490205490565b61023361039f366004611507565b610ebf565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f0bc3227100000000000000000000000000000000000000000000000000000000148061043657507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60607f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00600101805461046d90611589565b80601f016020809104026020016040519081016040528092919081815260200182805461049990611589565b80156104e45780601f106104bb576101008083540402835291602001916104e4565b820191905f5260205f20905b8154815290600101906020018083116104c757829003601f168201915b5050505050905090565b5f82602052637f5e9f20600c52335f52816034600c2055815f52602c5160601c337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560205fa350600192915050565b5f8360601b33602052637f5e9f208117600c526034600c208054600181011561057b5780851115610575576313be252b5f526004601cfd5b84810382555b50506387a211a28117600c526020600c208054808511156105a35763f4d678b85f526004601cfd5b84810382555050835f526020600c208381540181555082602052600c5160601c8160601c7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a3505060019392505050565b5f8061060161043c565b8051906020012090506040517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81528160208201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6604082015246606082015230608082015260a081209250505090565b33734200000000000000000000000000000000000010146106c0576040517f38da3b1500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821661070d576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107178282611104565b8173ffffffffffffffffffffffffffffffffffffffff167f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d41213968858260405161075f91815260200190565b60405180910390a25050565b73ffffffffffffffffffffffffffffffffffffffff83166107b8576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107c23383611180565b6040805133602482015273ffffffffffffffffffffffffffffffffffffffff85166044820152606480820185905282518083039091018152608490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fd9f500460000000000000000000000000000000000000000000000000000000017905290517f7056f41f00000000000000000000000000000000000000000000000000000000815273420000000000000000000000000000000000002390637056f41f9061089c908590309086906004016115da565b5f604051808303815f87803b1580156108b3575f80fd5b505af11580156108c5573d5f803e3d5ffd5b5050604080518681526020810186905273ffffffffffffffffffffffffffffffffffffffff881693503392507ffcea3600a13c757f2758710b089cc9752781c35d2a9d6804370ed18cd82f0bb691015b60405180910390a350505050565b60607f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00600201805461046d90611589565b33734200000000000000000000000000000000000010146109a1576040517f38da3b1500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166109ee576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109f88282611180565b8173ffffffffffffffffffffffffffffffffffffffff167fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca58260405161075f91815260200190565b5f6387a211a2600c52335f526020600c20805480841115610a685763f4d678b85f526004601cfd5b83810382555050825f526020600c208281540181555081602052600c5160601c337fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a350600192915050565b5f610ac061043c565b80519060200120905084421115610ade57631a15a3cc5f526004601cfd5b6040518860601b60601c98508760601b60601c975065383775081901600e52885f526020600c2080547f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f83528360208401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6604084015246606084015230608084015260a08320602e527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c983528a60208401528960408401528860608401528060808401528760a084015260c08320604e526042602c205f528660ff16602052856040528460605260208060805f60015afa8b3d5114610be65763ddafbaef5f526004601cfd5b019055777f5e9f20000000000000000000000000000000000000000088176040526034602c2087905587897f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925602060608501a360405250505f606052505050505050565b73ffffffffffffffffffffffffffffffffffffffff8216610c97576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373420000000000000000000000000000000000002314610ce4576040517f065d515000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff1673420000000000000000000000000000000000002373ffffffffffffffffffffffffffffffffffffffff166338ffde186040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d58573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d7c9190611617565b73ffffffffffffffffffffffffffffffffffffffff1614610dc9576040517fbc22e2aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f73420000000000000000000000000000000000002373ffffffffffffffffffffffffffffffffffffffff1663247944626040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e27573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e4b9190611632565b9050610e578383611104565b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fc75e22a0b57fb7740dbfc0caa5c6b7a82a2139964e7f1b7be7ac4e8be0f719ba8484604051610915929190918252602082015260400190565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff165f81158015610f095750825b90505f8267ffffffffffffffff166001148015610f255750303b155b905081158015610f33575080155b15610f6a576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610fcb5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b7f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb0080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8b161781557f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb016110558a82611694565b50600281016110648982611694565b5060030180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff881617905583156110f45784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b505050565b6805345cdf77eb68f44c54818101818110156111275763e5cfe9575f526004601cfd5b806805345cdf77eb68f44c5550506387a211a2600c52815f526020600c208181540181555080602052600c5160601c5f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a35050565b6387a211a2600c52815f526020600c208054808311156111a75763f4d678b85f526004601cfd5b82900390556805345cdf77eb68f44c805482900390555f81815273ffffffffffffffffffffffffffffffffffffffff83167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602083a35050565b5f60208284031215611211575f80fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114611240575f80fd5b9392505050565b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081525f6112406020830184611247565b73ffffffffffffffffffffffffffffffffffffffff811681146112c6575f80fd5b50565b5f80604083850312156112da575f80fd5b82356112e5816112a5565b946020939093013593505050565b5f805f60608486031215611305575f80fd5b8335611310816112a5565b92506020840135611320816112a5565b929592945050506040919091013590565b5f60208284031215611341575f80fd5b8135611240816112a5565b5f805f6060848603121561135e575f80fd5b8335611369816112a5565b95602085013595506040909401359392505050565b803560ff8116811461138e575f80fd5b919050565b5f805f805f805f60e0888a0312156113a9575f80fd5b87356113b4816112a5565b965060208801356113c4816112a5565b955060408801359450606088013593506113e06080890161137e565b925060a0880135915060c0880135905092959891949750929550565b5f806040838503121561140d575f80fd5b8235611418816112a5565b91506020830135611428816112a5565b809150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f82601f83011261146f575f80fd5b813567ffffffffffffffff8082111561148a5761148a611433565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156114d0576114d0611433565b816040528381528660208588010111156114e8575f80fd5b836020870160208301375f602085830101528094505050505092915050565b5f805f806080858703121561151a575f80fd5b8435611525816112a5565b9350602085013567ffffffffffffffff80821115611541575f80fd5b61154d88838901611460565b94506040870135915080821115611562575f80fd5b5061156f87828801611460565b92505061157e6060860161137e565b905092959194509250565b600181811c9082168061159d57607f821691505b6020821081036115d4577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b83815273ffffffffffffffffffffffffffffffffffffffff83166020820152606060408201525f61160e6060830184611247565b95945050505050565b5f60208284031215611627575f80fd5b8151611240816112a5565b5f60208284031215611642575f80fd5b5051919050565b601f8211156110ff57805f5260205f20601f840160051c8101602085101561166e5750805b601f840160051c820191505b8181101561168d575f815560010161167a565b5050505050565b815167ffffffffffffffff8111156116ae576116ae611433565b6116c2816116bc8454611589565b84611649565b602080601f831160018114611714575f84156116de5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556117a8565b5f858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561176057888601518255948401946001909101908401611741565b508582101561179c57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b505060018460011b0185555b50505050505056fea164736f6c6343000819000a", + "initialized": true, + "kind": "Create", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [ + { + "account": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + }, + { + "account": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + }, + { + "account": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + }, + { + "account": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", + "isWrite": true, + "newValue": "0x000000000000000000000000000000000000000000000000ffffffffffffffff", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + } + ], + "value": 0 + }, + { + "accessor": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0x60806040526040516103ae3803806103ae8339810160408190526100229161023c565b61002c8282610033565b505061031b565b61003c82610091565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561008557610080828261010c565b505050565b61008d61017f565b5050565b806001600160a01b03163b5f036100cb57604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516101289190610305565b5f60405180830381855af49150503d805f8114610160576040519150601f19603f3d011682016040523d82523d5f602084013e610165565b606091505b5090925090506101768583836101a0565b95945050505050565b341561019e5760405163b398979f60e01b815260040160405180910390fd5b565b6060826101b5576101b0826101ff565b6101f8565b81511580156101cc57506001600160a01b0384163b155b156101f557604051639996b31560e01b81526001600160a01b03851660048201526024016100c2565b50805b9392505050565b80511561020f5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561024d575f80fd5b82516001600160a01b0381168114610263575f80fd5b60208401519092506001600160401b038082111561027f575f80fd5b818501915085601f830112610292575f80fd5b8151818111156102a4576102a4610228565b604051601f8201601f19908116603f011681019083821181831017156102cc576102cc610228565b816040528281528860208487010111156102e4575f80fd5b8260208601602083015e5f6020848301015280955050505050509250929050565b5f82518060208501845e5f920191825250919050565b6087806103275f395ff3fe6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a0000000000000000000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000104f6d2ee86000000000000000000000000237a66474b7b934b22574359500212977b656d9f000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e455243323000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005535550455200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "deployedCode": "0x6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a", + "initialized": true, + "kind": "Create", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [], + "value": 0 + }, + { + "accessor": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "account": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0x", + "deployedCode": "0x", + "initialized": true, + "kind": "Resume", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [], + "value": 0 + }, + { + "accessor": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0x", + "deployedCode": "0x", + "initialized": true, + "kind": "Resume", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [ + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc" + }, + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": true, + "newValue": "0x0000000000000000000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc" + } + ], + "value": 0 + }, + { + "accessor": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "account": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0xf6d2ee86000000000000000000000000237a66474b7b934b22574359500212977b656d9f000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e4552433230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055355504552000000000000000000000000000000000000000000000000000000", + "deployedCode": "0x", + "initialized": true, + "kind": "DelegateCall", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [ + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + }, + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + }, + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": true, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000001", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + }, + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000001", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000001", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + }, + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": true, + "newValue": "0x0000000000000000000000000000000000000000000000010000000000000001", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000001", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + }, + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00" + }, + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": true, + "newValue": "0x000000000000000000000000237a66474b7b934b22574359500212977b656d9f", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00" + }, + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb01" + }, + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": true, + "newValue": "0x5375706572636861696e4552433230000000000000000000000000000000001e", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb01" + }, + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb02" + }, + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": true, + "newValue": "0x535550455200000000000000000000000000000000000000000000000000000a", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb02" + }, + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb03" + }, + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": true, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000012", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb03" + }, + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000010000000000000001", + "previousValue": "0x0000000000000000000000000000000000000000000000010000000000000001", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + }, + { + "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "isWrite": true, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000001", + "previousValue": "0x0000000000000000000000000000000000000000000000010000000000000001", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + } + ], + "value": 0 + }, + { + "accessor": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", + "account": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0x", + "deployedCode": "0x", + "initialized": true, + "kind": "Resume", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [], + "value": 0 + }, + { + "accessor": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0x60806040526040516103ae3803806103ae8339810160408190526100229161023c565b61002c8282610033565b505061031b565b61003c82610091565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561008557610080828261010c565b505050565b61008d61017f565b5050565b806001600160a01b03163b5f036100cb57604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516101289190610305565b5f60405180830381855af49150503d805f8114610160576040519150601f19603f3d011682016040523d82523d5f602084013e610165565b606091505b5090925090506101768583836101a0565b95945050505050565b341561019e5760405163b398979f60e01b815260040160405180910390fd5b565b6060826101b5576101b0826101ff565b6101f8565b81511580156101cc57506001600160a01b0384163b155b156101f557604051639996b31560e01b81526001600160a01b03851660048201526024016100c2565b50805b9392505050565b80511561020f5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561024d575f80fd5b82516001600160a01b0381168114610263575f80fd5b60208401519092506001600160401b038082111561027f575f80fd5b818501915085601f830112610292575f80fd5b8151818111156102a4576102a4610228565b604051601f8201601f19908116603f011681019083821181831017156102cc576102cc610228565b816040528281528860208487010111156102e4575f80fd5b8260208601602083015e5f6020848301015280955050505050509250929050565b5f82518060208501845e5f920191825250919050565b6087806103275f395ff3fe6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a0000000000000000000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000104f6d2ee86000000000000000000000000237a66474b7b934b22574359500212977b656d9f000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e455243323000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005535550455200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "deployedCode": "0x6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a", + "initialized": true, + "kind": "Create", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [], + "value": 0 + }, + { + "accessor": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "account": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0x", + "deployedCode": "0x", + "initialized": true, + "kind": "Resume", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [], + "value": 0 + }, + { + "accessor": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0x", + "deployedCode": "0x", + "initialized": true, + "kind": "Resume", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [ + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc" + }, + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": true, + "newValue": "0x0000000000000000000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc" + } + ], + "value": 0 + }, + { + "accessor": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "account": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0xf6d2ee86000000000000000000000000237a66474b7b934b22574359500212977b656d9f000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e4552433230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055355504552000000000000000000000000000000000000000000000000000000", + "deployedCode": "0x", + "initialized": true, + "kind": "DelegateCall", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [ + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + }, + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + }, + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": true, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000001", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + }, + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000001", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000001", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + }, + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": true, + "newValue": "0x0000000000000000000000000000000000000000000000010000000000000001", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000001", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + }, + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00" + }, + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": true, + "newValue": "0x000000000000000000000000237a66474b7b934b22574359500212977b656d9f", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00" + }, + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb01" + }, + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": true, + "newValue": "0x5375706572636861696e4552433230000000000000000000000000000000001e", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb01" + }, + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb02" + }, + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": true, + "newValue": "0x535550455200000000000000000000000000000000000000000000000000000a", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb02" + }, + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb03" + }, + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": true, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000012", + "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", + "reverted": false, + "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb03" + }, + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": false, + "newValue": "0x0000000000000000000000000000000000000000000000010000000000000001", + "previousValue": "0x0000000000000000000000000000000000000000000000010000000000000001", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + }, + { + "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "isWrite": true, + "newValue": "0x0000000000000000000000000000000000000000000000000000000000000001", + "previousValue": "0x0000000000000000000000000000000000000000000000010000000000000001", + "reverted": false, + "slot": "0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00" + } + ], + "value": 0 + }, + { + "accessor": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", + "account": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0x", + "deployedCode": "0x", + "initialized": true, + "kind": "Resume", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [], + "value": 0 + }, + { + "accessor": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "account": "0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0x610100604052348015610010575f80fd5b5060405161070538038061070583398101604081905261002f9161006a565b6001600160a01b0393841660805291831660a05260c0521660e0526100b4565b80516001600160a01b0381168114610065575f80fd5b919050565b5f805f806080858703121561007d575f80fd5b6100868561004f565b93506100946020860161004f565b9250604085015191506100a96060860161004f565b905092959194509250565b60805160a05160c05160e0516106036101025f395f6102ab01525f818160a901526102cf01525f818161025d0152818161028601526102fa01525f81816101fb015261022401526106035ff3fe608060405260043610610058575f3560e01c80632ea02369116100415780632ea023691461009857806338ffde18146100cb5780637056f41f14610104575f80fd5b80631ecd26f21461005c5780632479446214610071575b5f80fd5b61006f61006a366004610484565b610117565b005b34801561007c575f80fd5b506100856101d2565b6040519081526020015b60405180910390f35b3480156100a3575f80fd5b506100857f000000000000000000000000000000000000000000000000000000000000000081565b3480156100d6575f80fd5b506100df6101e3565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161008f565b61006f610112366004610500565b6102cd565b5f808473ffffffffffffffffffffffffffffffffffffffff16348585604051610141929190610556565b5f6040518083038185875af1925050503d805f811461017b576040519150601f19603f3d011682016040523d82523d5f602084013e610180565b606091505b5091509150816101c757806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101be9190610565565b60405180910390fd5b505050505050505050565b5f6101de4660016105b8565b905090565b5f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016330361024657507f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036102a857507f000000000000000000000000000000000000000000000000000000000000000090565b507f000000000000000000000000000000000000000000000000000000000000000090565b7f000000000000000000000000000000000000000000000000000000000000000084036103e6575f6103557f00000000000000000000000000000000000000000000000000000000000000005f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506103ec92505050565b9050806103e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c65640000000000000000000000000000000000000000000000000060648201526084016101be565b505b50505050565b5f6103f9845a8585610401565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461043a575f80fd5b919050565b5f8083601f84011261044f575f80fd5b50813567ffffffffffffffff811115610466575f80fd5b60208301915083602082850101111561047d575f80fd5b9250929050565b5f805f805f805f60c0888a03121561049a575f80fd5b8735965060208801359550604088013594506104b860608901610417565b93506104c660808901610417565b925060a088013567ffffffffffffffff8111156104e1575f80fd5b6104ed8a828b0161043f565b989b979a50959850939692959293505050565b5f805f8060608587031215610513575f80fd5b8435935061052360208601610417565b9250604085013567ffffffffffffffff81111561053e575f80fd5b61054a8782880161043f565b95989497509550505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b808201808211156105f0577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9291505056fea164736f6c6343000819000a0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000", + "deployedCode": "0x608060405260043610610058575f3560e01c80632ea02369116100415780632ea023691461009857806338ffde18146100cb5780637056f41f14610104575f80fd5b80631ecd26f21461005c5780632479446214610071575b5f80fd5b61006f61006a366004610484565b610117565b005b34801561007c575f80fd5b506100856101d2565b6040519081526020015b60405180910390f35b3480156100a3575f80fd5b506100857f000000000000000000000000000000000000000000000000000000000000000281565b3480156100d6575f80fd5b506100df6101e3565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161008f565b61006f610112366004610500565b6102cd565b5f808473ffffffffffffffffffffffffffffffffffffffff16348585604051610141929190610556565b5f6040518083038185875af1925050503d805f811461017b576040519150601f19603f3d011682016040523d82523d5f602084013e610180565b606091505b5091509150816101c757806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101be9190610565565b60405180910390fd5b505050505050505050565b5f6101de4660016105b8565b905090565b5f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b16330361024657507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b90565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a1633036102a857507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a90565b507f000000000000000000000000000000000000000000000000000000000000000090565b7f000000000000000000000000000000000000000000000000000000000000000284036103e6575f6103557f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506103ec92505050565b9050806103e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c65640000000000000000000000000000000000000000000000000060648201526084016101be565b505b50505050565b5f6103f9845a8585610401565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461043a575f80fd5b919050565b5f8083601f84011261044f575f80fd5b50813567ffffffffffffffff811115610466575f80fd5b60208301915083602082850101111561047d575f80fd5b9250929050565b5f805f805f805f60c0888a03121561049a575f80fd5b8735965060208801359550604088013594506104b860608901610417565b93506104c660808901610417565b925060a088013567ffffffffffffffff8111156104e1575f80fd5b6104ed8a828b0161043f565b989b979a50959850939692959293505050565b5f805f8060608587031215610513575f80fd5b8435935061052360208601610417565b9250604085013567ffffffffffffffff81111561053e575f80fd5b61054a8782880161043f565b95989497509550505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b808201808211156105f0577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9291505056fea164736f6c6343000819000a", + "initialized": true, + "kind": "Create", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [], + "value": 0 + }, + { + "accessor": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "account": "0x7109709ECfa91a80626fF3989D68f67F5b1DD12D", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0x", + "deployedCode": "0x", + "initialized": true, + "kind": "Resume", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [], + "value": 0 + }, + { + "accessor": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "account": "0x7109709ECfa91a80626fF3989D68f67F5b1DD12D", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0x", + "deployedCode": "0x", + "initialized": true, + "kind": "Resume", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [], + "value": 0 + }, + { + "accessor": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "account": "0x7109709ECfa91a80626fF3989D68f67F5b1DD12D", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0x", + "deployedCode": "0x", + "initialized": true, + "kind": "Resume", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [], + "value": 0 + }, + { + "accessor": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "account": "0x7109709ECfa91a80626fF3989D68f67F5b1DD12D", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0x", + "deployedCode": "0x", + "initialized": true, + "kind": "Resume", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [], + "value": 0 + } + ] +} \ No newline at end of file diff --git a/packages/contracts-bedrock/test/properties/kontrol/KontrolBase.sol b/packages/contracts-bedrock/test/properties/kontrol/KontrolBase.sol new file mode 100644 index 000000000000..283c4e639113 --- /dev/null +++ b/packages/contracts-bedrock/test/properties/kontrol/KontrolBase.sol @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.25; + +import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; +import { Predeploys } from "src/libraries/Predeploys.sol"; +import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol"; +import { MockL2ToL2Messenger } from "test/properties/halmos/MockL2ToL2Messenger.sol"; +import { KontrolCheats } from "kontrol-cheatcodes/KontrolCheats.sol"; +import { RecordStateDiff } from "./helpers/RecordStateDiff.sol"; +import { Test } from "forge-std/Test.sol"; + +contract KontrolBase is Test, KontrolCheats, RecordStateDiff { + uint256 internal constant CURRENT_CHAIN_ID = 1; + uint256 internal constant DESTINATION_CHAIN_ID = 2; + uint256 internal constant ZERO_AMOUNT = 0; + + MockL2ToL2Messenger internal constant MESSENGER = MockL2ToL2Messenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); + + address internal remoteToken = address(bytes20(keccak256("remoteToken"))); + string internal name = "SuperchainERC20"; + string internal symbol = "SUPER"; + uint8 internal decimals = 18; + + OptimismSuperchainERC20 public superchainERC20Impl; + OptimismSuperchainERC20 internal sourceToken; + OptimismSuperchainERC20 internal destToken; + MockL2ToL2Messenger internal mockL2ToL2Messenger; + + // The second function to get the state diff saving the addresses with their names + function setUpNamed() public virtual recordStateDiff { + // Deploy the OptimismSuperchainERC20 contract implementation and the proxy to be used + superchainERC20Impl = new OptimismSuperchainERC20(); + sourceToken = OptimismSuperchainERC20( + address( + // TODO: Update to beacon proxy + new ERC1967Proxy( + address(superchainERC20Impl), + abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, name, symbol, decimals)) + ) + ) + ); + + destToken = OptimismSuperchainERC20( + address( + // TODO: Update to beacon proxy + new ERC1967Proxy( + address(superchainERC20Impl), + abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, name, symbol, decimals)) + ) + ) + ); + + mockL2ToL2Messenger = + new MockL2ToL2Messenger(address(sourceToken), address(destToken), DESTINATION_CHAIN_ID, address(0)); + + save_address(address(superchainERC20Impl), "superchainERC20Impl"); + save_address(address(sourceToken), "sourceToken"); + save_address(address(destToken), "destToken"); + save_address(address(mockL2ToL2Messenger), "mockL2ToL2Messenger"); + } + + function eqStrings(string memory a, string memory b) internal pure returns (bool) { + return keccak256(abi.encode(a)) == keccak256(abi.encode(b)); + } +} diff --git a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchain.k.sol b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol similarity index 82% rename from packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchain.k.sol rename to packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol index 94116cf536e2..ca73d5586123 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchain.k.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol @@ -3,52 +3,22 @@ pragma solidity 0.8.25; import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol"; import { MockL2ToL2Messenger } from "test/properties/halmos/MockL2ToL2Messenger.sol"; -import { HalmosBase } from "test/properties/helpers/HalmosBase.sol"; -import { KontrolCheats } from "kontrol-cheatcodes/KontrolCheats.sol"; - -contract OptimismSuperchainERC20Kontrol is KontrolCheats, HalmosBase { - MockL2ToL2Messenger internal constant MESSENGER = MockL2ToL2Messenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); - - OptimismSuperchainERC20 public superchainERC20Impl; - OptimismSuperchainERC20 internal sourceToken; - OptimismSuperchainERC20 internal destToken; - - function setUp() public { - // Deploy the OptimismSuperchainERC20 contract implementation and the proxy to be used - superchainERC20Impl = new OptimismSuperchainERC20(); - sourceToken = OptimismSuperchainERC20( - address( - // TODO: Update to beacon proxy - new ERC1967Proxy( - address(superchainERC20Impl), - abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, name, symbol, decimals)) - ) - ) - ); - - destToken = OptimismSuperchainERC20( - address( - // TODO: Update to beacon proxy - new ERC1967Proxy( - address(superchainERC20Impl), - abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, name, symbol, decimals)) - ) - ) - ); - - // Etch the mocked L2 to L2 Messenger since the messenger logic is out of scope for these test suite. Also, we - // avoid issues such as `TSTORE` opcode not being supported, or issues with `encodeVersionedNonce()` - address _mockL2ToL2CrossDomainMessenger = - address(new MockL2ToL2Messenger(address(sourceToken), address(destToken), DESTINATION_CHAIN_ID, address(0))); - vm.etch(address(MESSENGER), _mockL2ToL2CrossDomainMessenger.code); - // NOTE: We need to set the crossDomainMessageSender as an immutable or otherwise storage vars and not taken - // into account when etching on halmos. Setting a constant slot with setters and getters didn't work neither. +import { KontrolBase } from "test/properties/kontrol/KontrolBase.sol"; +import { InitialState } from "./deployments/InitialState.sol"; + +contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { + function setUpInlined() public { + superchainERC20Impl = OptimismSuperchainERC20(superchainERC20ImplAddress); + sourceToken = OptimismSuperchainERC20(sourceTokenAddress); + destToken = OptimismSuperchainERC20(destTokenAddress); + vm.etch(address(MESSENGER), mockL2ToL2MessengerAddress.code); } /// @notice Check setup works as expected function prove_setup() public { + setUpInlined(); + // Source token assert(sourceToken.remoteToken() == remoteToken); assert(eqStrings(sourceToken.name(), name)); @@ -68,6 +38,8 @@ contract OptimismSuperchainERC20Kontrol is KontrolCheats, HalmosBase { // Custom cross domain sender assert(MESSENGER.crossDomainMessageSender() == address(0)); + + assert(block.chainid >= 0); } /// @custom:property-id 6 @@ -81,10 +53,15 @@ contract OptimismSuperchainERC20Kontrol is KontrolCheats, HalmosBase { ) public { + setUpInlined(); + /* Preconditions */ vm.assume(_to != address(0)); vm.assume(_from != address(0)); + notBuiltinAddress(_from); + notBuiltinAddress(_to); + // Can't deal to unsupported cheatcode vm.prank(Predeploys.L2_STANDARD_BRIDGE); sourceToken.mint(_from, _initialBalance); @@ -136,6 +113,9 @@ contract OptimismSuperchainERC20Kontrol is KontrolCheats, HalmosBase { vm.assume(_to != address(0)); vm.assume(_to != address(Predeploys.CROSS_L2_INBOX) && _to != address(MESSENGER)); + notBuiltinAddress(_from); + notBuiltinAddress(_to); + uint256 _totalSupplyBefore = sourceToken.totalSupply(); uint256 _fromBalanceBefore = sourceToken.balanceOf(_from); uint256 _toBalanceBefore = sourceToken.balanceOf(_to); diff --git a/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialState.sol b/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialState.sol new file mode 100644 index 000000000000..cb47c362f4f6 --- /dev/null +++ b/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialState.sol @@ -0,0 +1,90 @@ +// SPDX-License-Identifier: UNLICENSED +// This file was autogenerated by running `kontrol load-state`. Do not edit this file manually. + +pragma solidity ^0.8.13; + +import { Vm } from "forge-std/Vm.sol"; + +import { InitialStateCode } from "./InitialStateCode.sol"; + +contract InitialState is InitialStateCode { + // Test contract address, 0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496 + address private constant FOUNDRY_TEST_ADDRESS = 0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496; + // Cheat code address, 0x7109709ECfa91a80626fF3989D68f67F5b1DD12D + address private constant VM_ADDRESS = address(uint160(uint256(keccak256("hevm cheat code")))); + Vm private constant vm = Vm(VM_ADDRESS); + + address internal constant sourceTokenAddress = 0x2e234DAe75C793f67A35089C9d99245E1C58470b; + address internal constant superchainERC20ImplAddress = 0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f; + address internal constant mockL2ToL2MessengerAddress = 0x5991A2dF15A8F6A256D3Ec51E99254Cd3fb576A9; + address internal constant destTokenAddress = 0xF62849F9A0B5Bf2913b396098F7c7019b51A820a; + + function recreateState() public { + bytes32 slot; + bytes32 value; + vm.etch(superchainERC20ImplAddress, superchainERC20ImplCode); + slot = hex"f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00"; + value = hex"000000000000000000000000000000000000000000000000ffffffffffffffff"; + vm.store(superchainERC20ImplAddress, slot, value); + vm.etch(sourceTokenAddress, sourceTokenCode); + slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; + value = hex"0000000000000000000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f"; + vm.store(sourceTokenAddress, slot, value); + slot = hex"f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00"; + value = hex"0000000000000000000000000000000000000000000000000000000000000001"; + vm.store(sourceTokenAddress, slot, value); + slot = hex"f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00"; + value = hex"0000000000000000000000000000000000000000000000010000000000000001"; + vm.store(sourceTokenAddress, slot, value); + slot = hex"07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00"; + value = hex"000000000000000000000000237a66474b7b934b22574359500212977b656d9f"; + vm.store(sourceTokenAddress, slot, value); + slot = hex"07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb01"; + value = hex"5375706572636861696e4552433230000000000000000000000000000000001e"; + vm.store(sourceTokenAddress, slot, value); + slot = hex"07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb02"; + value = hex"535550455200000000000000000000000000000000000000000000000000000a"; + vm.store(sourceTokenAddress, slot, value); + slot = hex"07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb03"; + value = hex"0000000000000000000000000000000000000000000000000000000000000012"; + vm.store(sourceTokenAddress, slot, value); + slot = hex"f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00"; + value = hex"0000000000000000000000000000000000000000000000000000000000000001"; + vm.store(sourceTokenAddress, slot, value); + vm.etch(destTokenAddress, destTokenCode); + slot = hex"360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc"; + value = hex"0000000000000000000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f"; + vm.store(destTokenAddress, slot, value); + slot = hex"f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00"; + value = hex"0000000000000000000000000000000000000000000000000000000000000001"; + vm.store(destTokenAddress, slot, value); + slot = hex"f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00"; + value = hex"0000000000000000000000000000000000000000000000010000000000000001"; + vm.store(destTokenAddress, slot, value); + slot = hex"07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00"; + value = hex"000000000000000000000000237a66474b7b934b22574359500212977b656d9f"; + vm.store(destTokenAddress, slot, value); + slot = hex"07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb01"; + value = hex"5375706572636861696e4552433230000000000000000000000000000000001e"; + vm.store(destTokenAddress, slot, value); + slot = hex"07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb02"; + value = hex"535550455200000000000000000000000000000000000000000000000000000a"; + vm.store(destTokenAddress, slot, value); + slot = hex"07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb03"; + value = hex"0000000000000000000000000000000000000000000000000000000000000012"; + vm.store(destTokenAddress, slot, value); + slot = hex"f0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00"; + value = hex"0000000000000000000000000000000000000000000000000000000000000001"; + vm.store(destTokenAddress, slot, value); + vm.etch(mockL2ToL2MessengerAddress, mockL2ToL2MessengerCode); + } + + function _notExternalAddress(address user) public pure { + vm.assume(user != FOUNDRY_TEST_ADDRESS); + vm.assume(user != VM_ADDRESS); + vm.assume(user != sourceTokenAddress); + vm.assume(user != superchainERC20ImplAddress); + vm.assume(user != mockL2ToL2MessengerAddress); + vm.assume(user != destTokenAddress); + } +} diff --git a/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol b/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol new file mode 100644 index 000000000000..e4c80facdabf --- /dev/null +++ b/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: UNLICENSED +// This file was autogenerated by running `kontrol load-state`. Do not edit this file manually. + +pragma solidity ^0.8.13; + +contract InitialStateCode { + bytes internal constant superchainERC20ImplCode = + hex"608060405234801561000f575f80fd5b5060043610610163575f3560e01c806378a3727b116100c7578063d505accf1161007d578063d9f5004611610063578063d9f5004614610356578063dd62ed3e14610369578063f6d2ee8614610391575f80fd5b8063d505accf146102fc578063d6c0b2c41461030f575f80fd5b806395d89b41116100ad57806395d89b41146102ce5780639dc29fac146102d6578063a9059cbb146102e9575f80fd5b806378a3727b146102965780637ecebe00146102a9575f80fd5b8063313ce5671161011c57806340c10f191161010257806340c10f191461022057806354fd4d501461023557806370a0823114610271575f80fd5b8063313ce567146101e45780633644e51514610218575f80fd5b8063095ea7b31161014c578063095ea7b3146101a457806318160ddd146101b757806323b872dd146101d1575f80fd5b806301ffc9a71461016757806306fdde031461018f575b5f80fd5b61017a610175366004611201565b6103a4565b60405190151581526020015b60405180910390f35b61019761043c565b6040516101869190611293565b61017a6101b23660046112c9565b6104ee565b6805345cdf77eb68f44c545b604051908152602001610186565b61017a6101df3660046112f3565b61053d565b7f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb035460405160ff9091168152602001610186565b6101c36105f7565b61023361022e3660046112c9565b610673565b005b6101976040518060400160405280600c81526020017f312e302e302d626574612e31000000000000000000000000000000000000000081525081565b6101c361027f366004611331565b6387a211a2600c9081525f91909152602090205490565b6102336102a436600461134c565b61076b565b6101c36102b7366004611331565b6338377508600c9081525f91909152602090205490565b610197610923565b6102336102e43660046112c9565b610954565b61017a6102f73660046112c9565b610a40565b61023361030a366004611393565b610ab7565b7f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb005460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610186565b6102336103643660046112f3565b610c4a565b6101c36103773660046113fc565b602052637f5e9f20600c9081525f91909152603490205490565b61023361039f366004611507565b610ebf565b5f7fffffffff0000000000000000000000000000000000000000000000000000000082167f0bc3227100000000000000000000000000000000000000000000000000000000148061043657507f01ffc9a7000000000000000000000000000000000000000000000000000000007fffffffff000000000000000000000000000000000000000000000000000000008316145b92915050565b60607f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00600101805461046d90611589565b80601f016020809104026020016040519081016040528092919081815260200182805461049990611589565b80156104e45780601f106104bb576101008083540402835291602001916104e4565b820191905f5260205f20905b8154815290600101906020018083116104c757829003601f168201915b5050505050905090565b5f82602052637f5e9f20600c52335f52816034600c2055815f52602c5160601c337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560205fa350600192915050565b5f8360601b33602052637f5e9f208117600c526034600c208054600181011561057b5780851115610575576313be252b5f526004601cfd5b84810382555b50506387a211a28117600c526020600c208054808511156105a35763f4d678b85f526004601cfd5b84810382555050835f526020600c208381540181555082602052600c5160601c8160601c7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a3505060019392505050565b5f8061060161043c565b8051906020012090506040517f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f81528160208201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6604082015246606082015230608082015260a081209250505090565b33734200000000000000000000000000000000000010146106c0576040517f38da3b1500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff821661070d576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107178282611104565b8173ffffffffffffffffffffffffffffffffffffffff167f0f6798a560793a54c3bcfe86a93cde1e73087d944c0ea20544137d41213968858260405161075f91815260200190565b60405180910390a25050565b73ffffffffffffffffffffffffffffffffffffffff83166107b8576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6107c23383611180565b6040805133602482015273ffffffffffffffffffffffffffffffffffffffff85166044820152606480820185905282518083039091018152608490910182526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fd9f500460000000000000000000000000000000000000000000000000000000017905290517f7056f41f00000000000000000000000000000000000000000000000000000000815273420000000000000000000000000000000000002390637056f41f9061089c908590309086906004016115da565b5f604051808303815f87803b1580156108b3575f80fd5b505af11580156108c5573d5f803e3d5ffd5b5050604080518681526020810186905273ffffffffffffffffffffffffffffffffffffffff881693503392507ffcea3600a13c757f2758710b089cc9752781c35d2a9d6804370ed18cd82f0bb691015b60405180910390a350505050565b60607f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00600201805461046d90611589565b33734200000000000000000000000000000000000010146109a1576040517f38da3b1500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff82166109ee576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109f88282611180565b8173ffffffffffffffffffffffffffffffffffffffff167fcc16f5dbb4873280815c1ee09dbd06736cffcc184412cf7a71a0fdb75d397ca58260405161075f91815260200190565b5f6387a211a2600c52335f526020600c20805480841115610a685763f4d678b85f526004601cfd5b83810382555050825f526020600c208281540181555081602052600c5160601c337fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a350600192915050565b5f610ac061043c565b80519060200120905084421115610ade57631a15a3cc5f526004601cfd5b6040518860601b60601c98508760601b60601c975065383775081901600e52885f526020600c2080547f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f83528360208401527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6604084015246606084015230608084015260a08320602e527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c983528a60208401528960408401528860608401528060808401528760a084015260c08320604e526042602c205f528660ff16602052856040528460605260208060805f60015afa8b3d5114610be65763ddafbaef5f526004601cfd5b019055777f5e9f20000000000000000000000000000000000000000088176040526034602c2087905587897f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925602060608501a360405250505f606052505050505050565b73ffffffffffffffffffffffffffffffffffffffff8216610c97576040517fd92e233d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3373420000000000000000000000000000000000002314610ce4576040517f065d515000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff1673420000000000000000000000000000000000002373ffffffffffffffffffffffffffffffffffffffff166338ffde186040518163ffffffff1660e01b8152600401602060405180830381865afa158015610d58573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d7c9190611617565b73ffffffffffffffffffffffffffffffffffffffff1614610dc9576040517fbc22e2aa00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5f73420000000000000000000000000000000000002373ffffffffffffffffffffffffffffffffffffffff1663247944626040518163ffffffff1660e01b8152600401602060405180830381865afa158015610e27573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e4b9190611632565b9050610e578383611104565b8273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fc75e22a0b57fb7740dbfc0caa5c6b7a82a2139964e7f1b7be7ac4e8be0f719ba8484604051610915929190918252602082015260400190565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff16159067ffffffffffffffff165f81158015610f095750825b90505f8267ffffffffffffffff166001148015610f255750303b155b905081158015610f33575080155b15610f6a576040517ff92ee8a900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b84547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001660011785558315610fcb5784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff16680100000000000000001785555b7f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb0080547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8b161781557f07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb016110558a82611694565b50600281016110648982611694565b5060030180547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff881617905583156110f45784547fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b505050505050505050565b505050565b6805345cdf77eb68f44c54818101818110156111275763e5cfe9575f526004601cfd5b806805345cdf77eb68f44c5550506387a211a2600c52815f526020600c208181540181555080602052600c5160601c5f7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a35050565b6387a211a2600c52815f526020600c208054808311156111a75763f4d678b85f526004601cfd5b82900390556805345cdf77eb68f44c805482900390555f81815273ffffffffffffffffffffffffffffffffffffffff83167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602083a35050565b5f60208284031215611211575f80fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114611240575f80fd5b9392505050565b5f81518084528060208401602086015e5f6020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081525f6112406020830184611247565b73ffffffffffffffffffffffffffffffffffffffff811681146112c6575f80fd5b50565b5f80604083850312156112da575f80fd5b82356112e5816112a5565b946020939093013593505050565b5f805f60608486031215611305575f80fd5b8335611310816112a5565b92506020840135611320816112a5565b929592945050506040919091013590565b5f60208284031215611341575f80fd5b8135611240816112a5565b5f805f6060848603121561135e575f80fd5b8335611369816112a5565b95602085013595506040909401359392505050565b803560ff8116811461138e575f80fd5b919050565b5f805f805f805f60e0888a0312156113a9575f80fd5b87356113b4816112a5565b965060208801356113c4816112a5565b955060408801359450606088013593506113e06080890161137e565b925060a0880135915060c0880135905092959891949750929550565b5f806040838503121561140d575f80fd5b8235611418816112a5565b91506020830135611428816112a5565b809150509250929050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b5f82601f83011261146f575f80fd5b813567ffffffffffffffff8082111561148a5761148a611433565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f011681019082821181831017156114d0576114d0611433565b816040528381528660208588010111156114e8575f80fd5b836020870160208301375f602085830101528094505050505092915050565b5f805f806080858703121561151a575f80fd5b8435611525816112a5565b9350602085013567ffffffffffffffff80821115611541575f80fd5b61154d88838901611460565b94506040870135915080821115611562575f80fd5b5061156f87828801611460565b92505061157e6060860161137e565b905092959194509250565b600181811c9082168061159d57607f821691505b6020821081036115d4577f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b50919050565b83815273ffffffffffffffffffffffffffffffffffffffff83166020820152606060408201525f61160e6060830184611247565b95945050505050565b5f60208284031215611627575f80fd5b8151611240816112a5565b5f60208284031215611642575f80fd5b5051919050565b601f8211156110ff57805f5260205f20601f840160051c8101602085101561166e5750805b601f840160051c820191505b8181101561168d575f815560010161167a565b5050505050565b815167ffffffffffffffff8111156116ae576116ae611433565b6116c2816116bc8454611589565b84611649565b602080601f831160018114611714575f84156116de5750858301515b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600386901b1c1916600185901b1785556117a8565b5f858152602081207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08616915b8281101561176057888601518255948401946001909101908401611741565b508582101561179c57878501517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff600388901b60f8161c191681555b505060018460011b0185555b50505050505056fea164736f6c6343000819000a"; + bytes internal constant sourceTokenCode = + hex"6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a"; + bytes internal constant destTokenCode = + hex"6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a"; + bytes internal constant mockL2ToL2MessengerCode = + hex"608060405260043610610058575f3560e01c80632ea02369116100415780632ea023691461009857806338ffde18146100cb5780637056f41f14610104575f80fd5b80631ecd26f21461005c5780632479446214610071575b5f80fd5b61006f61006a366004610484565b610117565b005b34801561007c575f80fd5b506100856101d2565b6040519081526020015b60405180910390f35b3480156100a3575f80fd5b506100857f000000000000000000000000000000000000000000000000000000000000000281565b3480156100d6575f80fd5b506100df6101e3565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161008f565b61006f610112366004610500565b6102cd565b5f808473ffffffffffffffffffffffffffffffffffffffff16348585604051610141929190610556565b5f6040518083038185875af1925050503d805f811461017b576040519150601f19603f3d011682016040523d82523d5f602084013e610180565b606091505b5091509150816101c757806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101be9190610565565b60405180910390fd5b505050505050505050565b5f6101de4660016105b8565b905090565b5f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b16330361024657507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b90565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a1633036102a857507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a90565b507f000000000000000000000000000000000000000000000000000000000000000090565b7f000000000000000000000000000000000000000000000000000000000000000284036103e6575f6103557f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506103ec92505050565b9050806103e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c65640000000000000000000000000000000000000000000000000060648201526084016101be565b505b50505050565b5f6103f9845a8585610401565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461043a575f80fd5b919050565b5f8083601f84011261044f575f80fd5b50813567ffffffffffffffff811115610466575f80fd5b60208301915083602082850101111561047d575f80fd5b9250929050565b5f805f805f805f60c0888a03121561049a575f80fd5b8735965060208801359550604088013594506104b860608901610417565b93506104c660808901610417565b925060a088013567ffffffffffffffff8111156104e1575f80fd5b6104ed8a828b0161043f565b989b979a50959850939692959293505050565b5f805f8060608587031215610513575f80fd5b8435935061052360208601610417565b9250604085013567ffffffffffffffff81111561053e575f80fd5b61054a8782880161043f565b95989497509550505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b808201808211156105f0577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9291505056fea164736f6c6343000819000a"; +} diff --git a/packages/contracts-bedrock/test/properties/kontrol/helpers/LibStateDiff.sol b/packages/contracts-bedrock/test/properties/kontrol/helpers/LibStateDiff.sol new file mode 100644 index 000000000000..d5269b082604 --- /dev/null +++ b/packages/contracts-bedrock/test/properties/kontrol/helpers/LibStateDiff.sol @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: MIT +// (The MIT License) + +// Copyright 2020-2024 Optimism + +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: + +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +pragma solidity 0.8.25; + +import { stdJson } from "forge-std/StdJson.sol"; +import { VmSafe } from "forge-std/Vm.sol"; + +/// @title LibStateDiff +/// @author refcell +/// @notice Library to write StateDiff output to json. +library LibStateDiff { + /// @notice Accepts an array of AccountAccess structs from the Vm and encodes them as a json string. + /// @param _accountAccesses Array of AccountAccess structs. + /// @return serialized_ string + function encodeAccountAccesses(VmSafe.AccountAccess[] memory _accountAccesses) + internal + returns (string memory serialized_) + { + string[] memory accountAccesses = new string[](_accountAccesses.length); + for (uint256 i = 0; i < _accountAccesses.length; i++) { + accountAccesses[i] = serializeAccountAccess(_accountAccesses[i]); + } + serialized_ = stdJson.serialize("accountAccessElem", "accountAccesses", accountAccesses); + } + + /// @notice Turns an AccountAccess into a json serialized string + /// @param _accountAccess The AccountAccess to serialize + /// @return serialized_ The json serialized string + function serializeAccountAccess(VmSafe.AccountAccess memory _accountAccess) + internal + returns (string memory serialized_) + { + string memory json = "foo"; + json = stdJson.serialize("accountAccess", "chainInfo", serializeChainInfo(_accountAccess.chainInfo)); + json = stdJson.serialize("accountAccess", "kind", serializeAccountAccessKind(_accountAccess.kind)); + json = stdJson.serialize("accountAccess", "account", _accountAccess.account); + json = stdJson.serialize("accountAccess", "accessor", _accountAccess.accessor); + json = stdJson.serialize("accountAccess", "initialized", _accountAccess.initialized); + json = stdJson.serialize("accountAccess", "oldBalance", _accountAccess.oldBalance); + json = stdJson.serialize("accountAccess", "newBalance", _accountAccess.newBalance); + json = stdJson.serialize("accountAccess", "deployedCode", _accountAccess.deployedCode); + json = stdJson.serialize("accountAccess", "value", _accountAccess.value); + json = stdJson.serialize("accountAccess", "data", _accountAccess.data); + json = stdJson.serialize("accountAccess", "reverted", _accountAccess.reverted); + json = stdJson.serialize( + "accountAccess", "storageAccesses", serializeStorageAccesses(_accountAccess.storageAccesses) + ); + serialized_ = json; + } + + /// @notice Accepts a VmSafe.ChainInfo struct and encodes it as a json string. + /// @param _chainInfo The ChainInfo struct to serialize + /// @return serialized_ string + function serializeChainInfo(VmSafe.ChainInfo memory _chainInfo) internal returns (string memory serialized_) { + string memory json = ""; + json = stdJson.serialize("chainInfo", "forkId", _chainInfo.forkId); + json = stdJson.serialize("chainInfo", "chainId", _chainInfo.chainId); + serialized_ = json; + } + + /// @notice Turns an AccountAccessKind into a string. + /// @param _kind The AccountAccessKind to serialize + /// @return serialized_ The string representation of the AccountAccessKind + function serializeAccountAccessKind(VmSafe.AccountAccessKind _kind) + internal + pure + returns (string memory serialized_) + { + if (_kind == VmSafe.AccountAccessKind.Call) { + serialized_ = "Call"; + } else if (_kind == VmSafe.AccountAccessKind.DelegateCall) { + serialized_ = "DelegateCall"; + } else if (_kind == VmSafe.AccountAccessKind.CallCode) { + serialized_ = "CallCode"; + } else if (_kind == VmSafe.AccountAccessKind.StaticCall) { + serialized_ = "StaticCall"; + } else if (_kind == VmSafe.AccountAccessKind.Create) { + serialized_ = "Create"; + } else if (_kind == VmSafe.AccountAccessKind.SelfDestruct) { + serialized_ = "SelfDestruct"; + } else { + serialized_ = "Resume"; + } + } + + /// @notice Accepts an array of StorageAccess structs from the Vm and encodes each as a json string. + /// @param _storageAccesses Array of StorageAccess structs. + /// @return serialized_ The list of json serialized StorageAccess structs. + function serializeStorageAccesses(VmSafe.StorageAccess[] memory _storageAccesses) + internal + returns (string[] memory serialized_) + { + serialized_ = new string[](_storageAccesses.length); + for (uint256 i = 0; i < _storageAccesses.length; i++) { + serialized_[i] = serializeStorageAccess(_storageAccesses[i]); + } + } + + /// @notice Turns a StorageAccess into a json serialized string + /// @param _storageAccess The StorageAccess to serialize + /// @return serialized_ The json serialized string + function serializeStorageAccess(VmSafe.StorageAccess memory _storageAccess) + internal + returns (string memory serialized_) + { + string memory json = ""; + json = stdJson.serialize("storageAccess", "account", _storageAccess.account); + json = stdJson.serialize("storageAccess", "slot", _storageAccess.slot); + json = stdJson.serialize("storageAccess", "isWrite", _storageAccess.isWrite); + json = stdJson.serialize("storageAccess", "previousValue", _storageAccess.previousValue); + json = stdJson.serialize("storageAccess", "newValue", _storageAccess.newValue); + json = stdJson.serialize("storageAccess", "reverted", _storageAccess.reverted); + serialized_ = json; + } +} diff --git a/packages/contracts-bedrock/test/properties/kontrol/helpers/RecordStateDiff.sol b/packages/contracts-bedrock/test/properties/kontrol/helpers/RecordStateDiff.sol new file mode 100644 index 000000000000..a49cb41b6545 --- /dev/null +++ b/packages/contracts-bedrock/test/properties/kontrol/helpers/RecordStateDiff.sol @@ -0,0 +1,74 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import { console2 as console } from "forge-std/console2.sol"; +import { stdJson } from "forge-std/StdJson.sol"; +import { Vm } from "forge-std/Vm.sol"; +import { VmSafe } from "forge-std/Vm.sol"; +import { LibStateDiff } from "./LibStateDiff.sol"; + +struct Contract { + string contractName; + address contractAddress; +} + +abstract contract RecordStateDiff { + Vm private constant vm = Vm(address(uint160(uint256(keccak256("hevm cheat code"))))); + + /// @notice Executes a function recording its state updates and saves it to the + /// the file $STATE_DIFF_DIR/$STATE_DIFF_FILE + /// @dev STATE_DIFF_DIR env var with file folder relative to the foundry root dir + /// @dev STATE_DIFF_NAME env var with state diff file name + modifier recordStateDiff() { + // Check if the specified JSON file exists and create it if not + string memory statediffFile = check_file(vm.envString("STATE_DIFF_DIR"), vm.envString("STATE_DIFF_NAME")); + vm.startStateDiffRecording(); + _; + VmSafe.AccountAccess[] memory accesses = vm.stopAndReturnStateDiff(); + string memory json = LibStateDiff.encodeAccountAccesses(accesses); + vm.writeJson({ json: json, path: statediffFile }); + } + + /// @notice Saves a an address with a name to $STATE_DIFF_DIR/$STATE_DIFF_NAMES + /// @dev STATE_DIFF_DIR env var with file folder relative to the foundry root dir + /// @dev ADDR_NAMES env var with named addresses file name + /// TODO: Investigate/fix why the resulting order of the strings in the json seems to not preseve the order + /// in which `save_address` is called when saving multiple addresses + function save_address(address addr, string memory name) public { + string memory address_names_file = check_file(vm.envString("STATE_DIFF_DIR"), vm.envString("ADDR_NAMES")); + vm.writeJson({ json: vm.serializeString("", vm.toString(addr), name), path: address_names_file }); + } + + /// @notice Checks if dir_name/file_name exists and creates it if not + function check_file(string memory dir_name, string memory file_name) public returns (string memory) { + string memory dirname = string.concat(vm.projectRoot(), "/", dir_name); + string memory filename = string.concat(vm.projectRoot(), "/", dir_name, "/", file_name); + if (vm.exists(filename)) return filename; + if (!vm.isDir(dirname)) ffi_two_arg("mkdir", "-p", dirname); // Create directory if doesn't exist + ffi_one_arg("touch", filename); // Create file. Might be redundant, but better make sure + return filename; + } + + /// @notice Execute one bash command with one argument + /// @dev Will revert if the command returns any output + /// TODO: abstract number of arguments per function + function ffi_one_arg(string memory command, string memory arg) public { + string[] memory inputs = new string[](2); + inputs[0] = command; + inputs[1] = arg; + bytes memory res = vm.ffi(inputs); + require(res.length == 0, "RecordStateDiff: Command execution failed"); + } + + /// @notice Execute one bash command with one argument + /// @dev Will revert if the command returns any output + /// TODO: abstract number of arguments per function + function ffi_two_arg(string memory command, string memory arg1, string memory arg2) public { + string[] memory inputs = new string[](3); + inputs[0] = command; + inputs[1] = arg1; + inputs[2] = arg2; + bytes memory res = vm.ffi(inputs); + require(res.length == 0, "RecordStateDiff: Command execution failed"); + } +} diff --git a/packages/contracts-bedrock/test/properties/kontrol/optimism-superchain-lemmas.md b/packages/contracts-bedrock/test/properties/kontrol/optimism-superchain-lemmas.md new file mode 100644 index 000000000000..6cb29280402f --- /dev/null +++ b/packages/contracts-bedrock/test/properties/kontrol/optimism-superchain-lemmas.md @@ -0,0 +1,33 @@ +```k +requires "foundry.md" + +module OPTIMISM-SUPERCHAIN-LEMMAS + imports BOOL + imports FOUNDRY + imports INT-SYMBOLIC + + // Convert booleans to their word equivalents + rule bool2Word(true) => 1 + rule bool2Word(false) => 0 + + // Associativity and Commutativity Rules: + rule A +Int (B +Int C) => (A +Int B) +Int C + rule A *Int B => B *Int A + + // Comparison Normalization: + rule A +Int B A A false requires 0 <=Int B + rule A false requires 0 <=Int B + + rule A -Int A => 0 + rule 0 +Int A => A + rule A +Int 0 => A + rule A -Int 0 => A + + rule chop(I) => I requires #rangeUInt(256, I) + rule chop (chop (X:Int) +Int Y:Int) => chop (X +Int Y) + rule chop (X:Int +Int chop (Y:Int)) => chop (X +Int Y) +endmodule +``` diff --git a/packages/contracts-bedrock/test/properties/kontrol/utils/record-state-diff.sh b/packages/contracts-bedrock/test/properties/kontrol/utils/record-state-diff.sh new file mode 100644 index 000000000000..f2e242e913ea --- /dev/null +++ b/packages/contracts-bedrock/test/properties/kontrol/utils/record-state-diff.sh @@ -0,0 +1,55 @@ +#!/bin/bash +set -euo pipefail + +################################################################################ +# WARNING: This script is meant to be run from the foundry root directory # +# bash ./test/properties/kontrol/utils/record-state-diff.sh to run it # +################################################################################ + +########################## +# ENVIRNONMENT VARIABLES # +########################## + +# JSON-related variables +export STATE_DIFF_DIR=supererc20-state-diff # Relative to the Foundry root directory +export STATE_DIFF_NAME=StateDiff.json +export ADDR_NAMES=AddressNames.json +CLEAN_JSON_PATH=test/kontrol/scripts/json/clean_json.py + +# Where the contract and function that produces the jsons live +RECORDING_CONTRACT_DIR=test/properties/kontrol # Relative to the Foundry root directory +RECORDING_CONTRACT_FILE=KontrolBase.sol # Name of the Solidity file +RECORDING_CONTRACT_NAME=KontrolBase # Name of the actual contract +RECORDING_CONTRACT_FUNCTION=setUpNamed # Name of the function with the recordStateDiff modifier + +RECORDING_CONTRACT_PATH="$RECORDING_CONTRACT_DIR/$RECORDING_CONTRACT_FILE:$RECORDING_CONTRACT_NAME" + +# Kontrol-related variables +GENERATED_CONTRACT_NAME=InitialState +GENERATED_CONTRACT_DIR=test/properties/kontrol/deployments # Relative to the Foundry root directory +GENERATED_CONTRACT_LICENSE=UNLICENSED + +#################### +# RECORD EXECUTION # +#################### + +# Run the function with the recordStateDiff modifier +forge script $RECORDING_CONTRACT_PATH --sig "$RECORDING_CONTRACT_FUNCTION" --ffi -vv +# state diff JSON comes out scaped from the last command +# We execute this script to unscape it so that it can be fed to Kontrol +python3 "$CLEAN_JSON_PATH" "$STATE_DIFF_DIR/$STATE_DIFF_NAME" + +############################### +# GENERATE SOLIDITY CONTRACTS # +############################### + +# Give the appropriate files to Kontrol to create the contracts +kontrol load-state "$GENERATED_CONTRACT_NAME" "$STATE_DIFF_DIR/$STATE_DIFF_NAME" \ + --contract-names "$STATE_DIFF_DIR/$ADDR_NAMES" \ + --output-dir "$GENERATED_CONTRACT_DIR" \ + --license "$GENERATED_CONTRACT_LICENSE" \ + --from-state-diff + +# Format the code to ensure compatibility with any CI checks +forge fmt "$GENERATED_CONTRACT_DIR/$GENERATED_CONTRACT_NAME.sol" +forge fmt "$GENERATED_CONTRACT_DIR/${GENERATED_CONTRACT_NAME}Code.sol" \ No newline at end of file From 293423808f2c484269f1895861a6f78679fd1413 Mon Sep 17 00:00:00 2001 From: drgorillamd <83670532+drgorillamd@users.noreply.github.com> Date: Mon, 2 Sep 2024 16:28:19 +0200 Subject: [PATCH 24/48] fix(test): init glob var in state diff generator --- .../supererc20-state-diff/StateDiff.json | 29 +++++++++++++++---- .../test/properties/kontrol/KontrolBase.sol | 13 ++++++--- .../kontrol/OptimismSuperchainERC20.k.sol | 1 + .../kontrol/deployments/InitialState.sol | 4 +-- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json b/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json index f1dba0541bbf..4b083086ffa9 100644 --- a/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json +++ b/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json @@ -1,5 +1,22 @@ { "accountAccesses": [ + { + "accessor": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", + "account": "0x7109709ECfa91a80626fF3989D68f67F5b1DD12D", + "chainInfo": { + "chainId": 31337, + "forkId": 0 + }, + "data": "0x", + "deployedCode": "0x", + "initialized": true, + "kind": "Resume", + "newBalance": 0, + "oldBalance": 0, + "reverted": false, + "storageAccesses": [], + "value": 0 + }, { "accessor": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", "account": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", @@ -57,7 +74,7 @@ "chainId": 31337, "forkId": 0 }, - "data": "0x60806040526040516103ae3803806103ae8339810160408190526100229161023c565b61002c8282610033565b505061031b565b61003c82610091565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561008557610080828261010c565b505050565b61008d61017f565b5050565b806001600160a01b03163b5f036100cb57604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516101289190610305565b5f60405180830381855af49150503d805f8114610160576040519150601f19603f3d011682016040523d82523d5f602084013e610165565b606091505b5090925090506101768583836101a0565b95945050505050565b341561019e5760405163b398979f60e01b815260040160405180910390fd5b565b6060826101b5576101b0826101ff565b6101f8565b81511580156101cc57506001600160a01b0384163b155b156101f557604051639996b31560e01b81526001600160a01b03851660048201526024016100c2565b50805b9392505050565b80511561020f5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561024d575f80fd5b82516001600160a01b0381168114610263575f80fd5b60208401519092506001600160401b038082111561027f575f80fd5b818501915085601f830112610292575f80fd5b8151818111156102a4576102a4610228565b604051601f8201601f19908116603f011681019083821181831017156102cc576102cc610228565b816040528281528860208487010111156102e4575f80fd5b8260208601602083015e5f6020848301015280955050505050509250929050565b5f82518060208501845e5f920191825250919050565b6087806103275f395ff3fe6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a0000000000000000000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000104f6d2ee86000000000000000000000000237a66474b7b934b22574359500212977b656d9f000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e455243323000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005535550455200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "data": "0x60806040526040516103ae3803806103ae8339810160408190526100229161023c565b61002c8282610033565b505061031b565b61003c82610091565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561008557610080828261010c565b505050565b61008d61017f565b5050565b806001600160a01b03163b5f036100cb57604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516101289190610305565b5f60405180830381855af49150503d805f8114610160576040519150601f19603f3d011682016040523d82523d5f602084013e610165565b606091505b5090925090506101768583836101a0565b95945050505050565b341561019e5760405163b398979f60e01b815260040160405180910390fd5b565b6060826101b5576101b0826101ff565b6101f8565b81511580156101cc57506001600160a01b0384163b155b156101f557604051639996b31560e01b81526001600160a01b03851660048201526024016100c2565b50805b9392505050565b80511561020f5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561024d575f80fd5b82516001600160a01b0381168114610263575f80fd5b60208401519092506001600160401b038082111561027f575f80fd5b818501915085601f830112610292575f80fd5b8151818111156102a4576102a4610228565b604051601f8201601f19908116603f011681019083821181831017156102cc576102cc610228565b816040528281528860208487010111156102e4575f80fd5b8260208601602083015e5f6020848301015280955050505050509250929050565b5f82518060208501845e5f920191825250919050565b6087806103275f395ff3fe6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a0000000000000000000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000104f6d2ee86000000000000000000000000857a393dd324e79ff1c0a1ec826a66072e3a881d000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e455243323000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005535550455200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "deployedCode": "0x6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a", "initialized": true, "kind": "Create", @@ -125,7 +142,7 @@ "chainId": 31337, "forkId": 0 }, - "data": "0xf6d2ee86000000000000000000000000237a66474b7b934b22574359500212977b656d9f000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e4552433230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055355504552000000000000000000000000000000000000000000000000000000", + "data": "0xf6d2ee86000000000000000000000000857a393dd324e79ff1c0a1ec826a66072e3a881d000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e4552433230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055355504552000000000000000000000000000000000000000000000000000000", "deployedCode": "0x", "initialized": true, "kind": "DelegateCall", @@ -184,7 +201,7 @@ { "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", "isWrite": true, - "newValue": "0x000000000000000000000000237a66474b7b934b22574359500212977b656d9f", + "newValue": "0x000000000000000000000000857a393dd324e79ff1c0a1ec826a66072e3a881d", "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", "reverted": false, "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00" @@ -280,7 +297,7 @@ "chainId": 31337, "forkId": 0 }, - "data": "0x60806040526040516103ae3803806103ae8339810160408190526100229161023c565b61002c8282610033565b505061031b565b61003c82610091565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561008557610080828261010c565b505050565b61008d61017f565b5050565b806001600160a01b03163b5f036100cb57604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516101289190610305565b5f60405180830381855af49150503d805f8114610160576040519150601f19603f3d011682016040523d82523d5f602084013e610165565b606091505b5090925090506101768583836101a0565b95945050505050565b341561019e5760405163b398979f60e01b815260040160405180910390fd5b565b6060826101b5576101b0826101ff565b6101f8565b81511580156101cc57506001600160a01b0384163b155b156101f557604051639996b31560e01b81526001600160a01b03851660048201526024016100c2565b50805b9392505050565b80511561020f5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561024d575f80fd5b82516001600160a01b0381168114610263575f80fd5b60208401519092506001600160401b038082111561027f575f80fd5b818501915085601f830112610292575f80fd5b8151818111156102a4576102a4610228565b604051601f8201601f19908116603f011681019083821181831017156102cc576102cc610228565b816040528281528860208487010111156102e4575f80fd5b8260208601602083015e5f6020848301015280955050505050509250929050565b5f82518060208501845e5f920191825250919050565b6087806103275f395ff3fe6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a0000000000000000000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000104f6d2ee86000000000000000000000000237a66474b7b934b22574359500212977b656d9f000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e455243323000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005535550455200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "data": "0x60806040526040516103ae3803806103ae8339810160408190526100229161023c565b61002c8282610033565b505061031b565b61003c82610091565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561008557610080828261010c565b505050565b61008d61017f565b5050565b806001600160a01b03163b5f036100cb57604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516101289190610305565b5f60405180830381855af49150503d805f8114610160576040519150601f19603f3d011682016040523d82523d5f602084013e610165565b606091505b5090925090506101768583836101a0565b95945050505050565b341561019e5760405163b398979f60e01b815260040160405180910390fd5b565b6060826101b5576101b0826101ff565b6101f8565b81511580156101cc57506001600160a01b0384163b155b156101f557604051639996b31560e01b81526001600160a01b03851660048201526024016100c2565b50805b9392505050565b80511561020f5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561024d575f80fd5b82516001600160a01b0381168114610263575f80fd5b60208401519092506001600160401b038082111561027f575f80fd5b818501915085601f830112610292575f80fd5b8151818111156102a4576102a4610228565b604051601f8201601f19908116603f011681019083821181831017156102cc576102cc610228565b816040528281528860208487010111156102e4575f80fd5b8260208601602083015e5f6020848301015280955050505050509250929050565b5f82518060208501845e5f920191825250919050565b6087806103275f395ff3fe6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a0000000000000000000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000104f6d2ee86000000000000000000000000857a393dd324e79ff1c0a1ec826a66072e3a881d000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e455243323000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005535550455200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "deployedCode": "0x6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a", "initialized": true, "kind": "Create", @@ -348,7 +365,7 @@ "chainId": 31337, "forkId": 0 }, - "data": "0xf6d2ee86000000000000000000000000237a66474b7b934b22574359500212977b656d9f000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e4552433230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055355504552000000000000000000000000000000000000000000000000000000", + "data": "0xf6d2ee86000000000000000000000000857a393dd324e79ff1c0a1ec826a66072e3a881d000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e4552433230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055355504552000000000000000000000000000000000000000000000000000000", "deployedCode": "0x", "initialized": true, "kind": "DelegateCall", @@ -407,7 +424,7 @@ { "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", "isWrite": true, - "newValue": "0x000000000000000000000000237a66474b7b934b22574359500212977b656d9f", + "newValue": "0x000000000000000000000000857a393dd324e79ff1c0a1ec826a66072e3a881d", "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", "reverted": false, "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00" diff --git a/packages/contracts-bedrock/test/properties/kontrol/KontrolBase.sol b/packages/contracts-bedrock/test/properties/kontrol/KontrolBase.sol index 283c4e639113..9f39bb54f8b1 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/KontrolBase.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/KontrolBase.sol @@ -16,10 +16,10 @@ contract KontrolBase is Test, KontrolCheats, RecordStateDiff { MockL2ToL2Messenger internal constant MESSENGER = MockL2ToL2Messenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); - address internal remoteToken = address(bytes20(keccak256("remoteToken"))); - string internal name = "SuperchainERC20"; - string internal symbol = "SUPER"; - uint8 internal decimals = 18; + address internal remoteToken; + string internal name; + string internal symbol; + uint8 internal decimals; OptimismSuperchainERC20 public superchainERC20Impl; OptimismSuperchainERC20 internal sourceToken; @@ -28,6 +28,11 @@ contract KontrolBase is Test, KontrolCheats, RecordStateDiff { // The second function to get the state diff saving the addresses with their names function setUpNamed() public virtual recordStateDiff { + remoteToken = makeAddr("remoteToken"); + name = "SuperchainERC20"; + symbol = "SUPER"; + decimals = 18; + // Deploy the OptimismSuperchainERC20 contract implementation and the proxy to be used superchainERC20Impl = new OptimismSuperchainERC20(); sourceToken = OptimismSuperchainERC20( diff --git a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol index ca73d5586123..f0ca6954ae82 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol @@ -20,6 +20,7 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { setUpInlined(); // Source token + assert(remoteToken != address(0)); assert(sourceToken.remoteToken() == remoteToken); assert(eqStrings(sourceToken.name(), name)); assert(eqStrings(sourceToken.symbol(), symbol)); diff --git a/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialState.sol b/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialState.sol index cb47c362f4f6..990c69510c6c 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialState.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialState.sol @@ -37,7 +37,7 @@ contract InitialState is InitialStateCode { value = hex"0000000000000000000000000000000000000000000000010000000000000001"; vm.store(sourceTokenAddress, slot, value); slot = hex"07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00"; - value = hex"000000000000000000000000237a66474b7b934b22574359500212977b656d9f"; + value = hex"000000000000000000000000857a393dd324e79ff1c0a1ec826a66072e3a881d"; vm.store(sourceTokenAddress, slot, value); slot = hex"07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb01"; value = hex"5375706572636861696e4552433230000000000000000000000000000000001e"; @@ -62,7 +62,7 @@ contract InitialState is InitialStateCode { value = hex"0000000000000000000000000000000000000000000000010000000000000001"; vm.store(destTokenAddress, slot, value); slot = hex"07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00"; - value = hex"000000000000000000000000237a66474b7b934b22574359500212977b656d9f"; + value = hex"000000000000000000000000857a393dd324e79ff1c0a1ec826a66072e3a881d"; vm.store(destTokenAddress, slot, value); slot = hex"07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb01"; value = hex"5375706572636861696e4552433230000000000000000000000000000000001e"; From 98e443a2a3af7c8d31764f09b25ce2b86cc4d86f Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Mon, 2 Sep 2024 20:53:15 -0300 Subject: [PATCH 25/48] Revert "fix(test): init glob var in state diff generator" The changes are no longer needed since with the constructor args flag it works This reverts commit 293423808f2c484269f1895861a6f78679fd1413. --- .../supererc20-state-diff/StateDiff.json | 29 ++++--------------- .../test/properties/kontrol/KontrolBase.sol | 13 +++------ .../kontrol/OptimismSuperchainERC20.k.sol | 1 - .../kontrol/deployments/InitialState.sol | 4 +-- 4 files changed, 12 insertions(+), 35 deletions(-) diff --git a/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json b/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json index 4b083086ffa9..f1dba0541bbf 100644 --- a/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json +++ b/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json @@ -1,22 +1,5 @@ { "accountAccesses": [ - { - "accessor": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", - "account": "0x7109709ECfa91a80626fF3989D68f67F5b1DD12D", - "chainInfo": { - "chainId": 31337, - "forkId": 0 - }, - "data": "0x", - "deployedCode": "0x", - "initialized": true, - "kind": "Resume", - "newBalance": 0, - "oldBalance": 0, - "reverted": false, - "storageAccesses": [], - "value": 0 - }, { "accessor": "0x7FA9385bE102ac3EAc297483Dd6233D62b3e1496", "account": "0x5615dEB798BB3E4dFa0139dFa1b3D433Cc23b72f", @@ -74,7 +57,7 @@ "chainId": 31337, "forkId": 0 }, - "data": "0x60806040526040516103ae3803806103ae8339810160408190526100229161023c565b61002c8282610033565b505061031b565b61003c82610091565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561008557610080828261010c565b505050565b61008d61017f565b5050565b806001600160a01b03163b5f036100cb57604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516101289190610305565b5f60405180830381855af49150503d805f8114610160576040519150601f19603f3d011682016040523d82523d5f602084013e610165565b606091505b5090925090506101768583836101a0565b95945050505050565b341561019e5760405163b398979f60e01b815260040160405180910390fd5b565b6060826101b5576101b0826101ff565b6101f8565b81511580156101cc57506001600160a01b0384163b155b156101f557604051639996b31560e01b81526001600160a01b03851660048201526024016100c2565b50805b9392505050565b80511561020f5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561024d575f80fd5b82516001600160a01b0381168114610263575f80fd5b60208401519092506001600160401b038082111561027f575f80fd5b818501915085601f830112610292575f80fd5b8151818111156102a4576102a4610228565b604051601f8201601f19908116603f011681019083821181831017156102cc576102cc610228565b816040528281528860208487010111156102e4575f80fd5b8260208601602083015e5f6020848301015280955050505050509250929050565b5f82518060208501845e5f920191825250919050565b6087806103275f395ff3fe6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a0000000000000000000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000104f6d2ee86000000000000000000000000857a393dd324e79ff1c0a1ec826a66072e3a881d000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e455243323000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005535550455200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "data": "0x60806040526040516103ae3803806103ae8339810160408190526100229161023c565b61002c8282610033565b505061031b565b61003c82610091565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561008557610080828261010c565b505050565b61008d61017f565b5050565b806001600160a01b03163b5f036100cb57604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516101289190610305565b5f60405180830381855af49150503d805f8114610160576040519150601f19603f3d011682016040523d82523d5f602084013e610165565b606091505b5090925090506101768583836101a0565b95945050505050565b341561019e5760405163b398979f60e01b815260040160405180910390fd5b565b6060826101b5576101b0826101ff565b6101f8565b81511580156101cc57506001600160a01b0384163b155b156101f557604051639996b31560e01b81526001600160a01b03851660048201526024016100c2565b50805b9392505050565b80511561020f5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561024d575f80fd5b82516001600160a01b0381168114610263575f80fd5b60208401519092506001600160401b038082111561027f575f80fd5b818501915085601f830112610292575f80fd5b8151818111156102a4576102a4610228565b604051601f8201601f19908116603f011681019083821181831017156102cc576102cc610228565b816040528281528860208487010111156102e4575f80fd5b8260208601602083015e5f6020848301015280955050505050509250929050565b5f82518060208501845e5f920191825250919050565b6087806103275f395ff3fe6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a0000000000000000000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000104f6d2ee86000000000000000000000000237a66474b7b934b22574359500212977b656d9f000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e455243323000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005535550455200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "deployedCode": "0x6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a", "initialized": true, "kind": "Create", @@ -142,7 +125,7 @@ "chainId": 31337, "forkId": 0 }, - "data": "0xf6d2ee86000000000000000000000000857a393dd324e79ff1c0a1ec826a66072e3a881d000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e4552433230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055355504552000000000000000000000000000000000000000000000000000000", + "data": "0xf6d2ee86000000000000000000000000237a66474b7b934b22574359500212977b656d9f000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e4552433230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055355504552000000000000000000000000000000000000000000000000000000", "deployedCode": "0x", "initialized": true, "kind": "DelegateCall", @@ -201,7 +184,7 @@ { "account": "0x2e234DAe75C793f67A35089C9d99245E1C58470b", "isWrite": true, - "newValue": "0x000000000000000000000000857a393dd324e79ff1c0a1ec826a66072e3a881d", + "newValue": "0x000000000000000000000000237a66474b7b934b22574359500212977b656d9f", "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", "reverted": false, "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00" @@ -297,7 +280,7 @@ "chainId": 31337, "forkId": 0 }, - "data": "0x60806040526040516103ae3803806103ae8339810160408190526100229161023c565b61002c8282610033565b505061031b565b61003c82610091565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561008557610080828261010c565b505050565b61008d61017f565b5050565b806001600160a01b03163b5f036100cb57604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516101289190610305565b5f60405180830381855af49150503d805f8114610160576040519150601f19603f3d011682016040523d82523d5f602084013e610165565b606091505b5090925090506101768583836101a0565b95945050505050565b341561019e5760405163b398979f60e01b815260040160405180910390fd5b565b6060826101b5576101b0826101ff565b6101f8565b81511580156101cc57506001600160a01b0384163b155b156101f557604051639996b31560e01b81526001600160a01b03851660048201526024016100c2565b50805b9392505050565b80511561020f5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561024d575f80fd5b82516001600160a01b0381168114610263575f80fd5b60208401519092506001600160401b038082111561027f575f80fd5b818501915085601f830112610292575f80fd5b8151818111156102a4576102a4610228565b604051601f8201601f19908116603f011681019083821181831017156102cc576102cc610228565b816040528281528860208487010111156102e4575f80fd5b8260208601602083015e5f6020848301015280955050505050509250929050565b5f82518060208501845e5f920191825250919050565b6087806103275f395ff3fe6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a0000000000000000000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000104f6d2ee86000000000000000000000000857a393dd324e79ff1c0a1ec826a66072e3a881d000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e455243323000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005535550455200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "data": "0x60806040526040516103ae3803806103ae8339810160408190526100229161023c565b61002c8282610033565b505061031b565b61003c82610091565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b905f90a280511561008557610080828261010c565b505050565b61008d61017f565b5050565b806001600160a01b03163b5f036100cb57604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b60605f80846001600160a01b0316846040516101289190610305565b5f60405180830381855af49150503d805f8114610160576040519150601f19603f3d011682016040523d82523d5f602084013e610165565b606091505b5090925090506101768583836101a0565b95945050505050565b341561019e5760405163b398979f60e01b815260040160405180910390fd5b565b6060826101b5576101b0826101ff565b6101f8565b81511580156101cc57506001600160a01b0384163b155b156101f557604051639996b31560e01b81526001600160a01b03851660048201526024016100c2565b50805b9392505050565b80511561020f5780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b634e487b7160e01b5f52604160045260245ffd5b5f806040838503121561024d575f80fd5b82516001600160a01b0381168114610263575f80fd5b60208401519092506001600160401b038082111561027f575f80fd5b818501915085601f830112610292575f80fd5b8151818111156102a4576102a4610228565b604051601f8201601f19908116603f011681019083821181831017156102cc576102cc610228565b816040528281528860208487010111156102e4575f80fd5b8260208601602083015e5f6020848301015280955050505050509250929050565b5f82518060208501845e5f920191825250919050565b6087806103275f395ff3fe6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a0000000000000000000000005615deb798bb3e4dfa0139dfa1b3d433cc23b72f00000000000000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000000104f6d2ee86000000000000000000000000237a66474b7b934b22574359500212977b656d9f000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e455243323000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005535550455200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "deployedCode": "0x6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a", "initialized": true, "kind": "Create", @@ -365,7 +348,7 @@ "chainId": 31337, "forkId": 0 }, - "data": "0xf6d2ee86000000000000000000000000857a393dd324e79ff1c0a1ec826a66072e3a881d000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e4552433230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055355504552000000000000000000000000000000000000000000000000000000", + "data": "0xf6d2ee86000000000000000000000000237a66474b7b934b22574359500212977b656d9f000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000f5375706572636861696e4552433230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000055355504552000000000000000000000000000000000000000000000000000000", "deployedCode": "0x", "initialized": true, "kind": "DelegateCall", @@ -424,7 +407,7 @@ { "account": "0xF62849F9A0B5Bf2913b396098F7c7019b51A820a", "isWrite": true, - "newValue": "0x000000000000000000000000857a393dd324e79ff1c0a1ec826a66072e3a881d", + "newValue": "0x000000000000000000000000237a66474b7b934b22574359500212977b656d9f", "previousValue": "0x0000000000000000000000000000000000000000000000000000000000000000", "reverted": false, "slot": "0x07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00" diff --git a/packages/contracts-bedrock/test/properties/kontrol/KontrolBase.sol b/packages/contracts-bedrock/test/properties/kontrol/KontrolBase.sol index 9f39bb54f8b1..283c4e639113 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/KontrolBase.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/KontrolBase.sol @@ -16,10 +16,10 @@ contract KontrolBase is Test, KontrolCheats, RecordStateDiff { MockL2ToL2Messenger internal constant MESSENGER = MockL2ToL2Messenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); - address internal remoteToken; - string internal name; - string internal symbol; - uint8 internal decimals; + address internal remoteToken = address(bytes20(keccak256("remoteToken"))); + string internal name = "SuperchainERC20"; + string internal symbol = "SUPER"; + uint8 internal decimals = 18; OptimismSuperchainERC20 public superchainERC20Impl; OptimismSuperchainERC20 internal sourceToken; @@ -28,11 +28,6 @@ contract KontrolBase is Test, KontrolCheats, RecordStateDiff { // The second function to get the state diff saving the addresses with their names function setUpNamed() public virtual recordStateDiff { - remoteToken = makeAddr("remoteToken"); - name = "SuperchainERC20"; - symbol = "SUPER"; - decimals = 18; - // Deploy the OptimismSuperchainERC20 contract implementation and the proxy to be used superchainERC20Impl = new OptimismSuperchainERC20(); sourceToken = OptimismSuperchainERC20( diff --git a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol index f0ca6954ae82..ca73d5586123 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol @@ -20,7 +20,6 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { setUpInlined(); // Source token - assert(remoteToken != address(0)); assert(sourceToken.remoteToken() == remoteToken); assert(eqStrings(sourceToken.name(), name)); assert(eqStrings(sourceToken.symbol(), symbol)); diff --git a/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialState.sol b/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialState.sol index 990c69510c6c..cb47c362f4f6 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialState.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialState.sol @@ -37,7 +37,7 @@ contract InitialState is InitialStateCode { value = hex"0000000000000000000000000000000000000000000000010000000000000001"; vm.store(sourceTokenAddress, slot, value); slot = hex"07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00"; - value = hex"000000000000000000000000857a393dd324e79ff1c0a1ec826a66072e3a881d"; + value = hex"000000000000000000000000237a66474b7b934b22574359500212977b656d9f"; vm.store(sourceTokenAddress, slot, value); slot = hex"07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb01"; value = hex"5375706572636861696e4552433230000000000000000000000000000000001e"; @@ -62,7 +62,7 @@ contract InitialState is InitialStateCode { value = hex"0000000000000000000000000000000000000000000000010000000000000001"; vm.store(destTokenAddress, slot, value); slot = hex"07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb00"; - value = hex"000000000000000000000000857a393dd324e79ff1c0a1ec826a66072e3a881d"; + value = hex"000000000000000000000000237a66474b7b934b22574359500212977b656d9f"; vm.store(destTokenAddress, slot, value); slot = hex"07f04e84143df95a6373fcf376312ae41da81a193a3089073a54f47a74d8fb01"; value = hex"5375706572636861696e4552433230000000000000000000000000000000001e"; From 830753625dcf7302a56f857bd9f741fb0c709416 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Tue, 3 Sep 2024 00:10:20 -0300 Subject: [PATCH 26/48] chore: update setup on kontrol tests and add assumes * feat: create temporary file to debug symoblic tests with foundry --- .../kontrol/FoundryDebugSymbolic.t.sol | 141 ++++++++++++++++++ .../kontrol/OptimismSuperchainERC20.k.sol | 56 +++++-- 2 files changed, 187 insertions(+), 10 deletions(-) create mode 100644 packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol diff --git a/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol b/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol new file mode 100644 index 000000000000..dd82f69dc6af --- /dev/null +++ b/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; +import { Predeploys } from "src/libraries/Predeploys.sol"; +import { MockL2ToL2Messenger } from "test/properties/halmos/MockL2ToL2Messenger.sol"; +import { KontrolBase } from "test/properties/kontrol/KontrolBase.sol"; +import { InitialState } from "./deployments/InitialState.sol"; +import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol"; + +import "forge-std/Test.sol"; + +// TODO: File to debug faster with foundry replicating scenarios where the proof failed, needs removal afterwards. +contract FoundryDebugTests is KontrolBase { + function setUp() public { + // Deploy the OptimismSuperchainERC20 contract implementation and the proxy to be used + superchainERC20Impl = new OptimismSuperchainERC20(); + sourceToken = OptimismSuperchainERC20( + address( + // TODO: Update to beacon proxy + new ERC1967Proxy( + address(superchainERC20Impl), + abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, name, symbol, decimals)) + ) + ) + ); + + destToken = OptimismSuperchainERC20( + address( + // TODO: Update to beacon proxy + new ERC1967Proxy( + address(superchainERC20Impl), + abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, name, symbol, decimals)) + ) + ) + ); + + mockL2ToL2Messenger = + new MockL2ToL2Messenger(address(sourceToken), address(destToken), DESTINATION_CHAIN_ID, address(0)); + vm.etch(address(MESSENGER), address(mockL2ToL2Messenger).code); + } + + /// @notice Check setup works as expected + function test_proveSetup() public { + // Source token + assert(sourceToken.remoteToken() == remoteToken); + assert(eqStrings(sourceToken.name(), name)); + assert(eqStrings(sourceToken.symbol(), symbol)); + assert(sourceToken.decimals() == decimals); + vm.prank(address(sourceToken)); + assert(MESSENGER.crossDomainMessageSender() == address(sourceToken)); + + // Destination token + assert(destToken.remoteToken() == remoteToken); + assert(eqStrings(destToken.name(), name)); + assert(eqStrings(destToken.symbol(), symbol)); + assert(destToken.decimals() == decimals); + assert(MESSENGER.DESTINATION_CHAIN_ID() == DESTINATION_CHAIN_ID); + vm.prank(address(destToken)); + assert(MESSENGER.crossDomainMessageSender() == address(destToken)); + + // Custom cross domain sender + assert(MESSENGER.crossDomainMessageSender() == address(0)); + } + + /// @custom:property-id 7 + /// @custom:property Calls to relayERC20 always succeed as long as the sender the cross-domain caller are valid + function test_proveRelayERC20OnlyFromL2ToL2Messenger() public { + address _crossDomainSender = address(0); + address _sender = address(0); + address _from = address(645326474426547203313410069153905908525362434350); + address _to = address(728815563385977040452943777879061427756277306519); + uint256 _amount = 0; + + /* Precondition */ + vm.assume(_to != address(0)); + // Deploying a new messenger because of an issue of not being able to etch the storage layout of the mock + // contract. So needed to a new one setting the symbolic immutable variable for the crossDomainSender. + // Used 0 address on source token so when the `soureToken` calls it if returns the symbolic `_crossDomainSender` + vm.etch( + address(MESSENGER), address(new MockL2ToL2Messenger(address(0), address(0), 0, _crossDomainSender)).code + ); + + vm.prank(_sender); + /* Action */ + try sourceToken.relayERC20(_from, _to, _amount) { + /* Postconditions */ + assert(_sender == address(MESSENGER) && MESSENGER.crossDomainMessageSender() == address(sourceToken)); + } catch { + assert(_sender != address(MESSENGER) || MESSENGER.crossDomainMessageSender() != address(sourceToken)); + } + } + + /// @custom:property-id 8 + /// @custom:property `sendERC20` with a value of zero does not modify accounting + function test_proveSendERC20ZeroCall() public { + /* Preconditions */ + // 0x4200000000000000000000000000000000000024 + address _from = address(376793390874373408599387495934666716005045108772); + // 0x7Fa9385Be102aC3eac297483DD6233d62B3e1497 + address _to = address(728815563385977040452943777879061427756277306519); + uint256 _chainId = 0; + + console.log("from : ", _from); + console.log("to : ", _to); + + uint256 _totalSupplyBefore = sourceToken.totalSupply(); + uint256 _fromBalanceBefore = sourceToken.balanceOf(_from); + uint256 _toBalanceBefore = sourceToken.balanceOf(_to); + + vm.startPrank(_from); + /* Action */ + sourceToken.sendERC20(_to, ZERO_AMOUNT, _chainId); + + /* Postcondition */ + assert(sourceToken.totalSupply() == _totalSupplyBefore); + assert(sourceToken.balanceOf(_from) == _fromBalanceBefore); + assert(sourceToken.balanceOf(_to) == _toBalanceBefore); + } + + /// @custom:property-id 9 + /// @custom:property `relayERC20` with a value of zero does not modify accounting + function test_proveRelayERC20ZeroCall() public { + /* Preconditions */ + address _from = address(0); + address _to = address(1); + + uint256 _totalSupplyBefore = sourceToken.totalSupply(); + uint256 _fromBalanceBefore = sourceToken.balanceOf(_from); + uint256 _toBalanceBefore = sourceToken.balanceOf(_to); + + vm.prank(address(MESSENGER)); + /* Action */ + sourceToken.relayERC20(_from, _to, ZERO_AMOUNT); + + /* Postconditions */ + assert(sourceToken.totalSupply() == _totalSupplyBefore); + assert(sourceToken.balanceOf(_from) == _fromBalanceBefore); + assert(sourceToken.balanceOf(_to) == _toBalanceBefore); + } +} diff --git a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol index ca73d5586123..e805b26c0aa3 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol @@ -38,8 +38,6 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { // Custom cross domain sender assert(MESSENGER.crossDomainMessageSender() == address(0)); - - assert(block.chainid >= 0); } /// @custom:property-id 6 @@ -59,8 +57,8 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { vm.assume(_to != address(0)); vm.assume(_from != address(0)); - notBuiltinAddress(_from); - notBuiltinAddress(_to); + vm.assume(notBuiltinAddress(_from)); + vm.assume(notBuiltinAddress(_to)); // Can't deal to unsupported cheatcode vm.prank(Predeploys.L2_STANDARD_BRIDGE); @@ -87,6 +85,11 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { ) public { + setUpInlined(); + + vm.assume(notBuiltinAddress(_from)); + vm.assume(notBuiltinAddress(_to)); + /* Precondition */ vm.assume(_to != address(0)); // Deploying a new messenger because of an issue of not being able to etch the storage layout of the mock @@ -109,12 +112,14 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /// @custom:property-id 8 /// @custom:property `sendERC20` with a value of zero does not modify accounting function prove_sendERC20ZeroCall(address _from, address _to, uint256 _chainId) public { + setUpInlined(); + /* Preconditions */ vm.assume(_to != address(0)); vm.assume(_to != address(Predeploys.CROSS_L2_INBOX) && _to != address(MESSENGER)); - notBuiltinAddress(_from); - notBuiltinAddress(_to); + vm.assume(notBuiltinAddress(_from)); + vm.assume(notBuiltinAddress(_to)); uint256 _totalSupplyBefore = sourceToken.totalSupply(); uint256 _fromBalanceBefore = sourceToken.balanceOf(_from); @@ -133,12 +138,16 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /// @custom:property-id 9 /// @custom:property `relayERC20` with a value of zero does not modify accounting function prove_relayERC20ZeroCall(address _from, address _to) public { - uint256 _totalSupplyBefore = sourceToken.totalSupply(); + setUpInlined(); + /* Preconditions */ + vm.assume(_to != address(0)); + + uint256 _totalSupplyBefore = sourceToken.totalSupply(); uint256 _fromBalanceBefore = sourceToken.balanceOf(_from); uint256 _toBalanceBefore = sourceToken.balanceOf(_to); - vm.prank(address(MESSENGER)); + vm.prank(address(MESSENGER)); /* Action */ sourceToken.relayERC20(_from, _to, ZERO_AMOUNT); @@ -158,7 +167,13 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { ) public { + setUpInlined(); + /* Preconditions */ + vm.assume(notBuiltinAddress(_sender)); + vm.assume(notBuiltinAddress(_to)); + + vm.assume(_sender != address(0)); vm.assume(_to != address(0)); vm.prank(Predeploys.L2_STANDARD_BRIDGE); @@ -180,9 +195,13 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /// @custom:property `relayERC20` increases the token's totalSupply in the destination chain exactly by the input /// amount function prove_relayERC20IncreasesTotalSupply(address _from, address _to, uint256 _amount) public { - vm.assume(_to != address(0)); + setUpInlined(); /* Preconditions */ + vm.assume(_to != address(0)); + vm.assume(notBuiltinAddress(_from)); + vm.assume(notBuiltinAddress(_to)); + uint256 _totalSupplyBefore = sourceToken.totalSupply(); uint256 _toBalanceBefore = sourceToken.balanceOf(_to); @@ -198,7 +217,12 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /// @custom:property-id 12 /// @custom:property Increases the total supply on the amount minted by the bridge function prove_mint(address _from, uint256 _amount) public { + setUpInlined(); + /* Preconditions */ + vm.assume(_from != address(0)); + vm.assume(notBuiltinAddress(_from)); + uint256 _totalSupplyBefore = sourceToken.totalSupply(); uint256 _balanceBefore = sourceToken.balanceOf(_from); @@ -214,7 +238,12 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /// @custom:property-id 13 /// @custom:property Supertoken total supply only decreases on the amount burned by the bridge function prove_burn(address _from, uint256 _amount) public { + setUpInlined(); + /* Preconditions */ + vm.assume(_from != address(0)); + vm.assume(notBuiltinAddress(_from)); + vm.prank(Predeploys.L2_STANDARD_BRIDGE); sourceToken.mint(_from, _amount); @@ -232,7 +261,9 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /// @custom:property-id 14 /// @custom:property Supertoken total supply starts at zero - function prove_totalSupplyStartsAtZero() public view { + function prove_totalSupplyStartsAtZero() public { + setUpInlined(); + /* Postconditions */ assert(sourceToken.totalSupply() == 0); } @@ -244,6 +275,11 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /// @custom:property `sendERC20` decreases total supply in source chain and increases it in destination chain /// exactly by the input amount function prove_crossChainMintAndBurn(address _from, address _to, uint256 _amount, uint256 _chainId) public { + setUpInlined(); + + vm.assume(notBuiltinAddress(_from)); + vm.assume(notBuiltinAddress(_to)); + /* Preconditions */ vm.assume(_to != address(0)); vm.assume(_from != address(0)); From e40c1c1327e36461cef04e157555c43c34a8c0e0 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Tue, 3 Sep 2024 15:12:19 -0300 Subject: [PATCH 27/48] refactor: mock messenger storage layout wip due failing tests --- .../supererc20-state-diff/StateDiff.json | 4 +- .../halmos/OptimismSuperchainERC20.t.sol | 3 +- .../kontrol/FoundryDebugSymbolic.t.sol | 62 +++++++++-------- .../test/properties/kontrol/KontrolBase.sol | 5 +- .../kontrol/OptimismSuperchainERC20.k.sol | 28 +++++--- .../kontrol/deployments/InitialStateCode.sol | 2 +- .../kontrol/helpers/MockL2ToL2Messenger.sol | 67 +++++++++++++++++++ 7 files changed, 124 insertions(+), 47 deletions(-) create mode 100644 packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol diff --git a/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json b/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json index f1dba0541bbf..27e9fa3c0c13 100644 --- a/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json +++ b/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json @@ -503,8 +503,8 @@ "chainId": 31337, "forkId": 0 }, - "data": "0x610100604052348015610010575f80fd5b5060405161070538038061070583398101604081905261002f9161006a565b6001600160a01b0393841660805291831660a05260c0521660e0526100b4565b80516001600160a01b0381168114610065575f80fd5b919050565b5f805f806080858703121561007d575f80fd5b6100868561004f565b93506100946020860161004f565b9250604085015191506100a96060860161004f565b905092959194509250565b60805160a05160c05160e0516106036101025f395f6102ab01525f818160a901526102cf01525f818161025d0152818161028601526102fa01525f81816101fb015261022401526106035ff3fe608060405260043610610058575f3560e01c80632ea02369116100415780632ea023691461009857806338ffde18146100cb5780637056f41f14610104575f80fd5b80631ecd26f21461005c5780632479446214610071575b5f80fd5b61006f61006a366004610484565b610117565b005b34801561007c575f80fd5b506100856101d2565b6040519081526020015b60405180910390f35b3480156100a3575f80fd5b506100857f000000000000000000000000000000000000000000000000000000000000000081565b3480156100d6575f80fd5b506100df6101e3565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161008f565b61006f610112366004610500565b6102cd565b5f808473ffffffffffffffffffffffffffffffffffffffff16348585604051610141929190610556565b5f6040518083038185875af1925050503d805f811461017b576040519150601f19603f3d011682016040523d82523d5f602084013e610180565b606091505b5091509150816101c757806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101be9190610565565b60405180910390fd5b505050505050505050565b5f6101de4660016105b8565b905090565b5f73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016330361024657507f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036102a857507f000000000000000000000000000000000000000000000000000000000000000090565b507f000000000000000000000000000000000000000000000000000000000000000090565b7f000000000000000000000000000000000000000000000000000000000000000084036103e6575f6103557f00000000000000000000000000000000000000000000000000000000000000005f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506103ec92505050565b9050806103e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c65640000000000000000000000000000000000000000000000000060648201526084016101be565b505b50505050565b5f6103f9845a8585610401565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461043a575f80fd5b919050565b5f8083601f84011261044f575f80fd5b50813567ffffffffffffffff811115610466575f80fd5b60208301915083602082850101111561047d575f80fd5b9250929050565b5f805f805f805f60c0888a03121561049a575f80fd5b8735965060208801359550604088013594506104b860608901610417565b93506104c660808901610417565b925060a088013567ffffffffffffffff8111156104e1575f80fd5b6104ed8a828b0161043f565b989b979a50959850939692959293505050565b5f805f8060608587031215610513575f80fd5b8435935061052360208601610417565b9250604085013567ffffffffffffffff81111561053e575f80fd5b61054a8782880161043f565b95989497509550505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b808201808211156105f0577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9291505056fea164736f6c6343000819000a0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000", - "deployedCode": "0x608060405260043610610058575f3560e01c80632ea02369116100415780632ea023691461009857806338ffde18146100cb5780637056f41f14610104575f80fd5b80631ecd26f21461005c5780632479446214610071575b5f80fd5b61006f61006a366004610484565b610117565b005b34801561007c575f80fd5b506100856101d2565b6040519081526020015b60405180910390f35b3480156100a3575f80fd5b506100857f000000000000000000000000000000000000000000000000000000000000000281565b3480156100d6575f80fd5b506100df6101e3565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161008f565b61006f610112366004610500565b6102cd565b5f808473ffffffffffffffffffffffffffffffffffffffff16348585604051610141929190610556565b5f6040518083038185875af1925050503d805f811461017b576040519150601f19603f3d011682016040523d82523d5f602084013e610180565b606091505b5091509150816101c757806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101be9190610565565b60405180910390fd5b505050505050505050565b5f6101de4660016105b8565b905090565b5f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b16330361024657507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b90565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a1633036102a857507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a90565b507f000000000000000000000000000000000000000000000000000000000000000090565b7f000000000000000000000000000000000000000000000000000000000000000284036103e6575f6103557f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506103ec92505050565b9050806103e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c65640000000000000000000000000000000000000000000000000060648201526084016101be565b505b50505050565b5f6103f9845a8585610401565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461043a575f80fd5b919050565b5f8083601f84011261044f575f80fd5b50813567ffffffffffffffff811115610466575f80fd5b60208301915083602082850101111561047d575f80fd5b9250929050565b5f805f805f805f60c0888a03121561049a575f80fd5b8735965060208801359550604088013594506104b860608901610417565b93506104c660808901610417565b925060a088013567ffffffffffffffff8111156104e1575f80fd5b6104ed8a828b0161043f565b989b979a50959850939692959293505050565b5f805f8060608587031215610513575f80fd5b8435935061052360208601610417565b9250604085013567ffffffffffffffff81111561053e575f80fd5b61054a8782880161043f565b95989497509550505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b808201808211156105f0577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9291505056fea164736f6c6343000819000a", + "data": "0x610100604052348015610010575f80fd5b5060405161078f38038061078f83398101604081905261002f9161006d565b6001600160a01b039384166080529190921660a05260c09190915260e0526100ad565b80516001600160a01b0381168114610068575f80fd5b919050565b5f805f8060808587031215610080575f80fd5b61008985610052565b935061009760208601610052565b6040860151606090960151949790965092505050565b60805160a05160c05160e05161068e6101015f395f8181609501526101b801525f818160d9015261037801525f818161030f0152818161033801526103a301525f81816102ad01526102d6015261068e5ff3fe60806040526004361061006e575f3560e01c806338ffde181161004c57806338ffde18146100fb5780637056f41f14610134578063722c2a4d14610147578063f230b4c2146101a7575f80fd5b80631ecd26f21461007257806324794462146100875780632ea02369146100c8575b5f80fd5b61008561008036600461052d565b6101da565b005b348015610092575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000005b6040519081526020015b60405180910390f35b3480156100d3575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000081565b348015610106575f80fd5b5061010f610295565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100bf565b6100856101423660046105a9565b610376565b348015610152575f80fd5b506100856101613660046105ff565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101b2575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000081565b5f808473ffffffffffffffffffffffffffffffffffffffff1634858560405161020492919061061f565b5f6040518083038185875af1925050503d805f811461023e576040519150601f19603f3d011682016040523d82523d5f602084013e610243565b606091505b50915091508161028a57806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610281919061062e565b60405180910390fd5b505050505050505050565b5f73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036102f857507f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016330361035a57507f000000000000000000000000000000000000000000000000000000000000000090565b505f5473ffffffffffffffffffffffffffffffffffffffff1690565b7f0000000000000000000000000000000000000000000000000000000000000000840361048f575f6103fe7f00000000000000000000000000000000000000000000000000000000000000005f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061049592505050565b90508061048d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c6564000000000000000000000000000000000000000000000000006064820152608401610281565b505b50505050565b5f6104a2845a85856104aa565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146104e3575f80fd5b919050565b5f8083601f8401126104f8575f80fd5b50813567ffffffffffffffff81111561050f575f80fd5b602083019150836020828501011115610526575f80fd5b9250929050565b5f805f805f805f60c0888a031215610543575f80fd5b873596506020880135955060408801359450610561606089016104c0565b935061056f608089016104c0565b925060a088013567ffffffffffffffff81111561058a575f80fd5b6105968a828b016104e8565b989b979a50959850939692959293505050565b5f805f80606085870312156105bc575f80fd5b843593506105cc602086016104c0565b9250604085013567ffffffffffffffff8111156105e7575f80fd5b6105f3878288016104e8565b95989497509550505050565b5f6020828403121561060f575f80fd5b610618826104c0565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", + "deployedCode": "0x60806040526004361061006e575f3560e01c806338ffde181161004c57806338ffde18146100fb5780637056f41f14610134578063722c2a4d14610147578063f230b4c2146101a7575f80fd5b80631ecd26f21461007257806324794462146100875780632ea02369146100c8575b5f80fd5b61008561008036600461052d565b6101da565b005b348015610092575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000035b6040519081526020015b60405180910390f35b3480156100d3575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000281565b348015610106575f80fd5b5061010f610295565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100bf565b6100856101423660046105a9565b610376565b348015610152575f80fd5b506100856101613660046105ff565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101b2575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000381565b5f808473ffffffffffffffffffffffffffffffffffffffff1634858560405161020492919061061f565b5f6040518083038185875af1925050503d805f811461023e576040519150601f19603f3d011682016040523d82523d5f602084013e610243565b606091505b50915091508161028a57806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610281919061062e565b60405180910390fd5b505050505050505050565b5f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b1633036102f857507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b90565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a16330361035a57507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a90565b505f5473ffffffffffffffffffffffffffffffffffffffff1690565b7f0000000000000000000000000000000000000000000000000000000000000002840361048f575f6103fe7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061049592505050565b90508061048d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c6564000000000000000000000000000000000000000000000000006064820152608401610281565b505b50505050565b5f6104a2845a85856104aa565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146104e3575f80fd5b919050565b5f8083601f8401126104f8575f80fd5b50813567ffffffffffffffff81111561050f575f80fd5b602083019150836020828501011115610526575f80fd5b9250929050565b5f805f805f805f60c0888a031215610543575f80fd5b873596506020880135955060408801359450610561606089016104c0565b935061056f608089016104c0565b925060a088013567ffffffffffffffff81111561058a575f80fd5b6105968a828b016104e8565b989b979a50959850939692959293505050565b5f805f80606085870312156105bc575f80fd5b843593506105cc602086016104c0565b9250604085013567ffffffffffffffff8111156105e7575f80fd5b6105f3878288016104e8565b95989497509550505050565b5f6020828403121561060f575f80fd5b610618826104c0565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a", "initialized": true, "kind": "Create", "newBalance": 0, diff --git a/packages/contracts-bedrock/test/properties/halmos/OptimismSuperchainERC20.t.sol b/packages/contracts-bedrock/test/properties/halmos/OptimismSuperchainERC20.t.sol index cdbae879d0ff..37c591f24a63 100644 --- a/packages/contracts-bedrock/test/properties/halmos/OptimismSuperchainERC20.t.sol +++ b/packages/contracts-bedrock/test/properties/halmos/OptimismSuperchainERC20.t.sol @@ -50,6 +50,7 @@ contract SymTest_OptimismSuperchainERC20 is SymTest, HalmosBase { /// @notice Check setup works as expected function check_setup() public { // Source token + assert(remoteToken != address(0)); assert(sourceToken.remoteToken() == remoteToken); assert(eqStrings(sourceToken.name(), name)); assert(eqStrings(sourceToken.symbol(), symbol)); @@ -200,8 +201,6 @@ contract SymTest_OptimismSuperchainERC20 is SymTest, HalmosBase { /// @custom:property `relayERC20` increases the token's totalSupply in the destination chain exactly by the input /// amount function check_relayERC20IncreasesTotalSupply(address _from, address _to, uint256 _amount) public { - vm.assume(_to != address(0)); - /* Preconditions */ uint256 _totalSupplyBefore = sourceToken.totalSupply(); uint256 _toBalanceBefore = sourceToken.balanceOf(_to); diff --git a/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol b/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol index dd82f69dc6af..e6fd0b5ac8d9 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.25; import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { MockL2ToL2Messenger } from "test/properties/halmos/MockL2ToL2Messenger.sol"; +import { MockL2ToL2Messenger } from "test/properties/kontrol/helpers/MockL2ToL2Messenger.sol"; import { KontrolBase } from "test/properties/kontrol/KontrolBase.sol"; import { InitialState } from "./deployments/InitialState.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol"; @@ -36,13 +36,14 @@ contract FoundryDebugTests is KontrolBase { ); mockL2ToL2Messenger = - new MockL2ToL2Messenger(address(sourceToken), address(destToken), DESTINATION_CHAIN_ID, address(0)); + new MockL2ToL2Messenger(address(sourceToken), address(destToken), DESTINATION_CHAIN_ID, SOURCE); vm.etch(address(MESSENGER), address(mockL2ToL2Messenger).code); } /// @notice Check setup works as expected function test_proveSetup() public { // Source token + assert(remoteToken != address(0)); assert(sourceToken.remoteToken() == remoteToken); assert(eqStrings(sourceToken.name(), name)); assert(eqStrings(sourceToken.symbol(), symbol)); @@ -65,40 +66,43 @@ contract FoundryDebugTests is KontrolBase { /// @custom:property-id 7 /// @custom:property Calls to relayERC20 always succeed as long as the sender the cross-domain caller are valid - function test_proveRelayERC20OnlyFromL2ToL2Messenger() public { - address _crossDomainSender = address(0); - address _sender = address(0); - address _from = address(645326474426547203313410069153905908525362434350); - address _to = address(728815563385977040452943777879061427756277306519); - uint256 _amount = 0; - - /* Precondition */ - vm.assume(_to != address(0)); - // Deploying a new messenger because of an issue of not being able to etch the storage layout of the mock - // contract. So needed to a new one setting the symbolic immutable variable for the crossDomainSender. - // Used 0 address on source token so when the `soureToken` calls it if returns the symbolic `_crossDomainSender` - vm.etch( - address(MESSENGER), address(new MockL2ToL2Messenger(address(0), address(0), 0, _crossDomainSender)).code - ); - - vm.prank(_sender); - /* Action */ - try sourceToken.relayERC20(_from, _to, _amount) { - /* Postconditions */ - assert(_sender == address(MESSENGER) && MESSENGER.crossDomainMessageSender() == address(sourceToken)); - } catch { - assert(_sender != address(MESSENGER) || MESSENGER.crossDomainMessageSender() != address(sourceToken)); - } - } + // function test_proveRelayERC20OnlyFromL2ToL2Messenger() public { + // address _crossDomainSender = address(0); + // address _sender = address(0); + // address _from = address(645326474426547203313410069153905908525362434350); + // address _to = address(728815563385977040452943777879061427756277306519); + // uint256 _amount = 0; + + // /* Precondition */ + // vm.assume(_to != address(0)); + // // Deploying a new messenger because of an issue of not being able to etch the storage layout of the mock + // // contract. So needed to a new one setting the symbolic immutable variable for the crossDomainSender. + // // Used 0 address on source token so when the `soureToken` calls it if returns the symbolic + // `_crossDomainSender` + // vm.etch( + // address(MESSENGER), address(new MockL2ToL2Messenger(address(0), address(0), 0, _crossDomainSender)).code + // ); + + // vm.prank(_sender); + // /* Action */ + // try sourceToken.relayERC20(_from, _to, _amount) { + // /* Postconditions */ + // assert(_sender == address(MESSENGER) && MESSENGER.crossDomainMessageSender() == address(sourceToken)); + // } catch { + // assert(_sender != address(MESSENGER) || MESSENGER.crossDomainMessageSender() != address(sourceToken)); + // } + // } /// @custom:property-id 8 /// @custom:property `sendERC20` with a value of zero does not modify accounting function test_proveSendERC20ZeroCall() public { /* Preconditions */ // 0x4200000000000000000000000000000000000024 - address _from = address(376793390874373408599387495934666716005045108772); + // address _from = address(376793390874373408599387495934666716005045108772); // // 0x7Fa9385Be102aC3eac297483DD6233d62B3e1497 - address _to = address(728815563385977040452943777879061427756277306519); + // address _to = address(728815563385977040452943777879061427756277306519); // + address _from = address(263400868551549723330807389252719309078400616204); // 0x2e234dAE75c793F67a35089C9D99245e1C58470c + address _to = address(1); uint256 _chainId = 0; console.log("from : ", _from); diff --git a/packages/contracts-bedrock/test/properties/kontrol/KontrolBase.sol b/packages/contracts-bedrock/test/properties/kontrol/KontrolBase.sol index 283c4e639113..467b8c225540 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/KontrolBase.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/KontrolBase.sol @@ -4,7 +4,7 @@ pragma solidity ^0.8.25; import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol"; -import { MockL2ToL2Messenger } from "test/properties/halmos/MockL2ToL2Messenger.sol"; +import { MockL2ToL2Messenger } from "test/properties/kontrol/helpers/MockL2ToL2Messenger.sol"; import { KontrolCheats } from "kontrol-cheatcodes/KontrolCheats.sol"; import { RecordStateDiff } from "./helpers/RecordStateDiff.sol"; import { Test } from "forge-std/Test.sol"; @@ -12,6 +12,7 @@ import { Test } from "forge-std/Test.sol"; contract KontrolBase is Test, KontrolCheats, RecordStateDiff { uint256 internal constant CURRENT_CHAIN_ID = 1; uint256 internal constant DESTINATION_CHAIN_ID = 2; + uint256 internal constant SOURCE = 3; uint256 internal constant ZERO_AMOUNT = 0; MockL2ToL2Messenger internal constant MESSENGER = MockL2ToL2Messenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); @@ -51,7 +52,7 @@ contract KontrolBase is Test, KontrolCheats, RecordStateDiff { ); mockL2ToL2Messenger = - new MockL2ToL2Messenger(address(sourceToken), address(destToken), DESTINATION_CHAIN_ID, address(0)); + new MockL2ToL2Messenger(address(sourceToken), address(destToken), DESTINATION_CHAIN_ID, SOURCE); save_address(address(superchainERC20Impl), "superchainERC20Impl"); save_address(address(sourceToken), "sourceToken"); diff --git a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol index e805b26c0aa3..1a1a83ec3c22 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol @@ -3,7 +3,7 @@ pragma solidity 0.8.25; import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; -import { MockL2ToL2Messenger } from "test/properties/halmos/MockL2ToL2Messenger.sol"; +import { MockL2ToL2Messenger } from "test/properties/kontrol/helpers/MockL2ToL2Messenger.sol"; import { KontrolBase } from "test/properties/kontrol/KontrolBase.sol"; import { InitialState } from "./deployments/InitialState.sol"; @@ -36,8 +36,12 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { vm.prank(address(destToken)); assert(MESSENGER.crossDomainMessageSender() == address(destToken)); - // Custom cross domain sender + // Messenger + assert(MESSENGER.SOURCE() == SOURCE); assert(MESSENGER.crossDomainMessageSender() == address(0)); + // Check the setter works properly + MESSENGER.forTest_setCustomCrossDomainSender(address(420)); + assert(MESSENGER.crossDomainMessageSender() == address(420)); } /// @custom:property-id 6 @@ -87,17 +91,13 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { { setUpInlined(); + /* Preconditions */ + vm.assume(_to != address(0)); vm.assume(notBuiltinAddress(_from)); vm.assume(notBuiltinAddress(_to)); + vm.assume(notBuiltinAddress(_sender)); - /* Precondition */ - vm.assume(_to != address(0)); - // Deploying a new messenger because of an issue of not being able to etch the storage layout of the mock - // contract. So needed to a new one setting the symbolic immutable variable for the crossDomainSender. - // Used 0 address on source token so when the `soureToken` calls it if returns the symbolic `_crossDomainSender` - vm.etch( - address(MESSENGER), address(new MockL2ToL2Messenger(address(0), address(0), 0, _crossDomainSender)).code - ); + MESSENGER.forTest_setCustomCrossDomainSender(_crossDomainSender); vm.prank(_sender); /* Action */ @@ -117,6 +117,8 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /* Preconditions */ vm.assume(_to != address(0)); vm.assume(_to != address(Predeploys.CROSS_L2_INBOX) && _to != address(MESSENGER)); + // TODO + vm.assume(_from != address(0)); vm.assume(notBuiltinAddress(_from)); vm.assume(notBuiltinAddress(_to)); @@ -142,6 +144,10 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /* Preconditions */ vm.assume(_to != address(0)); + vm.assume(notBuiltinAddress(_from)); + vm.assume(notBuiltinAddress(_to)); + // TODO + vm.assume(_to != address(1)); uint256 _totalSupplyBefore = sourceToken.totalSupply(); uint256 _fromBalanceBefore = sourceToken.balanceOf(_from); @@ -274,7 +280,7 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /// @custom:property-id 23 /// @custom:property `sendERC20` decreases total supply in source chain and increases it in destination chain /// exactly by the input amount - function prove_crossChainMintAndBurn(address _from, address _to, uint256 _amount, uint256 _chainId) public { + function prove_crossChainSendERC20(address _from, address _to, uint256 _amount, uint256 _chainId) public { setUpInlined(); vm.assume(notBuiltinAddress(_from)); diff --git a/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol b/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol index e4c80facdabf..87eb904359d4 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol @@ -11,5 +11,5 @@ contract InitialStateCode { bytes internal constant destTokenCode = hex"6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a"; bytes internal constant mockL2ToL2MessengerCode = - hex"608060405260043610610058575f3560e01c80632ea02369116100415780632ea023691461009857806338ffde18146100cb5780637056f41f14610104575f80fd5b80631ecd26f21461005c5780632479446214610071575b5f80fd5b61006f61006a366004610484565b610117565b005b34801561007c575f80fd5b506100856101d2565b6040519081526020015b60405180910390f35b3480156100a3575f80fd5b506100857f000000000000000000000000000000000000000000000000000000000000000281565b3480156100d6575f80fd5b506100df6101e3565b60405173ffffffffffffffffffffffffffffffffffffffff909116815260200161008f565b61006f610112366004610500565b6102cd565b5f808473ffffffffffffffffffffffffffffffffffffffff16348585604051610141929190610556565b5f6040518083038185875af1925050503d805f811461017b576040519150601f19603f3d011682016040523d82523d5f602084013e610180565b606091505b5091509150816101c757806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016101be9190610565565b60405180910390fd5b505050505050505050565b5f6101de4660016105b8565b905090565b5f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b16330361024657507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b90565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a1633036102a857507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a90565b507f000000000000000000000000000000000000000000000000000000000000000090565b7f000000000000000000000000000000000000000000000000000000000000000284036103e6575f6103557f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f920191909152506103ec92505050565b9050806103e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c65640000000000000000000000000000000000000000000000000060648201526084016101be565b505b50505050565b5f6103f9845a8585610401565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff8116811461043a575f80fd5b919050565b5f8083601f84011261044f575f80fd5b50813567ffffffffffffffff811115610466575f80fd5b60208301915083602082850101111561047d575f80fd5b9250929050565b5f805f805f805f60c0888a03121561049a575f80fd5b8735965060208801359550604088013594506104b860608901610417565b93506104c660808901610417565b925060a088013567ffffffffffffffff8111156104e1575f80fd5b6104ed8a828b0161043f565b989b979a50959850939692959293505050565b5f805f8060608587031215610513575f80fd5b8435935061052360208601610417565b9250604085013567ffffffffffffffff81111561053e575f80fd5b61054a8782880161043f565b95989497509550505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011684010191505092915050565b808201808211156105f0577f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b9291505056fea164736f6c6343000819000a"; + hex"60806040526004361061006e575f3560e01c806338ffde181161004c57806338ffde18146100fb5780637056f41f14610134578063722c2a4d14610147578063f230b4c2146101a7575f80fd5b80631ecd26f21461007257806324794462146100875780632ea02369146100c8575b5f80fd5b61008561008036600461052d565b6101da565b005b348015610092575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000035b6040519081526020015b60405180910390f35b3480156100d3575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000281565b348015610106575f80fd5b5061010f610295565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100bf565b6100856101423660046105a9565b610376565b348015610152575f80fd5b506100856101613660046105ff565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101b2575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000381565b5f808473ffffffffffffffffffffffffffffffffffffffff1634858560405161020492919061061f565b5f6040518083038185875af1925050503d805f811461023e576040519150601f19603f3d011682016040523d82523d5f602084013e610243565b606091505b50915091508161028a57806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610281919061062e565b60405180910390fd5b505050505050505050565b5f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b1633036102f857507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b90565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a16330361035a57507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a90565b505f5473ffffffffffffffffffffffffffffffffffffffff1690565b7f0000000000000000000000000000000000000000000000000000000000000002840361048f575f6103fe7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061049592505050565b90508061048d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c6564000000000000000000000000000000000000000000000000006064820152608401610281565b505b50505050565b5f6104a2845a85856104aa565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146104e3575f80fd5b919050565b5f8083601f8401126104f8575f80fd5b50813567ffffffffffffffff81111561050f575f80fd5b602083019150836020828501011115610526575f80fd5b9250929050565b5f805f805f805f60c0888a031215610543575f80fd5b873596506020880135955060408801359450610561606089016104c0565b935061056f608089016104c0565b925060a088013567ffffffffffffffff81111561058a575f80fd5b6105968a828b016104e8565b989b979a50959850939692959293505050565b5f805f80606085870312156105bc575f80fd5b843593506105cc602086016104c0565b9250604085013567ffffffffffffffff8111156105e7575f80fd5b6105f3878288016104e8565b95989497509550505050565b5f6020828403121561060f575f80fd5b610618826104c0565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a"; } diff --git a/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol b/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol new file mode 100644 index 000000000000..78898c42ea31 --- /dev/null +++ b/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.25; + +import "src/L2/L2ToL2CrossDomainMessenger.sol"; +import { SafeCall } from "src/libraries/SafeCall.sol"; + +// TODO: Try to merge to a single mocked contract used by fuzzing and symbolic invariant tests - only if possible +// and is a low priorty +contract MockL2ToL2Messenger is IL2ToL2CrossDomainMessenger { + address internal immutable SOURCE_TOKEN; + address internal immutable DESTINATION_TOKEN; + uint256 public immutable DESTINATION_CHAIN_ID; + uint256 public immutable SOURCE; + + // Custom cross domain sender to be used when neither the source nor destination token are the callers + address internal customCrossDomainSender; + + constructor(address _sourceToken, address _destinationToken, uint256 _destinationChainId, uint256 _source) { + SOURCE_TOKEN = _sourceToken; + DESTINATION_TOKEN = _destinationToken; + DESTINATION_CHAIN_ID = _destinationChainId; + SOURCE = _source; + } + + // Mock the sendMessage function to execute the message call and simulate an atomic environmanet if the destination + // chain id matches the defined one + function sendMessage(uint256 _destination, address, bytes calldata _message) external payable { + // Mocking the environment to allow atomicity by executing the message call + if (_destination == DESTINATION_CHAIN_ID) { + (bool _success) = SafeCall.call(DESTINATION_TOKEN, 0, _message); + if (!_success) revert("MockL2ToL2Messenger: sendMessage failed"); + } + } + + // Mock the relay message function to just call the target address with the input message + function relayMessage( + uint256, + uint256, + uint256, + address, + address _target, + bytes calldata _message + ) + external + payable + { + (bool succ, bytes memory ret) = _target.call{ value: msg.value }(_message); + if (!succ) revert(string(ret)); + } + + function crossDomainMessageSource() external view returns (uint256 _source) { + _source = SOURCE; + } + + // Mock this function so it just always returns the expected if called by the supertoken to pass throgh the + // `address(this)` checks, or otherwise defaults to the custom cross domain sender + function crossDomainMessageSender() external view returns (address _sender) { + if (msg.sender == SOURCE_TOKEN) _sender = SOURCE_TOKEN; + else if (msg.sender == DESTINATION_TOKEN) _sender = DESTINATION_TOKEN; + else _sender = customCrossDomainSender; + } + + /// Setter function for the customCrossDomainSender + function forTest_setCustomCrossDomainSender(address _customCrossDomainSender) external { + customCrossDomainSender = _customCrossDomainSender; + } +} From 342f33fc046c21ae8c55571a74f183d1ef3a07f4 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Wed, 4 Sep 2024 13:41:48 -0300 Subject: [PATCH 28/48] chore: replicate new scenarios on foundry debug --- .../kontrol/FoundryDebugSymbolic.t.sol | 88 +++++++++++-------- 1 file changed, 51 insertions(+), 37 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol b/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol index e6fd0b5ac8d9..22bf6c9e9c0e 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol @@ -66,48 +66,48 @@ contract FoundryDebugTests is KontrolBase { /// @custom:property-id 7 /// @custom:property Calls to relayERC20 always succeed as long as the sender the cross-domain caller are valid - // function test_proveRelayERC20OnlyFromL2ToL2Messenger() public { - // address _crossDomainSender = address(0); - // address _sender = address(0); - // address _from = address(645326474426547203313410069153905908525362434350); - // address _to = address(728815563385977040452943777879061427756277306519); - // uint256 _amount = 0; - - // /* Precondition */ - // vm.assume(_to != address(0)); - // // Deploying a new messenger because of an issue of not being able to etch the storage layout of the mock - // // contract. So needed to a new one setting the symbolic immutable variable for the crossDomainSender. - // // Used 0 address on source token so when the `soureToken` calls it if returns the symbolic - // `_crossDomainSender` - // vm.etch( - // address(MESSENGER), address(new MockL2ToL2Messenger(address(0), address(0), 0, _crossDomainSender)).code - // ); - - // vm.prank(_sender); - // /* Action */ - // try sourceToken.relayERC20(_from, _to, _amount) { - // /* Postconditions */ - // assert(_sender == address(MESSENGER) && MESSENGER.crossDomainMessageSender() == address(sourceToken)); - // } catch { - // assert(_sender != address(MESSENGER) || MESSENGER.crossDomainMessageSender() != address(sourceToken)); - // } - // } + function test_proveRelayERC20OnlyFromL2ToL2Messenger() public { + address _crossDomainSender = address(263400868551549723330807389252719309078400616202); + address _sender = address(376793390874373408599387495934666716005045108771); + address _from = address(645326474426547203313410069153905908525362434350); + address _to = address(728815563385977040452943777879061427756277306519); + uint256 _amount = 0; + + /* Precondition */ + MESSENGER.forTest_setCustomCrossDomainSender(_crossDomainSender); + + vm.prank(_sender); + /* Action */ + try sourceToken.relayERC20(_from, _to, _amount) { + /* Postconditions */ + console.log("here 1"); + console.log("sender", _sender); + console.log("crossDomainSender", MESSENGER.crossDomainMessageSender()); + console.log("sourceToken", address(sourceToken)); + assert(_sender == address(MESSENGER) && MESSENGER.crossDomainMessageSender() == address(sourceToken)); + } catch { + console.log("here 2"); + assert(_sender != address(MESSENGER) || MESSENGER.crossDomainMessageSender() != address(sourceToken)); + } + } + // NUMBER_CELL = 16777217 + // VV2__from_114b9705 = 645326474426547203313410069153905908525362434350 + // CALLER_ID = 645326474426547203313410069153905908525362434350 + // TIMESTAMP_CELL = 1073741825 + // ORIGIN_ID = 645326474426547203313410069153905908525362434350 + // VV3__to_114b9705 = 728815563385977040452943777879061427756277306519 + // VV0__crossDomainSender_114b9705 = 263400868551549723330807389252719309078400616202 + // VV4__amount_114b9705 = 0 + // VV1__sender_114b9705 = 376793390874373408599387495934666716005045108771 /// @custom:property-id 8 /// @custom:property `sendERC20` with a value of zero does not modify accounting function test_proveSendERC20ZeroCall() public { /* Preconditions */ - // 0x4200000000000000000000000000000000000024 - // address _from = address(376793390874373408599387495934666716005045108772); // - // 0x7Fa9385Be102aC3eac297483DD6233d62B3e1497 - // address _to = address(728815563385977040452943777879061427756277306519); // - address _from = address(263400868551549723330807389252719309078400616204); // 0x2e234dAE75c793F67a35089C9D99245e1C58470c - address _to = address(1); + address _from = address(376793390874373408599387495934666716005045108772); // 0x4200000000000000000000000000000000000024 + address _to = address(728815563385977040452943777879061427756277306519); // 0x7Fa9385Be102aC3eac297483DD6233d62B3e1497 uint256 _chainId = 0; - console.log("from : ", _from); - console.log("to : ", _to); - uint256 _totalSupplyBefore = sourceToken.totalSupply(); uint256 _fromBalanceBefore = sourceToken.balanceOf(_from); uint256 _toBalanceBefore = sourceToken.balanceOf(_to); @@ -122,12 +122,20 @@ contract FoundryDebugTests is KontrolBase { assert(sourceToken.balanceOf(_to) == _toBalanceBefore); } + // VV2__chainId_114b9705 = 0 + // VV0__from_114b9705 = 376793390874373408599387495934666716005045108772 + // NUMBER_CELL = 16777217 + // VV1__to_114b9705 = 728815563385977040452943777879061427756277306519 + // CALLER_ID = 645326474426547203313410069153905908525362434350 + // TIMESTAMP_CELL = 1073741825 + // ORIGIN_ID = 645326474426547203313410069153905908525362434350 + /// @custom:property-id 9 /// @custom:property `relayERC20` with a value of zero does not modify accounting function test_proveRelayERC20ZeroCall() public { /* Preconditions */ - address _from = address(0); - address _to = address(1); + address _from = address(728815563385977040452943777879061427756277306519); + address _to = address(728815563385977040452943777879061427756277306519); uint256 _totalSupplyBefore = sourceToken.totalSupply(); uint256 _fromBalanceBefore = sourceToken.balanceOf(_from); @@ -142,4 +150,10 @@ contract FoundryDebugTests is KontrolBase { assert(sourceToken.balanceOf(_from) == _fromBalanceBefore); assert(sourceToken.balanceOf(_to) == _toBalanceBefore); } + // NUMBER_CELL = 16777217 + // VV1__to_114b9705 = 728815563385977040452943777879061427756277306519 + // CALLER_ID = 645326474426547203313410069153905908525362434350 + // TIMESTAMP_CELL = 1073741825 + // ORIGIN_ID = 645326474426547203313410069153905908525362434350 + // VV0__from_114b9705 = 728815563385977040452943777879061427756277306519 } From 79158b84a5ac993665f0dbd0f8e6ef1782c551b5 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Thu, 5 Sep 2024 01:36:29 -0300 Subject: [PATCH 29/48] refactor: cross domain sender logic on mock messenger --- .../supererc20-state-diff/StateDiff.json | 4 +- .../kontrol/FoundryDebugSymbolic.t.sol | 83 +++++++++++++++---- .../kontrol/OptimismSuperchainERC20.k.sol | 12 ++- .../kontrol/deployments/InitialStateCode.sol | 2 +- .../kontrol/helpers/MockL2ToL2Messenger.sol | 19 +++-- 5 files changed, 95 insertions(+), 25 deletions(-) diff --git a/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json b/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json index 27e9fa3c0c13..bf720166938b 100644 --- a/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json +++ b/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json @@ -503,8 +503,8 @@ "chainId": 31337, "forkId": 0 }, - "data": "0x610100604052348015610010575f80fd5b5060405161078f38038061078f83398101604081905261002f9161006d565b6001600160a01b039384166080529190921660a05260c09190915260e0526100ad565b80516001600160a01b0381168114610068575f80fd5b919050565b5f805f8060808587031215610080575f80fd5b61008985610052565b935061009760208601610052565b6040860151606090960151949790965092505050565b60805160a05160c05160e05161068e6101015f395f8181609501526101b801525f818160d9015261037801525f818161030f0152818161033801526103a301525f81816102ad01526102d6015261068e5ff3fe60806040526004361061006e575f3560e01c806338ffde181161004c57806338ffde18146100fb5780637056f41f14610134578063722c2a4d14610147578063f230b4c2146101a7575f80fd5b80631ecd26f21461007257806324794462146100875780632ea02369146100c8575b5f80fd5b61008561008036600461052d565b6101da565b005b348015610092575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000005b6040519081526020015b60405180910390f35b3480156100d3575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000081565b348015610106575f80fd5b5061010f610295565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100bf565b6100856101423660046105a9565b610376565b348015610152575f80fd5b506100856101613660046105ff565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101b2575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000081565b5f808473ffffffffffffffffffffffffffffffffffffffff1634858560405161020492919061061f565b5f6040518083038185875af1925050503d805f811461023e576040519150601f19603f3d011682016040523d82523d5f602084013e610243565b606091505b50915091508161028a57806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610281919061062e565b60405180910390fd5b505050505050505050565b5f73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036102f857507f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016330361035a57507f000000000000000000000000000000000000000000000000000000000000000090565b505f5473ffffffffffffffffffffffffffffffffffffffff1690565b7f0000000000000000000000000000000000000000000000000000000000000000840361048f575f6103fe7f00000000000000000000000000000000000000000000000000000000000000005f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061049592505050565b90508061048d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c6564000000000000000000000000000000000000000000000000006064820152608401610281565b505b50505050565b5f6104a2845a85856104aa565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146104e3575f80fd5b919050565b5f8083601f8401126104f8575f80fd5b50813567ffffffffffffffff81111561050f575f80fd5b602083019150836020828501011115610526575f80fd5b9250929050565b5f805f805f805f60c0888a031215610543575f80fd5b873596506020880135955060408801359450610561606089016104c0565b935061056f608089016104c0565b925060a088013567ffffffffffffffff81111561058a575f80fd5b6105968a828b016104e8565b989b979a50959850939692959293505050565b5f805f80606085870312156105bc575f80fd5b843593506105cc602086016104c0565b9250604085013567ffffffffffffffff8111156105e7575f80fd5b6105f3878288016104e8565b95989497509550505050565b5f6020828403121561060f575f80fd5b610618826104c0565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", - "deployedCode": "0x60806040526004361061006e575f3560e01c806338ffde181161004c57806338ffde18146100fb5780637056f41f14610134578063722c2a4d14610147578063f230b4c2146101a7575f80fd5b80631ecd26f21461007257806324794462146100875780632ea02369146100c8575b5f80fd5b61008561008036600461052d565b6101da565b005b348015610092575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000035b6040519081526020015b60405180910390f35b3480156100d3575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000281565b348015610106575f80fd5b5061010f610295565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100bf565b6100856101423660046105a9565b610376565b348015610152575f80fd5b506100856101613660046105ff565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101b2575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000381565b5f808473ffffffffffffffffffffffffffffffffffffffff1634858560405161020492919061061f565b5f6040518083038185875af1925050503d805f811461023e576040519150601f19603f3d011682016040523d82523d5f602084013e610243565b606091505b50915091508161028a57806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610281919061062e565b60405180910390fd5b505050505050505050565b5f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b1633036102f857507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b90565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a16330361035a57507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a90565b505f5473ffffffffffffffffffffffffffffffffffffffff1690565b7f0000000000000000000000000000000000000000000000000000000000000002840361048f575f6103fe7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061049592505050565b90508061048d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c6564000000000000000000000000000000000000000000000000006064820152608401610281565b505b50505050565b5f6104a2845a85856104aa565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146104e3575f80fd5b919050565b5f8083601f8401126104f8575f80fd5b50813567ffffffffffffffff81111561050f575f80fd5b602083019150836020828501011115610526575f80fd5b9250929050565b5f805f805f805f60c0888a031215610543575f80fd5b873596506020880135955060408801359450610561606089016104c0565b935061056f608089016104c0565b925060a088013567ffffffffffffffff81111561058a575f80fd5b6105968a828b016104e8565b989b979a50959850939692959293505050565b5f805f80606085870312156105bc575f80fd5b843593506105cc602086016104c0565b9250604085013567ffffffffffffffff8111156105e7575f80fd5b6105f3878288016104e8565b95989497509550505050565b5f6020828403121561060f575f80fd5b610618826104c0565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a", + "data": "0x610100604052348015610010575f80fd5b5060405161082d38038061082d83398101604081905261002f9161006d565b6001600160a01b039384166080529190921660a05260c09190915260e0526100ad565b80516001600160a01b0381168114610068575f80fd5b919050565b5f805f8060808587031215610080575f80fd5b61008985610052565b935061009760208601610052565b6040860151606090960151949790965092505050565b60805160a05160c05160e05161072c6101015f395f818160a001526101ed01525f818160e4015261041601525f8181610381015281816103aa015261044101525f818161031d0152610346015261072c5ff3fe608060405260043610610079575f3560e01c80637056f41f1161004c5780637056f41f1461013f578063722c2a4d1461015257806391db7f49146101b2578063f230b4c2146101dc575f80fd5b80631ecd26f21461007d57806324794462146100925780632ea02369146100d357806338ffde1814610106575b5f80fd5b61009061008b3660046105cb565b61020f565b005b34801561009d575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000005b6040519081526020015b60405180910390f35b3480156100de575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000081565b348015610111575f80fd5b5061011a6102ca565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ca565b61009061014d366004610647565b610414565b34801561015d575f80fd5b5061009061016c36600461069d565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101bd575f80fd5b5060015473ffffffffffffffffffffffffffffffffffffffff1661011a565b3480156101e7575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000081565b5f808473ffffffffffffffffffffffffffffffffffffffff163485856040516102399291906106bd565b5f6040518083038185875af1925050503d805f8114610273576040519150601f19603f3d011682016040523d82523d5f602084013e610278565b606091505b5091509150816102bf57806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102b691906106cc565b60405180910390fd5b505050505050505050565b5f805473ffffffffffffffffffffffffffffffffffffffff161561030657505f5473ffffffffffffffffffffffffffffffffffffffff166103d1565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016330361036a57507f00000000000000000000000000000000000000000000000000000000000000006103d1565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036103ce57507f00000000000000000000000000000000000000000000000000000000000000006103d1565b505f5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905590565b7f0000000000000000000000000000000000000000000000000000000000000000840361052d575f61049c7f00000000000000000000000000000000000000000000000000000000000000005f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061053392505050565b90508061052b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c65640000000000000000000000000000000000000000000000000060648201526084016102b6565b505b50505050565b5f610540845a8585610548565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610581575f80fd5b919050565b5f8083601f840112610596575f80fd5b50813567ffffffffffffffff8111156105ad575f80fd5b6020830191508360208285010111156105c4575f80fd5b9250929050565b5f805f805f805f60c0888a0312156105e1575f80fd5b8735965060208801359550604088013594506105ff6060890161055e565b935061060d6080890161055e565b925060a088013567ffffffffffffffff811115610628575f80fd5b6106348a828b01610586565b989b979a50959850939692959293505050565b5f805f806060858703121561065a575f80fd5b8435935061066a6020860161055e565b9250604085013567ffffffffffffffff811115610685575f80fd5b61069187828801610586565b95989497509550505050565b5f602082840312156106ad575f80fd5b6106b68261055e565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", + "deployedCode": "0x608060405260043610610079575f3560e01c80637056f41f1161004c5780637056f41f1461013f578063722c2a4d1461015257806391db7f49146101b2578063f230b4c2146101dc575f80fd5b80631ecd26f21461007d57806324794462146100925780632ea02369146100d357806338ffde1814610106575b5f80fd5b61009061008b3660046105cb565b61020f565b005b34801561009d575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000035b6040519081526020015b60405180910390f35b3480156100de575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000281565b348015610111575f80fd5b5061011a6102ca565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ca565b61009061014d366004610647565b610414565b34801561015d575f80fd5b5061009061016c36600461069d565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101bd575f80fd5b5060015473ffffffffffffffffffffffffffffffffffffffff1661011a565b3480156101e7575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000381565b5f808473ffffffffffffffffffffffffffffffffffffffff163485856040516102399291906106bd565b5f6040518083038185875af1925050503d805f8114610273576040519150601f19603f3d011682016040523d82523d5f602084013e610278565b606091505b5091509150816102bf57806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102b691906106cc565b60405180910390fd5b505050505050505050565b5f805473ffffffffffffffffffffffffffffffffffffffff161561030657505f5473ffffffffffffffffffffffffffffffffffffffff166103d1565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b16330361036a57507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b6103d1565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a1633036103ce57507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a6103d1565b505f5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905590565b7f0000000000000000000000000000000000000000000000000000000000000002840361052d575f61049c7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061053392505050565b90508061052b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c65640000000000000000000000000000000000000000000000000060648201526084016102b6565b505b50505050565b5f610540845a8585610548565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610581575f80fd5b919050565b5f8083601f840112610596575f80fd5b50813567ffffffffffffffff8111156105ad575f80fd5b6020830191508360208285010111156105c4575f80fd5b9250929050565b5f805f805f805f60c0888a0312156105e1575f80fd5b8735965060208801359550604088013594506105ff6060890161055e565b935061060d6080890161055e565b925060a088013567ffffffffffffffff811115610628575f80fd5b6106348a828b01610586565b989b979a50959850939692959293505050565b5f805f806060858703121561065a575f80fd5b8435935061066a6020860161055e565b9250604085013567ffffffffffffffff811115610685575f80fd5b61069187828801610586565b95989497509550505050565b5f602082840312156106ad575f80fd5b6106b68261055e565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a", "initialized": true, "kind": "Create", "newBalance": 0, diff --git a/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol b/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol index 22bf6c9e9c0e..e43e0bf96178 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol @@ -12,6 +12,8 @@ import "forge-std/Test.sol"; // TODO: File to debug faster with foundry replicating scenarios where the proof failed, needs removal afterwards. contract FoundryDebugTests is KontrolBase { + event CrossDomainMessageSender(address _sender); + function setUp() public { // Deploy the OptimismSuperchainERC20 contract implementation and the proxy to be used superchainERC20Impl = new OptimismSuperchainERC20(); @@ -66,8 +68,15 @@ contract FoundryDebugTests is KontrolBase { /// @custom:property-id 7 /// @custom:property Calls to relayERC20 always succeed as long as the sender the cross-domain caller are valid - function test_proveRelayERC20OnlyFromL2ToL2Messenger() public { - address _crossDomainSender = address(263400868551549723330807389252719309078400616202); + function test_proveRelayERC20OnlyFromL2ToL2Messenger() + // address _crossDomainSender, + // address _sender, + // address _from, + // address _to, + // uint256 _amount + public + { + address _crossDomainSender = address(0); address _sender = address(376793390874373408599387495934666716005045108771); address _from = address(645326474426547203313410069153905908525362434350); address _to = address(728815563385977040452943777879061427756277306519); @@ -76,29 +85,30 @@ contract FoundryDebugTests is KontrolBase { /* Precondition */ MESSENGER.forTest_setCustomCrossDomainSender(_crossDomainSender); + // Expect the cross domain sender to be emitted so after confirming it matches, we can use it for checks + vm.expectEmit(true, true, true, true); + emit CrossDomainMessageSender(_crossDomainSender); + vm.prank(_sender); /* Action */ try sourceToken.relayERC20(_from, _to, _amount) { /* Postconditions */ - console.log("here 1"); - console.log("sender", _sender); - console.log("crossDomainSender", MESSENGER.crossDomainMessageSender()); - console.log("sourceToken", address(sourceToken)); - assert(_sender == address(MESSENGER) && MESSENGER.crossDomainMessageSender() == address(sourceToken)); + assert(_sender == address(MESSENGER) && _crossDomainSender == address(sourceToken)); } catch { - console.log("here 2"); - assert(_sender != address(MESSENGER) || MESSENGER.crossDomainMessageSender() != address(sourceToken)); + // Emit to bypass the check when the call fails + emit CrossDomainMessageSender(_crossDomainSender); + assert(_sender != address(MESSENGER) || _crossDomainSender != address(sourceToken)); } } - // NUMBER_CELL = 16777217 - // VV2__from_114b9705 = 645326474426547203313410069153905908525362434350 - // CALLER_ID = 645326474426547203313410069153905908525362434350 - // TIMESTAMP_CELL = 1073741825 // ORIGIN_ID = 645326474426547203313410069153905908525362434350 - // VV3__to_114b9705 = 728815563385977040452943777879061427756277306519 - // VV0__crossDomainSender_114b9705 = 263400868551549723330807389252719309078400616202 // VV4__amount_114b9705 = 0 // VV1__sender_114b9705 = 376793390874373408599387495934666716005045108771 + // CALLER_ID = 645326474426547203313410069153905908525362434350 + // NUMBER_CELL = 16777217 + // VV2__from_114b9705 = 645326474426547203313410069153905908525362434350 + // VV0__crossDomainSender_114b9705 = 0 + // VV3__to_114b9705 = 728815563385977040452943777879061427756277306519 + // TIMESTAMP_CELL = 1073741825 /// @custom:property-id 8 /// @custom:property `sendERC20` with a value of zero does not modify accounting @@ -156,4 +166,47 @@ contract FoundryDebugTests is KontrolBase { // TIMESTAMP_CELL = 1073741825 // ORIGIN_ID = 645326474426547203313410069153905908525362434350 // VV0__from_114b9705 = 728815563385977040452943777879061427756277306519 + + function test_proveCrossChainSendERC20() public { + /* Preconditions */ + address _from = address(376793390874373408599387495934666716005045108752); + address _to = address(728815563385977040452943777879061427756277306519); + uint256 _amount = 0; + uint256 _chainId = 2; + // Mint the amount to send + vm.prank(Predeploys.L2_STANDARD_BRIDGE); + sourceToken.mint(_from, _amount); + + uint256 fromBalanceBefore = sourceToken.balanceOf(_from); + uint256 toBalanceBefore = destToken.balanceOf(_to); + uint256 sourceTotalSupplyBefore = sourceToken.totalSupply(); + uint256 destTotalSupplyBefore = destToken.totalSupply(); + + vm.prank(_from); + /* Action */ + try sourceToken.sendERC20(_to, _amount, _chainId) { + /* Postconditions */ + // Source + assert(sourceToken.balanceOf(_from) == fromBalanceBefore - _amount); + assert(sourceToken.totalSupply() == sourceTotalSupplyBefore - _amount); + + // Destination + if (_chainId == DESTINATION_CHAIN_ID) { + // If the destination chain matches the one of the dest token, check that the amount was minted + assert(destToken.balanceOf(_to) == toBalanceBefore + _amount); + assert(destToken.totalSupply() == destTotalSupplyBefore + _amount); + } else { + // Otherwise the balances should remain the same + assert(destToken.balanceOf(_to) == toBalanceBefore); + assert(destToken.totalSupply() == destTotalSupplyBefore); + } + } catch { + // Shouldn't fail + assert(false); + } + } + // VV1__to_114b9705 = 728815563385977040452943777879061427756277306519 + // VV0__from_114b9705 = 376793390874373408599387495934666716005045108752 + // VV2__amount_114b9705 = 0 + // VV3__chainId_114b9705 = 2 } diff --git a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol index 1a1a83ec3c22..30a9d5540411 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol @@ -8,6 +8,8 @@ import { KontrolBase } from "test/properties/kontrol/KontrolBase.sol"; import { InitialState } from "./deployments/InitialState.sol"; contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { + event CrossDomainMessageSender(address _sender); + function setUpInlined() public { superchainERC20Impl = OptimismSuperchainERC20(superchainERC20ImplAddress); sourceToken = OptimismSuperchainERC20(sourceTokenAddress); @@ -99,13 +101,19 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { MESSENGER.forTest_setCustomCrossDomainSender(_crossDomainSender); + // Expect the cross domain sender to be emitted so after confirming it matches, we can use it for checks + vm.expectEmit(true, true, true, true); + emit CrossDomainMessageSender(_crossDomainSender); + vm.prank(_sender); /* Action */ try sourceToken.relayERC20(_from, _to, _amount) { /* Postconditions */ - assert(_sender == address(MESSENGER) && MESSENGER.crossDomainMessageSender() == address(sourceToken)); + assert(_sender == address(MESSENGER) && _crossDomainSender == address(sourceToken)); } catch { - assert(_sender != address(MESSENGER) || MESSENGER.crossDomainMessageSender() != address(sourceToken)); + // Emit to bypass the check when the call fails + emit CrossDomainMessageSender(_crossDomainSender); + assert(_sender != address(MESSENGER) || _crossDomainSender != address(sourceToken)); } } diff --git a/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol b/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol index 87eb904359d4..cb5574e45706 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol @@ -11,5 +11,5 @@ contract InitialStateCode { bytes internal constant destTokenCode = hex"6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a"; bytes internal constant mockL2ToL2MessengerCode = - hex"60806040526004361061006e575f3560e01c806338ffde181161004c57806338ffde18146100fb5780637056f41f14610134578063722c2a4d14610147578063f230b4c2146101a7575f80fd5b80631ecd26f21461007257806324794462146100875780632ea02369146100c8575b5f80fd5b61008561008036600461052d565b6101da565b005b348015610092575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000035b6040519081526020015b60405180910390f35b3480156100d3575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000281565b348015610106575f80fd5b5061010f610295565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100bf565b6100856101423660046105a9565b610376565b348015610152575f80fd5b506100856101613660046105ff565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101b2575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000381565b5f808473ffffffffffffffffffffffffffffffffffffffff1634858560405161020492919061061f565b5f6040518083038185875af1925050503d805f811461023e576040519150601f19603f3d011682016040523d82523d5f602084013e610243565b606091505b50915091508161028a57806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610281919061062e565b60405180910390fd5b505050505050505050565b5f73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b1633036102f857507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b90565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a16330361035a57507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a90565b505f5473ffffffffffffffffffffffffffffffffffffffff1690565b7f0000000000000000000000000000000000000000000000000000000000000002840361048f575f6103fe7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061049592505050565b90508061048d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c6564000000000000000000000000000000000000000000000000006064820152608401610281565b505b50505050565b5f6104a2845a85856104aa565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff811681146104e3575f80fd5b919050565b5f8083601f8401126104f8575f80fd5b50813567ffffffffffffffff81111561050f575f80fd5b602083019150836020828501011115610526575f80fd5b9250929050565b5f805f805f805f60c0888a031215610543575f80fd5b873596506020880135955060408801359450610561606089016104c0565b935061056f608089016104c0565b925060a088013567ffffffffffffffff81111561058a575f80fd5b6105968a828b016104e8565b989b979a50959850939692959293505050565b5f805f80606085870312156105bc575f80fd5b843593506105cc602086016104c0565b9250604085013567ffffffffffffffff8111156105e7575f80fd5b6105f3878288016104e8565b95989497509550505050565b5f6020828403121561060f575f80fd5b610618826104c0565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a"; + hex"608060405260043610610079575f3560e01c80637056f41f1161004c5780637056f41f1461013f578063722c2a4d1461015257806391db7f49146101b2578063f230b4c2146101dc575f80fd5b80631ecd26f21461007d57806324794462146100925780632ea02369146100d357806338ffde1814610106575b5f80fd5b61009061008b3660046105cb565b61020f565b005b34801561009d575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000035b6040519081526020015b60405180910390f35b3480156100de575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000281565b348015610111575f80fd5b5061011a6102ca565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ca565b61009061014d366004610647565b610414565b34801561015d575f80fd5b5061009061016c36600461069d565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101bd575f80fd5b5060015473ffffffffffffffffffffffffffffffffffffffff1661011a565b3480156101e7575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000381565b5f808473ffffffffffffffffffffffffffffffffffffffff163485856040516102399291906106bd565b5f6040518083038185875af1925050503d805f8114610273576040519150601f19603f3d011682016040523d82523d5f602084013e610278565b606091505b5091509150816102bf57806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102b691906106cc565b60405180910390fd5b505050505050505050565b5f805473ffffffffffffffffffffffffffffffffffffffff161561030657505f5473ffffffffffffffffffffffffffffffffffffffff166103d1565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b16330361036a57507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b6103d1565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a1633036103ce57507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a6103d1565b505f5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905590565b7f0000000000000000000000000000000000000000000000000000000000000002840361052d575f61049c7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061053392505050565b90508061052b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c65640000000000000000000000000000000000000000000000000060648201526084016102b6565b505b50505050565b5f610540845a8585610548565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610581575f80fd5b919050565b5f8083601f840112610596575f80fd5b50813567ffffffffffffffff8111156105ad575f80fd5b6020830191508360208285010111156105c4575f80fd5b9250929050565b5f805f805f805f60c0888a0312156105e1575f80fd5b8735965060208801359550604088013594506105ff6060890161055e565b935061060d6080890161055e565b925060a088013567ffffffffffffffff811115610628575f80fd5b6106348a828b01610586565b989b979a50959850939692959293505050565b5f805f806060858703121561065a575f80fd5b8435935061066a6020860161055e565b9250604085013567ffffffffffffffff811115610685575f80fd5b61069187828801610586565b95989497509550505050565b5f602082840312156106ad575f80fd5b6106b68261055e565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a"; } diff --git a/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol b/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol index 78898c42ea31..f382064fe092 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol @@ -3,10 +3,13 @@ pragma solidity 0.8.25; import "src/L2/L2ToL2CrossDomainMessenger.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; +import "forge-std/Test.sol"; // TODO: Try to merge to a single mocked contract used by fuzzing and symbolic invariant tests - only if possible // and is a low priorty -contract MockL2ToL2Messenger is IL2ToL2CrossDomainMessenger { +contract MockL2ToL2Messenger { + event CrossDomainMessageSender(address _sender); + address internal immutable SOURCE_TOKEN; address internal immutable DESTINATION_TOKEN; uint256 public immutable DESTINATION_CHAIN_ID; @@ -14,6 +17,7 @@ contract MockL2ToL2Messenger is IL2ToL2CrossDomainMessenger { // Custom cross domain sender to be used when neither the source nor destination token are the callers address internal customCrossDomainSender; + address internal lastCrossDomainSender; constructor(address _sourceToken, address _destinationToken, uint256 _destinationChainId, uint256 _source) { SOURCE_TOKEN = _sourceToken; @@ -46,18 +50,23 @@ contract MockL2ToL2Messenger is IL2ToL2CrossDomainMessenger { { (bool succ, bytes memory ret) = _target.call{ value: msg.value }(_message); if (!succ) revert(string(ret)); + + // Emit an event to access to the last cross domain sender, can't be stored in storage due to view + // restriction + emit CrossDomainMessageSender(customCrossDomainSender); } function crossDomainMessageSource() external view returns (uint256 _source) { _source = SOURCE; } - // Mock this function so it just always returns the expected if called by the supertoken to pass throgh the - // `address(this)` checks, or otherwise defaults to the custom cross domain sender + // Mock this function so it defaults to the custom domain sender if set, otherwise it defaults to the address of the + // token that called the function - reverts if neither are met function crossDomainMessageSender() external view returns (address _sender) { - if (msg.sender == SOURCE_TOKEN) _sender = SOURCE_TOKEN; + if (customCrossDomainSender != address(0)) _sender = customCrossDomainSender; + else if (msg.sender == SOURCE_TOKEN) _sender = SOURCE_TOKEN; else if (msg.sender == DESTINATION_TOKEN) _sender = DESTINATION_TOKEN; - else _sender = customCrossDomainSender; + else _sender = address(0); } /// Setter function for the customCrossDomainSender From dc0c2c4dd9219d08f2389a5499308dbd252b014b Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Thu, 5 Sep 2024 09:55:41 -0300 Subject: [PATCH 30/48] chore: remove event, add a boolean and update cross domain sender visibility on mock messenger --- .../supererc20-state-diff/StateDiff.json | 4 ++-- .../kontrol/FoundryDebugSymbolic.t.sol | 23 ++++++++----------- .../kontrol/deployments/InitialStateCode.sol | 2 +- .../kontrol/helpers/MockL2ToL2Messenger.sol | 11 ++++----- 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json b/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json index bf720166938b..ef187ae5be04 100644 --- a/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json +++ b/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json @@ -503,8 +503,8 @@ "chainId": 31337, "forkId": 0 }, - "data": "0x610100604052348015610010575f80fd5b5060405161082d38038061082d83398101604081905261002f9161006d565b6001600160a01b039384166080529190921660a05260c09190915260e0526100ad565b80516001600160a01b0381168114610068575f80fd5b919050565b5f805f8060808587031215610080575f80fd5b61008985610052565b935061009760208601610052565b6040860151606090960151949790965092505050565b60805160a05160c05160e05161072c6101015f395f818160a001526101ed01525f818160e4015261041601525f8181610381015281816103aa015261044101525f818161031d0152610346015261072c5ff3fe608060405260043610610079575f3560e01c80637056f41f1161004c5780637056f41f1461013f578063722c2a4d1461015257806391db7f49146101b2578063f230b4c2146101dc575f80fd5b80631ecd26f21461007d57806324794462146100925780632ea02369146100d357806338ffde1814610106575b5f80fd5b61009061008b3660046105cb565b61020f565b005b34801561009d575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000005b6040519081526020015b60405180910390f35b3480156100de575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000081565b348015610111575f80fd5b5061011a6102ca565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ca565b61009061014d366004610647565b610414565b34801561015d575f80fd5b5061009061016c36600461069d565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101bd575f80fd5b5060015473ffffffffffffffffffffffffffffffffffffffff1661011a565b3480156101e7575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000081565b5f808473ffffffffffffffffffffffffffffffffffffffff163485856040516102399291906106bd565b5f6040518083038185875af1925050503d805f8114610273576040519150601f19603f3d011682016040523d82523d5f602084013e610278565b606091505b5091509150816102bf57806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102b691906106cc565b60405180910390fd5b505050505050505050565b5f805473ffffffffffffffffffffffffffffffffffffffff161561030657505f5473ffffffffffffffffffffffffffffffffffffffff166103d1565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016330361036a57507f00000000000000000000000000000000000000000000000000000000000000006103d1565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036103ce57507f00000000000000000000000000000000000000000000000000000000000000006103d1565b505f5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905590565b7f0000000000000000000000000000000000000000000000000000000000000000840361052d575f61049c7f00000000000000000000000000000000000000000000000000000000000000005f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061053392505050565b90508061052b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c65640000000000000000000000000000000000000000000000000060648201526084016102b6565b505b50505050565b5f610540845a8585610548565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610581575f80fd5b919050565b5f8083601f840112610596575f80fd5b50813567ffffffffffffffff8111156105ad575f80fd5b6020830191508360208285010111156105c4575f80fd5b9250929050565b5f805f805f805f60c0888a0312156105e1575f80fd5b8735965060208801359550604088013594506105ff6060890161055e565b935061060d6080890161055e565b925060a088013567ffffffffffffffff811115610628575f80fd5b6106348a828b01610586565b989b979a50959850939692959293505050565b5f805f806060858703121561065a575f80fd5b8435935061066a6020860161055e565b9250604085013567ffffffffffffffff811115610685575f80fd5b61069187828801610586565b95989497509550505050565b5f602082840312156106ad575f80fd5b6106b68261055e565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", - "deployedCode": "0x608060405260043610610079575f3560e01c80637056f41f1161004c5780637056f41f1461013f578063722c2a4d1461015257806391db7f49146101b2578063f230b4c2146101dc575f80fd5b80631ecd26f21461007d57806324794462146100925780632ea02369146100d357806338ffde1814610106575b5f80fd5b61009061008b3660046105cb565b61020f565b005b34801561009d575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000035b6040519081526020015b60405180910390f35b3480156100de575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000281565b348015610111575f80fd5b5061011a6102ca565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ca565b61009061014d366004610647565b610414565b34801561015d575f80fd5b5061009061016c36600461069d565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101bd575f80fd5b5060015473ffffffffffffffffffffffffffffffffffffffff1661011a565b3480156101e7575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000381565b5f808473ffffffffffffffffffffffffffffffffffffffff163485856040516102399291906106bd565b5f6040518083038185875af1925050503d805f8114610273576040519150601f19603f3d011682016040523d82523d5f602084013e610278565b606091505b5091509150816102bf57806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102b691906106cc565b60405180910390fd5b505050505050505050565b5f805473ffffffffffffffffffffffffffffffffffffffff161561030657505f5473ffffffffffffffffffffffffffffffffffffffff166103d1565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b16330361036a57507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b6103d1565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a1633036103ce57507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a6103d1565b505f5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905590565b7f0000000000000000000000000000000000000000000000000000000000000002840361052d575f61049c7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061053392505050565b90508061052b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c65640000000000000000000000000000000000000000000000000060648201526084016102b6565b505b50505050565b5f610540845a8585610548565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610581575f80fd5b919050565b5f8083601f840112610596575f80fd5b50813567ffffffffffffffff8111156105ad575f80fd5b6020830191508360208285010111156105c4575f80fd5b9250929050565b5f805f805f805f60c0888a0312156105e1575f80fd5b8735965060208801359550604088013594506105ff6060890161055e565b935061060d6080890161055e565b925060a088013567ffffffffffffffff811115610628575f80fd5b6106348a828b01610586565b989b979a50959850939692959293505050565b5f805f806060858703121561065a575f80fd5b8435935061066a6020860161055e565b9250604085013567ffffffffffffffff811115610685575f80fd5b61069187828801610586565b95989497509550505050565b5f602082840312156106ad575f80fd5b6106b68261055e565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a", + "data": "0x610100604052348015610010575f80fd5b506040516107fd3803806107fd83398101604081905261002f9161006d565b6001600160a01b039384166080529190921660a05260c09190915260e0526100ad565b80516001600160a01b0381168114610068575f80fd5b919050565b5f805f8060808587031215610080575f80fd5b61008985610052565b935061009760208601610052565b6040860151606090960151949790965092505050565b60805160a05160c05160e0516106fc6101015f395f8181609501526101b801525f818160d901526103e601525f8181610394015281816103bd015261041101525f8181610332015261035b01526106fc5ff3fe60806040526004361061006e575f3560e01c806338ffde181161004c57806338ffde18146100fb5780637056f41f14610134578063722c2a4d14610147578063f230b4c2146101a7575f80fd5b80631ecd26f21461007257806324794462146100875780632ea02369146100c8575b5f80fd5b61008561008036600461059b565b6101da565b005b348015610092575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000005b6040519081526020015b60405180910390f35b3480156100d3575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000081565b348015610106575f80fd5b5061010f6102e1565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100bf565b610085610142366004610617565b6103e4565b348015610152575f80fd5b5061008561016136600461066d565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101b2575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000081565b5f808473ffffffffffffffffffffffffffffffffffffffff1634858560405161020492919061068d565b5f6040518083038185875af1925050503d805f811461023e576040519150601f19603f3d011682016040523d82523d5f602084013e610243565b606091505b50915091508161028a57806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610281919061069c565b60405180910390fd5b5f5460405173ffffffffffffffffffffffffffffffffffffffff90911681527fa0a4943ae409d8e364a5a387db7729ebd60cddcf3b9f3bcffa12108e6499daae9060200160405180910390a1505050505050505050565b5f805473ffffffffffffffffffffffffffffffffffffffff161561031b57505f5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016330361037d57507f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036103df57507f000000000000000000000000000000000000000000000000000000000000000090565b505f90565b7f000000000000000000000000000000000000000000000000000000000000000084036104fd575f61046c7f00000000000000000000000000000000000000000000000000000000000000005f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061050392505050565b9050806104fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c6564000000000000000000000000000000000000000000000000006064820152608401610281565b505b50505050565b5f610510845a8585610518565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610551575f80fd5b919050565b5f8083601f840112610566575f80fd5b50813567ffffffffffffffff81111561057d575f80fd5b602083019150836020828501011115610594575f80fd5b9250929050565b5f805f805f805f60c0888a0312156105b1575f80fd5b8735965060208801359550604088013594506105cf6060890161052e565b93506105dd6080890161052e565b925060a088013567ffffffffffffffff8111156105f8575f80fd5b6106048a828b01610556565b989b979a50959850939692959293505050565b5f805f806060858703121561062a575f80fd5b8435935061063a6020860161052e565b9250604085013567ffffffffffffffff811115610655575f80fd5b61066187828801610556565b95989497509550505050565b5f6020828403121561067d575f80fd5b6106868261052e565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", + "deployedCode": "0x60806040526004361061006e575f3560e01c806338ffde181161004c57806338ffde18146100fb5780637056f41f14610134578063722c2a4d14610147578063f230b4c2146101a7575f80fd5b80631ecd26f21461007257806324794462146100875780632ea02369146100c8575b5f80fd5b61008561008036600461059b565b6101da565b005b348015610092575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000035b6040519081526020015b60405180910390f35b3480156100d3575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000281565b348015610106575f80fd5b5061010f6102e1565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100bf565b610085610142366004610617565b6103e4565b348015610152575f80fd5b5061008561016136600461066d565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101b2575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000381565b5f808473ffffffffffffffffffffffffffffffffffffffff1634858560405161020492919061068d565b5f6040518083038185875af1925050503d805f811461023e576040519150601f19603f3d011682016040523d82523d5f602084013e610243565b606091505b50915091508161028a57806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610281919061069c565b60405180910390fd5b5f5460405173ffffffffffffffffffffffffffffffffffffffff90911681527fa0a4943ae409d8e364a5a387db7729ebd60cddcf3b9f3bcffa12108e6499daae9060200160405180910390a1505050505050505050565b5f805473ffffffffffffffffffffffffffffffffffffffff161561031b57505f5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b16330361037d57507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b90565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a1633036103df57507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a90565b505f90565b7f000000000000000000000000000000000000000000000000000000000000000284036104fd575f61046c7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061050392505050565b9050806104fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c6564000000000000000000000000000000000000000000000000006064820152608401610281565b505b50505050565b5f610510845a8585610518565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610551575f80fd5b919050565b5f8083601f840112610566575f80fd5b50813567ffffffffffffffff81111561057d575f80fd5b602083019150836020828501011115610594575f80fd5b9250929050565b5f805f805f805f60c0888a0312156105b1575f80fd5b8735965060208801359550604088013594506105cf6060890161052e565b93506105dd6080890161052e565b925060a088013567ffffffffffffffff8111156105f8575f80fd5b6106048a828b01610556565b989b979a50959850939692959293505050565b5f805f806060858703121561062a575f80fd5b8435935061063a6020860161052e565b9250604085013567ffffffffffffffff811115610655575f80fd5b61066187828801610556565b95989497509550505050565b5f6020828403121561067d575f80fd5b6106868261052e565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a", "initialized": true, "kind": "Create", "newBalance": 0, diff --git a/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol b/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol index e43e0bf96178..4d8e3fd9f2be 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol @@ -86,29 +86,26 @@ contract FoundryDebugTests is KontrolBase { MESSENGER.forTest_setCustomCrossDomainSender(_crossDomainSender); // Expect the cross domain sender to be emitted so after confirming it matches, we can use it for checks - vm.expectEmit(true, true, true, true); - emit CrossDomainMessageSender(_crossDomainSender); vm.prank(_sender); /* Action */ try sourceToken.relayERC20(_from, _to, _amount) { /* Postconditions */ - assert(_sender == address(MESSENGER) && _crossDomainSender == address(sourceToken)); + assert(_sender == address(MESSENGER) && MESSENGER.customCrossDomainSender() == address(sourceToken)); } catch { // Emit to bypass the check when the call fails - emit CrossDomainMessageSender(_crossDomainSender); - assert(_sender != address(MESSENGER) || _crossDomainSender != address(sourceToken)); + assert(_sender != address(MESSENGER) || MESSENGER.customCrossDomainSender() != address(sourceToken)); } } - // ORIGIN_ID = 645326474426547203313410069153905908525362434350 // VV4__amount_114b9705 = 0 - // VV1__sender_114b9705 = 376793390874373408599387495934666716005045108771 - // CALLER_ID = 645326474426547203313410069153905908525362434350 - // NUMBER_CELL = 16777217 - // VV2__from_114b9705 = 645326474426547203313410069153905908525362434350 - // VV0__crossDomainSender_114b9705 = 0 - // VV3__to_114b9705 = 728815563385977040452943777879061427756277306519 - // TIMESTAMP_CELL = 1073741825 + // ORIGIN_ID = 645326474426547203313410069153905908525362434350 + // TIMESTAMP_CELL = 1073741825 + // VV1__sender_114b9705 = 376793390874373408599387495934666716005045108771 + // NUMBER_CELL = 16777217 + // VV3__to_114b9705 = 728815563385977040452943777879061427756277306519 + // VV2__from_114b9705 = 645326474426547203313410069153905908525362434350 + // CALLER_ID = 645326474426547203313410069153905908525362434350 + // VV0__crossDomainSender_114b9705 = 0 /// @custom:property-id 8 /// @custom:property `sendERC20` with a value of zero does not modify accounting diff --git a/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol b/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol index cb5574e45706..29fa901e36a6 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol @@ -11,5 +11,5 @@ contract InitialStateCode { bytes internal constant destTokenCode = hex"6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a"; bytes internal constant mockL2ToL2MessengerCode = - hex"608060405260043610610079575f3560e01c80637056f41f1161004c5780637056f41f1461013f578063722c2a4d1461015257806391db7f49146101b2578063f230b4c2146101dc575f80fd5b80631ecd26f21461007d57806324794462146100925780632ea02369146100d357806338ffde1814610106575b5f80fd5b61009061008b3660046105cb565b61020f565b005b34801561009d575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000035b6040519081526020015b60405180910390f35b3480156100de575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000281565b348015610111575f80fd5b5061011a6102ca565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ca565b61009061014d366004610647565b610414565b34801561015d575f80fd5b5061009061016c36600461069d565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101bd575f80fd5b5060015473ffffffffffffffffffffffffffffffffffffffff1661011a565b3480156101e7575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000381565b5f808473ffffffffffffffffffffffffffffffffffffffff163485856040516102399291906106bd565b5f6040518083038185875af1925050503d805f8114610273576040519150601f19603f3d011682016040523d82523d5f602084013e610278565b606091505b5091509150816102bf57806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102b691906106cc565b60405180910390fd5b505050505050505050565b5f805473ffffffffffffffffffffffffffffffffffffffff161561030657505f5473ffffffffffffffffffffffffffffffffffffffff166103d1565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b16330361036a57507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b6103d1565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a1633036103ce57507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a6103d1565b505f5b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831617905590565b7f0000000000000000000000000000000000000000000000000000000000000002840361052d575f61049c7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061053392505050565b90508061052b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c65640000000000000000000000000000000000000000000000000060648201526084016102b6565b505b50505050565b5f610540845a8585610548565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610581575f80fd5b919050565b5f8083601f840112610596575f80fd5b50813567ffffffffffffffff8111156105ad575f80fd5b6020830191508360208285010111156105c4575f80fd5b9250929050565b5f805f805f805f60c0888a0312156105e1575f80fd5b8735965060208801359550604088013594506105ff6060890161055e565b935061060d6080890161055e565b925060a088013567ffffffffffffffff811115610628575f80fd5b6106348a828b01610586565b989b979a50959850939692959293505050565b5f805f806060858703121561065a575f80fd5b8435935061066a6020860161055e565b9250604085013567ffffffffffffffff811115610685575f80fd5b61069187828801610586565b95989497509550505050565b5f602082840312156106ad575f80fd5b6106b68261055e565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a"; + hex"60806040526004361061006e575f3560e01c806338ffde181161004c57806338ffde18146100fb5780637056f41f14610134578063722c2a4d14610147578063f230b4c2146101a7575f80fd5b80631ecd26f21461007257806324794462146100875780632ea02369146100c8575b5f80fd5b61008561008036600461059b565b6101da565b005b348015610092575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000035b6040519081526020015b60405180910390f35b3480156100d3575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000281565b348015610106575f80fd5b5061010f6102e1565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100bf565b610085610142366004610617565b6103e4565b348015610152575f80fd5b5061008561016136600461066d565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101b2575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000381565b5f808473ffffffffffffffffffffffffffffffffffffffff1634858560405161020492919061068d565b5f6040518083038185875af1925050503d805f811461023e576040519150601f19603f3d011682016040523d82523d5f602084013e610243565b606091505b50915091508161028a57806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610281919061069c565b60405180910390fd5b5f5460405173ffffffffffffffffffffffffffffffffffffffff90911681527fa0a4943ae409d8e364a5a387db7729ebd60cddcf3b9f3bcffa12108e6499daae9060200160405180910390a1505050505050505050565b5f805473ffffffffffffffffffffffffffffffffffffffff161561031b57505f5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b16330361037d57507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b90565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a1633036103df57507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a90565b505f90565b7f000000000000000000000000000000000000000000000000000000000000000284036104fd575f61046c7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061050392505050565b9050806104fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c6564000000000000000000000000000000000000000000000000006064820152608401610281565b505b50505050565b5f610510845a8585610518565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610551575f80fd5b919050565b5f8083601f840112610566575f80fd5b50813567ffffffffffffffff81111561057d575f80fd5b602083019150836020828501011115610594575f80fd5b9250929050565b5f805f805f805f60c0888a0312156105b1575f80fd5b8735965060208801359550604088013594506105cf6060890161052e565b93506105dd6080890161052e565b925060a088013567ffffffffffffffff8111156105f8575f80fd5b6106048a828b01610556565b989b979a50959850939692959293505050565b5f805f806060858703121561062a575f80fd5b8435935061063a6020860161052e565b9250604085013567ffffffffffffffff811115610655575f80fd5b61066187828801610556565b95989497509550505050565b5f6020828403121561067d575f80fd5b6106868261052e565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a"; } diff --git a/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol b/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol index f382064fe092..a3f4b5a7fe5c 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol @@ -16,8 +16,8 @@ contract MockL2ToL2Messenger { uint256 public immutable SOURCE; // Custom cross domain sender to be used when neither the source nor destination token are the callers - address internal customCrossDomainSender; - address internal lastCrossDomainSender; + address public customCrossDomainSender; + bool internal crossDomainSenderSet; constructor(address _sourceToken, address _destinationToken, uint256 _destinationChainId, uint256 _source) { SOURCE_TOKEN = _sourceToken; @@ -50,10 +50,6 @@ contract MockL2ToL2Messenger { { (bool succ, bytes memory ret) = _target.call{ value: msg.value }(_message); if (!succ) revert(string(ret)); - - // Emit an event to access to the last cross domain sender, can't be stored in storage due to view - // restriction - emit CrossDomainMessageSender(customCrossDomainSender); } function crossDomainMessageSource() external view returns (uint256 _source) { @@ -63,7 +59,7 @@ contract MockL2ToL2Messenger { // Mock this function so it defaults to the custom domain sender if set, otherwise it defaults to the address of the // token that called the function - reverts if neither are met function crossDomainMessageSender() external view returns (address _sender) { - if (customCrossDomainSender != address(0)) _sender = customCrossDomainSender; + if (crossDomainSenderSet) _sender = customCrossDomainSender; else if (msg.sender == SOURCE_TOKEN) _sender = SOURCE_TOKEN; else if (msg.sender == DESTINATION_TOKEN) _sender = DESTINATION_TOKEN; else _sender = address(0); @@ -71,6 +67,7 @@ contract MockL2ToL2Messenger { /// Setter function for the customCrossDomainSender function forTest_setCustomCrossDomainSender(address _customCrossDomainSender) external { + crossDomainSenderSet = true; customCrossDomainSender = _customCrossDomainSender; } } From befba99c102091cec53be08199e316da0575116a Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Thu, 5 Sep 2024 09:57:32 -0300 Subject: [PATCH 31/48] chore: unnecessary remove 0 address asingment --- .../contracts-bedrock/supererc20-state-diff/StateDiff.json | 4 ++-- .../test/properties/kontrol/deployments/InitialStateCode.sol | 2 +- .../test/properties/kontrol/helpers/MockL2ToL2Messenger.sol | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json b/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json index ef187ae5be04..f87791116f3c 100644 --- a/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json +++ b/packages/contracts-bedrock/supererc20-state-diff/StateDiff.json @@ -503,8 +503,8 @@ "chainId": 31337, "forkId": 0 }, - "data": "0x610100604052348015610010575f80fd5b506040516107fd3803806107fd83398101604081905261002f9161006d565b6001600160a01b039384166080529190921660a05260c09190915260e0526100ad565b80516001600160a01b0381168114610068575f80fd5b919050565b5f805f8060808587031215610080575f80fd5b61008985610052565b935061009760208601610052565b6040860151606090960151949790965092505050565b60805160a05160c05160e0516106fc6101015f395f8181609501526101b801525f818160d901526103e601525f8181610394015281816103bd015261041101525f8181610332015261035b01526106fc5ff3fe60806040526004361061006e575f3560e01c806338ffde181161004c57806338ffde18146100fb5780637056f41f14610134578063722c2a4d14610147578063f230b4c2146101a7575f80fd5b80631ecd26f21461007257806324794462146100875780632ea02369146100c8575b5f80fd5b61008561008036600461059b565b6101da565b005b348015610092575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000005b6040519081526020015b60405180910390f35b3480156100d3575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000081565b348015610106575f80fd5b5061010f6102e1565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100bf565b610085610142366004610617565b6103e4565b348015610152575f80fd5b5061008561016136600461066d565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101b2575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000081565b5f808473ffffffffffffffffffffffffffffffffffffffff1634858560405161020492919061068d565b5f6040518083038185875af1925050503d805f811461023e576040519150601f19603f3d011682016040523d82523d5f602084013e610243565b606091505b50915091508161028a57806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610281919061069c565b60405180910390fd5b5f5460405173ffffffffffffffffffffffffffffffffffffffff90911681527fa0a4943ae409d8e364a5a387db7729ebd60cddcf3b9f3bcffa12108e6499daae9060200160405180910390a1505050505050505050565b5f805473ffffffffffffffffffffffffffffffffffffffff161561031b57505f5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016330361037d57507f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036103df57507f000000000000000000000000000000000000000000000000000000000000000090565b505f90565b7f000000000000000000000000000000000000000000000000000000000000000084036104fd575f61046c7f00000000000000000000000000000000000000000000000000000000000000005f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061050392505050565b9050806104fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c6564000000000000000000000000000000000000000000000000006064820152608401610281565b505b50505050565b5f610510845a8585610518565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610551575f80fd5b919050565b5f8083601f840112610566575f80fd5b50813567ffffffffffffffff81111561057d575f80fd5b602083019150836020828501011115610594575f80fd5b9250929050565b5f805f805f805f60c0888a0312156105b1575f80fd5b8735965060208801359550604088013594506105cf6060890161052e565b93506105dd6080890161052e565b925060a088013567ffffffffffffffff8111156105f8575f80fd5b6106048a828b01610556565b989b979a50959850939692959293505050565b5f805f806060858703121561062a575f80fd5b8435935061063a6020860161052e565b9250604085013567ffffffffffffffff811115610655575f80fd5b61066187828801610556565b95989497509550505050565b5f6020828403121561067d575f80fd5b6106868261052e565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", - "deployedCode": "0x60806040526004361061006e575f3560e01c806338ffde181161004c57806338ffde18146100fb5780637056f41f14610134578063722c2a4d14610147578063f230b4c2146101a7575f80fd5b80631ecd26f21461007257806324794462146100875780632ea02369146100c8575b5f80fd5b61008561008036600461059b565b6101da565b005b348015610092575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000035b6040519081526020015b60405180910390f35b3480156100d3575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000281565b348015610106575f80fd5b5061010f6102e1565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100bf565b610085610142366004610617565b6103e4565b348015610152575f80fd5b5061008561016136600461066d565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101b2575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000381565b5f808473ffffffffffffffffffffffffffffffffffffffff1634858560405161020492919061068d565b5f6040518083038185875af1925050503d805f811461023e576040519150601f19603f3d011682016040523d82523d5f602084013e610243565b606091505b50915091508161028a57806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610281919061069c565b60405180910390fd5b5f5460405173ffffffffffffffffffffffffffffffffffffffff90911681527fa0a4943ae409d8e364a5a387db7729ebd60cddcf3b9f3bcffa12108e6499daae9060200160405180910390a1505050505050505050565b5f805473ffffffffffffffffffffffffffffffffffffffff161561031b57505f5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b16330361037d57507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b90565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a1633036103df57507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a90565b505f90565b7f000000000000000000000000000000000000000000000000000000000000000284036104fd575f61046c7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061050392505050565b9050806104fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c6564000000000000000000000000000000000000000000000000006064820152608401610281565b505b50505050565b5f610510845a8585610518565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610551575f80fd5b919050565b5f8083601f840112610566575f80fd5b50813567ffffffffffffffff81111561057d575f80fd5b602083019150836020828501011115610594575f80fd5b9250929050565b5f805f805f805f60c0888a0312156105b1575f80fd5b8735965060208801359550604088013594506105cf6060890161052e565b93506105dd6080890161052e565b925060a088013567ffffffffffffffff8111156105f8575f80fd5b6106048a828b01610556565b989b979a50959850939692959293505050565b5f805f806060858703121561062a575f80fd5b8435935061063a6020860161052e565b9250604085013567ffffffffffffffff811115610655575f80fd5b61066187828801610556565b95989497509550505050565b5f6020828403121561067d575f80fd5b6106868261052e565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a", + "data": "0x610100604052348015610010575f80fd5b5060405161080038038061080083398101604081905261002f9161006d565b6001600160a01b039384166080529190921660a05260c09190915260e0526100ad565b80516001600160a01b0381168114610068575f80fd5b919050565b5f805f8060808587031215610080575f80fd5b61008985610052565b935061009760208601610052565b6040860151606090960151949790965092505050565b60805160a05160c05160e0516106ff6101015f395f818160a0015261020601525f818160e401526103e901525f818161039b015281816103c4015261041401525f8181610339015261036201526106ff5ff3fe608060405260043610610079575f3560e01c80637056f41f1161004c5780637056f41f1461013f578063722c2a4d1461015257806382dc9c8b146101ca578063f230b4c2146101f5575f80fd5b80631ecd26f21461007d57806324794462146100925780632ea02369146100d357806338ffde1814610106575b5f80fd5b61009061008b36600461059e565b610228565b005b34801561009d575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000005b6040519081526020015b60405180910390f35b3480156100de575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000081565b348015610111575f80fd5b5061011a6102e3565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ca565b61009061014d36600461061a565b6103e7565b34801561015d575f80fd5b5061009061016c366004610670565b5f805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffff0000000000000000000000000000000000000000009092169190911774010000000000000000000000000000000000000000179055565b3480156101d5575f80fd5b505f5461011a9073ffffffffffffffffffffffffffffffffffffffff1681565b348015610200575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000081565b5f808473ffffffffffffffffffffffffffffffffffffffff16348585604051610252929190610690565b5f6040518083038185875af1925050503d805f811461028c576040519150601f19603f3d011682016040523d82523d5f602084013e610291565b606091505b5091509150816102d857806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102cf919061069f565b60405180910390fd5b505050505050505050565b5f805474010000000000000000000000000000000000000000900460ff161561032257505f5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016330361038457507f000000000000000000000000000000000000000000000000000000000000000090565b73ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001633036103e457507f00000000000000000000000000000000000000000000000000000000000000005b90565b7f00000000000000000000000000000000000000000000000000000000000000008403610500575f61046f7f00000000000000000000000000000000000000000000000000000000000000005f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061050692505050565b9050806104fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c65640000000000000000000000000000000000000000000000000060648201526084016102cf565b505b50505050565b5f610513845a858561051b565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610554575f80fd5b919050565b5f8083601f840112610569575f80fd5b50813567ffffffffffffffff811115610580575f80fd5b602083019150836020828501011115610597575f80fd5b9250929050565b5f805f805f805f60c0888a0312156105b4575f80fd5b8735965060208801359550604088013594506105d260608901610531565b93506105e060808901610531565b925060a088013567ffffffffffffffff8111156105fb575f80fd5b6106078a828b01610559565b989b979a50959850939692959293505050565b5f805f806060858703121561062d575f80fd5b8435935061063d60208601610531565b9250604085013567ffffffffffffffff811115610658575f80fd5b61066487828801610559565b95989497509550505050565b5f60208284031215610680575f80fd5b61068982610531565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000003", + "deployedCode": "0x608060405260043610610079575f3560e01c80637056f41f1161004c5780637056f41f1461013f578063722c2a4d1461015257806382dc9c8b146101ca578063f230b4c2146101f5575f80fd5b80631ecd26f21461007d57806324794462146100925780632ea02369146100d357806338ffde1814610106575b5f80fd5b61009061008b36600461059e565b610228565b005b34801561009d575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000035b6040519081526020015b60405180910390f35b3480156100de575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000281565b348015610111575f80fd5b5061011a6102e3565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ca565b61009061014d36600461061a565b6103e7565b34801561015d575f80fd5b5061009061016c366004610670565b5f805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffff0000000000000000000000000000000000000000009092169190911774010000000000000000000000000000000000000000179055565b3480156101d5575f80fd5b505f5461011a9073ffffffffffffffffffffffffffffffffffffffff1681565b348015610200575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000381565b5f808473ffffffffffffffffffffffffffffffffffffffff16348585604051610252929190610690565b5f6040518083038185875af1925050503d805f811461028c576040519150601f19603f3d011682016040523d82523d5f602084013e610291565b606091505b5091509150816102d857806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102cf919061069f565b60405180910390fd5b505050505050505050565b5f805474010000000000000000000000000000000000000000900460ff161561032257505f5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b16330361038457507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b90565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a1633036103e457507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5b90565b7f00000000000000000000000000000000000000000000000000000000000000028403610500575f61046f7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061050692505050565b9050806104fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c65640000000000000000000000000000000000000000000000000060648201526084016102cf565b505b50505050565b5f610513845a858561051b565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610554575f80fd5b919050565b5f8083601f840112610569575f80fd5b50813567ffffffffffffffff811115610580575f80fd5b602083019150836020828501011115610597575f80fd5b9250929050565b5f805f805f805f60c0888a0312156105b4575f80fd5b8735965060208801359550604088013594506105d260608901610531565b93506105e060808901610531565b925060a088013567ffffffffffffffff8111156105fb575f80fd5b6106078a828b01610559565b989b979a50959850939692959293505050565b5f805f806060858703121561062d575f80fd5b8435935061063d60208601610531565b9250604085013567ffffffffffffffff811115610658575f80fd5b61066487828801610559565b95989497509550505050565b5f60208284031215610680575f80fd5b61068982610531565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a", "initialized": true, "kind": "Create", "newBalance": 0, diff --git a/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol b/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol index 29fa901e36a6..fa587471be8b 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/deployments/InitialStateCode.sol @@ -11,5 +11,5 @@ contract InitialStateCode { bytes internal constant destTokenCode = hex"6080604052600a600c565b005b60186014601a565b605d565b565b5f60587f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc5473ffffffffffffffffffffffffffffffffffffffff1690565b905090565b365f80375f80365f845af43d5f803e8080156076573d5ff35b3d5ffdfea164736f6c6343000819000a"; bytes internal constant mockL2ToL2MessengerCode = - hex"60806040526004361061006e575f3560e01c806338ffde181161004c57806338ffde18146100fb5780637056f41f14610134578063722c2a4d14610147578063f230b4c2146101a7575f80fd5b80631ecd26f21461007257806324794462146100875780632ea02369146100c8575b5f80fd5b61008561008036600461059b565b6101da565b005b348015610092575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000035b6040519081526020015b60405180910390f35b3480156100d3575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000281565b348015610106575f80fd5b5061010f6102e1565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100bf565b610085610142366004610617565b6103e4565b348015610152575f80fd5b5061008561016136600461066d565b5f80547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff92909216919091179055565b3480156101b2575f80fd5b506100b57f000000000000000000000000000000000000000000000000000000000000000381565b5f808473ffffffffffffffffffffffffffffffffffffffff1634858560405161020492919061068d565b5f6040518083038185875af1925050503d805f811461023e576040519150601f19603f3d011682016040523d82523d5f602084013e610243565b606091505b50915091508161028a57806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610281919061069c565b60405180910390fd5b5f5460405173ffffffffffffffffffffffffffffffffffffffff90911681527fa0a4943ae409d8e364a5a387db7729ebd60cddcf3b9f3bcffa12108e6499daae9060200160405180910390a1505050505050505050565b5f805473ffffffffffffffffffffffffffffffffffffffff161561031b57505f5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b16330361037d57507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b90565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a1633036103df57507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a90565b505f90565b7f000000000000000000000000000000000000000000000000000000000000000284036104fd575f61046c7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061050392505050565b9050806104fb576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c6564000000000000000000000000000000000000000000000000006064820152608401610281565b505b50505050565b5f610510845a8585610518565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610551575f80fd5b919050565b5f8083601f840112610566575f80fd5b50813567ffffffffffffffff81111561057d575f80fd5b602083019150836020828501011115610594575f80fd5b9250929050565b5f805f805f805f60c0888a0312156105b1575f80fd5b8735965060208801359550604088013594506105cf6060890161052e565b93506105dd6080890161052e565b925060a088013567ffffffffffffffff8111156105f8575f80fd5b6106048a828b01610556565b989b979a50959850939692959293505050565b5f805f806060858703121561062a575f80fd5b8435935061063a6020860161052e565b9250604085013567ffffffffffffffff811115610655575f80fd5b61066187828801610556565b95989497509550505050565b5f6020828403121561067d575f80fd5b6106868261052e565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a"; + hex"608060405260043610610079575f3560e01c80637056f41f1161004c5780637056f41f1461013f578063722c2a4d1461015257806382dc9c8b146101ca578063f230b4c2146101f5575f80fd5b80631ecd26f21461007d57806324794462146100925780632ea02369146100d357806338ffde1814610106575b5f80fd5b61009061008b36600461059e565b610228565b005b34801561009d575f80fd5b507f00000000000000000000000000000000000000000000000000000000000000035b6040519081526020015b60405180910390f35b3480156100de575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000281565b348015610111575f80fd5b5061011a6102e3565b60405173ffffffffffffffffffffffffffffffffffffffff90911681526020016100ca565b61009061014d36600461061a565b6103e7565b34801561015d575f80fd5b5061009061016c366004610670565b5f805473ffffffffffffffffffffffffffffffffffffffff9092167fffffffffffffffffffffff0000000000000000000000000000000000000000009092169190911774010000000000000000000000000000000000000000179055565b3480156101d5575f80fd5b505f5461011a9073ffffffffffffffffffffffffffffffffffffffff1681565b348015610200575f80fd5b506100c07f000000000000000000000000000000000000000000000000000000000000000381565b5f808473ffffffffffffffffffffffffffffffffffffffff16348585604051610252929190610690565b5f6040518083038185875af1925050503d805f811461028c576040519150601f19603f3d011682016040523d82523d5f602084013e610291565b606091505b5091509150816102d857806040517f08c379a00000000000000000000000000000000000000000000000000000000081526004016102cf919061069f565b60405180910390fd5b505050505050505050565b5f805474010000000000000000000000000000000000000000900460ff161561032257505f5473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b16330361038457507f0000000000000000000000002e234dae75c793f67a35089c9d99245e1c58470b90565b73ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a1633036103e457507f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5b90565b7f00000000000000000000000000000000000000000000000000000000000000028403610500575f61046f7f000000000000000000000000f62849f9a0b5bf2913b396098f7c7019b51a820a5f85858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061050692505050565b9050806104fe576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602760248201527f4d6f636b4c32546f4c324d657373656e6765723a2073656e644d65737361676560448201527f206661696c65640000000000000000000000000000000000000000000000000060648201526084016102cf565b505b50505050565b5f610513845a858561051b565b949350505050565b5f805f835160208501868989f195945050505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610554575f80fd5b919050565b5f8083601f840112610569575f80fd5b50813567ffffffffffffffff811115610580575f80fd5b602083019150836020828501011115610597575f80fd5b9250929050565b5f805f805f805f60c0888a0312156105b4575f80fd5b8735965060208801359550604088013594506105d260608901610531565b93506105e060808901610531565b925060a088013567ffffffffffffffff8111156105fb575f80fd5b6106078a828b01610559565b989b979a50959850939692959293505050565b5f805f806060858703121561062d575f80fd5b8435935061063d60208601610531565b9250604085013567ffffffffffffffff811115610658575f80fd5b61066487828801610559565b95989497509550505050565b5f60208284031215610680575f80fd5b61068982610531565b9392505050565b818382375f9101908152919050565b602081525f82518060208401528060208501604085015e5f6040828501015260407fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8301168401019150509291505056fea164736f6c6343000819000a"; } diff --git a/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol b/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol index a3f4b5a7fe5c..dc4af91bca30 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol @@ -61,8 +61,8 @@ contract MockL2ToL2Messenger { function crossDomainMessageSender() external view returns (address _sender) { if (crossDomainSenderSet) _sender = customCrossDomainSender; else if (msg.sender == SOURCE_TOKEN) _sender = SOURCE_TOKEN; + // Leaving the `if` here for clarity else if (msg.sender == DESTINATION_TOKEN) _sender = DESTINATION_TOKEN; - else _sender = address(0); } /// Setter function for the customCrossDomainSender From 498f85fda2add8689dd2f02918bad254dcc30e20 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Thu, 5 Sep 2024 10:08:15 -0300 Subject: [PATCH 32/48] chore: update failing scenario on foundry debug tests --- .../kontrol/FoundryDebugSymbolic.t.sol | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol b/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol index 4d8e3fd9f2be..fe442d9c9363 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol @@ -76,7 +76,7 @@ contract FoundryDebugTests is KontrolBase { // uint256 _amount public { - address _crossDomainSender = address(0); + address _crossDomainSender = address(263400868551549723330807389252719309078400616203); address _sender = address(376793390874373408599387495934666716005045108771); address _from = address(645326474426547203313410069153905908525362434350); address _to = address(728815563385977040452943777879061427756277306519); @@ -97,15 +97,13 @@ contract FoundryDebugTests is KontrolBase { assert(_sender != address(MESSENGER) || MESSENGER.customCrossDomainSender() != address(sourceToken)); } } + // VV1__sender_114b9705 = 376793390874373408599387495934666716005045108771 + // VV3__to_114b9705 = 728815563385977040452943777879061427756277306519 + // VV0__crossDomainSender_114b9705 = 263400868551549723330807389252719309078400616203 + // ORIGIN_ID = 645326474426547203313410069153905908525362434350 + // CALLER_ID = 645326474426547203313410069153905908525362434350 // VV4__amount_114b9705 = 0 - // ORIGIN_ID = 645326474426547203313410069153905908525362434350 - // TIMESTAMP_CELL = 1073741825 - // VV1__sender_114b9705 = 376793390874373408599387495934666716005045108771 - // NUMBER_CELL = 16777217 - // VV3__to_114b9705 = 728815563385977040452943777879061427756277306519 - // VV2__from_114b9705 = 645326474426547203313410069153905908525362434350 - // CALLER_ID = 645326474426547203313410069153905908525362434350 - // VV0__crossDomainSender_114b9705 = 0 + // VV2__from_114b9705 = 645326474426547203313410069153905908525362434350 /// @custom:property-id 8 /// @custom:property `sendERC20` with a value of zero does not modify accounting From 51efcda731e1c902172072392b6faac9b65dcf2b Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Fri, 6 Sep 2024 14:23:06 -0300 Subject: [PATCH 33/48] refactor: use symoblic storage cheatcode --- packages/contracts-bedrock/kontrol.toml | 44 +++++++++++++++++++ .../kontrol/FoundryDebugSymbolic.t.sol | 8 ++-- .../kontrol/OptimismSuperchainERC20.k.sol | 20 ++++----- 3 files changed, 58 insertions(+), 14 deletions(-) create mode 100644 packages/contracts-bedrock/kontrol.toml diff --git a/packages/contracts-bedrock/kontrol.toml b/packages/contracts-bedrock/kontrol.toml new file mode 100644 index 000000000000..cfdc385ff02a --- /dev/null +++ b/packages/contracts-bedrock/kontrol.toml @@ -0,0 +1,44 @@ +[build.default] +foundry-project-root = '.' +regen = false +rekompile = false +verbose = false +debug = false +auxiliary-lemmas = true + +# require = 'lemmas.k' +# module-import = 'TestBase:KONTROL-LEMMAS' + +[prove.default] +foundry-project-root = '.' +verbose = false +debug = false +max-depth = 25000 +reinit = false +cse = false +workers = 1 +failure-information = true +counterexample-information = true +minimize-proofs = false +fail-fast = true +smt-timeout = 1000 +break-every-step = false +break-on-jumpi = false +break-on-calls = false +break-on-storage = false +break-on-basic-blocks = false +break-on-cheatcodes = false +run-constructor = true +symbolic-immutables = false +schedule = CANCUN +init-node-from = supererc20-state-diff/StateDiff.json + +[show.default] +foundry-project-root = '.' +verbose = false +debug = false +use-hex-encoding = false + +[view-kcfg.default] +foundry-project-root = '.' +use-hex-encoding = false \ No newline at end of file diff --git a/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol b/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol index fe442d9c9363..fb7e2157bf8e 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol @@ -164,10 +164,10 @@ contract FoundryDebugTests is KontrolBase { function test_proveCrossChainSendERC20() public { /* Preconditions */ - address _from = address(376793390874373408599387495934666716005045108752); + address _from = address(728815563385977040452943777879061427756277306519); address _to = address(728815563385977040452943777879061427756277306519); uint256 _amount = 0; - uint256 _chainId = 2; + uint256 _chainId = 0; // Mint the amount to send vm.prank(Predeploys.L2_STANDARD_BRIDGE); sourceToken.mint(_from, _amount); @@ -201,7 +201,7 @@ contract FoundryDebugTests is KontrolBase { } } // VV1__to_114b9705 = 728815563385977040452943777879061427756277306519 - // VV0__from_114b9705 = 376793390874373408599387495934666716005045108752 + // VV0__from_114b9705 = 728815563385977040452943777879061427756277306519 // VV2__amount_114b9705 = 0 - // VV3__chainId_114b9705 = 2 + // VV3__chainId_114b9705 = 0 } diff --git a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol index 30a9d5540411..cd648791ad21 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol @@ -92,6 +92,7 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { public { setUpInlined(); + kevm.symbolicStorage(address(MESSENGER)); /* Preconditions */ vm.assume(_to != address(0)); @@ -99,7 +100,7 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { vm.assume(notBuiltinAddress(_to)); vm.assume(notBuiltinAddress(_sender)); - MESSENGER.forTest_setCustomCrossDomainSender(_crossDomainSender); + // MESSENGER.forTest_setCustomCrossDomainSender(_crossDomainSender); // Expect the cross domain sender to be emitted so after confirming it matches, we can use it for checks vm.expectEmit(true, true, true, true); @@ -109,7 +110,8 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /* Action */ try sourceToken.relayERC20(_from, _to, _amount) { /* Postconditions */ - assert(_sender == address(MESSENGER) && _crossDomainSender == address(sourceToken)); + assert(_sender == address(MESSENGER)); + assert(_crossDomainSender == address(sourceToken)); } catch { // Emit to bypass the check when the call fails emit CrossDomainMessageSender(_crossDomainSender); @@ -125,8 +127,6 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /* Preconditions */ vm.assume(_to != address(0)); vm.assume(_to != address(Predeploys.CROSS_L2_INBOX) && _to != address(MESSENGER)); - // TODO - vm.assume(_from != address(0)); vm.assume(notBuiltinAddress(_from)); vm.assume(notBuiltinAddress(_to)); @@ -254,12 +254,14 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { function prove_burn(address _from, uint256 _amount) public { setUpInlined(); + kevm.symbolicStorage(address(sourceToken)); + /* Preconditions */ vm.assume(_from != address(0)); vm.assume(notBuiltinAddress(_from)); - vm.prank(Predeploys.L2_STANDARD_BRIDGE); - sourceToken.mint(_from, _amount); + // vm.prank(Predeploys.L2_STANDARD_BRIDGE); + // sourceToken.mint(_from, _amount); uint256 _totalSupplyBefore = sourceToken.totalSupply(); uint256 _balanceBefore = sourceToken.balanceOf(_from); @@ -291,6 +293,8 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { function prove_crossChainSendERC20(address _from, address _to, uint256 _amount, uint256 _chainId) public { setUpInlined(); + kevm.symbolicStorage(address(sourceToken)); + vm.assume(notBuiltinAddress(_from)); vm.assume(notBuiltinAddress(_to)); @@ -298,10 +302,6 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { vm.assume(_to != address(0)); vm.assume(_from != address(0)); - // Mint the amount to send - vm.prank(Predeploys.L2_STANDARD_BRIDGE); - sourceToken.mint(_from, _amount); - uint256 fromBalanceBefore = sourceToken.balanceOf(_from); uint256 toBalanceBefore = destToken.balanceOf(_to); uint256 sourceTotalSupplyBefore = sourceToken.totalSupply(); From d89717f75fcb823b846fbca3c34f374e7b31bffd Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Fri, 6 Sep 2024 19:01:57 -0300 Subject: [PATCH 34/48] chore: optimize tests and apply symbolic storage everywhere possible --- packages/contracts-bedrock/kontrol.toml | 70 +++++++++---------- .../{kontrol => }/FoundryDebugSymbolic.t.sol | 1 - .../kontrol/OptimismSuperchainERC20.k.sol | 28 ++++---- .../kontrol/helpers/MockL2ToL2Messenger.sol | 1 - 4 files changed, 48 insertions(+), 52 deletions(-) rename packages/contracts-bedrock/test/properties/{kontrol => }/FoundryDebugSymbolic.t.sol (99%) diff --git a/packages/contracts-bedrock/kontrol.toml b/packages/contracts-bedrock/kontrol.toml index cfdc385ff02a..dcd4d584e2af 100644 --- a/packages/contracts-bedrock/kontrol.toml +++ b/packages/contracts-bedrock/kontrol.toml @@ -1,44 +1,44 @@ [build.default] -foundry-project-root = '.' -regen = false -rekompile = false -verbose = false -debug = false -auxiliary-lemmas = true +foundry-project-root = './' +regen = false +rekompile = false +verbose = false +debug = false +auxiliary-lemmas = true -# require = 'lemmas.k' -# module-import = 'TestBase:KONTROL-LEMMAS' +# require = 'lemmas.k' +# module-import = 'TestBase:KONTROL-LEMMAS' [prove.default] -foundry-project-root = '.' -verbose = false -debug = false -max-depth = 25000 -reinit = false -cse = false -workers = 1 -failure-information = true +foundry-project-root = '.' +verbose = false +debug = false +max-depth = 25000 +reinit = false +cse = false +workers = 1 +failure-information = true counterexample-information = true -minimize-proofs = false -fail-fast = true -smt-timeout = 1000 -break-every-step = false -break-on-jumpi = false -break-on-calls = false -break-on-storage = false -break-on-basic-blocks = false -break-on-cheatcodes = false -run-constructor = true -symbolic-immutables = false -schedule = CANCUN -init-node-from = supererc20-state-diff/StateDiff.json +minimize-proofs = false +fail-fast = true +smt-timeout = 1000 +break-every-step = false +break-on-jumpi = false +break-on-calls = false +break-on-storage = false +break-on-basic-blocks = false +break-on-cheatcodes = false +run-constructor = true +symbolic-immutables = false +schedule = CANCUN +init-node-from = supererc20-state-diff/StateDiff.json [show.default] -foundry-project-root = '.' -verbose = false -debug = false -use-hex-encoding = false +foundry-project-root = '.' +verbose = false +debug = false +use-hex-encoding = false [view-kcfg.default] -foundry-project-root = '.' -use-hex-encoding = false \ No newline at end of file +foundry-project-root = '.' +use-hex-encoding = false diff --git a/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol b/packages/contracts-bedrock/test/properties/FoundryDebugSymbolic.t.sol similarity index 99% rename from packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol rename to packages/contracts-bedrock/test/properties/FoundryDebugSymbolic.t.sol index fb7e2157bf8e..f8895c57e3f7 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/FoundryDebugSymbolic.t.sol +++ b/packages/contracts-bedrock/test/properties/FoundryDebugSymbolic.t.sol @@ -5,7 +5,6 @@ import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; import { Predeploys } from "src/libraries/Predeploys.sol"; import { MockL2ToL2Messenger } from "test/properties/kontrol/helpers/MockL2ToL2Messenger.sol"; import { KontrolBase } from "test/properties/kontrol/KontrolBase.sol"; -import { InitialState } from "./deployments/InitialState.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol"; import "forge-std/Test.sol"; diff --git a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol index cd648791ad21..8abad75d6fef 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol @@ -49,7 +49,6 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /// @custom:property-id 6 /// @custom:property Calls to sendERC20 succeed as long as caller has enough balance function prove_sendERC20SucceedsOnlyIfEnoughBalance( - uint256 _initialBalance, address _from, uint256 _amount, address _to, @@ -59,6 +58,8 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { { setUpInlined(); + kevm.symbolicStorage(address(sourceToken)); + /* Preconditions */ vm.assume(_to != address(0)); vm.assume(_from != address(0)); @@ -67,8 +68,10 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { vm.assume(notBuiltinAddress(_to)); // Can't deal to unsupported cheatcode - vm.prank(Predeploys.L2_STANDARD_BRIDGE); - sourceToken.mint(_from, _initialBalance); + // vm.prank(Predeploys.L2_STANDARD_BRIDGE); + // sourceToken.mint(_from, _initialBalance); + + uint256 _initialBalance = sourceToken.balanceOf(_from); vm.prank(_from); /* Action */ @@ -92,7 +95,6 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { public { setUpInlined(); - kevm.symbolicStorage(address(MESSENGER)); /* Preconditions */ vm.assume(_to != address(0)); @@ -100,12 +102,9 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { vm.assume(notBuiltinAddress(_to)); vm.assume(notBuiltinAddress(_sender)); + kevm.symbolicStorage(address(MESSENGER)); // MESSENGER.forTest_setCustomCrossDomainSender(_crossDomainSender); - // Expect the cross domain sender to be emitted so after confirming it matches, we can use it for checks - vm.expectEmit(true, true, true, true); - emit CrossDomainMessageSender(_crossDomainSender); - vm.prank(_sender); /* Action */ try sourceToken.relayERC20(_from, _to, _amount) { @@ -126,7 +125,8 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /* Preconditions */ vm.assume(_to != address(0)); - vm.assume(_to != address(Predeploys.CROSS_L2_INBOX) && _to != address(MESSENGER)); + vm.assume(_to != address(Predeploys.CROSS_L2_INBOX)); + vm.assume(_to != address(MESSENGER)); vm.assume(notBuiltinAddress(_from)); vm.assume(notBuiltinAddress(_to)); @@ -154,8 +154,6 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { vm.assume(_to != address(0)); vm.assume(notBuiltinAddress(_from)); vm.assume(notBuiltinAddress(_to)); - // TODO - vm.assume(_to != address(1)); uint256 _totalSupplyBefore = sourceToken.totalSupply(); uint256 _fromBalanceBefore = sourceToken.balanceOf(_from); @@ -254,8 +252,6 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { function prove_burn(address _from, uint256 _amount) public { setUpInlined(); - kevm.symbolicStorage(address(sourceToken)); - /* Preconditions */ vm.assume(_from != address(0)); vm.assume(notBuiltinAddress(_from)); @@ -263,6 +259,8 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { // vm.prank(Predeploys.L2_STANDARD_BRIDGE); // sourceToken.mint(_from, _amount); + kevm.symbolicStorage(address(sourceToken)); + uint256 _totalSupplyBefore = sourceToken.totalSupply(); uint256 _balanceBefore = sourceToken.balanceOf(_from); @@ -293,8 +291,6 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { function prove_crossChainSendERC20(address _from, address _to, uint256 _amount, uint256 _chainId) public { setUpInlined(); - kevm.symbolicStorage(address(sourceToken)); - vm.assume(notBuiltinAddress(_from)); vm.assume(notBuiltinAddress(_to)); @@ -302,6 +298,8 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { vm.assume(_to != address(0)); vm.assume(_from != address(0)); + kevm.symbolicStorage(address(sourceToken)); + uint256 fromBalanceBefore = sourceToken.balanceOf(_from); uint256 toBalanceBefore = destToken.balanceOf(_to); uint256 sourceTotalSupplyBefore = sourceToken.totalSupply(); diff --git a/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol b/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol index dc4af91bca30..a199bfb792c1 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol @@ -61,7 +61,6 @@ contract MockL2ToL2Messenger { function crossDomainMessageSender() external view returns (address _sender) { if (crossDomainSenderSet) _sender = customCrossDomainSender; else if (msg.sender == SOURCE_TOKEN) _sender = SOURCE_TOKEN; - // Leaving the `if` here for clarity else if (msg.sender == DESTINATION_TOKEN) _sender = DESTINATION_TOKEN; } From d6d46daf28406af7955bf68c2628382cccef5134 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Fri, 6 Sep 2024 23:46:42 -0300 Subject: [PATCH 35/48] chore: remove symbolik storage feature since it failed --- .../properties/FoundryDebugSymbolic.t.sol | 42 +------------------ .../kontrol/OptimismSuperchainERC20.k.sol | 22 ++++------ 2 files changed, 11 insertions(+), 53 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/FoundryDebugSymbolic.t.sol b/packages/contracts-bedrock/test/properties/FoundryDebugSymbolic.t.sol index f8895c57e3f7..fb01e0ec40b7 100644 --- a/packages/contracts-bedrock/test/properties/FoundryDebugSymbolic.t.sol +++ b/packages/contracts-bedrock/test/properties/FoundryDebugSymbolic.t.sol @@ -62,48 +62,10 @@ contract FoundryDebugTests is KontrolBase { assert(MESSENGER.crossDomainMessageSender() == address(destToken)); // Custom cross domain sender - assert(MESSENGER.crossDomainMessageSender() == address(0)); + MESSENGER.forTest_setCustomCrossDomainSender(address(420)); + assert(MESSENGER.crossDomainMessageSender() == address(420)); } - /// @custom:property-id 7 - /// @custom:property Calls to relayERC20 always succeed as long as the sender the cross-domain caller are valid - function test_proveRelayERC20OnlyFromL2ToL2Messenger() - // address _crossDomainSender, - // address _sender, - // address _from, - // address _to, - // uint256 _amount - public - { - address _crossDomainSender = address(263400868551549723330807389252719309078400616203); - address _sender = address(376793390874373408599387495934666716005045108771); - address _from = address(645326474426547203313410069153905908525362434350); - address _to = address(728815563385977040452943777879061427756277306519); - uint256 _amount = 0; - - /* Precondition */ - MESSENGER.forTest_setCustomCrossDomainSender(_crossDomainSender); - - // Expect the cross domain sender to be emitted so after confirming it matches, we can use it for checks - - vm.prank(_sender); - /* Action */ - try sourceToken.relayERC20(_from, _to, _amount) { - /* Postconditions */ - assert(_sender == address(MESSENGER) && MESSENGER.customCrossDomainSender() == address(sourceToken)); - } catch { - // Emit to bypass the check when the call fails - assert(_sender != address(MESSENGER) || MESSENGER.customCrossDomainSender() != address(sourceToken)); - } - } - // VV1__sender_114b9705 = 376793390874373408599387495934666716005045108771 - // VV3__to_114b9705 = 728815563385977040452943777879061427756277306519 - // VV0__crossDomainSender_114b9705 = 263400868551549723330807389252719309078400616203 - // ORIGIN_ID = 645326474426547203313410069153905908525362434350 - // CALLER_ID = 645326474426547203313410069153905908525362434350 - // VV4__amount_114b9705 = 0 - // VV2__from_114b9705 = 645326474426547203313410069153905908525362434350 - /// @custom:property-id 8 /// @custom:property `sendERC20` with a value of zero does not modify accounting function test_proveSendERC20ZeroCall() public { diff --git a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol index 8abad75d6fef..64cc8099e28c 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol @@ -49,6 +49,7 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /// @custom:property-id 6 /// @custom:property Calls to sendERC20 succeed as long as caller has enough balance function prove_sendERC20SucceedsOnlyIfEnoughBalance( + uint256 _initialBalance, address _from, uint256 _amount, address _to, @@ -58,8 +59,6 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { { setUpInlined(); - kevm.symbolicStorage(address(sourceToken)); - /* Preconditions */ vm.assume(_to != address(0)); vm.assume(_from != address(0)); @@ -68,10 +67,8 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { vm.assume(notBuiltinAddress(_to)); // Can't deal to unsupported cheatcode - // vm.prank(Predeploys.L2_STANDARD_BRIDGE); - // sourceToken.mint(_from, _initialBalance); - - uint256 _initialBalance = sourceToken.balanceOf(_from); + vm.prank(Predeploys.L2_STANDARD_BRIDGE); + sourceToken.mint(_from, _initialBalance); vm.prank(_from); /* Action */ @@ -102,8 +99,8 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { vm.assume(notBuiltinAddress(_to)); vm.assume(notBuiltinAddress(_sender)); - kevm.symbolicStorage(address(MESSENGER)); - // MESSENGER.forTest_setCustomCrossDomainSender(_crossDomainSender); + // kevm.symbolicStorage(address(MESSENGER)); + MESSENGER.forTest_setCustomCrossDomainSender(_crossDomainSender); vm.prank(_sender); /* Action */ @@ -256,10 +253,8 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { vm.assume(_from != address(0)); vm.assume(notBuiltinAddress(_from)); - // vm.prank(Predeploys.L2_STANDARD_BRIDGE); - // sourceToken.mint(_from, _amount); - - kevm.symbolicStorage(address(sourceToken)); + vm.prank(Predeploys.L2_STANDARD_BRIDGE); + sourceToken.mint(_from, _amount); uint256 _totalSupplyBefore = sourceToken.totalSupply(); uint256 _balanceBefore = sourceToken.balanceOf(_from); @@ -298,7 +293,8 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { vm.assume(_to != address(0)); vm.assume(_from != address(0)); - kevm.symbolicStorage(address(sourceToken)); + vm.prank(Predeploys.L2_STANDARD_BRIDGE); + sourceToken.mint(_from, _amount); uint256 fromBalanceBefore = sourceToken.balanceOf(_from); uint256 toBalanceBefore = destToken.balanceOf(_to); From 6b83938c04efeed7da682dca0f94ade4845de0a1 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Sat, 7 Sep 2024 15:20:07 -0300 Subject: [PATCH 36/48] chore: add quotes on kontrol toml --- packages/contracts-bedrock/kontrol.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts-bedrock/kontrol.toml b/packages/contracts-bedrock/kontrol.toml index dcd4d584e2af..4970211ee7a2 100644 --- a/packages/contracts-bedrock/kontrol.toml +++ b/packages/contracts-bedrock/kontrol.toml @@ -31,7 +31,7 @@ break-on-cheatcodes = false run-constructor = true symbolic-immutables = false schedule = CANCUN -init-node-from = supererc20-state-diff/StateDiff.json +init-node-from = 'supererc20-state-diff/StateDiff.json' [show.default] foundry-project-root = '.' From b4dd8dff5c03e61b72c3e2868745eb548c627860 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Sat, 7 Sep 2024 15:23:28 -0300 Subject: [PATCH 37/48] chore: update models to run on foundry debug, they dont fail on foundry --- .../properties/FoundryDebugSymbolic.t.sol | 68 ++++--------------- 1 file changed, 12 insertions(+), 56 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/FoundryDebugSymbolic.t.sol b/packages/contracts-bedrock/test/properties/FoundryDebugSymbolic.t.sol index fb01e0ec40b7..8fbe8e5c668b 100644 --- a/packages/contracts-bedrock/test/properties/FoundryDebugSymbolic.t.sol +++ b/packages/contracts-bedrock/test/properties/FoundryDebugSymbolic.t.sol @@ -70,8 +70,8 @@ contract FoundryDebugTests is KontrolBase { /// @custom:property `sendERC20` with a value of zero does not modify accounting function test_proveSendERC20ZeroCall() public { /* Preconditions */ - address _from = address(376793390874373408599387495934666716005045108772); // 0x4200000000000000000000000000000000000024 - address _to = address(728815563385977040452943777879061427756277306519); // 0x7Fa9385Be102aC3eac297483DD6233d62B3e1497 + address _from = address(511347974759188522659820409854212399244223280810); + address _to = address(376793390874373408599387495934666716005045108769); // 0x7Fa9385Be102aC3eac297483DD6233d62B3e1497 uint256 _chainId = 0; uint256 _totalSupplyBefore = sourceToken.totalSupply(); @@ -87,20 +87,19 @@ contract FoundryDebugTests is KontrolBase { assert(sourceToken.balanceOf(_from) == _fromBalanceBefore); assert(sourceToken.balanceOf(_to) == _toBalanceBefore); } - - // VV2__chainId_114b9705 = 0 - // VV0__from_114b9705 = 376793390874373408599387495934666716005045108772 - // NUMBER_CELL = 16777217 - // VV1__to_114b9705 = 728815563385977040452943777879061427756277306519 + // ORIGIN_ID = 645326474426547203313410069153905908525362434350 // CALLER_ID = 645326474426547203313410069153905908525362434350 + // NUMBER_CELL = 16777217 + // VV2__chainId_114b9705 = 0 + // VV0__from_114b9705 = 511347974759188522659820409854212399244223280810 // TIMESTAMP_CELL = 1073741825 - // ORIGIN_ID = 645326474426547203313410069153905908525362434350 + // VV1__to_114b9705 = 376793390874373408599387495934666716005045108769 /// @custom:property-id 9 /// @custom:property `relayERC20` with a value of zero does not modify accounting function test_proveRelayERC20ZeroCall() public { /* Preconditions */ - address _from = address(728815563385977040452943777879061427756277306519); + address _from = address(645326474426547203313410069153905908525362434350); address _to = address(728815563385977040452943777879061427756277306519); uint256 _totalSupplyBefore = sourceToken.totalSupply(); @@ -116,53 +115,10 @@ contract FoundryDebugTests is KontrolBase { assert(sourceToken.balanceOf(_from) == _fromBalanceBefore); assert(sourceToken.balanceOf(_to) == _toBalanceBefore); } - // NUMBER_CELL = 16777217 - // VV1__to_114b9705 = 728815563385977040452943777879061427756277306519 - // CALLER_ID = 645326474426547203313410069153905908525362434350 - // TIMESTAMP_CELL = 1073741825 + // VV0__from_114b9705 = 645326474426547203313410069153905908525362434350 // ORIGIN_ID = 645326474426547203313410069153905908525362434350 - // VV0__from_114b9705 = 728815563385977040452943777879061427756277306519 - - function test_proveCrossChainSendERC20() public { - /* Preconditions */ - address _from = address(728815563385977040452943777879061427756277306519); - address _to = address(728815563385977040452943777879061427756277306519); - uint256 _amount = 0; - uint256 _chainId = 0; - // Mint the amount to send - vm.prank(Predeploys.L2_STANDARD_BRIDGE); - sourceToken.mint(_from, _amount); - - uint256 fromBalanceBefore = sourceToken.balanceOf(_from); - uint256 toBalanceBefore = destToken.balanceOf(_to); - uint256 sourceTotalSupplyBefore = sourceToken.totalSupply(); - uint256 destTotalSupplyBefore = destToken.totalSupply(); - - vm.prank(_from); - /* Action */ - try sourceToken.sendERC20(_to, _amount, _chainId) { - /* Postconditions */ - // Source - assert(sourceToken.balanceOf(_from) == fromBalanceBefore - _amount); - assert(sourceToken.totalSupply() == sourceTotalSupplyBefore - _amount); - - // Destination - if (_chainId == DESTINATION_CHAIN_ID) { - // If the destination chain matches the one of the dest token, check that the amount was minted - assert(destToken.balanceOf(_to) == toBalanceBefore + _amount); - assert(destToken.totalSupply() == destTotalSupplyBefore + _amount); - } else { - // Otherwise the balances should remain the same - assert(destToken.balanceOf(_to) == toBalanceBefore); - assert(destToken.totalSupply() == destTotalSupplyBefore); - } - } catch { - // Shouldn't fail - assert(false); - } - } + // CALLER_ID = 645326474426547203313410069153905908525362434350 + // NUMBER_CELL = 16777217 // VV1__to_114b9705 = 728815563385977040452943777879061427756277306519 - // VV0__from_114b9705 = 728815563385977040452943777879061427756277306519 - // VV2__amount_114b9705 = 0 - // VV3__chainId_114b9705 = 0 + // TIMESTAMP_CELL = 1073741825 } From 9633e793be688ba498f3ee2d7d690793ff97dc26 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Mon, 9 Sep 2024 15:34:23 -0300 Subject: [PATCH 38/48] fix: kontrol toml file --- packages/contracts-bedrock/kontrol.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts-bedrock/kontrol.toml b/packages/contracts-bedrock/kontrol.toml index 4970211ee7a2..4a7b02a4f85b 100644 --- a/packages/contracts-bedrock/kontrol.toml +++ b/packages/contracts-bedrock/kontrol.toml @@ -30,8 +30,8 @@ break-on-basic-blocks = false break-on-cheatcodes = false run-constructor = true symbolic-immutables = false -schedule = CANCUN -init-node-from = 'supererc20-state-diff/StateDiff.json' +schedule = 'CANCUN' +init-node-from-diff = 'supererc20-state-diff/StateDiff.json' [show.default] foundry-project-root = '.' From f6a03efb2189270d6c542b69ca2fca121e9d050e Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:08:28 -0300 Subject: [PATCH 39/48] fix: kontrol toml --- packages/contracts-bedrock/kontrol.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts-bedrock/kontrol.toml b/packages/contracts-bedrock/kontrol.toml index 4a7b02a4f85b..998cd05b41f3 100644 --- a/packages/contracts-bedrock/kontrol.toml +++ b/packages/contracts-bedrock/kontrol.toml @@ -10,7 +10,7 @@ auxiliary-lemmas = true # module-import = 'TestBase:KONTROL-LEMMAS' [prove.default] -foundry-project-root = '.' +foundry-project-root = './' verbose = false debug = false max-depth = 25000 @@ -31,7 +31,7 @@ break-on-cheatcodes = false run-constructor = true symbolic-immutables = false schedule = 'CANCUN' -init-node-from-diff = 'supererc20-state-diff/StateDiff.json' +init-node-from = 'supererc20-state-diff/StateDiff.json' [show.default] foundry-project-root = '.' From 6a9b8bd229bde15c4884525ae2ccc6ab94e8a2b0 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:14:21 -0300 Subject: [PATCH 40/48] chore: remove unnecessary changes --- .../test/kontrol/scripts/run-kontrol.sh | 3 +- .../kontrol/optimism-superchain-lemmas.md | 33 ------------------- 2 files changed, 1 insertion(+), 35 deletions(-) delete mode 100644 packages/contracts-bedrock/test/properties/kontrol/optimism-superchain-lemmas.md diff --git a/packages/contracts-bedrock/test/kontrol/scripts/run-kontrol.sh b/packages/contracts-bedrock/test/kontrol/scripts/run-kontrol.sh index e87527086aeb..2d61b4243fe6 100755 --- a/packages/contracts-bedrock/test/kontrol/scripts/run-kontrol.sh +++ b/packages/contracts-bedrock/test/kontrol/scripts/run-kontrol.sh @@ -140,8 +140,7 @@ if [ "$SCRIPT_TESTS" == true ]; then "L1StandardBridgeKontrol.prove_finalizeBridgeERC20_paused" \ "L1StandardBridgeKontrol.prove_finalizeBridgeETH_paused" \ "L1ERC721BridgeKontrol.prove_finalizeBridgeERC721_paused" \ - "L1CrossDomainMessengerKontrol.prove_relayMessage_paused" \ - "OptimismSuperchainERC20Kontrol.prove_setup" + "L1CrossDomainMessengerKontrol.prove_relayMessage_paused" ) elif [ "$CUSTOM_TESTS" != 0 ]; then test_list=( "${@:${CUSTOM_TESTS}}" ) diff --git a/packages/contracts-bedrock/test/properties/kontrol/optimism-superchain-lemmas.md b/packages/contracts-bedrock/test/properties/kontrol/optimism-superchain-lemmas.md deleted file mode 100644 index 6cb29280402f..000000000000 --- a/packages/contracts-bedrock/test/properties/kontrol/optimism-superchain-lemmas.md +++ /dev/null @@ -1,33 +0,0 @@ -```k -requires "foundry.md" - -module OPTIMISM-SUPERCHAIN-LEMMAS - imports BOOL - imports FOUNDRY - imports INT-SYMBOLIC - - // Convert booleans to their word equivalents - rule bool2Word(true) => 1 - rule bool2Word(false) => 0 - - // Associativity and Commutativity Rules: - rule A +Int (B +Int C) => (A +Int B) +Int C - rule A *Int B => B *Int A - - // Comparison Normalization: - rule A +Int B A A false requires 0 <=Int B - rule A false requires 0 <=Int B - - rule A -Int A => 0 - rule 0 +Int A => A - rule A +Int 0 => A - rule A -Int 0 => A - - rule chop(I) => I requires #rangeUInt(256, I) - rule chop (chop (X:Int) +Int Y:Int) => chop (X +Int Y) - rule chop (X:Int +Int chop (Y:Int)) => chop (X +Int Y) -endmodule -``` From a9b30501ba9368959386fc0be3d2f9dc462a0a2e Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Tue, 10 Sep 2024 12:16:22 -0300 Subject: [PATCH 41/48] chore: remove halmos --- .gitmodules | 3 - packages/contracts-bedrock/foundry.toml | 8 +- packages/contracts-bedrock/halmos.toml | 15 - packages/contracts-bedrock/justfile | 6 - .../contracts-bedrock/lib/halmos-cheatcodes | 1 - .../test/invariants/PROPERTIES.md | 64 ++-- .../test/properties/PROPERTIES.md | 100 +++--- .../properties/halmos/MockL2ToL2Messenger.sol | 65 ---- .../halmos/OptimismSuperchainERC20.t.sol | 302 ------------------ .../test/properties/helpers/HalmosBase.sol | 19 -- 10 files changed, 83 insertions(+), 500 deletions(-) delete mode 100644 packages/contracts-bedrock/halmos.toml delete mode 160000 packages/contracts-bedrock/lib/halmos-cheatcodes delete mode 100644 packages/contracts-bedrock/test/properties/halmos/MockL2ToL2Messenger.sol delete mode 100644 packages/contracts-bedrock/test/properties/halmos/OptimismSuperchainERC20.t.sol delete mode 100644 packages/contracts-bedrock/test/properties/helpers/HalmosBase.sol diff --git a/.gitmodules b/.gitmodules index 222d45be7ccc..21ecaedbb77a 100644 --- a/.gitmodules +++ b/.gitmodules @@ -29,6 +29,3 @@ [submodule "packages/contracts-bedrock/lib/openzeppelin-contracts-v5"] path = packages/contracts-bedrock/lib/openzeppelin-contracts-v5 url = https://github.com/OpenZeppelin/openzeppelin-contracts -[submodule "packages/contracts-bedrock/lib/halmos-cheatcodes"] - path = packages/contracts-bedrock/lib/halmos-cheatcodes - url = https://github.com/a16z/halmos-cheatcodes diff --git a/packages/contracts-bedrock/foundry.toml b/packages/contracts-bedrock/foundry.toml index 1e268eadb67b..03cccb74539e 100644 --- a/packages/contracts-bedrock/foundry.toml +++ b/packages/contracts-bedrock/foundry.toml @@ -21,8 +21,7 @@ remappings = [ 'ds-test/=lib/forge-std/lib/ds-test/src', 'safe-contracts/=lib/safe-contracts/contracts', 'kontrol-cheatcodes/=lib/kontrol-cheatcodes/src', - 'gelato/=lib/automate/contracts', - 'halmos-cheatcodes/=lib/halmos-cheatcodes/' + 'gelato/=lib/automate/contracts' ] extra_output = ['devdoc', 'userdoc', 'metadata', 'storageLayout'] bytecode_hash = 'none' @@ -104,11 +103,6 @@ src = 'test/properties/medusa/' test = 'test/properties/medusa/' script = 'test/properties/medusa/' -[profile.halmos] -src = 'test/properties/halmos/' -test = 'test/properties/halmos/' -script = 'test/properties/halmos/' - [profile.kontrol-properties] src = "test/properties/kontrol" test = "test/properties/kontrol" diff --git a/packages/contracts-bedrock/halmos.toml b/packages/contracts-bedrock/halmos.toml deleted file mode 100644 index c431c6c3a532..000000000000 --- a/packages/contracts-bedrock/halmos.toml +++ /dev/null @@ -1,15 +0,0 @@ -# Halmos configuration file - -## The version needed is `halmos 0.1.15.dev2+gc3f45dd` -## Just running `halmos` will run the tests with the default configuration - -[global] -# Contract to test -match-contract = "SymTest_" - -# Path to the Forge artifacts directory -forge_build_out = "./forge-artifacts" - - -# Storage layout -storage_layout = "generic" \ No newline at end of file diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index 696809d86476..b770adbe3552 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -30,12 +30,6 @@ test-medusa timeout='100': FOUNDRY_PROFILE=medusa medusa fuzz --timeout {{timeout}} -test-halmos-all VERBOSE="-v": - FOUNDRY_PROFILE=halmos halmos {{VERBOSE}} - -test-halmos TEST VERBOSE="-v": - FOUNDRY_PROFILE=halmos halmos --function {{TEST}} {{VERBOSE}} - test-rerun: build-go-ffi forge test --rerun -vvv diff --git a/packages/contracts-bedrock/lib/halmos-cheatcodes b/packages/contracts-bedrock/lib/halmos-cheatcodes deleted file mode 160000 index c0d865508c0f..000000000000 --- a/packages/contracts-bedrock/lib/halmos-cheatcodes +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c0d865508c0fee0a11b97732c5e90f9cad6b65a5 diff --git a/packages/contracts-bedrock/test/invariants/PROPERTIES.md b/packages/contracts-bedrock/test/invariants/PROPERTIES.md index 5a5cc71d73b5..f06c9be65f16 100644 --- a/packages/contracts-bedrock/test/invariants/PROPERTIES.md +++ b/packages/contracts-bedrock/test/invariants/PROPERTIES.md @@ -10,56 +10,56 @@ legend: ## Unit test -| id | description | halmos | medusa | -| --- | ---------------------------------------------------------------------------------- | ------ | ------ | -| 0 | supertoken token address does not depend on the executing chain’s chainID | [ ] | [ ] | -| 1 | supertoken token address depends on name, remote token, address and decimals | [ ] | [ ] | -| 2 | convert() should only allow converting legacy tokens to supertoken and viceversa | [ ] | [ ] | -| 3 | convert() only allows migrations between tokens representing the same remote asset | [ ] | [ ] | -| 4 | convert() only allows migrations from tokens with the same decimals | [ ] | [ ] | -| 5 | convert() burns the same amount of one token that it mints of the other | [ ] | [ ] | +| id | description | kontrol | medusa | +| --- | ---------------------------------------------------------------------------------- | ------- | ------ | +| 0 | supertoken token address does not depend on the executing chain’s chainID | [ ] | [ ] | +| 1 | supertoken token address depends on name, remote token, address and decimals | [ ] | [ ] | +| 2 | convert() should only allow converting legacy tokens to supertoken and viceversa | [ ] | [ ] | +| 3 | convert() only allows migrations between tokens representing the same remote asset | [ ] | [ ] | +| 4 | convert() only allows migrations from tokens with the same decimals | [ ] | [ ] | +| 5 | convert() burns the same amount of one token that it mints of the other | [ ] | [ ] | ## Valid state -| id | description | halmos | medusa | +| id | description | kontrol | medusa | | --- | ------------------------------------------------------------------------------------------ | ------- | ------ | | 6 | calls to sendERC20 succeed as long as caller has enough balance | [x] | [ ] | | 7 | calls to relayERC20 always succeed as long as the sender and cross-domain caller are valid | **[~]** | [ ] | ## Variable transition -| id | description | halmos | medusa | -| --- | ------------------------------------------------------------------------------------------------- | ------ | ------ | -| 8 | sendERC20 with a value of zero does not modify accounting | [x] | [ ] | -| 9 | relayERC20 with a value of zero does not modify accounting | [x] | [ ] | -| 10 | sendERC20 decreases the token's totalSupply in the source chain exactly by the input amount | [x] | [ ] | -| 11 | relayERC20 increases the token's totalSupply in the destination chain exactly by the input amount | [x] | [ ] | -| 12 | supertoken total supply only increases on calls to mint() by the L2toL2StandardBridge | [x] | [ ] | -| 13 | supertoken total supply only decreases on calls to burn() by the L2toL2StandardBridge | [x] | [ ] | -| 14 | supertoken total supply starts at zero | [x] | [ ] | -| 15 | deploying a supertoken registers its remote token in the factory | [ ] | [ ] | -| 16 | deploying an OptimismMintableERC20 registers its remote token in the factory | [ ] | [ ] | +| id | description | kontrol | medusa | +| --- | ------------------------------------------------------------------------------------------------- | ------- | ------ | +| 8 | sendERC20 with a value of zero does not modify accounting | [x] | [ ] | +| 9 | relayERC20 with a value of zero does not modify accounting | [x] | [ ] | +| 10 | sendERC20 decreases the token's totalSupply in the source chain exactly by the input amount | [x] | [ ] | +| 11 | relayERC20 increases the token's totalSupply in the destination chain exactly by the input amount | [x] | [ ] | +| 12 | supertoken total supply only increases on calls to mint() by the L2toL2StandardBridge | [x] | [ ] | +| 13 | supertoken total supply only decreases on calls to burn() by the L2toL2StandardBridge | [x] | [ ] | +| 14 | supertoken total supply starts at zero | [x] | [ ] | +| 15 | deploying a supertoken registers its remote token in the factory | [ ] | [ ] | +| 16 | deploying an OptimismMintableERC20 registers its remote token in the factory | [ ] | [ ] | ## High level -| id | description | halmos | medusa | -| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------ | ------ | -| 17 | only calls to convert(legacy, super) can increase a supertoken’s total supply across chains | [ ] | [ ] | -| 18 | only calls to convert(super, legacy) can decrease a supertoken’s total supply across chains | [ ] | [ ] | -| 19 | sum of total supply across all chains is always <= to convert(legacy, super)- convert(super, legacy) | [ ] | [ ] | -| 20 | tokens sendERC20-ed on a source chain to a destination chain can be relayERC20-ed on it as long as the source chain is in the dependency set of the destination chain | [ ] | [ ] | -| 21 | sum of supertoken total supply across all chains is = to convert(legacy, super)- convert(super, legacy) when all cross-chain messages are processed | [ ] | [ ] | +| id | description | kontrol | medusa | +| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------ | +| 17 | only calls to convert(legacy, super) can increase a supertoken’s total supply across chains | [ ] | [ ] | +| 18 | only calls to convert(super, legacy) can decrease a supertoken’s total supply across chains | [ ] | [ ] | +| 19 | sum of total supply across all chains is always <= to convert(legacy, super)- convert(super, legacy) | [ ] | [ ] | +| 20 | tokens sendERC20-ed on a source chain to a destination chain can be relayERC20-ed on it as long as the source chain is in the dependency set of the destination chain | [ ] | [ ] | +| 21 | sum of supertoken total supply across all chains is = to convert(legacy, super)- convert(super, legacy) when all cross-chain messages are processed | [ ] | [ ] | ## Atomic bridging pseudo-properties As another layer of defense, the following properties are defined which assume bridging operations to be atomic (that is, the sequencer and L2Inbox and CrossDomainMessenger contracts are fully abstracted away, `sendERC20` triggering the `relayERC20` call on the same transaction) It’s worth noting that these properties will not hold for a live system -| id | description | halmos | medusa | -| --- | ---------------------------------------------------------------------------------------------------------------------------------- | ------ | ------ | -| 22 | sendERC20 decreases sender balance in source chain and increases receiver balance in destination chain exactly by the input amount | [ ] | [x] | -| 23 | sendERC20 decreases total supply in source chain and increases it in destination chain exactly by the input amount | [ ] | [x] | -| 24 | sum of supertoken total supply across all chains is always equal to convert(legacy, super)- convert(super, legacy) | [ ] | [~] | +| id | description | kontrol | medusa | +| --- | ---------------------------------------------------------------------------------------------------------------------------------- | ------- | ------ | +| 22 | sendERC20 decreases sender balance in source chain and increases receiver balance in destination chain exactly by the input amount | [ ] | [x] | +| 23 | sendERC20 decreases total supply in source chain and increases it in destination chain exactly by the input amount | [ ] | [x] | +| 24 | sum of supertoken total supply across all chains is always equal to convert(legacy, super)- convert(super, legacy) | [ ] | [~] | # Expected external interactions diff --git a/packages/contracts-bedrock/test/properties/PROPERTIES.md b/packages/contracts-bedrock/test/properties/PROPERTIES.md index 25dc9b036d3b..f6876df77469 100644 --- a/packages/contracts-bedrock/test/properties/PROPERTIES.md +++ b/packages/contracts-bedrock/test/properties/PROPERTIES.md @@ -5,7 +5,7 @@ This document defines a set of properties global to the supertoken ecosystem, for which we will: - run a [Medusa](https://github.com/crytic/medusa) fuzzing campaign, trying to break system invariants -- formally prove with [Halmos](https://github.com/a16z/halmos) whenever possible +- formally prove with [Kontrol](https://docs.runtimeverification.com/kontrol) whenever possible ## Milestones @@ -23,33 +23,33 @@ Given the [OP monorepo](https://github.com/ethereum-optimism/optimism) already h - including it in the mainline OP monorepo, in a subdirectory of the existing test contracts such as `test/invariants/medusa/superc20/` - keep the campaign in wonderland's fork of the repository, in its own feature branch, in which case the deliverable would consist primarily of: - - a summary of the results, extending this document - - PRs with extra unit tests replicating found issues to the main repo where applicable + - a summary of the results, extending this document + - PRs with extra unit tests replicating found issues to the main repo where applicable ## Contracts in scope -- [ ] [OptimismMintableERC20Factory](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol) (modifications to enable `convert` not yet merged) -- [ ] [OptimismSuperchainERC20](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol1) -- [ ] [OptimismSuperchainERC20Factory](https://github.com/defi-wonderland/optimism/pull/8/files#diff-09838f5703c353d0f7c5ff395acc04c1768ef58becac67404bc17e1fb0018517) (not yet merged) -- [ ] [L2StandardBridgeInterop](https://github.com/defi-wonderland/optimism/pull/10/files#diff-56cf869412631eac0a04a03f7d026596f64a1e00fcffa713bc770d67c6856c2f) (not yet merged) +- [ ] [OptimismMintableERC20Factory](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC20Factory.sol) (modifications to enable `convert` not yet merged) +- [ ] [OptimismSuperchainERC20](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/L2/OptimismSuperchainERC20.sol1) +- [ ] [OptimismSuperchainERC20Factory](https://github.com/defi-wonderland/optimism/pull/8/files#diff-09838f5703c353d0f7c5ff395acc04c1768ef58becac67404bc17e1fb0018517) (not yet merged) +- [ ] [L2StandardBridgeInterop](https://github.com/defi-wonderland/optimism/pull/10/files#diff-56cf869412631eac0a04a03f7d026596f64a1e00fcffa713bc770d67c6856c2f) (not yet merged) ## Behavior assumed correct -- [ ] inclusion of relay transactions -- [ ] sequencer implementation -- [ ] [OptimismMintableERC20](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC20.sol) -- [ ] [L2ToL2CrossDomainMessenger](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2CrossDomainMessenger.sol) -- [ ] [CrossL2Inbox](https://github.com/defi-wonderland/src/L2/CrossL2Inbox.sol) +- [ ] inclusion of relay transactions +- [ ] sequencer implementation +- [ ] [OptimismMintableERC20](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/universal/OptimismMintableERC20.sol) +- [ ] [L2ToL2CrossDomainMessenger](https://github.com/defi-wonderland/optimism/blob/develop/packages/contracts-bedrock/src/L2/L2CrossDomainMessenger.sol) +- [ ] [CrossL2Inbox](https://github.com/defi-wonderland/src/L2/CrossL2Inbox.sol) ## Pain points -- existing fuzzing tools use the same EVM to run the tested contracts as they do for asserting invariants, tracking ghost variables and everything else necessary to provision a fuzzing campaign. While this is usually very convenient, it means that we can’t assert on the behaviour/state of *different* chains from within a fuzzing campaign. This means we will have to walk around the requirement of supertokens having the same address across all chains, and implement a way to mock tokens existing in different chains. We will strive to formally prove it in a unitary fashion to mitigate this in properties 0 and 1 +- existing fuzzing tools use the same EVM to run the tested contracts as they do for asserting invariants, tracking ghost variables and everything else necessary to provision a fuzzing campaign. While this is usually very convenient, it means that we can’t assert on the behaviour/state of _different_ chains from within a fuzzing campaign. This means we will have to walk around the requirement of supertokens having the same address across all chains, and implement a way to mock tokens existing in different chains. We will strive to formally prove it in a unitary fashion to mitigate this in properties 0 and 1 - a buffer to represent 'in transit' messages should be implemented to assert on invariants relating to the non-atomicity of bridging from one chain to another. It is yet to be determined if it’ll be a FIFO queue (assuming ideal message ordering by sequencers) or it’ll have random-access capability to simulate messages arriving out of order ## Definitions -- *legacy token:* an OptimismMintableERC20 or L2StandardERC20 token on the suprechain that has either been deployed by the factory after the liquidity migration upgrade to the latter, or has been deployed before it **but** added to factory’s `deployments` mapping as part of the upgrade. This testing campaign is not concerned with tokens on L1 or not listed in the factory’s `deployments` mapping. -- *supertoken:* a SuperchainERC20 contract deployed by the `OptimismSuperchainERC20Factory` +- _legacy token:_ an OptimismMintableERC20 or L2StandardERC20 token on the suprechain that has either been deployed by the factory after the liquidity migration upgrade to the latter, or has been deployed before it **but** added to factory’s `deployments` mapping as part of the upgrade. This testing campaign is not concerned with tokens on L1 or not listed in the factory’s `deployments` mapping. +- _supertoken:_ a SuperchainERC20 contract deployed by the `OptimismSuperchainERC20Factory` # Ecosystem properties @@ -63,56 +63,56 @@ legend: ## Unit test -| id | milestone | description | halmos | medusa | -| --- | --- | --- | --- | --- | -| 0 | Factories | supertoken token address does not depend on the executing chain’s chainID | [ ] | [ ] | -| 1 | Factories | supertoken token address depends on remote token, name, symbol and decimals | [ ] | [ ] | -| 2 | Liquidity Migration | convert() should only allow converting legacy tokens to supertoken and viceversa | [ ] | [ ] | -| 3 | Liquidity Migration | convert() only allows migrations between tokens representing the same remote asset | [ ] | [ ] | -| 4 | Liquidity Migration | convert() only allows migrations from tokens with the same decimals | [ ] | [ ] | -| 5 | Liquidity Migration | convert() burns the same amount of legacy token that it mints of supertoken, and viceversa | [ ] | [ ] | +| id | milestone | description | kontrol | medusa | +| --- | ------------------- | ------------------------------------------------------------------------------------------ | ------- | ------ | +| 0 | Factories | supertoken token address does not depend on the executing chain’s chainID | [ ] | [ ] | +| 1 | Factories | supertoken token address depends on remote token, name, symbol and decimals | [ ] | [ ] | +| 2 | Liquidity Migration | convert() should only allow converting legacy tokens to supertoken and viceversa | [ ] | [ ] | +| 3 | Liquidity Migration | convert() only allows migrations between tokens representing the same remote asset | [ ] | [ ] | +| 4 | Liquidity Migration | convert() only allows migrations from tokens with the same decimals | [ ] | [ ] | +| 5 | Liquidity Migration | convert() burns the same amount of legacy token that it mints of supertoken, and viceversa | [ ] | [ ] | ## Valid state -| id | milestone | description | halmos | medusa | -| --- | --- | --- | --- | --- | +| id | milestone | description | kontrol | medusa | +| --- | --------- | ------------------------------------------------------------------------------------------ | ------- | ------ | | 6 | SupERC20 | calls to sendERC20 succeed as long as caller has enough balance | [x] | [ ] | | 7 | SupERC20 | calls to relayERC20 always succeed as long as the sender and cross-domain caller are valid | **[~]** | [ ] | ## Variable transition -| id | milestone | description | halmos | medusa | -| --- | --- | --- | --- | --- | -| 8 | SupERC20 | sendERC20 with a value of zero does not modify accounting | [x] | [ ] | -| 9 | SupERC20 | relayERC20 with a value of zero does not modify accounting | [x] | [ ] | -| 10 | SupERC20 | sendERC20 decreases the token's totalSupply in the source chain exactly by the input amount | [x] | [ ] | -| 11 | SupERC20 | relayERC20 increases the token's totalSupply in the destination chain exactly by the input amount | [x] | [ ] | -| 12 | Liquidity Migration | supertoken total supply only increases on calls to mint() by the L2toL2StandardBridge | [x] | [~] | -| 13 | Liquidity Migration | supertoken total supply only decreases on calls to burn() by the L2toL2StandardBridge | [x] | [ ] | -| 14 | SupERC20 | supertoken total supply starts at zero | [x] | [x] | -| 15 | Factories | deploying a supertoken registers its remote token in the factory | [ ] | [ ] | -| 16 | Factories | deploying an OptimismMintableERC20 registers its remote token in the factory | [ ] | [ ] | +| id | milestone | description | kontrol | medusa | +| --- | ------------------- | ------------------------------------------------------------------------------------------------- | ------- | ------ | +| 8 | SupERC20 | sendERC20 with a value of zero does not modify accounting | [x] | [ ] | +| 9 | SupERC20 | relayERC20 with a value of zero does not modify accounting | [x] | [ ] | +| 10 | SupERC20 | sendERC20 decreases the token's totalSupply in the source chain exactly by the input amount | [x] | [ ] | +| 11 | SupERC20 | relayERC20 increases the token's totalSupply in the destination chain exactly by the input amount | [x] | [ ] | +| 12 | Liquidity Migration | supertoken total supply only increases on calls to mint() by the L2toL2StandardBridge | [x] | [~] | +| 13 | Liquidity Migration | supertoken total supply only decreases on calls to burn() by the L2toL2StandardBridge | [x] | [ ] | +| 14 | SupERC20 | supertoken total supply starts at zero | [x] | [x] | +| 15 | Factories | deploying a supertoken registers its remote token in the factory | [ ] | [ ] | +| 16 | Factories | deploying an OptimismMintableERC20 registers its remote token in the factory | [ ] | [ ] | ## High level -| id | milestone | description | halmos | medusa | -| --- | --- | --- | --- | --- | -| 17 | Liquidity Migration | only calls to convert(legacy, super) can increase a supertoken’s total supply across chains | [ ] | [ ] | -| 18 | Liquidity Migration | only calls to convert(super, legacy) can decrease a supertoken’s total supply across chains | [ ] | [ ] | -| 19 | Liquidity Migration | sum of supertoken total supply across all chains is always <= to convert(legacy, super)- convert(super, legacy) | [ ] | [ ] | -| 20 | SupERC20 | tokens sendERC20-ed on a source chain to a destination chain can be relayERC20-ed on it as long as the source chain is in the dependency set of the destination chain | [ ] | [ ] | -| 21 | Liquidity Migration | sum of supertoken total supply across all chains is = to convert(legacy, super)- convert(super, legacy) when all cross-chain messages are processed | [ ] | [ ] | +| id | milestone | description | kontrol | medusa | +| --- | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------ | +| 17 | Liquidity Migration | only calls to convert(legacy, super) can increase a supertoken’s total supply across chains | [ ] | [ ] | +| 18 | Liquidity Migration | only calls to convert(super, legacy) can decrease a supertoken’s total supply across chains | [ ] | [ ] | +| 19 | Liquidity Migration | sum of supertoken total supply across all chains is always <= to convert(legacy, super)- convert(super, legacy) | [ ] | [ ] | +| 20 | SupERC20 | tokens sendERC20-ed on a source chain to a destination chain can be relayERC20-ed on it as long as the source chain is in the dependency set of the destination chain | [ ] | [ ] | +| 21 | Liquidity Migration | sum of supertoken total supply across all chains is = to convert(legacy, super)- convert(super, legacy) when all cross-chain messages are processed | [ ] | [ ] | ## Atomic bridging pseudo-properties As another layer of defense, the following properties are defined which assume bridging operations to be atomic (that is, the sequencer and L2Inbox and CrossDomainMessenger contracts are fully abstracted away, `sendERC20` triggering the `relayERC20` call on the same transaction) It’s worth noting that these properties will not hold for a live system -| id | milestone | description | halmos | medusa | -| --- | --- | --- | --- | --- | -| 22 | SupERC20 | sendERC20 decreases sender balance in source chain and increases receiver balance in destination chain exactly by the input amount | [ ] | [x] | -| 23 | SupERC20 | sendERC20 decreases total supply in source chain and increases it in destination chain exactly by the input amount | [ ] | [x] | -| 24 | Liquidity Migration | sum of supertoken total supply across all chains is always equal to convert(legacy, super)- convert(super, legacy) | [ ] | [~] | +| id | milestone | description | kontrol | medusa | +| --- | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------- | ------ | +| 22 | SupERC20 | sendERC20 decreases sender balance in source chain and increases receiver balance in destination chain exactly by the input amount | [ ] | [x] | +| 23 | SupERC20 | sendERC20 decreases total supply in source chain and increases it in destination chain exactly by the input amount | [ ] | [x] | +| 24 | Liquidity Migration | sum of supertoken total supply across all chains is always equal to convert(legacy, super)- convert(super, legacy) | [ ] | [~] | # Expected external interactions @@ -122,5 +122,5 @@ It’s worth noting that these properties will not hold for a live system here we’ll list possible interactions that we intend the fuzzing campaign to support in order to help break invariants -- [ ] changing the decimals of tokens after deployment -- [ ] `convert()` ing between multiple (3+) representations of the same remote token, by having different names/symbols +- [ ] changing the decimals of tokens after deployment +- [ ] `convert()` ing between multiple (3+) representations of the same remote token, by having different names/symbols diff --git a/packages/contracts-bedrock/test/properties/halmos/MockL2ToL2Messenger.sol b/packages/contracts-bedrock/test/properties/halmos/MockL2ToL2Messenger.sol deleted file mode 100644 index 5f7aa3b301e7..000000000000 --- a/packages/contracts-bedrock/test/properties/halmos/MockL2ToL2Messenger.sol +++ /dev/null @@ -1,65 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.25; - -import "src/L2/L2ToL2CrossDomainMessenger.sol"; -import { SafeCall } from "src/libraries/SafeCall.sol"; - -// TODO: Try to merge to a single mocked contract used by fuzzing and symbolic invariant tests - only if possible -// and is a low priorty -contract MockL2ToL2Messenger is IL2ToL2CrossDomainMessenger { - /// NOTE: Setting everything as immutable bc the storage layour is ignored when etching. - address internal immutable SOURCE_TOKEN; - address internal immutable DESTINATION_TOKEN; - uint256 public immutable DESTINATION_CHAIN_ID; - // Custom cross domain sender to be used when neither the source nor destination token are the callers - address internal immutable CUSTOM_CROSS_DOMAIN_SENDER; - - constructor( - address _sourceToken, - address _destinationToken, - uint256 _destinationChainId, - address _customCrossDomainSender - ) { - SOURCE_TOKEN = _sourceToken; - DESTINATION_TOKEN = _destinationToken; - DESTINATION_CHAIN_ID = _destinationChainId; - CUSTOM_CROSS_DOMAIN_SENDER = _customCrossDomainSender; - } - - function sendMessage(uint256 _destination, address, bytes calldata _message) external payable { - // Mocking the environment to allow atomicity by executing the message call - if (_destination == DESTINATION_CHAIN_ID) { - (bool _success) = SafeCall.call(DESTINATION_TOKEN, 0, _message); - if (!_success) revert("MockL2ToL2Messenger: sendMessage failed"); - } - } - - function relayMessage( - uint256, - uint256, - uint256, - address, - address _target, - bytes calldata _message - ) - external - payable - { - (bool succ, bytes memory ret) = _target.call{ value: msg.value }(_message); - if (!succ) revert(string(ret)); - - // TODO: Add more logic? Like replacing the (unsupported) `TSTORE` updates with `SSTORE` - or add the checks - } - - function crossDomainMessageSource() external view returns (uint256 _source) { - _source = block.chainid + 1; - } - - // Mock this function so it just always returns the expected if called by the supertoken, or otherwise defaults to - // the custom cross domain sender - function crossDomainMessageSender() external view returns (address _sender) { - if (msg.sender == SOURCE_TOKEN) _sender = SOURCE_TOKEN; - else if (msg.sender == DESTINATION_TOKEN) _sender = DESTINATION_TOKEN; - else _sender = CUSTOM_CROSS_DOMAIN_SENDER; - } -} diff --git a/packages/contracts-bedrock/test/properties/halmos/OptimismSuperchainERC20.t.sol b/packages/contracts-bedrock/test/properties/halmos/OptimismSuperchainERC20.t.sol deleted file mode 100644 index 37c591f24a63..000000000000 --- a/packages/contracts-bedrock/test/properties/halmos/OptimismSuperchainERC20.t.sol +++ /dev/null @@ -1,302 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.25; - -import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; -import { SymTest } from "halmos-cheatcodes/src/SymTest.sol"; -import { Predeploys } from "src/libraries/Predeploys.sol"; -import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol"; -import { MockL2ToL2Messenger } from "./MockL2ToL2Messenger.sol"; -import { HalmosBase } from "../helpers/HalmosBase.sol"; - -contract SymTest_OptimismSuperchainERC20 is SymTest, HalmosBase { - MockL2ToL2Messenger internal constant MESSENGER = MockL2ToL2Messenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); - - OptimismSuperchainERC20 public superchainERC20Impl; - OptimismSuperchainERC20 internal sourceToken; - OptimismSuperchainERC20 internal destToken; - - function setUp() public { - // Deploy the OptimismSuperchainERC20 contract implementation and the proxy to be used - superchainERC20Impl = new OptimismSuperchainERC20(); - sourceToken = OptimismSuperchainERC20( - address( - // TODO: Update to beacon proxy - new ERC1967Proxy( - address(superchainERC20Impl), - abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, name, symbol, decimals)) - ) - ) - ); - - destToken = OptimismSuperchainERC20( - address( - // TODO: Update to beacon proxy - new ERC1967Proxy( - address(superchainERC20Impl), - abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, name, symbol, decimals)) - ) - ) - ); - - // Etch the mocked L2 to L2 Messenger since the messenger logic is out of scope for these test suite. Also, we - // avoid issues such as `TSTORE` opcode not being supported, or issues with `encodeVersionedNonce()` - address _mockL2ToL2CrossDomainMessenger = - address(new MockL2ToL2Messenger(address(sourceToken), address(destToken), DESTINATION_CHAIN_ID, address(0))); - vm.etch(address(MESSENGER), _mockL2ToL2CrossDomainMessenger.code); - // NOTE: We need to set the crossDomainMessageSender as an immutable or otherwise storage vars and not taken - // into account when etching on halmos. Setting a constant slot with setters and getters didn't work neither. - } - - /// @notice Check setup works as expected - function check_setup() public { - // Source token - assert(remoteToken != address(0)); - assert(sourceToken.remoteToken() == remoteToken); - assert(eqStrings(sourceToken.name(), name)); - assert(eqStrings(sourceToken.symbol(), symbol)); - assert(sourceToken.decimals() == decimals); - vm.prank(address(sourceToken)); - assert(MESSENGER.crossDomainMessageSender() == address(sourceToken)); - - // Destination token - assert(destToken.remoteToken() == remoteToken); - assert(eqStrings(destToken.name(), name)); - assert(eqStrings(destToken.symbol(), symbol)); - assert(destToken.decimals() == decimals); - assert(MESSENGER.DESTINATION_CHAIN_ID() == DESTINATION_CHAIN_ID); - vm.prank(address(destToken)); - assert(MESSENGER.crossDomainMessageSender() == address(destToken)); - - // Custom cross domain sender - assert(MESSENGER.crossDomainMessageSender() == address(0)); - } - - /// @custom:property-id 6 - /// @custom:property Calls to sendERC20 succeed as long as caller has enough balance - function check_sendERC20SucceedsOnlyIfEnoughBalance( - uint256 _initialBalance, - address _from, - uint256 _amount, - address _to, - uint256 _chainId - ) - public - { - /* Preconditions */ - vm.assume(_to != address(0)); - vm.assume(_from != address(0)); - - // Can't deal to unsupported cheatcode - vm.prank(Predeploys.L2_STANDARD_BRIDGE); - sourceToken.mint(_from, _initialBalance); - - vm.prank(_from); - /* Action */ - try sourceToken.sendERC20(_to, _amount, _chainId) { - /* Postcondition */ - assert(_initialBalance >= _amount); - } catch { - assert(_initialBalance < _amount); - } - } - - /// @custom:property-id 7 - /// @custom:property Calls to relayERC20 always succeed as long as the sender the cross-domain caller are valid - function check_relayERC20OnlyFromL2ToL2Messenger( - address _crossDomainSender, - address _sender, - address _from, - address _to, - uint256 _amount - ) - public - { - /* Precondition */ - vm.assume(_to != address(0)); - // Deploying a new messenger because of an issue of not being able to etch the storage layout of the mock - // contract. So needed to a new one setting the symbolic immutable variable for the crossDomainSender. - // Used 0 address on source token so when the `soureToken` calls it if returns the symbolic `_crossDomainSender` - vm.etch( - address(MESSENGER), address(new MockL2ToL2Messenger(address(0), address(0), 0, _crossDomainSender)).code - ); - - vm.prank(_sender); - /* Action */ - try sourceToken.relayERC20(_from, _to, _amount) { - /* Postconditions */ - assert(_sender == address(MESSENGER) && MESSENGER.crossDomainMessageSender() == address(sourceToken)); - } catch { - assert(_sender != address(MESSENGER) || MESSENGER.crossDomainMessageSender() != address(sourceToken)); - } - } - - /// @custom:property-id 8 - /// @custom:property `sendERC20` with a value of zero does not modify accounting - function check_sendERC20ZeroCall(address _from, address _to, uint256 _chainId) public { - /* Preconditions */ - vm.assume(_to != address(0)); - vm.assume(_to != address(Predeploys.CROSS_L2_INBOX) && _to != address(MESSENGER)); - - uint256 _totalSupplyBefore = sourceToken.totalSupply(); - uint256 _fromBalanceBefore = sourceToken.balanceOf(_from); - uint256 _toBalanceBefore = sourceToken.balanceOf(_to); - - vm.startPrank(_from); - /* Action */ - sourceToken.sendERC20(_to, ZERO_AMOUNT, _chainId); - - /* Postcondition */ - assert(sourceToken.totalSupply() == _totalSupplyBefore); - assert(sourceToken.balanceOf(_from) == _fromBalanceBefore); - assert(sourceToken.balanceOf(_to) == _toBalanceBefore); - } - - /// @custom:property-id 9 - /// @custom:property `relayERC20` with a value of zero does not modify accounting - function check_relayERC20ZeroCall(address _from, address _to) public { - uint256 _totalSupplyBefore = sourceToken.totalSupply(); - /* Preconditions */ - uint256 _fromBalanceBefore = sourceToken.balanceOf(_from); - uint256 _toBalanceBefore = sourceToken.balanceOf(_to); - vm.prank(address(MESSENGER)); - - /* Action */ - sourceToken.relayERC20(_from, _to, ZERO_AMOUNT); - - /* Postconditions */ - assert(sourceToken.totalSupply() == _totalSupplyBefore); - assert(sourceToken.balanceOf(_from) == _fromBalanceBefore); - assert(sourceToken.balanceOf(_to) == _toBalanceBefore); - } - - /// @custom:property-id 10 - /// @custom:property `sendERC20` decreases the token's totalSupply in the source chain exactly by the input amount - function check_sendERC20DecreasesTotalSupply( - address _sender, - address _to, - uint256 _amount, - uint256 _chainId - ) - public - { - /* Preconditions */ - vm.assume(_to != address(0)); - - vm.prank(Predeploys.L2_STANDARD_BRIDGE); - sourceToken.mint(_sender, _amount); - - uint256 _totalSupplyBefore = sourceToken.totalSupply(); - uint256 _balanceBefore = sourceToken.balanceOf(_sender); - - vm.prank(_sender); - /* Action */ - sourceToken.sendERC20(Predeploys.CROSS_L2_INBOX, _amount, _chainId); - - /* Postconditions */ - assert(sourceToken.totalSupply() == _totalSupplyBefore - _amount); - assert(sourceToken.balanceOf(_sender) == _balanceBefore - _amount); - } - - /// @custom:property-id 11 - /// @custom:property `relayERC20` increases the token's totalSupply in the destination chain exactly by the input - /// amount - function check_relayERC20IncreasesTotalSupply(address _from, address _to, uint256 _amount) public { - /* Preconditions */ - uint256 _totalSupplyBefore = sourceToken.totalSupply(); - uint256 _toBalanceBefore = sourceToken.balanceOf(_to); - - vm.prank(address(MESSENGER)); - /* Action */ - sourceToken.relayERC20(_from, _to, _amount); - - /* Postconditions */ - assert(sourceToken.totalSupply() == _totalSupplyBefore + _amount); - assert(sourceToken.balanceOf(_to) == _toBalanceBefore + _amount); - } - - /// @custom:property-id 12 - /// @custom:property Increases the total supply on the amount minted by the bridge - function check_mint(address _from, uint256 _amount) public { - /* Preconditions */ - uint256 _totalSupplyBefore = sourceToken.totalSupply(); - uint256 _balanceBefore = sourceToken.balanceOf(_from); - - vm.startPrank(Predeploys.L2_STANDARD_BRIDGE); - /* Action */ - sourceToken.mint(_from, _amount); - - /* Postconditions */ - assert(sourceToken.totalSupply() == _totalSupplyBefore + _amount); - assert(sourceToken.balanceOf(_from) == _balanceBefore + _amount); - } - - /// @custom:property-id 13 - /// @custom:property Supertoken total supply only decreases on the amount burned by the bridge - function check_burn(address _from, uint256 _amount) public { - /* Preconditions */ - vm.prank(Predeploys.L2_STANDARD_BRIDGE); - sourceToken.mint(_from, _amount); - - uint256 _totalSupplyBefore = sourceToken.totalSupply(); - uint256 _balanceBefore = sourceToken.balanceOf(_from); - - vm.prank(Predeploys.L2_STANDARD_BRIDGE); - /* Action */ - sourceToken.burn(_from, _amount); - - /* Postconditions */ - assert(sourceToken.totalSupply() == _totalSupplyBefore - _amount); - assert(sourceToken.balanceOf(_from) == _balanceBefore - _amount); - } - - /// @custom:property-id 14 - /// @custom:property Supertoken total supply starts at zero - function check_totalSupplyStartsAtZero() public view { - /* Postconditions */ - assert(sourceToken.totalSupply() == 0); - } - - /// @custom:property-id 22 - /// @custom:property `sendERC20` decreases sender balance in source chain and increases receiver balance in - /// destination chain exactly by the input amount - /// @custom:property-id 23 - /// @custom:property `sendERC20` decreases total supply in source chain and increases it in destination chain - /// exactly by the input amount - function check_crossChainMintAndBurn(address _from, address _to, uint256 _amount, uint256 _chainId) public { - /* Preconditions */ - vm.assume(_to != address(0)); - vm.assume(_from != address(0)); - - // Mint the amount to send - vm.prank(Predeploys.L2_STANDARD_BRIDGE); - sourceToken.mint(_from, _amount); - - uint256 fromBalanceBefore = sourceToken.balanceOf(_from); - uint256 toBalanceBefore = destToken.balanceOf(_to); - uint256 sourceTotalSupplyBefore = sourceToken.totalSupply(); - uint256 destTotalSupplyBefore = destToken.totalSupply(); - - vm.prank(_from); - /* Action */ - try sourceToken.sendERC20(_to, _amount, _chainId) { - /* Postconditions */ - // Source - assert(sourceToken.balanceOf(_from) == fromBalanceBefore - _amount); - assert(sourceToken.totalSupply() == sourceTotalSupplyBefore - _amount); - - // Destination - if (_chainId == DESTINATION_CHAIN_ID) { - // If the destination chain matches the one of the dest token, check that the amount was minted - assert(destToken.balanceOf(_to) == toBalanceBefore + _amount); - assert(destToken.totalSupply() == destTotalSupplyBefore + _amount); - } else { - // Otherwise the balances should remain the same - assert(destToken.balanceOf(_to) == toBalanceBefore); - assert(destToken.totalSupply() == destTotalSupplyBefore); - } - } catch { - // Shouldn't fail - assert(false); - } - } -} diff --git a/packages/contracts-bedrock/test/properties/helpers/HalmosBase.sol b/packages/contracts-bedrock/test/properties/helpers/HalmosBase.sol deleted file mode 100644 index 5ffac1050d14..000000000000 --- a/packages/contracts-bedrock/test/properties/helpers/HalmosBase.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.25; - -import { Test } from "forge-std/Test.sol"; - -contract HalmosBase is Test { - uint256 internal constant CURRENT_CHAIN_ID = 1; - uint256 internal constant DESTINATION_CHAIN_ID = 2; - uint256 internal constant ZERO_AMOUNT = 0; - - address internal remoteToken = address(bytes20(keccak256("remoteToken"))); - string internal name = "SuperchainERC20"; - string internal symbol = "SUPER"; - uint8 internal decimals = 18; - - function eqStrings(string memory a, string memory b) internal pure returns (bool) { - return keccak256(abi.encode(a)) == keccak256(abi.encode(b)); - } -} From 8e39079f89a2f59ffeeae14dfe19aea870717fc6 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Tue, 10 Sep 2024 12:18:55 -0300 Subject: [PATCH 42/48] chore: update properties tackled --- .../contracts-bedrock/test/invariants/PROPERTIES.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/contracts-bedrock/test/invariants/PROPERTIES.md b/packages/contracts-bedrock/test/invariants/PROPERTIES.md index f06c9be65f16..122fef21da51 100644 --- a/packages/contracts-bedrock/test/invariants/PROPERTIES.md +++ b/packages/contracts-bedrock/test/invariants/PROPERTIES.md @@ -24,14 +24,14 @@ legend: | id | description | kontrol | medusa | | --- | ------------------------------------------------------------------------------------------ | ------- | ------ | | 6 | calls to sendERC20 succeed as long as caller has enough balance | [x] | [ ] | -| 7 | calls to relayERC20 always succeed as long as the sender and cross-domain caller are valid | **[~]** | [ ] | +| 7 | calls to relayERC20 always succeed as long as the sender and cross-domain caller are valid | **[x]** | [ ] | ## Variable transition | id | description | kontrol | medusa | | --- | ------------------------------------------------------------------------------------------------- | ------- | ------ | -| 8 | sendERC20 with a value of zero does not modify accounting | [x] | [ ] | -| 9 | relayERC20 with a value of zero does not modify accounting | [x] | [ ] | +| 8 | sendERC20 with a value of zero does not modify accounting | [] | [ ] | +| 9 | relayERC20 with a value of zero does not modify accounting | [] | [ ] | | 10 | sendERC20 decreases the token's totalSupply in the source chain exactly by the input amount | [x] | [ ] | | 11 | relayERC20 increases the token's totalSupply in the destination chain exactly by the input amount | [x] | [ ] | | 12 | supertoken total supply only increases on calls to mint() by the L2toL2StandardBridge | [x] | [ ] | @@ -57,8 +57,8 @@ It’s worth noting that these properties will not hold for a live system | id | description | kontrol | medusa | | --- | ---------------------------------------------------------------------------------------------------------------------------------- | ------- | ------ | -| 22 | sendERC20 decreases sender balance in source chain and increases receiver balance in destination chain exactly by the input amount | [ ] | [x] | -| 23 | sendERC20 decreases total supply in source chain and increases it in destination chain exactly by the input amount | [ ] | [x] | +| 22 | sendERC20 decreases sender balance in source chain and increases receiver balance in destination chain exactly by the input amount | [x] | [x] | +| 23 | sendERC20 decreases total supply in source chain and increases it in destination chain exactly by the input amount | [x] | [x] | | 24 | sum of supertoken total supply across all chains is always equal to convert(legacy, super)- convert(super, legacy) | [ ] | [~] | # Expected external interactions From 6342513bcc158431aad62c0131a44fb34f0531a6 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Tue, 10 Sep 2024 12:25:29 -0300 Subject: [PATCH 43/48] chore: remove outdated properties file --- .../test/invariants/PROPERTIES.md | 73 ------------------- 1 file changed, 73 deletions(-) delete mode 100644 packages/contracts-bedrock/test/invariants/PROPERTIES.md diff --git a/packages/contracts-bedrock/test/invariants/PROPERTIES.md b/packages/contracts-bedrock/test/invariants/PROPERTIES.md deleted file mode 100644 index 122fef21da51..000000000000 --- a/packages/contracts-bedrock/test/invariants/PROPERTIES.md +++ /dev/null @@ -1,73 +0,0 @@ -# supertoken properties - -legend: - -- `[ ]`: property not yet tested -- `**[ ]**`: property not yet tested, dev/research team has asked for extra focus on it -- `[X]`: tested/proven property -- `[~]`: partially tested/proven property -- `:(`: property won't be tested due to some limitation - -## Unit test - -| id | description | kontrol | medusa | -| --- | ---------------------------------------------------------------------------------- | ------- | ------ | -| 0 | supertoken token address does not depend on the executing chain’s chainID | [ ] | [ ] | -| 1 | supertoken token address depends on name, remote token, address and decimals | [ ] | [ ] | -| 2 | convert() should only allow converting legacy tokens to supertoken and viceversa | [ ] | [ ] | -| 3 | convert() only allows migrations between tokens representing the same remote asset | [ ] | [ ] | -| 4 | convert() only allows migrations from tokens with the same decimals | [ ] | [ ] | -| 5 | convert() burns the same amount of one token that it mints of the other | [ ] | [ ] | - -## Valid state - -| id | description | kontrol | medusa | -| --- | ------------------------------------------------------------------------------------------ | ------- | ------ | -| 6 | calls to sendERC20 succeed as long as caller has enough balance | [x] | [ ] | -| 7 | calls to relayERC20 always succeed as long as the sender and cross-domain caller are valid | **[x]** | [ ] | - -## Variable transition - -| id | description | kontrol | medusa | -| --- | ------------------------------------------------------------------------------------------------- | ------- | ------ | -| 8 | sendERC20 with a value of zero does not modify accounting | [] | [ ] | -| 9 | relayERC20 with a value of zero does not modify accounting | [] | [ ] | -| 10 | sendERC20 decreases the token's totalSupply in the source chain exactly by the input amount | [x] | [ ] | -| 11 | relayERC20 increases the token's totalSupply in the destination chain exactly by the input amount | [x] | [ ] | -| 12 | supertoken total supply only increases on calls to mint() by the L2toL2StandardBridge | [x] | [ ] | -| 13 | supertoken total supply only decreases on calls to burn() by the L2toL2StandardBridge | [x] | [ ] | -| 14 | supertoken total supply starts at zero | [x] | [ ] | -| 15 | deploying a supertoken registers its remote token in the factory | [ ] | [ ] | -| 16 | deploying an OptimismMintableERC20 registers its remote token in the factory | [ ] | [ ] | - -## High level - -| id | description | kontrol | medusa | -| --- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------ | -| 17 | only calls to convert(legacy, super) can increase a supertoken’s total supply across chains | [ ] | [ ] | -| 18 | only calls to convert(super, legacy) can decrease a supertoken’s total supply across chains | [ ] | [ ] | -| 19 | sum of total supply across all chains is always <= to convert(legacy, super)- convert(super, legacy) | [ ] | [ ] | -| 20 | tokens sendERC20-ed on a source chain to a destination chain can be relayERC20-ed on it as long as the source chain is in the dependency set of the destination chain | [ ] | [ ] | -| 21 | sum of supertoken total supply across all chains is = to convert(legacy, super)- convert(super, legacy) when all cross-chain messages are processed | [ ] | [ ] | - -## Atomic bridging pseudo-properties - -As another layer of defense, the following properties are defined which assume bridging operations to be atomic (that is, the sequencer and L2Inbox and CrossDomainMessenger contracts are fully abstracted away, `sendERC20` triggering the `relayERC20` call on the same transaction) -It’s worth noting that these properties will not hold for a live system - -| id | description | kontrol | medusa | -| --- | ---------------------------------------------------------------------------------------------------------------------------------- | ------- | ------ | -| 22 | sendERC20 decreases sender balance in source chain and increases receiver balance in destination chain exactly by the input amount | [x] | [x] | -| 23 | sendERC20 decreases total supply in source chain and increases it in destination chain exactly by the input amount | [x] | [x] | -| 24 | sum of supertoken total supply across all chains is always equal to convert(legacy, super)- convert(super, legacy) | [ ] | [~] | - -# Expected external interactions - -- regular ERC20 operations between any accounts on the same chain, provided by [crytic ERC20 properties](https://github.com/crytic/properties?tab=readme-ov-file#erc20-tests) - -# Invariant-breaking candidates (brain dump) - -here we’ll list possible interactions that we intend the fuzzing campaign to support in order to help break invariants - -- [ ] changing the decimals of tokens after deployment -- [ ] `convert()` ing between multiple (3+) representations of the same remote token, by having different names/symbols From 763a5385cc3a807fe84515ea7c0ea682748d4590 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Tue, 10 Sep 2024 12:32:04 -0300 Subject: [PATCH 44/48] chore: remove medusa stuff --- packages/contracts-bedrock/.gitignore | 1 - packages/contracts-bedrock/foundry.toml | 5 - packages/contracts-bedrock/justfile | 4 - packages/contracts-bedrock/kontrol.toml | 2 +- packages/contracts-bedrock/medusa.json | 82 ------------ .../medusa/Protocol.properties.t.sol | 93 ------------- .../medusa/handlers/Protocol.handler.t.sol | 122 ------------------ 7 files changed, 1 insertion(+), 308 deletions(-) delete mode 100644 packages/contracts-bedrock/medusa.json delete mode 100644 packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol delete mode 100644 packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol diff --git a/packages/contracts-bedrock/.gitignore b/packages/contracts-bedrock/.gitignore index e0676cfa0fd5..381d4fd80714 100644 --- a/packages/contracts-bedrock/.gitignore +++ b/packages/contracts-bedrock/.gitignore @@ -6,7 +6,6 @@ broadcast kout-deployment kout-proofs test/kontrol/logs -test/properties/medusa/corpus/ out/ # Metrics diff --git a/packages/contracts-bedrock/foundry.toml b/packages/contracts-bedrock/foundry.toml index 03cccb74539e..4d120365d1b1 100644 --- a/packages/contracts-bedrock/foundry.toml +++ b/packages/contracts-bedrock/foundry.toml @@ -98,11 +98,6 @@ out = 'kout-proofs' test = 'test/kontrol/proofs' script = 'test/kontrol/proofs' -[profile.medusa] -src = 'test/properties/medusa/' -test = 'test/properties/medusa/' -script = 'test/properties/medusa/' - [profile.kontrol-properties] src = "test/properties/kontrol" test = "test/properties/kontrol" diff --git a/packages/contracts-bedrock/justfile b/packages/contracts-bedrock/justfile index b770adbe3552..69a737d4430d 100644 --- a/packages/contracts-bedrock/justfile +++ b/packages/contracts-bedrock/justfile @@ -26,10 +26,6 @@ test-kontrol: build-go-ffi build kontrol-summary-full test-kontrol-no-build test-kontrol-no-build: ./test/kontrol/scripts/run-kontrol.sh script -test-medusa timeout='100': - FOUNDRY_PROFILE=medusa medusa fuzz --timeout {{timeout}} - - test-rerun: build-go-ffi forge test --rerun -vvv diff --git a/packages/contracts-bedrock/kontrol.toml b/packages/contracts-bedrock/kontrol.toml index 998cd05b41f3..12da12c128b1 100644 --- a/packages/contracts-bedrock/kontrol.toml +++ b/packages/contracts-bedrock/kontrol.toml @@ -31,7 +31,7 @@ break-on-cheatcodes = false run-constructor = true symbolic-immutables = false schedule = 'CANCUN' -init-node-from = 'supererc20-state-diff/StateDiff.json' +init-node-from-diff = 'supererc20-state-diff/StateDiff.json' [show.default] foundry-project-root = '.' diff --git a/packages/contracts-bedrock/medusa.json b/packages/contracts-bedrock/medusa.json deleted file mode 100644 index cb4737956fea..000000000000 --- a/packages/contracts-bedrock/medusa.json +++ /dev/null @@ -1,82 +0,0 @@ -{ - "fuzzing": { - "workers": 10, - "workerResetLimit": 50, - "timeout": 0, - "testLimit": 500000, - "callSequenceLength": 100, - "corpusDirectory": "test/properties/medusa/corpus/", - "coverageEnabled": true, - "targetContracts": ["ProtocolProperties"], - "targetContractsBalances": [], - "constructorArgs": {}, - "deployerAddress": "0x30000", - "senderAddresses": [ - "0x10000", - "0x20000", - "0x30000" - ], - "blockNumberDelayMax": 60480, - "blockTimestampDelayMax": 604800, - "blockGasLimit": 30000000, - "transactionGasLimit": 12500000, - "testing": { - "stopOnFailedTest": true, - "stopOnFailedContractMatching": false, - "stopOnNoTests": true, - "testAllContracts": false, - "traceAll": true, - "assertionTesting": { - "enabled": true, - "testViewMethods": true, - "panicCodeConfig": { - "failOnCompilerInsertedPanic": false, - "failOnAssertion": true, - "failOnArithmeticUnderflow": false, - "failOnDivideByZero": false, - "failOnEnumTypeConversionOutOfBounds": false, - "failOnIncorrectStorageAccess": false, - "failOnPopEmptyArray": false, - "failOnOutOfBoundsArrayAccess": false, - "failOnAllocateTooMuchMemory": false, - "failOnCallUninitializedVariable": false - } - }, - "propertyTesting": { - "enabled": false, - "testPrefixes": [ - "property_" - ] - }, - "optimizationTesting": { - "enabled": false, - "testPrefixes": [ - "optimize_" - ] - }, - "targetFunctionSignatures": [], - "excludeFunctionSignatures": [] - }, - "chainConfig": { - "codeSizeCheckDisabled": true, - "cheatCodes": { - "cheatCodesEnabled": true, - "enableFFI": false - } - } - }, - "compilation": { - "platform": "crytic-compile", - "platformConfig": { - "target": ".", - "solcVersion": "", - "exportDirectory": "", - "args": ["--foundry-out-directory", "artifacts","--foundry-compile-all"] - } - }, - "logging": { - "level": "info", - "logDirectory": "", - "noColor": false - } -} diff --git a/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol b/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol deleted file mode 100644 index dd169dc5748d..000000000000 --- a/packages/contracts-bedrock/test/properties/medusa/Protocol.properties.t.sol +++ /dev/null @@ -1,93 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.25; - -import { ProtocolHandler } from "./handlers/Protocol.handler.t.sol"; -import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; -import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; - -contract ProtocolProperties is ProtocolHandler { - using EnumerableMap for EnumerableMap.Bytes32ToUintMap; - - // TODO: will need rework after - // - non-atomic bridge - // - `convert` - /// @custom:property-id 24 - /// @custom:property sum of supertoken total supply across all chains is always equal to convert(legacy, super)- - /// convert(super, legacy) - function property_totalSupplyAcrossChainsEqualsMints() external view { - // iterate over unique deploy salts aka supertokens that are supposed to be compatible with each other - for (uint256 deploySaltIndex = 0; deploySaltIndex < ghost_totalSupplyAcrossChains.length(); deploySaltIndex++) { - uint256 totalSupply = 0; - (bytes32 currentSalt, uint256 trackedSupply) = ghost_totalSupplyAcrossChains.at(deploySaltIndex); - // and then over all the (mocked) chain ids where that supertoken could be deployed - for (uint256 validChainId = 0; validChainId < MAX_CHAINS; validChainId++) { - address supertoken = MESSENGER.superTokenAddresses(validChainId, currentSalt); - if (supertoken != address(0)) { - totalSupply += OptimismSuperchainERC20(supertoken).totalSupply(); - } - } - assert(trackedSupply == totalSupply); - } - } - - /// @notice deploy a new supertoken with deploy salt determined by params, to the given (of course mocked) chainId - /// @custom:property-id 14 - /// @custom:property supertoken total supply starts at zero - function property_DeployNewSupertoken( - TokenDeployParams memory params, - uint256 chainId - ) - external - validateTokenDeployParams(params) - { - chainId = bound(chainId, 0, MAX_CHAINS - 1); - OptimismSuperchainERC20 supertoken = _deploySupertoken( - remoteTokens[params.remoteTokenIndex], - WORDS[params.nameIndex], - WORDS[params.symbolIndex], - DECIMALS[params.decimalsIndex], - chainId - ); - // 14 - assert(supertoken.totalSupply() == 0); - } - - /// @custom:property-id 22 - /// @custom:property sendERC20 decreases sender balance in source chain and increases receiver balance in - /// destination chain exactly by the input amount - /// @custom:property-id 23 - /// @custom:property sendERC20 decreases total supply in source chain and increases it in destination chain exactly - /// by the input amount - function property_SelfBridgeSupertoken(uint256 fromIndex, uint256 destinationChainId, uint256 amount) external { - destinationChainId = bound(destinationChainId, 0, MAX_CHAINS - 1); - fromIndex = bound(fromIndex, 0, allSuperTokens.length - 1); - OptimismSuperchainERC20 sourceToken = OptimismSuperchainERC20(allSuperTokens[fromIndex]); - OptimismSuperchainERC20 destinationToken = - MESSENGER.crossChainMessageReceiver(address(sourceToken), destinationChainId); - // TODO: when implementing non-atomic bridging, allow for the token to - // not yet be deployed and funds be recovered afterwards. - require(address(destinationToken) != address(0)); - uint256 sourceBalanceBefore = sourceToken.balanceOf(msg.sender); - uint256 sourceSupplyBefore = sourceToken.totalSupply(); - uint256 destinationBalanceBefore = destinationToken.balanceOf(msg.sender); - uint256 destinationSupplyBefore = destinationToken.totalSupply(); - - vm.prank(msg.sender); - try sourceToken.sendERC20(msg.sender, amount, destinationChainId) { - uint256 sourceBalanceAfter = sourceToken.balanceOf(msg.sender); - uint256 destinationBalanceAfter = destinationToken.balanceOf(msg.sender); - // no free mint - assert(sourceBalanceBefore + destinationBalanceBefore == sourceBalanceAfter + destinationBalanceAfter); - // 22 - assert(sourceBalanceBefore - amount == sourceBalanceAfter); - assert(destinationBalanceBefore + amount == destinationBalanceAfter); - uint256 sourceSupplyAfter = sourceToken.totalSupply(); - uint256 destinationSupplyAfter = destinationToken.totalSupply(); - // 23 - assert(sourceSupplyBefore - amount == sourceSupplyAfter); - assert(destinationSupplyBefore + amount == destinationSupplyAfter); - } catch { - assert(address(destinationToken) == address(sourceToken) || sourceBalanceBefore < amount); - } - } -} diff --git a/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol b/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol deleted file mode 100644 index 266650486e34..000000000000 --- a/packages/contracts-bedrock/test/properties/medusa/handlers/Protocol.handler.t.sol +++ /dev/null @@ -1,122 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.25; - -import { TestBase } from "forge-std/Base.sol"; -import { StdUtils } from "forge-std/StdUtils.sol"; - -import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol"; -import { EnumerableMap } from "@openzeppelin/contracts/utils/structs/EnumerableMap.sol"; -import { OptimismSuperchainERC20 } from "src/L2/OptimismSuperchainERC20.sol"; -import { Predeploys } from "src/libraries/Predeploys.sol"; -import { MockL2ToL2CrossDomainMessenger } from "../../helpers/MockL2ToL2CrossDomainMessenger.t.sol"; - -contract ProtocolHandler is TestBase, StdUtils { - using EnumerableMap for EnumerableMap.Bytes32ToUintMap; - - uint8 internal constant MAX_CHAINS = 4; - uint8 internal constant INITIAL_TOKENS = 1; - uint8 internal constant INITIAL_SUPERTOKENS = 1; - uint8 internal constant SUPERTOKEN_INITIAL_MINT = 100; - address internal constant BRIDGE = Predeploys.L2_STANDARD_BRIDGE; - MockL2ToL2CrossDomainMessenger internal constant MESSENGER = - MockL2ToL2CrossDomainMessenger(Predeploys.L2_TO_L2_CROSS_DOMAIN_MESSENGER); - OptimismSuperchainERC20 internal superchainERC20Impl; - // NOTE: having more options for this enables the fuzzer to configure - // different supertokens for the same remote token - string[] internal WORDS = ["TOKENS"]; - uint8[] internal DECIMALS = [6, 18]; - - struct TokenDeployParams { - uint8 remoteTokenIndex; - uint8 nameIndex; - uint8 symbolIndex; - uint8 decimalsIndex; - } - - address[] internal remoteTokens; - address[] internal allSuperTokens; - - //@notice 'real' deploy salt => total supply sum across chains - EnumerableMap.Bytes32ToUintMap internal ghost_totalSupplyAcrossChains; - - constructor() { - vm.etch(address(MESSENGER), address(new MockL2ToL2CrossDomainMessenger()).code); - superchainERC20Impl = new OptimismSuperchainERC20(); - for (uint256 remoteTokenIndex = 0; remoteTokenIndex < INITIAL_TOKENS; remoteTokenIndex++) { - _deployRemoteToken(); - for (uint256 supertokenChainId = 0; supertokenChainId < INITIAL_SUPERTOKENS; supertokenChainId++) { - _deploySupertoken(remoteTokens[remoteTokenIndex], WORDS[0], WORDS[0], DECIMALS[0], supertokenChainId); - } - } - } - - /// @notice the deploy params are _indexes_ to pick from a pre-defined array of options and limit - /// the amount of supertokens for a given remoteAsset that are incompatible between them, as - /// two supertokens have to share decimals, name, symbol and remoteAsset to be considered - /// the same asset, and therefore bridgable. - modifier validateTokenDeployParams(TokenDeployParams memory params) { - params.remoteTokenIndex = uint8(bound(params.remoteTokenIndex, 0, remoteTokens.length - 1)); - params.nameIndex = uint8(bound(params.nameIndex, 0, WORDS.length - 1)); - params.symbolIndex = uint8(bound(params.symbolIndex, 0, WORDS.length - 1)); - params.decimalsIndex = uint8(bound(params.decimalsIndex, 0, DECIMALS.length - 1)); - _; - } - - function handler_MockNewRemoteToken() external { - _deployRemoteToken(); - } - - /// @notice pick one already-deployed supertoken and mint an arbitrary amount of it - /// necessary so there is something to be bridged :D - /// TODO: will be replaced when testing the factories and `convert()` - function handler_MintSupertoken(uint256 index, uint96 amount) external { - index = bound(index, 0, allSuperTokens.length - 1); - address addr = allSuperTokens[index]; - vm.prank(BRIDGE); - // medusa calls with different senders by default - OptimismSuperchainERC20(addr).mint(msg.sender, amount); - // currentValue will be zero if key is not present - (, uint256 currentValue) = ghost_totalSupplyAcrossChains.tryGet(MESSENGER.superTokenInitDeploySalts(addr)); - ghost_totalSupplyAcrossChains.set(MESSENGER.superTokenInitDeploySalts(addr), currentValue + amount); - } - - /// @notice deploy a remote token, that supertokens will be a representation of. They are never called, so there - /// is no need to actually deploy a contract for them - function _deployRemoteToken() internal { - // make sure they don't conflict with predeploys/preinstalls/precompiles/other tokens - remoteTokens.push(address(uint160(1000 + remoteTokens.length))); - } - - /// @notice deploy a new supertoken representing remoteToken - /// remoteToken, name, symbol and decimals determine the 'real' deploy salt - /// and supertokens sharing it are interoperable between them - /// we however use the chainId as part of the deploy salt to mock the ability of - /// supertokens to exist on different chains on a single EVM. - function _deploySupertoken( - address remoteToken, - string memory name, - string memory symbol, - uint8 decimals, - uint256 chainId - ) - internal - returns (OptimismSuperchainERC20 supertoken) - { - // this salt would be used in production. Tokens sharing it will be bridgable with each other - bytes32 realSalt = keccak256(abi.encode(remoteToken, name, symbol, decimals)); - // what we use in the tests to walk around two contracts needing two different addresses - // tbf we could be using CREATE1, but this feels more verbose - bytes32 hackySalt = keccak256(abi.encode(remoteToken, name, symbol, decimals, chainId)); - supertoken = OptimismSuperchainERC20( - address( - // TODO: Use the SuperchainERC20 Beacon Proxy - new ERC1967Proxy{ salt: hackySalt }( - address(superchainERC20Impl), - abi.encodeCall(OptimismSuperchainERC20.initialize, (remoteToken, name, symbol, decimals)) - ) - ) - ); - MESSENGER.registerSupertoken(realSalt, chainId, address(supertoken)); - allSuperTokens.push(address(supertoken)); - } -} From 413ae97ea6a00cb23d9d7b7c019577b774d51737 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Fri, 13 Sep 2024 12:02:30 -0300 Subject: [PATCH 45/48] chore: update tackled properties and polish natspec --- packages/contracts-bedrock/test/properties/PROPERTIES.md | 6 +++--- .../test/properties/kontrol/OptimismSuperchainERC20.k.sol | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/PROPERTIES.md b/packages/contracts-bedrock/test/properties/PROPERTIES.md index f6876df77469..748d9c306266 100644 --- a/packages/contracts-bedrock/test/properties/PROPERTIES.md +++ b/packages/contracts-bedrock/test/properties/PROPERTIES.md @@ -77,14 +77,14 @@ legend: | id | milestone | description | kontrol | medusa | | --- | --------- | ------------------------------------------------------------------------------------------ | ------- | ------ | | 6 | SupERC20 | calls to sendERC20 succeed as long as caller has enough balance | [x] | [ ] | -| 7 | SupERC20 | calls to relayERC20 always succeed as long as the sender and cross-domain caller are valid | **[~]** | [ ] | +| 7 | SupERC20 | calls to relayERC20 always succeed as long as the sender and cross-domain caller are valid | **[x]** | [ ] | ## Variable transition | id | milestone | description | kontrol | medusa | | --- | ------------------- | ------------------------------------------------------------------------------------------------- | ------- | ------ | -| 8 | SupERC20 | sendERC20 with a value of zero does not modify accounting | [x] | [ ] | -| 9 | SupERC20 | relayERC20 with a value of zero does not modify accounting | [x] | [ ] | +| 8 | SupERC20 | sendERC20 with a value of zero does not modify accounting | [:(] | [ ] | +| 9 | SupERC20 | relayERC20 with a value of zero does not modify accounting | [:(] | [ ] | | 10 | SupERC20 | sendERC20 decreases the token's totalSupply in the source chain exactly by the input amount | [x] | [ ] | | 11 | SupERC20 | relayERC20 increases the token's totalSupply in the destination chain exactly by the input amount | [x] | [ ] | | 12 | Liquidity Migration | supertoken total supply only increases on calls to mint() by the L2toL2StandardBridge | [x] | [~] | diff --git a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol index 64cc8099e28c..12847065b92b 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol @@ -10,6 +10,7 @@ import { InitialState } from "./deployments/InitialState.sol"; contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { event CrossDomainMessageSender(address _sender); + /// @notice Use this function instead of `setUp()` for performance reasons when running the proofs with Kontrol function setUpInlined() public { superchainERC20Impl = OptimismSuperchainERC20(superchainERC20ImplAddress); sourceToken = OptimismSuperchainERC20(sourceTokenAddress); @@ -81,7 +82,7 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { } /// @custom:property-id 7 - /// @custom:property Calls to relayERC20 always succeed as long as the sender the cross-domain caller are valid + /// @custom:property Calls to relayERC20 always succeed as long as the sender and the cross-domain caller are valid function prove_relayERC20OnlyFromL2ToL2Messenger( address _crossDomainSender, address _sender, @@ -99,7 +100,6 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { vm.assume(notBuiltinAddress(_to)); vm.assume(notBuiltinAddress(_sender)); - // kevm.symbolicStorage(address(MESSENGER)); MESSENGER.forTest_setCustomCrossDomainSender(_crossDomainSender); vm.prank(_sender); @@ -117,6 +117,7 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /// @custom:property-id 8 /// @custom:property `sendERC20` with a value of zero does not modify accounting + /// @custom:property-not-tested The proof fails - probably needs some fixes through lemmas and node pruning function prove_sendERC20ZeroCall(address _from, address _to, uint256 _chainId) public { setUpInlined(); @@ -144,6 +145,7 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { /// @custom:property-id 9 /// @custom:property `relayERC20` with a value of zero does not modify accounting + /// @custom:property-not-tested The proof fails - probably needs some fixes through lemmas and node pruning function prove_relayERC20ZeroCall(address _from, address _to) public { setUpInlined(); From 9c914ef932d1eeab5487a57a1329308953095074 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Mon, 16 Sep 2024 12:31:36 -0300 Subject: [PATCH 46/48] chore: update natspec on debug file --- .../test/properties/FoundryDebugSymbolic.t.sol | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/FoundryDebugSymbolic.t.sol b/packages/contracts-bedrock/test/properties/FoundryDebugSymbolic.t.sol index 8fbe8e5c668b..3ec067e25926 100644 --- a/packages/contracts-bedrock/test/properties/FoundryDebugSymbolic.t.sol +++ b/packages/contracts-bedrock/test/properties/FoundryDebugSymbolic.t.sol @@ -6,7 +6,6 @@ import { Predeploys } from "src/libraries/Predeploys.sol"; import { MockL2ToL2Messenger } from "test/properties/kontrol/helpers/MockL2ToL2Messenger.sol"; import { KontrolBase } from "test/properties/kontrol/KontrolBase.sol"; import { ERC1967Proxy } from "@openzeppelin/contracts-v5/proxy/ERC1967/ERC1967Proxy.sol"; - import "forge-std/Test.sol"; // TODO: File to debug faster with foundry replicating scenarios where the proof failed, needs removal afterwards. @@ -41,7 +40,7 @@ contract FoundryDebugTests is KontrolBase { vm.etch(address(MESSENGER), address(mockL2ToL2Messenger).code); } - /// @notice Check setup works as expected + /// Check setup works as expected function test_proveSetup() public { // Source token assert(remoteToken != address(0)); @@ -66,8 +65,8 @@ contract FoundryDebugTests is KontrolBase { assert(MESSENGER.crossDomainMessageSender() == address(420)); } - /// @custom:property-id 8 - /// @custom:property `sendERC20` with a value of zero does not modify accounting + // debug property-id 8 + // `sendERC20` with a value of zero does not modify accounting function test_proveSendERC20ZeroCall() public { /* Preconditions */ address _from = address(511347974759188522659820409854212399244223280810); @@ -95,8 +94,8 @@ contract FoundryDebugTests is KontrolBase { // TIMESTAMP_CELL = 1073741825 // VV1__to_114b9705 = 376793390874373408599387495934666716005045108769 - /// @custom:property-id 9 - /// @custom:property `relayERC20` with a value of zero does not modify accounting + // debug 9 + // `relayERC20` with a value of zero does not modify accounting function test_proveRelayERC20ZeroCall() public { /* Preconditions */ address _from = address(645326474426547203313410069153905908525362434350); From 602589f9fef2016c99659b0d2ca55ad82138acf3 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Mon, 16 Sep 2024 22:35:33 -0300 Subject: [PATCH 47/48] chore: update comments and attribution --- .../test/properties/kontrol/OptimismSuperchainERC20.k.sol | 2 +- .../test/properties/kontrol/helpers/RecordStateDiff.sol | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol index 12847065b92b..854ea6353af1 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/OptimismSuperchainERC20.k.sol @@ -67,7 +67,7 @@ contract OptimismSuperchainERC20Kontrol is KontrolBase, InitialState { vm.assume(notBuiltinAddress(_from)); vm.assume(notBuiltinAddress(_to)); - // Can't deal to unsupported cheatcode + // Mint the amount to the caller vm.prank(Predeploys.L2_STANDARD_BRIDGE); sourceToken.mint(_from, _initialBalance); diff --git a/packages/contracts-bedrock/test/properties/kontrol/helpers/RecordStateDiff.sol b/packages/contracts-bedrock/test/properties/kontrol/helpers/RecordStateDiff.sol index a49cb41b6545..5251d4b4d70b 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/helpers/RecordStateDiff.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/helpers/RecordStateDiff.sol @@ -1,4 +1,8 @@ +// This code was originally copied from: +// https://github.com/runtimeverification/kontrol/blob/86451cbdaef8bed0370bf804fa4b545ec8b7a28a/docs/external-computation/test/kontrol/state-diff/record-state-diff/RecordStateDiff.sol +// It has been slightly modified, including changes to the compiler version. // SPDX-License-Identifier: MIT + pragma solidity 0.8.25; import { console2 as console } from "forge-std/console2.sol"; From b714f0e94cb195db1a5ca0a059f2f3b1f8bce6a7 Mon Sep 17 00:00:00 2001 From: 0xDiscotech <131301107+0xDiscotech@users.noreply.github.com> Date: Tue, 17 Sep 2024 11:47:02 -0300 Subject: [PATCH 48/48] chore: update mock messenger description comment --- .../test/properties/kontrol/helpers/MockL2ToL2Messenger.sol | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol b/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol index a199bfb792c1..82a6ce20c768 100644 --- a/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol +++ b/packages/contracts-bedrock/test/properties/kontrol/helpers/MockL2ToL2Messenger.sol @@ -3,10 +3,8 @@ pragma solidity 0.8.25; import "src/L2/L2ToL2CrossDomainMessenger.sol"; import { SafeCall } from "src/libraries/SafeCall.sol"; -import "forge-std/Test.sol"; -// TODO: Try to merge to a single mocked contract used by fuzzing and symbolic invariant tests - only if possible -// and is a low priorty +// Mock contract for the L2ToL2Messenger contract where cross chain atomicity is simulated contract MockL2ToL2Messenger { event CrossDomainMessageSender(address _sender);