├── .prettierignore ├── test ├── emptyrun.coverage.ts └── helpers │ ├── executor-helpers.ts │ └── governance-helpers.ts ├── certora ├── .gitignore ├── harness │ ├── DummyERC20A.sol │ ├── DummyERC20B.sol │ ├── DummyERC20Impl.sol │ ├── mockTarget.sol │ ├── mockTargetPoly.sol │ ├── ArbitrumHarness.sol │ ├── OptimismHarness.sol │ └── L2BridgeExecutorHarness.sol ├── report │ └── Formal Verification Report of AAVE L2 Bridge.pdf ├── scripts │ ├── runComplexity.sh │ ├── verifyPolygon.sh │ ├── verifyArbitrum.sh │ └── verifyOptimism.sh ├── Makefile ├── specs │ ├── erc20.spec │ └── complexity.spec ├── munged │ ├── dependencies │ │ ├── polygon │ │ │ ├── fxportal │ │ │ │ ├── interfaces │ │ │ │ │ └── IFxMessageProcessor.sol │ │ │ │ ├── FxRoot.sol │ │ │ │ └── FxChild.sol │ │ │ └── CustomPolygonMapping.sol │ │ ├── optimism │ │ │ └── interfaces │ │ │ │ └── ICrossDomainMessenger.sol │ │ └── arbitrum │ │ │ ├── interfaces │ │ │ ├── IInbox.sol │ │ │ └── ArbRetryableTx.sol │ │ │ └── AddressAliasHelper.sol │ ├── bridges │ │ ├── ArbitrumBridgeExecutor.sol │ │ ├── OptimismBridgeExecutor.sol │ │ ├── L2BridgeExecutor.sol │ │ └── PolygonBridgeExecutor.sol │ └── interfaces │ │ └── IL2BridgeExecutor.sol ├── README.md └── applyHarness.patch ├── .solcover.js ├── Dockerfile ├── docs ├── PolygonBridgeArch.png ├── ArbitrumBridgeArch.png └── OptimismBridgeArch.png ├── audits ├── 26-07-2022_ChainSecurity_AaveL2BridgeExecutors.pdf └── 12-08-2021_MixBytes_AaveGovernanceCrosschainBridges.pdf ├── contracts ├── mocks │ ├── Selfdestructor.sol │ ├── Greeter.sol │ ├── GreeterPayload.sol │ ├── ArbGreeter.sol │ ├── SimpleBridgeExecutor.sol │ ├── SimpleL2BridgeExecutor.sol │ ├── PolygonMarketUpdate.sol │ ├── MockOvmL1CrossDomainMessenger.sol │ ├── MockOvmL2CrossDomainMessenger.sol │ └── MockInbox.sol ├── dependencies │ ├── arbitrum │ │ ├── interfaces │ │ │ ├── IOwnable.sol │ │ │ ├── IDelayedMessageProvider.sol │ │ │ ├── ArbRetryableTx.sol │ │ │ ├── IBridge.sol │ │ │ ├── ISequencerInbox.sol │ │ │ └── IInbox.sol │ │ ├── AddressAliasHelper.sol │ │ └── libraries │ │ │ └── IGasRefunder.sol │ ├── polygon │ │ ├── fxportal │ │ │ ├── interfaces │ │ │ │ └── IFxMessageProcessor.sol │ │ │ ├── FxRoot.sol │ │ │ └── FxChild.sol │ │ └── CustomPolygonMapping.sol │ └── optimism │ │ └── interfaces │ │ ├── IL2CrossDomainMessenger.sol │ │ └── ICrossDomainMessenger.sol ├── bridges │ ├── ArbitrumBridgeExecutor.sol │ ├── OptimismBridgeExecutor.sol │ ├── L2BridgeExecutor.sol │ └── PolygonBridgeExecutor.sol └── interfaces │ └── IL2BridgeExecutor.sol ├── .prettierrc ├── .eslintrc.json ├── .gitignore ├── tsconfig.json ├── contractAddresses.json ├── docker-compose.yml ├── example.env ├── deploy ├── helpers │ └── greeter.ts ├── gov-bridge-arbitrum.ts └── gov-bridge-optimism.ts ├── helpers ├── gov-constants.ts ├── arbitrum-helpers.ts ├── constants.ts ├── test-wallets.ts ├── arbitrum-contract-getters.ts ├── optimism-contract-getters.ts ├── wallet-helpers.ts ├── types.ts ├── contract-getters.ts ├── etherscan-verification.ts ├── task-helpers.ts ├── misc-utils.ts └── polygon-helpers.ts ├── .github └── workflows │ ├── node.js.yml │ ├── certora-polygon.yaml │ ├── certora-arbitrum.yaml │ └── certora-optimism.yaml ├── tasks ├── setup │ ├── get-info.ts │ └── print-default-wallets.ts ├── misc │ └── set-DRE.ts ├── governance │ └── check-polygon.ts ├── verify │ └── verify-template.ts ├── deploy │ ├── deployPolygonGovernance.ts │ └── deploy.ts └── l2 │ └── optimism.ts ├── LICENSE.md ├── helper-hardhat-config.ts ├── package.json └── hardhat.config.ts /.prettierignore: -------------------------------------------------------------------------------- 1 | artifacts 2 | cache 3 | node_modules -------------------------------------------------------------------------------- /test/emptyrun.coverage.ts: -------------------------------------------------------------------------------- 1 | describe('Empty coverage run', async function () {}); 2 | -------------------------------------------------------------------------------- /certora/.gitignore: -------------------------------------------------------------------------------- 1 | # certora 2 | .certora* 3 | .certora*.json 4 | **.last_conf* 5 | certora-logs 6 | -------------------------------------------------------------------------------- /.solcover.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | skipFiles: ['./mocks', './interfaces', './dependencies'], 3 | }; 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ethereum/solc:0.7.6 as build-deps 2 | 3 | FROM node:14 4 | COPY --from=build-deps /usr/bin/solc /usr/bin/solc -------------------------------------------------------------------------------- /docs/PolygonBridgeArch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/multijump/governance-crosschain-bridges/HEAD/docs/PolygonBridgeArch.png -------------------------------------------------------------------------------- /docs/ArbitrumBridgeArch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/multijump/governance-crosschain-bridges/HEAD/docs/ArbitrumBridgeArch.png -------------------------------------------------------------------------------- /docs/OptimismBridgeArch.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/multijump/governance-crosschain-bridges/HEAD/docs/OptimismBridgeArch.png -------------------------------------------------------------------------------- /certora/harness/DummyERC20A.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity ^0.8.10; 3 | import "./DummyERC20Impl.sol"; 4 | 5 | contract DummyERC20A is DummyERC20Impl {} -------------------------------------------------------------------------------- /certora/harness/DummyERC20B.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity ^0.8.10; 3 | import "./DummyERC20Impl.sol"; 4 | 5 | contract DummyERC20B is DummyERC20Impl {} -------------------------------------------------------------------------------- /audits/26-07-2022_ChainSecurity_AaveL2BridgeExecutors.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/multijump/governance-crosschain-bridges/HEAD/audits/26-07-2022_ChainSecurity_AaveL2BridgeExecutors.pdf -------------------------------------------------------------------------------- /audits/12-08-2021_MixBytes_AaveGovernanceCrosschainBridges.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/multijump/governance-crosschain-bridges/HEAD/audits/12-08-2021_MixBytes_AaveGovernanceCrosschainBridges.pdf -------------------------------------------------------------------------------- /certora/report/Formal Verification Report of AAVE L2 Bridge.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/multijump/governance-crosschain-bridges/HEAD/certora/report/Formal Verification Report of AAVE L2 Bridge.pdf -------------------------------------------------------------------------------- /contracts/mocks/Selfdestructor.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.10; 3 | 4 | contract Selfdestructor { 5 | function oops() external { 6 | selfdestruct(payable(msg.sender)); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "printWidth": 100, 3 | "trailingComma": "es5", 4 | "semi": true, 5 | "singleQuote": true, 6 | "tabWidth": 2, 7 | "overrides": [ 8 | { 9 | "files": "*.sol", 10 | "options": { 11 | "semi": true, 12 | "printWidth": 100 13 | } 14 | } 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /contracts/mocks/Greeter.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.10; 3 | 4 | contract Greeter { 5 | event MessageUpdated(string newMessage); 6 | string public message; 7 | 8 | constructor() {} 9 | 10 | function setMessage(string memory newMessage) public { 11 | message = newMessage; 12 | emit MessageUpdated(newMessage); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /contracts/dependencies/arbitrum/interfaces/IOwnable.sol: -------------------------------------------------------------------------------- 1 | // Copyright 2021-2022, Offchain Labs, Inc. 2 | // For license information, see https://github.com/nitro/blob/master/LICENSE 3 | // SPDX-License-Identifier: BUSL-1.1 4 | 5 | // solhint-disable-next-line compiler-version 6 | pragma solidity >=0.4.21 <0.9.0; 7 | 8 | interface IOwnable { 9 | function owner() external view returns (address); 10 | } 11 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "env": { "node": true }, 3 | "parser": "@typescript-eslint/parser", 4 | "plugins": ["@typescript-eslint"], 5 | "parserOptions": { 6 | "ecmaVersion": 2020, 7 | "sourceType": "module" 8 | }, 9 | "extends": [ 10 | "eslint:recommended", 11 | "plugin:@typescript-eslint/recommended", 12 | "prettier", 13 | "plugin:prettier/recommended" 14 | ], 15 | "rules": {} 16 | } -------------------------------------------------------------------------------- /contracts/mocks/GreeterPayload.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.10; 3 | 4 | import {Greeter} from './Greeter.sol'; 5 | 6 | contract GreeterPayload { 7 | event PayloadExecuted(address sender); 8 | 9 | function execute(address greeter, string memory newMessage) external { 10 | Greeter(greeter).setMessage(newMessage); 11 | emit PayloadExecuted(msg.sender); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | #Hardhat files 3 | cache 4 | artifacts 5 | 6 | node_modules 7 | dist/ 8 | build/ 9 | .vscode 10 | .idea 11 | /types 12 | /typechain 13 | temp.txt 14 | 15 | deployed-contracts.json 16 | 17 | coverage 18 | coverage.json 19 | .coverage_artifacts 20 | .coverage_cache 21 | .coverage_contracts 22 | 23 | .DS_Store 24 | 25 | deployments/ 26 | 27 | # Certora 28 | .certora* 29 | .last_confs 30 | certora_* 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es2019", 4 | "module": "commonjs", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "outDir": "dist", 8 | "noImplicitAny": false, 9 | "resolveJsonModule": true, 10 | "useUnknownInCatchVariables": false 11 | }, 12 | "include": ["./scripts", "./test", "./deploy", "./tasks", "./helpers", "./typechain"], 13 | "files": ["./hardhat.config.ts"] 14 | } 15 | -------------------------------------------------------------------------------- /certora/scripts/runComplexity.sh: -------------------------------------------------------------------------------- 1 | certoraRun contracts/bridges/OptimismBridgeExecutor.sol:OptimismBridgeExecutor \ 2 | --verify OptimismBridgeExecutor:certora/specs/complexity.spec \ 3 | --solc solc8.10 \ 4 | --optimistic_loop \ 5 | --staging \ 6 | --msg "OptimismBridgeExecutor complexity check" 7 | 8 | certoraRun contracts/bridges/ArbitrumBridgeExecutor.sol:ArbitrumBridgeExecutor \ 9 | --verify ArbitrumBridgeExecutor:certora/specs/complexity.spec \ 10 | --solc solc8.10 \ 11 | --optimistic_loop \ 12 | --staging \ 13 | --msg "ArbitrumBridgeExecutor complexity check" 14 | -------------------------------------------------------------------------------- /contractAddresses.json: -------------------------------------------------------------------------------- 1 | 2 | { 3 | "aaveToken": "0x83E98bc0Cc062AFF1A604833CA7AF44bA457e20f", 4 | "stkAaveToken": "0x631D5E3C45a459E8f98b9d6a2734fCe7b051f845", 5 | "strategy": "0x8f75607bc69441D3eA024D25a4376B97DC016cF5", 6 | "governance": "0x7e8E60E8209C5b3C6a7E83F93533be434d177C54", 7 | "executor": "0x954ff7Dd15edC6506895C75839AC53D91Ee08bA9", 8 | "fxRoot": "0x3d1d3E34f7fB6D26245E6640E1c50710eFFf15bA", 9 | "mapping": "0xEAa852323826C71cd7920C3b4c007184234c3945", 10 | "fxChild": "0xCf73231F28B7331BBe3124B907840A94851f9f11", 11 | "polygonBridgeExecutor": "0xC3d7fB8f45ef4b8530469d3683C7F3c5154F321b", 12 | "marketUpdate": "0x62cd8D056d0885089D9A9Ee62B0DcE39085526c0" 13 | } -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.5' 2 | 3 | services: 4 | contracts-env: 5 | env_file: 6 | - .env 7 | build: 8 | context: ./ 9 | working_dir: /src 10 | command: npm run run-env 11 | volumes: 12 | - ./:/src 13 | - $HOME/.tenderly/config.yaml:/root/.tenderly/config.yaml 14 | environment: 15 | MNEMONIC: ${MNEMONIC} 16 | ETHERSCAN_KEY: ${ETHERSCAN_KEY} 17 | INFURA_KEY: ${INFURA_KEY} 18 | ETHERSCAN_NETWORK: ${ETHERSCAN_NETWORK} 19 | TENDERLY_PROJECT: ${TENDERLY_PROJECT} 20 | TENDERLY_USERNAME: ${TENDERLY_USERNAME} 21 | ALCHEMY_KEY: ${ALCHEMY_KEY} 22 | TENDERLY_FORK_ID: ${TENDERLY_FORK_ID} 23 | TENDERLY_HEAD_ID: ${TENDERLY_HEAD_ID} 24 | -------------------------------------------------------------------------------- /example.env: -------------------------------------------------------------------------------- 1 | # Mnemonic, only first address will be used 2 | # Wallet Index - derive the wallet from this index path - defaults to 0 3 | MNEMONIC="" 4 | WALLET_INDEX="0" 5 | 6 | # Private Key 7 | PRIVATE_KEY="" 8 | 9 | # Add Alchemy or Infura provider keys, alchemy takes preference at the config level 10 | ALCHEMY_KEY="" 11 | INFURA_KEY="" 12 | 13 | 14 | # Optional Etherscan key, for automatize the verification of the contracts at Etherscan 15 | ETHERSCAN_KEY="" 16 | 17 | # Optional, if you plan to use Tenderly scripts 18 | TENDERLY_USERNAME="" 19 | TENDERLY_PROJECT="" 20 | # TENDERLY_FORK="" 21 | # TENDERLY_HEAD="" 22 | 23 | # Optional, OpenZeppelin Defender Relayer Keys 24 | DEFENDER_API_KEY="" 25 | DEFENDER_SECRET_KEY="" 26 | -------------------------------------------------------------------------------- /contracts/mocks/ArbGreeter.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.10; 3 | 4 | import {AddressAliasHelper} from './../dependencies/arbitrum/AddressAliasHelper.sol'; 5 | 6 | contract ArbGreeter { 7 | event Senders(address msgSender, address applyAlias, address undoAlias); 8 | 9 | event MessageUpdated(string newMessage); 10 | string public message; 11 | 12 | constructor() {} 13 | 14 | function setMessage(string calldata newMessage) public { 15 | message = newMessage; 16 | emit MessageUpdated(newMessage); 17 | } 18 | 19 | function sender() public { 20 | emit Senders( 21 | msg.sender, 22 | AddressAliasHelper.applyL1ToL2Alias(msg.sender), 23 | AddressAliasHelper.undoL1ToL2Alias(msg.sender) 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /contracts/dependencies/arbitrum/interfaces/IDelayedMessageProvider.sol: -------------------------------------------------------------------------------- 1 | // Copyright 2021-2022, Offchain Labs, Inc. 2 | // For license information, see https://github.com/nitro/blob/master/LICENSE 3 | // SPDX-License-Identifier: BUSL-1.1 4 | 5 | // solhint-disable-next-line compiler-version 6 | pragma solidity >=0.6.9 <0.9.0; 7 | 8 | interface IDelayedMessageProvider { 9 | /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator 10 | event InboxMessageDelivered(uint256 indexed messageNum, bytes data); 11 | 12 | /// @dev event emitted when a inbox message is added to the Bridge's delayed accumulator 13 | /// same as InboxMessageDelivered but the batch data is available in tx.input 14 | event InboxMessageDeliveredFromOrigin(uint256 indexed messageNum); 15 | } 16 | -------------------------------------------------------------------------------- /contracts/mocks/SimpleBridgeExecutor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import {BridgeExecutorBase} from '../bridges/BridgeExecutorBase.sol'; 5 | 6 | contract SimpleBridgeExecutor is BridgeExecutorBase { 7 | constructor( 8 | uint256 delay, 9 | uint256 gracePeriod, 10 | uint256 minimumDelay, 11 | uint256 maximumDelay, 12 | address guardian 13 | ) BridgeExecutorBase(delay, gracePeriod, minimumDelay, maximumDelay, guardian) {} 14 | 15 | function queue( 16 | address[] memory targets, 17 | uint256[] memory values, 18 | string[] memory signatures, 19 | bytes[] memory calldatas, 20 | bool[] memory withDelegatecalls 21 | ) external { 22 | _queue(targets, values, signatures, calldatas, withDelegatecalls); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /certora/Makefile: -------------------------------------------------------------------------------- 1 | default: help 2 | 3 | PATCH = applyHarness.patch 4 | CONTRACTS_DIR = ../contracts 5 | MUNGED_DIR = munged 6 | 7 | help: 8 | @echo "usage:" 9 | @echo " make clean: remove all generated files (those ignored by git)" 10 | @echo " make $(MUNGED_DIR): create $(MUNGED_DIR) directory by applying the patch file to $(CONTRACTS_DIR)" 11 | @echo " make record: record a new patch file capturing the differences between $(CONTRACTS_DIR) and $(MUNGED_DIR)" 12 | 13 | munged: $(wildcard $(CONTRACTS_DIR)/*.sol) $(PATCH) 14 | rm -rf $@ 15 | cp -r $(CONTRACTS_DIR) $@ 16 | patch -p0 -d $@ < $(PATCH) 17 | 18 | record: 19 | diff -ruN $(CONTRACTS_DIR) $(MUNGED_DIR) | sed 's+\.\./contracts/++g' | sed 's+munged/++g' > $(PATCH) 20 | 21 | clean: 22 | git clean -fdX 23 | touch $(PATCH) 24 | 25 | -------------------------------------------------------------------------------- /certora/specs/erc20.spec: -------------------------------------------------------------------------------- 1 | // erc20 methods 2 | methods { 3 | name() returns (string) => DISPATCHER(true) 4 | symbol() returns (string) => DISPATCHER(true) 5 | decimals() returns (string) => DISPATCHER(true) 6 | totalSupply() returns (uint256) => DISPATCHER(true) 7 | balanceOf(address) returns (uint256) => DISPATCHER(true) 8 | allowance(address,address) returns (uint) => DISPATCHER(true) 9 | approve(address,uint256) returns (bool) => DISPATCHER(true) 10 | transfer(address,uint256) returns (bool) => DISPATCHER(true) 11 | transferFrom(address,address,uint256) returns (bool) => DISPATCHER(true) 12 | } 13 | -------------------------------------------------------------------------------- /deploy/helpers/greeter.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | 4 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 5 | const { deployments, ethers, getNamedAccounts } = hre; 6 | const { deploy, execute, log, read } = deployments; 7 | const { deployer } = await getNamedAccounts(); 8 | 9 | const greeter = await deployments.getOrNull('Greeter'); 10 | 11 | if (greeter) { 12 | log(`Reusing greeter at: ${greeter.address}`); 13 | } else { 14 | await deploy('Greeter', { 15 | args: [], 16 | contract: 'Greeter', 17 | from: deployer, 18 | log: true, 19 | }); 20 | } 21 | }; 22 | 23 | export default func; 24 | func.dependencies = []; 25 | func.tags = ['Greeter']; 26 | -------------------------------------------------------------------------------- /contracts/dependencies/polygon/fxportal/interfaces/IFxMessageProcessor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | /** 5 | * @title IFxMessageProcessor 6 | * @notice Defines the interface to process message 7 | */ 8 | interface IFxMessageProcessor { 9 | /** 10 | * @notice Process the cross-chain message from a FxChild contract through the Ethereum/Polygon StateSender 11 | * @param stateId The id of the cross-chain message created in the Ethereum/Polygon StateSender 12 | * @param rootMessageSender The address that initially sent this message on Ethereum 13 | * @param data The data from the abi-encoded cross-chain message 14 | **/ 15 | function processMessageFromRoot( 16 | uint256 stateId, 17 | address rootMessageSender, 18 | bytes calldata data 19 | ) external; 20 | } 21 | -------------------------------------------------------------------------------- /certora/munged/dependencies/polygon/fxportal/interfaces/IFxMessageProcessor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | /** 5 | * @title IFxMessageProcessor 6 | * @notice Defines the interface to process message 7 | */ 8 | interface IFxMessageProcessor { 9 | /** 10 | * @notice Process the cross-chain message from a FxChild contract through the Ethereum/Polygon StateSender 11 | * @param stateId The id of the cross-chain message created in the Ethereum/Polygon StateSender 12 | * @param rootMessageSender The address that initially sent this message on Ethereum 13 | * @param data The data from the abi-encoded cross-chain message 14 | **/ 15 | function processMessageFromRoot( 16 | uint256 stateId, 17 | address rootMessageSender, 18 | bytes calldata data 19 | ) external; 20 | } 21 | -------------------------------------------------------------------------------- /contracts/mocks/SimpleL2BridgeExecutor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import {L2BridgeExecutor} from '../bridges/L2BridgeExecutor.sol'; 5 | 6 | contract SimpleL2BridgeExecutor is L2BridgeExecutor { 7 | modifier onlyEthereumGovernanceExecutor() override { 8 | if (msg.sender != _ethereumGovernanceExecutor) revert UnauthorizedEthereumExecutor(); 9 | _; 10 | } 11 | 12 | constructor( 13 | address ethereumGovernanceExecutor, 14 | uint256 delay, 15 | uint256 gracePeriod, 16 | uint256 minimumDelay, 17 | uint256 maximumDelay, 18 | address guardian 19 | ) 20 | L2BridgeExecutor( 21 | ethereumGovernanceExecutor, 22 | delay, 23 | gracePeriod, 24 | minimumDelay, 25 | maximumDelay, 26 | guardian 27 | ) 28 | {} 29 | } 30 | -------------------------------------------------------------------------------- /helpers/gov-constants.ts: -------------------------------------------------------------------------------- 1 | export const ADDRESSES = { 2 | INBOX_MAIN: '0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f', 3 | INBOX_RINKEBY: '0x578BAde599406A8fE3d24Fd7f7211c0911F5B29e', 4 | OVM_L1_MESSENGER_KOVAN: '0x4361d0F75A0186C05f971c566dC6bEa5957483fD', 5 | OVM_L1_MESSENGER_MAIN: '0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1', 6 | OVM_L2_MESSENGER: '0x4200000000000000000000000000000000000007', 7 | ETHEREUM_GOV_EXECUTOR: '0xEE56e2B3D491590B5b31738cC34d5232F378a8D5', 8 | OVM_GUARDIAN: '0xE50c8C619d05ff98b22Adf991F17602C774F785c', 9 | ARB_GUARDIAN: '0xbbd9f90699c1FA0D7A65870D241DD1f1217c96Eb', 10 | RETRYABLE_TICKET_TX_ADDRESS: '0x000000000000000000000000000000000000006E', 11 | }; 12 | 13 | export const CONSTANTS = { 14 | DELAY: 172800, 15 | GRACE_PERIOD: 259200, 16 | MIN_DELAY: 28800, 17 | MAX_DELAY: 604800, 18 | }; 19 | -------------------------------------------------------------------------------- /certora/scripts/verifyPolygon.sh: -------------------------------------------------------------------------------- 1 | certoraRun certora/harness/PolygonHarness.sol \ 2 | certora/harness/DummyERC20A.sol \ 3 | certora/harness/DummyERC20B.sol \ 4 | certora/harness/mockTargetPoly.sol \ 5 | --verify PolygonHarness:certora/specs/PolygonBridge.spec \ 6 | --link mockTargetPoly:_executor=PolygonHarness \ 7 | mockTargetPoly:_tokenA=DummyERC20A \ 8 | mockTargetPoly:_tokenB=DummyERC20B \ 9 | PolygonHarness:_mock=mockTargetPoly \ 10 | --solc solc8.10 \ 11 | --optimistic_loop \ 12 | --loop_iter 2 \ 13 | --cloud \ 14 | --rules $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14} ${15} ${16} \ 15 | --settings -contractRecursionLimit=1 \ 16 | --send_only \ 17 | --msg "Polygon all" 18 | # py ../EVMVerifier/scripts/certoraRun.py contracts/bridges/OptimismBridgeExecutor.sol \ 19 | -------------------------------------------------------------------------------- /helpers/arbitrum-helpers.ts: -------------------------------------------------------------------------------- 1 | import { ethers, BigNumber } from 'ethers'; 2 | import { ZERO_ADDRESS } from './constants'; 3 | 4 | export const ALIASING_OFFSET = '0x1111000000000000000000000000000000001111'; 5 | 6 | export const applyL1ToL2Alias = (l1Address: string) => { 7 | const offset = BigNumber.from(ALIASING_OFFSET); 8 | const l2Address = BigNumber.from(l1Address).add(offset).mod(BigNumber.from(2).pow(160)); 9 | if (l2Address.eq(0)) return ZERO_ADDRESS; 10 | return ethers.utils.getAddress(l2Address.toHexString()); 11 | }; 12 | 13 | export const undoL1ToL2Alias = (l2Address: string) => { 14 | const offset = BigNumber.from(ALIASING_OFFSET); 15 | const l1Address = BigNumber.from(l2Address).sub(offset).mod(BigNumber.from(2).pow(160)); 16 | if (l1Address.eq(0)) return ZERO_ADDRESS; 17 | return ethers.utils.getAddress(l1Address.toHexString()); 18 | }; 19 | -------------------------------------------------------------------------------- /certora/scripts/verifyArbitrum.sh: -------------------------------------------------------------------------------- 1 | certoraRun certora/harness/ArbitrumHarness.sol \ 2 | certora/harness/DummyERC20A.sol \ 3 | certora/harness/DummyERC20B.sol \ 4 | certora/harness/mockTarget.sol \ 5 | --verify ArbitrumHarness:certora/specs/Optimism_ArbitrumBridge.spec \ 6 | --link mockTarget:_executor=ArbitrumHarness \ 7 | mockTarget:_tokenA=DummyERC20A \ 8 | mockTarget:_tokenB=DummyERC20B \ 9 | ArbitrumHarness:_mock=mockTarget \ 10 | --solc solc8.10 \ 11 | --optimistic_loop \ 12 | --loop_iter 2 \ 13 | --cloud \ 14 | --rules $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14} ${15} ${16} \ 15 | --settings -contractRecursionLimit=1 \ 16 | --send_only \ 17 | --msg "Arbitrum all" 18 | # py ../EVMVerifier/scripts/certoraRun.py contracts/bridges/OptimismBridgeExecutor.sol \ 19 | # 20 | 21 | -------------------------------------------------------------------------------- /certora/scripts/verifyOptimism.sh: -------------------------------------------------------------------------------- 1 | certoraRun certora/harness/OptimismHarness.sol \ 2 | certora/harness/DummyERC20A.sol \ 3 | certora/harness/DummyERC20B.sol \ 4 | certora/harness/mockTarget.sol \ 5 | --verify OptimismHarness:certora/specs/Optimism_ArbitrumBridge.spec \ 6 | --link mockTarget:_executor=OptimismHarness \ 7 | mockTarget:_tokenA=DummyERC20A \ 8 | mockTarget:_tokenB=DummyERC20B \ 9 | OptimismHarness:_mock=mockTarget \ 10 | --solc solc8.10 \ 11 | --optimistic_loop \ 12 | --loop_iter 2 \ 13 | --cloud \ 14 | --rules $1 $2 $3 $4 $5 $6 $7 $8 $9 ${10} ${11} ${12} ${13} ${14} ${15} ${16} \ 15 | --settings -contractRecursionLimit=1 \ 16 | --send_only \ 17 | --msg "Optimisim all" 18 | # py ../EVMVerifier/scripts/certoraRun.py contracts/bridges/OptimismBridgeExecutor.sol \ 19 | # 20 | 21 | -------------------------------------------------------------------------------- /.github/workflows/node.js.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | 'on': 3 | push: 4 | branches: 5 | - master 6 | pull_request: 7 | branches: 8 | - master 9 | jobs: 10 | build: 11 | runs-on: ubuntu-latest 12 | env: 13 | ALCHEMY_KEY: '${{secrets.ALCHEMY_KEY}}' 14 | strategy: 15 | matrix: 16 | node-version: 17 | - 16.x 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v3 21 | with: 22 | submodules: recursive 23 | - name: Setup node 24 | uses: actions/setup-node@v3 25 | with: 26 | node-version: 16.0.x 27 | cache: npm 28 | - name: Install Dependencies 29 | run: npm ci 30 | - name: Compilation 31 | run: 'npm run compile' 32 | - name: Tests 33 | run: 'npm run test' 34 | - name: Fork Tests 35 | run: 'npm run test-fork' -------------------------------------------------------------------------------- /tasks/setup/get-info.ts: -------------------------------------------------------------------------------- 1 | import { task } from 'hardhat/config'; 2 | import { getMnemonicSigner } from '../../helpers/wallet-helpers'; 3 | import { DRE } from '../../helpers/misc-utils'; 4 | 5 | import dotenv from 'dotenv'; 6 | dotenv.config({ path: '../../.env' }); 7 | 8 | task('get-info', 'print-chain-data').setAction(async (_, localBRE) => { 9 | await localBRE.run('set-DRE'); 10 | console.log(`Chain ID: ${DRE.network.config.chainId}`); 11 | 12 | const blockNumber = await DRE.ethers.provider.getBlockNumber(); 13 | console.log(`Current Block Number: ${blockNumber}`); 14 | 15 | let mnemonicSigner = await getMnemonicSigner(1); 16 | mnemonicSigner = mnemonicSigner.connect(DRE.ethers.provider); 17 | const balance = await mnemonicSigner.getBalance(); 18 | console.log( 19 | `Balance of ${await mnemonicSigner.getAddress()}: ${DRE.ethers.utils.formatUnits(balance, 18)}` 20 | ); 21 | }); 22 | -------------------------------------------------------------------------------- /contracts/dependencies/optimism/interfaces/IL2CrossDomainMessenger.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.9; 3 | 4 | /* Interface Imports */ 5 | import { ICrossDomainMessenger } from "./ICrossDomainMessenger.sol"; 6 | 7 | /** 8 | * @title IL2CrossDomainMessenger 9 | */ 10 | interface IL2CrossDomainMessenger is ICrossDomainMessenger { 11 | /******************** 12 | * Public Functions * 13 | ********************/ 14 | 15 | /** 16 | * Relays a cross domain message to a contract. 17 | * @param _target Target contract address. 18 | * @param _sender Message sender address. 19 | * @param _message Message to send to the target. 20 | * @param _messageNonce Nonce for the provided message. 21 | */ 22 | function relayMessage( 23 | address _target, 24 | address _sender, 25 | bytes memory _message, 26 | uint256 _messageNonce 27 | ) external; 28 | } 29 | -------------------------------------------------------------------------------- /helpers/constants.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from 'ethers/lib/ethers'; 2 | import { parseUnits } from 'ethers/lib/utils'; 3 | 4 | export const PERCENTAGE_FACTOR = '10000'; 5 | export const HALF_PERCENTAGE = BigNumber.from(PERCENTAGE_FACTOR).div(2).toString(); 6 | export const WAD = BigNumber.from(10).pow(18).toString(); 7 | export const HALF_WAD = BigNumber.from(WAD).div(2).toString(); 8 | export const RAY = BigNumber.from(10).pow(27).toString(); 9 | export const HALF_RAY = BigNumber.from(RAY).div(2).toString(); 10 | export const WAD_RAY_RATIO = parseUnits('1', 9).toString(); 11 | export const oneEther = parseUnits('1', 18); 12 | export const oneRay = parseUnits('1', 27); 13 | export const MAX_UINT_AMOUNT = 14 | '115792089237316195423570985008687907853269984665640564039457584007913129639935'; 15 | export const ONE_YEAR = '31536000'; 16 | export const ZERO_ADDRESS = '0x0000000000000000000000000000000000000000'; 17 | export const ONE_ADDRESS = '0x0000000000000000000000000000000000000001'; 18 | -------------------------------------------------------------------------------- /contracts/dependencies/polygon/fxportal/FxRoot.sol: -------------------------------------------------------------------------------- 1 | /** 2 | *Submitted for verification at Etherscan.io on 2021-01-17 3 | */ 4 | 5 | // SPDX-License-Identifier: MIT 6 | pragma solidity ^0.8.10; 7 | 8 | interface IStateSender { 9 | function syncState(address receiver, bytes calldata data) external; 10 | } 11 | 12 | interface IFxStateSender { 13 | function sendMessageToChild(address _receiver, bytes calldata _data) external; 14 | } 15 | 16 | /** 17 | * @title FxRoot root contract for fx-portal 18 | */ 19 | contract FxRoot is IFxStateSender { 20 | IStateSender public stateSender; 21 | address public fxChild; 22 | 23 | constructor(address _stateSender) { 24 | stateSender = IStateSender(_stateSender); 25 | } 26 | 27 | function setFxChild(address _fxChild) public { 28 | require(fxChild == address(0x0)); 29 | fxChild = _fxChild; 30 | } 31 | 32 | function sendMessageToChild(address _receiver, bytes calldata _data) public override { 33 | bytes memory data = abi.encode(msg.sender, _receiver, _data); 34 | stateSender.syncState(fxChild, data); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /certora/munged/dependencies/polygon/fxportal/FxRoot.sol: -------------------------------------------------------------------------------- 1 | /** 2 | *Submitted for verification at Etherscan.io on 2021-01-17 3 | */ 4 | 5 | // SPDX-License-Identifier: MIT 6 | pragma solidity ^0.8.10; 7 | 8 | interface IStateSender { 9 | function syncState(address receiver, bytes calldata data) external; 10 | } 11 | 12 | interface IFxStateSender { 13 | function sendMessageToChild(address _receiver, bytes calldata _data) external; 14 | } 15 | 16 | /** 17 | * @title FxRoot root contract for fx-portal 18 | */ 19 | contract FxRoot is IFxStateSender { 20 | IStateSender public stateSender; 21 | address public fxChild; 22 | 23 | constructor(address _stateSender) { 24 | stateSender = IStateSender(_stateSender); 25 | } 26 | 27 | function setFxChild(address _fxChild) public { 28 | require(fxChild == address(0x0)); 29 | fxChild = _fxChild; 30 | } 31 | 32 | function sendMessageToChild(address _receiver, bytes calldata _data) public override { 33 | bytes memory data = abi.encode(msg.sender, _receiver, _data); 34 | stateSender.syncState(fxChild, data); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /helpers/test-wallets.ts: -------------------------------------------------------------------------------- 1 | const balance = '1000000000000000000000000'; 2 | 3 | export const accounts = [ 4 | { 5 | secretKey: '0xc5e8f61d1ab959b397eecc0a37a6517b8e67a0e7cf1f4bce5591f3ed80199122', 6 | balance, 7 | }, 8 | { 9 | secretKey: '0xd49743deccbccc5dc7baa8e69e5be03298da8688a15dd202e20f15d5e0e9a9fb', 10 | balance, 11 | }, 12 | { 13 | secretKey: '0x23c601ae397441f3ef6f1075dcb0031ff17fb079837beadaf3c84d96c6f3e569', 14 | balance, 15 | }, 16 | { 17 | secretKey: '0xee9d129c1997549ee09c0757af5939b2483d80ad649a0eda68e8b0357ad11131', 18 | balance, 19 | }, 20 | { 21 | secretKey: '0x87630b2d1de0fbd5044eb6891b3d9d98c34c8d310c852f98550ba774480e47cc', 22 | balance, 23 | }, 24 | { 25 | secretKey: '0x275cc4a2bfd4f612625204a20a2280ab53a6da2d14860c47a9f5affe58ad86d4', 26 | balance, 27 | }, 28 | { 29 | secretKey: '0xaee25d55ce586148a853ca83fdfacaf7bc42d5762c6e7187e6f8e822d8e6a650', 30 | balance, 31 | }, 32 | { 33 | secretKey: '0xa2e0097c961c67ec197b6865d7ecea6caffc68ebeb00e6050368c8f67fc9c588', 34 | balance, 35 | }, 36 | ]; 37 | -------------------------------------------------------------------------------- /deploy/gov-bridge-arbitrum.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | import { ADDRESSES, CONSTANTS } from '../helpers/gov-constants'; 4 | 5 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 6 | const { deployments, ethers, getNamedAccounts } = hre; 7 | const { deploy, execute, log, read } = deployments; 8 | const { deployer } = await getNamedAccounts(); 9 | 10 | const arbiGov = await deployments.getOrNull('ArbitrumGov'); 11 | 12 | if (arbiGov) { 13 | log(`Reusing optimistic governance at: ${arbiGov.address}`); 14 | } else { 15 | await deploy('ArbitrumGov', { 16 | args: [ 17 | ADDRESSES['ETHEREUM_GOV_EXECUTOR'], 18 | CONSTANTS['DELAY'], 19 | CONSTANTS['GRACE_PERIOD'], 20 | CONSTANTS['MIN_DELAY'], 21 | CONSTANTS['MAX_DELAY'], 22 | ADDRESSES['ARB_GUARDIAN'], 23 | ], 24 | contract: 'ArbitrumBridgeExecutor', 25 | from: deployer, 26 | log: true, 27 | }); 28 | } 29 | }; 30 | 31 | export default func; 32 | func.dependencies = []; 33 | func.tags = ['ArbitrumGov']; 34 | -------------------------------------------------------------------------------- /contracts/mocks/PolygonMarketUpdate.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.10; 3 | 4 | contract PolygonMarketUpdate { 5 | event UpdateExecuted(uint256 counter, uint256 testInt, address testAddress, uint256 fee); 6 | event DelegateUpdateExecuted(bytes32 testBytes, address sender); 7 | address constant UPDATED_ADDRESS = 0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9; 8 | 9 | uint256 private _counter; 10 | uint256 private _testInt; 11 | 12 | function execute(uint256 testInt) external payable returns (uint256) { 13 | _counter = _counter + 1; 14 | _testInt = testInt; 15 | emit UpdateExecuted(_counter, _testInt, UPDATED_ADDRESS, msg.value); 16 | return testInt; 17 | } 18 | 19 | function executeWithDelegate(bytes32 testBytes) external payable { 20 | emit DelegateUpdateExecuted(testBytes, msg.sender); 21 | } 22 | 23 | function alwaysFails() public { 24 | _counter = _counter + 1; 25 | require(1 < 0, 'THIS_ALWAYS_FAILS'); 26 | } 27 | 28 | function getCounter() public view returns (uint256) { 29 | return _counter; 30 | } 31 | 32 | function getTestInt() public view returns (uint256) { 33 | return _testInt; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /contracts/dependencies/optimism/interfaces/ICrossDomainMessenger.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >0.5.0 <0.9.0; 3 | 4 | /** 5 | * @title ICrossDomainMessenger 6 | */ 7 | interface ICrossDomainMessenger { 8 | /********** 9 | * Events * 10 | **********/ 11 | 12 | event SentMessage( 13 | address indexed target, 14 | address sender, 15 | bytes message, 16 | uint256 messageNonce, 17 | uint256 gasLimit 18 | ); 19 | event RelayedMessage(bytes32 indexed msgHash); 20 | event FailedRelayedMessage(bytes32 indexed msgHash); 21 | 22 | /************* 23 | * Variables * 24 | *************/ 25 | 26 | function xDomainMessageSender() external view returns (address); 27 | 28 | /******************** 29 | * Public Functions * 30 | ********************/ 31 | 32 | /** 33 | * Sends a cross domain message to the target messenger. 34 | * @param _target Target contract address. 35 | * @param _message Message to send to the target. 36 | * @param _gasLimit Gas limit for the provided message. 37 | */ 38 | function sendMessage( 39 | address _target, 40 | bytes calldata _message, 41 | uint32 _gasLimit 42 | ) external; 43 | } 44 | -------------------------------------------------------------------------------- /certora/munged/dependencies/optimism/interfaces/ICrossDomainMessenger.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity >0.5.0 <0.9.0; 3 | 4 | /** 5 | * @title ICrossDomainMessenger 6 | */ 7 | interface ICrossDomainMessenger { 8 | /********** 9 | * Events * 10 | **********/ 11 | 12 | event SentMessage( 13 | address indexed target, 14 | address sender, 15 | bytes message, 16 | uint256 messageNonce, 17 | uint256 gasLimit 18 | ); 19 | event RelayedMessage(bytes32 indexed msgHash); 20 | event FailedRelayedMessage(bytes32 indexed msgHash); 21 | 22 | /************* 23 | * Variables * 24 | *************/ 25 | 26 | function xDomainMessageSender() external view returns (address); 27 | 28 | /******************** 29 | * Public Functions * 30 | ********************/ 31 | 32 | /** 33 | * Sends a cross domain message to the target messenger. 34 | * @param _target Target contract address. 35 | * @param _message Message to send to the target. 36 | * @param _gasLimit Gas limit for the provided message. 37 | */ 38 | function sendMessage( 39 | address _target, 40 | bytes calldata _message, 41 | uint32 _gasLimit 42 | ) external; 43 | } 44 | -------------------------------------------------------------------------------- /deploy/gov-bridge-optimism.ts: -------------------------------------------------------------------------------- 1 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 2 | import { DeployFunction } from 'hardhat-deploy/types'; 3 | import { ADDRESSES, CONSTANTS } from '../helpers/gov-constants'; 4 | 5 | const func: DeployFunction = async function (hre: HardhatRuntimeEnvironment) { 6 | const { deployments, ethers, getNamedAccounts } = hre; 7 | const { deploy, execute, log, read } = deployments; 8 | const { deployer } = await getNamedAccounts(); 9 | 10 | const optimisticGov = await deployments.getOrNull('OptimisticGov'); 11 | 12 | if (optimisticGov) { 13 | log(`Reusing optimistic governance at: ${optimisticGov.address}`); 14 | } else { 15 | await deploy('OptimisticGov', { 16 | args: [ 17 | ADDRESSES['OVM_L2_MESSENGER'], 18 | ADDRESSES['ETHEREUM_GOV_EXECUTOR'], 19 | CONSTANTS['DELAY'], 20 | CONSTANTS['GRACE_PERIOD'], 21 | CONSTANTS['MIN_DELAY'], 22 | CONSTANTS['MAX_DELAY'], 23 | ADDRESSES['OVM_GUARDIAN'], 24 | ], 25 | contract: 'OptimismBridgeExecutor', 26 | from: deployer, 27 | log: true, 28 | }); 29 | } 30 | }; 31 | 32 | export default func; 33 | func.dependencies = []; 34 | func.tags = ['OptimisticGov']; 35 | -------------------------------------------------------------------------------- /helpers/arbitrum-contract-getters.ts: -------------------------------------------------------------------------------- 1 | import { Signer, BigNumber } from 'ethers'; 2 | import { 3 | ArbitrumBridgeExecutor, 4 | ArbitrumBridgeExecutor__factory, 5 | MockInbox, 6 | MockInbox__factory, 7 | } from '../typechain'; 8 | import { tEthereumAddress } from './types'; 9 | 10 | export const deployInbox = async (signer: Signer): Promise => { 11 | const mockInbox = await new MockInbox__factory(signer).deploy(); 12 | await mockInbox.deployTransaction.wait(); 13 | return mockInbox; 14 | }; 15 | 16 | export const deployArbitrumBridgeExecutor = async ( 17 | ethereumExecutor: tEthereumAddress, 18 | delay: BigNumber, 19 | gracePeriod: BigNumber, 20 | minimumDelay: BigNumber, 21 | maximumDelay: BigNumber, 22 | guardian: tEthereumAddress, 23 | signer: Signer 24 | ): Promise => { 25 | const arbBridgeExecutorFactory = new ArbitrumBridgeExecutor__factory(signer); 26 | const arbitrumBridgeExecutor = await arbBridgeExecutorFactory.deploy( 27 | ethereumExecutor, 28 | delay, 29 | gracePeriod, 30 | minimumDelay, 31 | maximumDelay, 32 | guardian 33 | ); 34 | await arbitrumBridgeExecutor.deployTransaction.wait(); 35 | return arbitrumBridgeExecutor; 36 | }; 37 | -------------------------------------------------------------------------------- /contracts/mocks/MockOvmL1CrossDomainMessenger.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.10; 3 | 4 | import {ICrossDomainMessenger} from '../dependencies/optimism/interfaces/ICrossDomainMessenger.sol'; 5 | import {MockOvmL2CrossDomainMessenger} from './MockOvmL2CrossDomainMessenger.sol'; 6 | 7 | contract MockOvmL1CrossDomainMessenger is ICrossDomainMessenger { 8 | address private sender; 9 | address private l2Messenger; 10 | 11 | function setSender(address _sender) external { 12 | sender = _sender; 13 | } 14 | 15 | function setL2Messenger(address _l2Messenger) external { 16 | l2Messenger = _l2Messenger; 17 | } 18 | 19 | function xDomainMessageSender() external view override returns (address) { 20 | return sender; 21 | } 22 | 23 | function sendMessage( 24 | address _target, 25 | bytes calldata _message, 26 | uint32 _gasLimit 27 | ) external override { 28 | MockOvmL2CrossDomainMessenger(l2Messenger).redirect(msg.sender, _target, _message, _gasLimit); 29 | } 30 | 31 | function redirect( 32 | address _target, 33 | bytes calldata _message, 34 | uint32 _gasLimit 35 | ) external { 36 | bool success; 37 | (success, ) = _target.call{gas: _gasLimit}(_message); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/helpers/executor-helpers.ts: -------------------------------------------------------------------------------- 1 | export enum ExecutorErrors { 2 | // IExecutorBase 3 | InvalidInitParams = 'InvalidInitParams()', 4 | NotGuardian = 'NotGuardian()', 5 | OnlyCallableByThis = 'OnlyCallableByThis()', 6 | MinimumDelayTooLong = 'MinimumDelayTooLong()', 7 | MaximumDelayTooShort = 'MaximumDelayTooShort()', 8 | GracePeriodTooShort = 'GracePeriodTooShort()', 9 | DelayShorterThanMin = 'DelayShorterThanMin()', 10 | DelayLongerThanMax = 'DelayLongerThanMax()', 11 | OnlyQueuedActions = 'OnlyQueuedActions()', 12 | TimelockNotFinished = 'TimelockNotFinished()', 13 | InvalidActionsSetId = 'InvalidActionsSetId()', 14 | EmptyTargets = 'EmptyTargets()', 15 | InconsistentParamsLength = 'InconsistentParamsLength()', 16 | DuplicateAction = 'DuplicateAction()', 17 | InsufficientBalance = 'InsufficientBalance()', 18 | FailedActionExecution = 'FailedActionExecution()', 19 | 20 | // PolygonBridgeExecutor 21 | UnauthorizedChildOrigin = 'UnauthorizedChildOrigin()', 22 | UnauthorizedRootOrigin = 'UnauthorizedRootOrigin()', 23 | 24 | // L2BridgeExecutor 25 | UnauthorizedEthereumExecutor = 'UnauthorizedEthereumExecutor()', 26 | } 27 | 28 | export enum ActionsSetState { 29 | Queued = 0, 30 | Executed = 1, 31 | Canceled = 2, 32 | Expired = 3, 33 | } 34 | -------------------------------------------------------------------------------- /contracts/dependencies/arbitrum/AddressAliasHelper.sol: -------------------------------------------------------------------------------- 1 | // Copyright 2021-2022, Offchain Labs, Inc. 2 | // For license information, see https://github.com/nitro/blob/master/LICENSE 3 | // SPDX-License-Identifier: BUSL-1.1 4 | 5 | pragma solidity ^0.8.0; 6 | 7 | library AddressAliasHelper { 8 | uint160 internal constant OFFSET = uint160(0x1111000000000000000000000000000000001111); 9 | 10 | /// @notice Utility function that converts the address in the L1 that submitted a tx to 11 | /// the inbox to the msg.sender viewed in the L2 12 | /// @param l1Address the address in the L1 that triggered the tx to L2 13 | /// @return l2Address L2 address as viewed in msg.sender 14 | function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { 15 | unchecked { 16 | l2Address = address(uint160(l1Address) + OFFSET); 17 | } 18 | } 19 | 20 | /// @notice Utility function that converts the msg.sender viewed in the L2 to the 21 | /// address in the L1 that submitted a tx to the inbox 22 | /// @param l2Address L2 address as viewed in msg.sender 23 | /// @return l1Address the address in the L1 that triggered the tx to L2 24 | function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { 25 | unchecked { 26 | l1Address = address(uint160(l2Address) - OFFSET); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /contracts/dependencies/polygon/fxportal/FxChild.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | // IStateReceiver represents interface to receive state 5 | interface IStateReceiver { 6 | function onStateReceive(uint256 stateId, bytes calldata data) external; 7 | } 8 | 9 | // IFxMessageProcessor represents interface to process message 10 | interface IFxMessageProcessor { 11 | function processMessageFromRoot( 12 | uint256 stateId, 13 | address rootMessageSender, 14 | bytes calldata data 15 | ) external; 16 | } 17 | 18 | /** 19 | * @title FxChild child contract for state receiver 20 | */ 21 | contract FxChild is IStateReceiver { 22 | address public fxRoot; 23 | 24 | event NewFxMessage(address rootMessageSender, address receiver, bytes data); 25 | 26 | function setFxRoot(address _fxRoot) public { 27 | require(fxRoot == address(0x0)); 28 | fxRoot = _fxRoot; 29 | } 30 | 31 | function onStateReceive(uint256 stateId, bytes calldata _data) external override { 32 | // require(msg.sender == address(0x0000000000000000000000000000000000001001), 'Invalid sender'); 33 | (address rootMessageSender, address receiver, bytes memory data) = abi.decode( 34 | _data, 35 | (address, address, bytes) 36 | ); 37 | emit NewFxMessage(rootMessageSender, receiver, data); 38 | IFxMessageProcessor(receiver).processMessageFromRoot(stateId, rootMessageSender, data); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /certora/munged/dependencies/polygon/fxportal/FxChild.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: MIT 2 | pragma solidity ^0.8.10; 3 | 4 | // IStateReceiver represents interface to receive state 5 | interface IStateReceiver { 6 | function onStateReceive(uint256 stateId, bytes calldata data) external; 7 | } 8 | 9 | // IFxMessageProcessor represents interface to process message 10 | interface IFxMessageProcessor { 11 | function processMessageFromRoot( 12 | uint256 stateId, 13 | address rootMessageSender, 14 | bytes calldata data 15 | ) external; 16 | } 17 | 18 | /** 19 | * @title FxChild child contract for state receiver 20 | */ 21 | contract FxChild is IStateReceiver { 22 | address public fxRoot; 23 | 24 | event NewFxMessage(address rootMessageSender, address receiver, bytes data); 25 | 26 | function setFxRoot(address _fxRoot) public { 27 | require(fxRoot == address(0x0)); 28 | fxRoot = _fxRoot; 29 | } 30 | 31 | function onStateReceive(uint256 stateId, bytes calldata _data) external override { 32 | // require(msg.sender == address(0x0000000000000000000000000000000000001001), 'Invalid sender'); 33 | (address rootMessageSender, address receiver, bytes memory data) = abi.decode( 34 | _data, 35 | (address, address, bytes) 36 | ); 37 | emit NewFxMessage(rootMessageSender, receiver, data); 38 | IFxMessageProcessor(receiver).processMessageFromRoot(stateId, rootMessageSender, data); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The 3-Clause BSD License 2 | 3 | Copyright 2021 Aave SAGL 4 | 5 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 8 | 9 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 10 | 11 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /certora/munged/dependencies/arbitrum/interfaces/IInbox.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.7.0; 2 | 3 | interface IInbox { 4 | function sendL2Message(bytes calldata messageData) external returns (uint256); 5 | 6 | function sendUnsignedTransaction( 7 | uint256 maxGas, 8 | uint256 gasPriceBid, 9 | uint256 nonce, 10 | address destAddr, 11 | uint256 amount, 12 | bytes calldata data 13 | ) external returns (uint256); 14 | 15 | function sendContractTransaction( 16 | uint256 maxGas, 17 | uint256 gasPriceBid, 18 | address destAddr, 19 | uint256 amount, 20 | bytes calldata data 21 | ) external returns (uint256); 22 | 23 | function sendL1FundedUnsignedTransaction( 24 | uint256 maxGas, 25 | uint256 gasPriceBid, 26 | uint256 nonce, 27 | address destAddr, 28 | bytes calldata data 29 | ) external payable returns (uint256); 30 | 31 | function sendL1FundedContractTransaction( 32 | uint256 maxGas, 33 | uint256 gasPriceBid, 34 | address destAddr, 35 | bytes calldata data 36 | ) external payable returns (uint256); 37 | 38 | function createRetryableTicket( 39 | address destAddr, 40 | uint256 arbTxCallValue, 41 | uint256 maxSubmissionCost, 42 | address submissionRefundAddress, 43 | address valueRefundAddress, 44 | uint256 maxGas, 45 | uint256 gasPriceBid, 46 | bytes calldata data 47 | ) external payable returns (uint256); 48 | 49 | function depositEth(uint256 maxSubmissionCost) external payable returns (uint256); 50 | } 51 | -------------------------------------------------------------------------------- /contracts/mocks/MockOvmL2CrossDomainMessenger.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.10; 3 | 4 | import {ICrossDomainMessenger} from '../dependencies/optimism/interfaces/ICrossDomainMessenger.sol'; 5 | import {MockOvmL1CrossDomainMessenger} from './MockOvmL1CrossDomainMessenger.sol'; 6 | 7 | contract MockOvmL2CrossDomainMessenger is ICrossDomainMessenger { 8 | address private sender; 9 | address private l1Messenger; 10 | 11 | function setSender(address _sender) external { 12 | sender = _sender; 13 | } 14 | 15 | function setL1Messenger(address _l1Messenger) external { 16 | l1Messenger = _l1Messenger; 17 | } 18 | 19 | function xDomainMessageSender() external view override returns (address) { 20 | return sender; 21 | } 22 | 23 | function sendMessage( 24 | address _target, 25 | bytes calldata _message, 26 | uint32 _gasLimit 27 | ) external override { 28 | MockOvmL1CrossDomainMessenger(l1Messenger).redirect(_target, _message, _gasLimit); 29 | } 30 | 31 | // This error must be defined here or else Hardhat will not recognize the selector 32 | error UnauthorizedEthereumExecutor(); 33 | 34 | function redirect( 35 | address _xDomainMessageSender, 36 | address _target, 37 | bytes calldata _message, 38 | uint32 _gasLimit 39 | ) external { 40 | sender = _xDomainMessageSender; 41 | (bool success, bytes memory data) = _target.call{gas: _gasLimit}(_message); 42 | if (!success) { 43 | assembly { 44 | revert(add(data, 32), mload(data)) 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /contracts/dependencies/arbitrum/libraries/IGasRefunder.sol: -------------------------------------------------------------------------------- 1 | // Copyright 2021-2022, Offchain Labs, Inc. 2 | // For license information, see https://github.com/nitro/blob/master/LICENSE 3 | // SPDX-License-Identifier: BUSL-1.1 4 | 5 | // solhint-disable-next-line compiler-version 6 | pragma solidity >=0.6.9 <0.9.0; 7 | 8 | interface IGasRefunder { 9 | function onGasSpent( 10 | address payable spender, 11 | uint256 gasUsed, 12 | uint256 calldataSize 13 | ) external returns (bool success); 14 | } 15 | 16 | abstract contract GasRefundEnabled { 17 | /// @dev this refunds the sender for execution costs of the tx 18 | /// calldata costs are only refunded if `msg.sender == tx.origin` to guarantee the value refunded relates to charging 19 | /// for the `tx.input`. this avoids a possible attack where you generate large calldata from a contract and get over-refunded 20 | modifier refundsGas(IGasRefunder gasRefunder) { 21 | uint256 startGasLeft = gasleft(); 22 | _; 23 | if (address(gasRefunder) != address(0)) { 24 | uint256 calldataSize = 0; 25 | // if triggered in a contract call, the spender may be overrefunded by appending dummy data to the call 26 | // so we check if it is a top level call, which would mean the sender paid calldata as part of tx.input 27 | // solhint-disable-next-line avoid-tx-origin 28 | if (msg.sender == tx.origin) { 29 | assembly { 30 | calldataSize := calldatasize() 31 | } 32 | } 33 | gasRefunder.onGasSpent(payable(msg.sender), startGasLeft - gasleft(), calldataSize); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /tasks/setup/print-default-wallets.ts: -------------------------------------------------------------------------------- 1 | import { task } from 'hardhat/config'; 2 | import { Signer } from 'ethers'; 3 | import { 4 | getPrivateKeySigner, 5 | getMnemonicSigner, 6 | getDefenderSigner, 7 | } from '../../helpers/wallet-helpers'; 8 | import { setDRE } from '../../helpers/misc-utils'; 9 | 10 | task('print-default-wallets', 'Show addresses available per .env secrets').setAction( 11 | async (_, hre) => { 12 | await setDRE(hre); 13 | const pkSigner: Signer | null = getPrivateKeySigner(); 14 | if (pkSigner == null) { 15 | console.log( 16 | `Mnemonic default wallet address: private key not provided or invalid` 17 | ); 18 | } else { 19 | const pkAddress = await pkSigner.getAddress(); 20 | console.log(`Private key default wallet address: ${pkAddress}`); 21 | } 22 | 23 | const mnemonicSigner: Signer | null = getMnemonicSigner(); 24 | if (mnemonicSigner == null) { 25 | console.log( 26 | `Mnemonic default wallet address: mnemonic not provided or invalid` 27 | ); 28 | } else { 29 | const mnemonicAddress = await mnemonicSigner.getAddress(); 30 | console.log(`Mnemonic default wallet address: ${mnemonicAddress}`); 31 | } 32 | 33 | const defenderSigner: Signer | null = getDefenderSigner(); 34 | if (defenderSigner == null) { 35 | console.log( 36 | `OpenZeppelin Defender default wallet address: defender credentials not provided or invalid` 37 | ); 38 | } else { 39 | const defenderAddress = await defenderSigner.getAddress(); 40 | console.log(`OpenZeppelin Defender default wallet address: ${defenderAddress}`); 41 | } 42 | } 43 | ); 44 | -------------------------------------------------------------------------------- /tasks/misc/set-DRE.ts: -------------------------------------------------------------------------------- 1 | import { task } from 'hardhat/config'; 2 | import { DRE, setDRE } from '../../helpers/misc-utils'; 3 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 4 | import { formatEther } from 'ethers/lib/utils'; 5 | 6 | task(`set-DRE`, `Inits the DRE, to have access to all the plugins' objects`).setAction( 7 | async (_, _DRE) => { 8 | if (DRE) { 9 | return; 10 | } 11 | if ( 12 | (_DRE as HardhatRuntimeEnvironment).network.name.includes('tenderly') || 13 | process.env.TENDERLY === 'true' 14 | ) { 15 | console.log('- Setting up Tenderly provider'); 16 | const net = _DRE.tenderly.network(); 17 | 18 | if (process.env.TENDERLY_FORK && process.env.TENDERLY_HEAD) { 19 | console.log('- Connecting to a Tenderly Fork'); 20 | await net.setFork(process.env.TENDERLY_FORK); 21 | await net.setHead(process.env.TENDERLY_HEAD); 22 | } else { 23 | console.log('- Creating a new Tenderly Fork'); 24 | await net.initializeFork(); 25 | } 26 | const provider = new _DRE.ethers.providers.Web3Provider(net); 27 | _DRE.ethers.provider = provider; 28 | console.log('- Initialized Tenderly fork:'); 29 | console.log(' - Fork: ', net.getFork()); 30 | console.log(' - Head: ', net.getHead()); 31 | console.log(' - First account:', await (await _DRE.ethers.getSigners())[0].getAddress()); 32 | console.log( 33 | ' - Balance:', 34 | formatEther(await (await _DRE.ethers.getSigners())[0].getBalance()) 35 | ); 36 | } 37 | console.log(' - Current BlockNumber:', await _DRE.ethers.provider.getBlockNumber()); 38 | console.log(''); 39 | setDRE(_DRE); 40 | return _DRE; 41 | } 42 | ); 43 | -------------------------------------------------------------------------------- /helpers/optimism-contract-getters.ts: -------------------------------------------------------------------------------- 1 | import { Signer, BigNumber } from 'ethers'; 2 | import { 3 | OptimismBridgeExecutor, 4 | OptimismBridgeExecutor__factory, 5 | MockOvmL1CrossDomainMessenger, 6 | MockOvmL1CrossDomainMessenger__factory, 7 | MockOvmL2CrossDomainMessenger, 8 | MockOvmL2CrossDomainMessenger__factory, 9 | } from '../typechain'; 10 | import { tEthereumAddress } from './types'; 11 | 12 | export const deployOvmMessengers = async ( 13 | signer: Signer 14 | ): Promise<[MockOvmL1CrossDomainMessenger, MockOvmL2CrossDomainMessenger]> => { 15 | const l1Messenger = await new MockOvmL1CrossDomainMessenger__factory(signer).deploy(); 16 | const l2Messenger = await new MockOvmL2CrossDomainMessenger__factory(signer).deploy(); 17 | await l1Messenger.deployTransaction.wait(); 18 | await l2Messenger.deployTransaction.wait(); 19 | await l1Messenger.setL2Messenger(l2Messenger.address); 20 | await l2Messenger.setL1Messenger(l1Messenger.address); 21 | return [l1Messenger, l2Messenger]; 22 | }; 23 | 24 | export const deployOptimismBridgeExecutor = async ( 25 | ovmMessenger: tEthereumAddress, 26 | ethereumExecutor: tEthereumAddress, 27 | delay: BigNumber, 28 | gracePeriod: BigNumber, 29 | minimumDelay: BigNumber, 30 | maximumDelay: BigNumber, 31 | guardian: tEthereumAddress, 32 | signer: Signer 33 | ): Promise => { 34 | const optimismBridgeExecutorFactory = new OptimismBridgeExecutor__factory(signer); 35 | const optimismBridgeExecutor = await optimismBridgeExecutorFactory.deploy( 36 | ovmMessenger, 37 | ethereumExecutor, 38 | delay, 39 | gracePeriod, 40 | minimumDelay, 41 | maximumDelay, 42 | guardian 43 | ); 44 | await optimismBridgeExecutor.deployTransaction.wait(); 45 | return optimismBridgeExecutor; 46 | }; 47 | -------------------------------------------------------------------------------- /certora/munged/dependencies/arbitrum/AddressAliasHelper.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: Apache-2.0 2 | 3 | /* 4 | * Copyright 2019-2021, Offchain Labs, Inc. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | pragma solidity >=0.7.5; 20 | 21 | library AddressAliasHelper { 22 | uint160 constant offset = uint160(0x1111000000000000000000000000000000001111); 23 | 24 | /// @notice Utility function that converts the address in the L1 that submitted a tx to 25 | /// the inbox to the msg.sender viewed in the L2 26 | /// @param l1Address the address in the L1 that triggered the tx to L2 27 | /// @return l2Address L2 address as viewed in msg.sender 28 | function applyL1ToL2Alias(address l1Address) internal pure returns (address l2Address) { 29 | l2Address = address(uint160(l1Address) + offset); 30 | } 31 | 32 | /// @notice Utility function that converts the msg.sender viewed in the L2 to the 33 | /// address in the L1 that submitted a tx to the inbox 34 | /// @param l2Address L2 address as viewed in msg.sender 35 | /// @return l1Address the address in the L1 that triggered the tx to L2 36 | function undoL1ToL2Alias(address l2Address) internal pure returns (address l1Address) { 37 | l1Address = address(uint160(l2Address) - offset); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /certora/harness/DummyERC20Impl.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | // with mint 5 | contract DummyERC20Impl { 6 | uint256 t; 7 | mapping (address => uint256) b; 8 | mapping (address => mapping (address => uint256)) a; 9 | 10 | string public name; 11 | string public symbol; 12 | uint public decimals; 13 | 14 | function myAddress() public returns (address) { 15 | return address(this); 16 | } 17 | 18 | function add(uint a, uint b) internal pure returns (uint256) { 19 | uint c = a +b; 20 | require (c >= a); 21 | return c; 22 | } 23 | function sub(uint a, uint b) internal pure returns (uint256) { 24 | require (a>=b); 25 | return a-b; 26 | } 27 | 28 | function totalSupply() external view returns (uint256) { 29 | return t; 30 | } 31 | function balanceOf(address account) external view returns (uint256) { 32 | return b[account]; 33 | } 34 | function transfer(address recipient, uint256 amount) external returns (bool) { 35 | b[msg.sender] = sub(b[msg.sender], amount); 36 | b[recipient] = add(b[recipient], amount); 37 | return true; 38 | } 39 | function allowance(address owner, address spender) external view returns (uint256) { 40 | return a[owner][spender]; 41 | } 42 | function approve(address spender, uint256 amount) external returns (bool) { 43 | a[msg.sender][spender] = amount; 44 | return true; 45 | } 46 | 47 | function transferFrom( 48 | address sender, 49 | address recipient, 50 | uint256 amount 51 | ) external returns (bool) { 52 | b[sender] = sub(b[sender], amount); 53 | b[recipient] = add(b[recipient], amount); 54 | a[sender][msg.sender] = sub(a[sender][msg.sender], amount); 55 | return true; 56 | } 57 | } -------------------------------------------------------------------------------- /certora/harness/mockTarget.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity ^0.8.10; 3 | import {OptimismHarness} from './OptimismHarness.sol'; 4 | import {DummyERC20Impl} from './DummyERC20Impl.sol'; 5 | 6 | // target call mock target for Optimism bridge 7 | contract mockTarget 8 | { 9 | OptimismHarness public _executor; 10 | DummyERC20Impl private _tokenA; 11 | DummyERC20Impl private _tokenB; 12 | address private _account1; 13 | address private _account2; 14 | uint256 private _amount1; 15 | uint256 private _amount2; 16 | 17 | function targetCall(bytes memory data) external returns (bool output) 18 | { 19 | uint8 funcId = abi.decode(data, (uint8)); 20 | if (funcId == 1 ){ 21 | _executor.updateMinimumDelay(_amount1); 22 | } 23 | else if (funcId == 2){ 24 | _executor.updateDelay(_amount1); 25 | } 26 | else if (funcId == 3){ 27 | _executor.updateEthereumGovernanceExecutor(_account1); 28 | } 29 | else if (funcId == 4){ 30 | _executor.updateGracePeriod(_amount1); 31 | } 32 | else if (funcId == 5) { 33 | _executor.cancel(_amount2); 34 | } 35 | else if (funcId == 6) { 36 | output = _tokenA.transfer(_account1, _amount1); 37 | return output; 38 | } 39 | else if (funcId == 7) { 40 | output = _tokenB.transfer(_account2, _amount2); 41 | return output; 42 | } 43 | else { 44 | // Reverting path 45 | return false; 46 | } 47 | return true; 48 | } 49 | 50 | function tokenA() public view returns (DummyERC20Impl) 51 | { 52 | return _tokenA; 53 | } 54 | 55 | function tokenB() public view returns (DummyERC20Impl) 56 | { 57 | return _tokenB; 58 | } 59 | 60 | function getTransferArguments() public view 61 | returns(address, address, uint256, uint256) { 62 | return (_account1, _account2, _amount1, _amount2); 63 | } 64 | } -------------------------------------------------------------------------------- /contracts/bridges/ArbitrumBridgeExecutor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import {AddressAliasHelper} from '../dependencies/arbitrum/AddressAliasHelper.sol'; 5 | import {L2BridgeExecutor} from './L2BridgeExecutor.sol'; 6 | 7 | /** 8 | * @title ArbitrumBridgeExecutor 9 | * @author Aave 10 | * @notice Implementation of the Arbitrum Bridge Executor, able to receive cross-chain transactions from Ethereum 11 | * @dev Queuing an ActionsSet into this Executor can only be done by the L2 Address Alias of the L1 EthereumGovernanceExecutor 12 | */ 13 | contract ArbitrumBridgeExecutor is L2BridgeExecutor { 14 | /// @inheritdoc L2BridgeExecutor 15 | modifier onlyEthereumGovernanceExecutor() override { 16 | if (AddressAliasHelper.undoL1ToL2Alias(msg.sender) != _ethereumGovernanceExecutor) 17 | revert UnauthorizedEthereumExecutor(); 18 | _; 19 | } 20 | 21 | /** 22 | * @dev Constructor 23 | * 24 | * @param ethereumGovernanceExecutor The address of the EthereumGovernanceExecutor 25 | * @param delay The delay before which an actions set can be executed 26 | * @param gracePeriod The time period after a delay during which an actions set can be executed 27 | * @param minimumDelay The minimum bound a delay can be set to 28 | * @param maximumDelay The maximum bound a delay can be set to 29 | * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) 30 | */ 31 | constructor( 32 | address ethereumGovernanceExecutor, 33 | uint256 delay, 34 | uint256 gracePeriod, 35 | uint256 minimumDelay, 36 | uint256 maximumDelay, 37 | address guardian 38 | ) 39 | L2BridgeExecutor( 40 | ethereumGovernanceExecutor, 41 | delay, 42 | gracePeriod, 43 | minimumDelay, 44 | maximumDelay, 45 | guardian 46 | ) 47 | { 48 | // Intentionally left blank 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /certora/munged/bridges/ArbitrumBridgeExecutor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import {AddressAliasHelper} from '../dependencies/arbitrum/AddressAliasHelper.sol'; 5 | import {L2BridgeExecutor} from './L2BridgeExecutor.sol'; 6 | 7 | /** 8 | * @title ArbitrumBridgeExecutor 9 | * @author Aave 10 | * @notice Implementation of the Arbitrum Bridge Executor, able to receive cross-chain transactions from Ethereum 11 | * @dev Queuing an ActionsSet into this Executor can only be done by the L2 Address Alias of the L1 EthereumGovernanceExecutor 12 | */ 13 | contract ArbitrumBridgeExecutor is L2BridgeExecutor { 14 | /// @inheritdoc L2BridgeExecutor 15 | modifier onlyEthereumGovernanceExecutor() override { 16 | if (AddressAliasHelper.undoL1ToL2Alias(msg.sender) != _ethereumGovernanceExecutor) 17 | revert UnauthorizedEthereumExecutor(); 18 | _; 19 | } 20 | 21 | /** 22 | * @dev Constructor 23 | * 24 | * @param ethereumGovernanceExecutor The address of the EthereumGovernanceExecutor 25 | * @param delay The delay before which an actions set can be executed 26 | * @param gracePeriod The time period after a delay during which an actions set can be executed 27 | * @param minimumDelay The minimum bound a delay can be set to 28 | * @param maximumDelay The maximum bound a delay can be set to 29 | * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) 30 | */ 31 | constructor( 32 | address ethereumGovernanceExecutor, 33 | uint256 delay, 34 | uint256 gracePeriod, 35 | uint256 minimumDelay, 36 | uint256 maximumDelay, 37 | address guardian 38 | ) 39 | L2BridgeExecutor( 40 | ethereumGovernanceExecutor, 41 | delay, 42 | gracePeriod, 43 | minimumDelay, 44 | maximumDelay, 45 | guardian 46 | ) 47 | { 48 | // Intentionally left blank 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /helper-hardhat-config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | eArbitrumNetwork, 3 | eEthereumNetwork, 4 | eOptimismNetwork, 5 | ePolygonNetwork, 6 | eXDaiNetwork, 7 | iParamsPerNetwork, 8 | } from './helpers/types'; 9 | 10 | const INFURA_KEY = process.env.INFURA_KEY || ''; 11 | const ALCHEMY_KEY = process.env.ALCHEMY_KEY || ''; 12 | const TENDERLY_FORK = process.env.TENDERLY_FORK || ''; 13 | 14 | export const NETWORKS_RPC_URL: iParamsPerNetwork = { 15 | [eEthereumNetwork.kovan]: ALCHEMY_KEY 16 | ? `https://eth-kovan.alchemyapi.io/v2/${ALCHEMY_KEY}` 17 | : `https://kovan.infura.io/v3/${INFURA_KEY}`, 18 | [eEthereumNetwork.ropsten]: ALCHEMY_KEY 19 | ? `https://eth-ropsten.alchemyapi.io/v2/${ALCHEMY_KEY}` 20 | : `https://ropsten.infura.io/v3/${INFURA_KEY}`, 21 | [eEthereumNetwork.rinkeby]: ALCHEMY_KEY 22 | ? `https://eth-rinkeby.alchemyapi.io/v2/${ALCHEMY_KEY}` 23 | : `https://rinkeby.infura.io/v3/${INFURA_KEY}`, 24 | [eEthereumNetwork.goerli]: ALCHEMY_KEY 25 | ? `https://eth-goerli.alchemyapi.io/v2/${ALCHEMY_KEY}` 26 | : `https://goerli.infura.io/v3/${INFURA_KEY}`, 27 | [eEthereumNetwork.main]: ALCHEMY_KEY 28 | ? `https://eth-mainnet.alchemyapi.io/v2/${ALCHEMY_KEY}` 29 | : `https://mainnet.infura.io/v3/${INFURA_KEY}`, 30 | [eEthereumNetwork.coverage]: 'http://localhost:8555', 31 | [eEthereumNetwork.hardhat]: 'http://localhost:8545', 32 | [eEthereumNetwork.tenderlyMain]: `https://rpc.tenderly.co/fork/${TENDERLY_FORK}`, 33 | [ePolygonNetwork.mumbai]: 'https://rpc-mumbai.maticvigil.com', 34 | [ePolygonNetwork.matic]: 'https://rpc-mainnet.matic.network', 35 | [eXDaiNetwork.xdai]: 'https://rpc.xdaichain.com/', 36 | [eArbitrumNetwork.arbitrum]: `https://arb1.arbitrum.io/rpc`, 37 | [eArbitrumNetwork.arbitrumTestnet]: `https://rinkeby.arbitrum.io/rpc`, 38 | [eOptimismNetwork.main]: `https://opt-mainnet.g.alchemy.com/v2/${ALCHEMY_KEY}`, 39 | [eOptimismNetwork.testnet]: `https://opt-kovan.g.alchemy.com/v2/${ALCHEMY_KEY}`, 40 | }; 41 | -------------------------------------------------------------------------------- /certora/harness/mockTargetPoly.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: agpl-3.0 2 | pragma solidity ^0.8.10; 3 | import {PolygonHarness} from './PolygonHarness.sol'; 4 | import {DummyERC20Impl} from './DummyERC20Impl.sol'; 5 | 6 | // target call mock target for Polygon bridge 7 | contract mockTargetPoly 8 | { 9 | PolygonHarness public _executor; 10 | DummyERC20Impl private _tokenA; 11 | DummyERC20Impl private _tokenB; 12 | address private _account1; 13 | address private _account2; 14 | uint256 private _amount1; 15 | uint256 private _amount2; 16 | 17 | function targetCall(bytes memory data) external returns (bool output) 18 | { 19 | uint8 funcId = abi.decode(data, (uint8)); 20 | if (funcId == 1 ){ 21 | _executor.updateMinimumDelay(_amount1); 22 | } 23 | else if (funcId == 2){ 24 | _executor.updateDelay(_amount1); 25 | } 26 | else if (funcId == 3){ 27 | _executor.updateFxRootSender(_account1); 28 | } 29 | else if (funcId == 4){ 30 | _executor.updateGracePeriod(_amount1); 31 | } 32 | else if (funcId == 5) { 33 | _executor.cancel(_amount2); 34 | } 35 | else if (funcId == 6) { 36 | output = _tokenA.transfer(_account1, _amount1); 37 | return output; 38 | } 39 | else if (funcId == 7) { 40 | output = _tokenB.transfer(_account2, _amount2); 41 | return output; 42 | } 43 | else if (funcId == 8) { 44 | _executor.updateFxChild(_account2); 45 | } 46 | else { 47 | // Reverting path 48 | return false; 49 | } 50 | return true; 51 | } 52 | 53 | function tokenA() public view returns (DummyERC20Impl) 54 | { 55 | return _tokenA; 56 | } 57 | 58 | function tokenB() public view returns (DummyERC20Impl) 59 | { 60 | return _tokenB; 61 | } 62 | 63 | function getTransferArguments() public view 64 | returns(address, address, uint256, uint256) { 65 | return (_account1, _account2, _amount1, _amount2); 66 | } 67 | } -------------------------------------------------------------------------------- /tasks/governance/check-polygon.ts: -------------------------------------------------------------------------------- 1 | import { task } from 'hardhat/config'; 2 | import dotenv from 'dotenv'; 3 | import { BigNumber } from 'ethers'; 4 | 5 | import { 6 | getMumbaiBlocktime, 7 | initPolygonMarketUpdateContract, 8 | listenForNewDelay, 9 | listenForUpdateExecuted, 10 | initPolygonBridgeExecutor, 11 | } from '../../helpers/polygon-helpers'; 12 | 13 | dotenv.config({ path: '../../.env' }); 14 | 15 | task('check-polygon', 'Create Proposal').setAction(async (_, localBRE) => { 16 | await localBRE.run('set-DRE'); 17 | 18 | console.log(`0___Setup___`); 19 | console.log(`Mumbai Blocktime: ${await (await getMumbaiBlocktime()).toString()}`); 20 | console.log(); 21 | const polygonMarketUpdate = await initPolygonMarketUpdateContract(); 22 | await listenForUpdateExecuted(polygonMarketUpdate); 23 | const polygonBridgeExecutor = await initPolygonBridgeExecutor(); 24 | await listenForNewDelay(polygonBridgeExecutor); 25 | console.log(); 26 | 27 | console.log('1__Test Market Update Contract'); 28 | console.log(`Current Counter: ${(await polygonMarketUpdate.getCounter()).toString()}`); 29 | console.log(`Executing Polygon Market Update...`); 30 | const executeTx = await polygonMarketUpdate.execute(81); 31 | await executeTx.wait(); 32 | while (polygonMarketUpdate.listenerCount() > 0) { 33 | console.log('Waiting for event'); 34 | await sleep(2000); 35 | } 36 | await sleep(3000); 37 | 38 | console.log(); 39 | console.log('2__Test Polygon Bridge Executor'); 40 | console.log(`Current Delay: ${(await polygonBridgeExecutor.getDelay()).toString()}`); 41 | // const delayTx = await polygonBridgeExecutor.setDelay(61); 42 | // await delayTx.wait(); 43 | /* while (polygonBridgeExecutor.listenerCount() > 0) { 44 | console.log('Waiting for event'); 45 | await sleep(2000); 46 | } 47 | await sleep(3000);*/ 48 | }); 49 | 50 | const sleep = async (ms: number) => { 51 | return new Promise((resolve) => setTimeout(resolve, ms)); 52 | }; 53 | -------------------------------------------------------------------------------- /certora/munged/interfaces/IL2BridgeExecutor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | /** 5 | * @title IL2BridgeExecutorBase 6 | * @author Aave 7 | * @notice Defines the basic interface for the L2BridgeExecutor abstract contract 8 | */ 9 | interface IL2BridgeExecutor { 10 | error UnauthorizedEthereumExecutor(); 11 | 12 | /** 13 | * @dev Emitted when the Ethereum Governance Executor is updated 14 | * @param oldEthereumGovernanceExecutor The address of the old EthereumGovernanceExecutor 15 | * @param newEthereumGovernanceExecutor The address of the new EthereumGovernanceExecutor 16 | **/ 17 | event EthereumGovernanceExecutorUpdate( 18 | address oldEthereumGovernanceExecutor, 19 | address newEthereumGovernanceExecutor 20 | ); 21 | 22 | /** 23 | * @notice Queue an ActionsSet 24 | * @dev If a signature is empty, calldata is used for the execution, calldata is appended to signature otherwise 25 | * @param targets Array of targets to be called by the actions set 26 | * @param values Array of values to pass in each call by the actions set 27 | * @param signatures Array of function signatures to encode in each call by the actions (can be empty) 28 | * @param calldatas Array of calldata to pass in each call by the actions set 29 | * @param withDelegatecalls Array of whether to delegatecall for each call of the actions set 30 | **/ 31 | function queue( 32 | address[] memory targets, 33 | uint256[] memory values, 34 | string[] memory signatures, 35 | bytes[] memory calldatas, 36 | bool[] memory withDelegatecalls 37 | ) external; 38 | 39 | /** 40 | * @notice Update the address of the Ethereum Governance Executor 41 | * @param ethereumGovernanceExecutor The address of the new EthereumGovernanceExecutor 42 | **/ 43 | function updateEthereumGovernanceExecutor(address ethereumGovernanceExecutor) external; 44 | 45 | /** 46 | * @notice Returns the address of the Ethereum Governance Executor 47 | * @return The address of the EthereumGovernanceExecutor 48 | **/ 49 | function getEthereumGovernanceExecutor() external view returns (address); 50 | } 51 | -------------------------------------------------------------------------------- /certora/README.md: -------------------------------------------------------------------------------- 1 | ## Verification Overview 2 | The current directory contains Certora's formal verification of AAVE's L2 bridge protocol. 3 | In this directory you will find three subdirectories: 4 | 5 | 1. specs - Contains all the specification files that were written by Certora for the bridge protocol. We have created two spec files, `Optimism_ArbitrumBridge.spec`, `PolygonBridge.spec`, the first is used to verify Optimism and Arbitrum executors and the second for Polygon. The choice for this separation relies on the contracts inhertiance of these three bridges protocols. 6 | We emphasize that essentially the rules in both specs are the same (by context, name and logical idea), but the implementation might be a bit different due to implementation differences (for example the name given to the 'external queue' function.) 7 | 8 | 2. scripts - Contains all the necessary run scripts to execute the spec files on the Certora Prover. These scripts composed of a run command of Certora Prover, contracts to take into account in the verification context, declaration of the compiler and a set of additional settings. Each script is named after the bridge type it verifies. Note that the Optimism and Arbitrum bridges are verified by the same spec file, but have separate scripts. 9 | 10 | 3. harness - Contains all the inheriting contracts that add/simplify functionalities to the original contract. You will also find a set of symbolic and dummy implementations of external contracts on which the executors rely. 11 | These harnesses, i.e. extensions/simplifications, are necessary to run the verifications. Assumptions and under-approximations are also done in a few places, in order to supply partial coverage where full coverage is not achievable. 12 | One harness worth explaining is the mockTarget and mockTargetPoly contracts that were created in order to simualte the low-level calls of the executed transactions. 13 | 14 |
15 | 16 | --- 17 | 18 | ## Running Instructions 19 | To run a verification job: 20 | 21 | 1. Open terminal and `cd` your way to the main AAVE L2 repository. 22 | 23 | 2. Run the script you'd like to get results for: 24 | ``` 25 | sh certora/scripts/verifyOptimism.sh 26 | ``` 27 | 28 |
29 | -------------------------------------------------------------------------------- /contracts/interfaces/IL2BridgeExecutor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import {IExecutorBase} from './IExecutorBase.sol'; 5 | 6 | /** 7 | * @title IL2BridgeExecutorBase 8 | * @author Aave 9 | * @notice Defines the basic interface for the L2BridgeExecutor abstract contract 10 | */ 11 | interface IL2BridgeExecutor is IExecutorBase { 12 | error UnauthorizedEthereumExecutor(); 13 | 14 | /** 15 | * @dev Emitted when the Ethereum Governance Executor is updated 16 | * @param oldEthereumGovernanceExecutor The address of the old EthereumGovernanceExecutor 17 | * @param newEthereumGovernanceExecutor The address of the new EthereumGovernanceExecutor 18 | **/ 19 | event EthereumGovernanceExecutorUpdate( 20 | address oldEthereumGovernanceExecutor, 21 | address newEthereumGovernanceExecutor 22 | ); 23 | 24 | /** 25 | * @notice Queue an ActionsSet 26 | * @dev If a signature is empty, calldata is used for the execution, calldata is appended to signature otherwise 27 | * @param targets Array of targets to be called by the actions set 28 | * @param values Array of values to pass in each call by the actions set 29 | * @param signatures Array of function signatures to encode in each call by the actions (can be empty) 30 | * @param calldatas Array of calldata to pass in each call by the actions set 31 | * @param withDelegatecalls Array of whether to delegatecall for each call of the actions set 32 | **/ 33 | function queue( 34 | address[] memory targets, 35 | uint256[] memory values, 36 | string[] memory signatures, 37 | bytes[] memory calldatas, 38 | bool[] memory withDelegatecalls 39 | ) external; 40 | 41 | /** 42 | * @notice Update the address of the Ethereum Governance Executor 43 | * @param ethereumGovernanceExecutor The address of the new EthereumGovernanceExecutor 44 | **/ 45 | function updateEthereumGovernanceExecutor(address ethereumGovernanceExecutor) external; 46 | 47 | /** 48 | * @notice Returns the address of the Ethereum Governance Executor 49 | * @return The address of the EthereumGovernanceExecutor 50 | **/ 51 | function getEthereumGovernanceExecutor() external view returns (address); 52 | } 53 | -------------------------------------------------------------------------------- /test/helpers/governance-helpers.ts: -------------------------------------------------------------------------------- 1 | import { Signer, BigNumber } from 'ethers'; 2 | import { tEthereumAddress } from '../../helpers/types'; 3 | import { AaveGovernanceV2 } from '../../typechain'; 4 | import { expect } from 'chai'; 5 | 6 | export const expectProposalState = async ( 7 | aaveGovernanceV2: AaveGovernanceV2, 8 | proposalId: number, 9 | state: number 10 | ): Promise => { 11 | expect(await aaveGovernanceV2.getProposalState(proposalId)).to.be.equal(state); 12 | }; 13 | 14 | export const createProposal = async ( 15 | aaveGovernanceV2: AaveGovernanceV2, 16 | signer: Signer, 17 | executor: string, 18 | targets: tEthereumAddress[], 19 | values: BigNumber[], 20 | signatures: string[], 21 | calldatas: string[], 22 | withDelegatecalls: boolean[], 23 | ipfsHash: string 24 | ) => { 25 | const proposalTx = await aaveGovernanceV2 26 | .connect(signer) 27 | .create(executor, targets, values, signatures, calldatas, withDelegatecalls, ipfsHash); 28 | await expect(proposalTx).to.emit(aaveGovernanceV2, 'ProposalCreated'); 29 | const proposalTxReceipt = await proposalTx.wait(); 30 | const proposalLog = aaveGovernanceV2.interface.parseLog(proposalTxReceipt.logs[0]); 31 | return aaveGovernanceV2.interface.decodeEventLog( 32 | proposalLog.eventFragment, 33 | proposalTxReceipt.logs[0].data 34 | ); 35 | }; 36 | 37 | export const triggerWhaleVotes = async ( 38 | aaveGovernanceV2: AaveGovernanceV2, 39 | whales: Signer[], 40 | proposalId: BigNumber, 41 | yesOrNo: boolean 42 | ): Promise => { 43 | const vote = async (signer: Signer) => { 44 | const tx = await aaveGovernanceV2.connect(signer).submitVote(proposalId, yesOrNo); 45 | await tx.wait(); 46 | }; 47 | const promises = whales.map(vote); 48 | await Promise.all(promises); 49 | }; 50 | 51 | export const queueProposal = async (aaveGovernanceV2: AaveGovernanceV2, proposalId: BigNumber) => { 52 | const queueTx = await aaveGovernanceV2.queue(proposalId); 53 | await expect(queueTx).to.emit(aaveGovernanceV2, 'ProposalQueued'); 54 | const queueTxReceipt = await queueTx.wait(); 55 | const queueLog = aaveGovernanceV2.interface.parseLog(queueTxReceipt.logs[1]); 56 | return aaveGovernanceV2.interface.decodeEventLog( 57 | queueLog.eventFragment, 58 | queueTxReceipt.logs[1].data 59 | ); 60 | }; 61 | -------------------------------------------------------------------------------- /helpers/wallet-helpers.ts: -------------------------------------------------------------------------------- 1 | import { Wallet, Signer, utils } from 'ethers'; 2 | import { DefenderRelaySigner, DefenderRelayProvider } from 'defender-relay-client/lib/ethers'; 3 | import dotenv from 'dotenv'; 4 | import { DRE } from './misc-utils'; 5 | 6 | dotenv.config({ path: '../.env' }); 7 | 8 | const PRIVATE_KEY = process.env.PRIVATE_KEY || ''; 9 | const MNEMONIC = process.env.MNEMONIC || ''; 10 | const INDEX = process.env.INDEX || '0'; 11 | const DEFENDER_API_KEY = process.env.DEFENDER_API_KEY || ''; 12 | const DEFENDER_SECRET_KEY = process.env.DEFENDER_SECRET_KEY || ''; 13 | const DEFAULT_WALLET = process.env.DEFAULT_WALLET; 14 | 15 | export const getPrivateKeySigner = (): Signer => { 16 | try { 17 | return new Wallet(PRIVATE_KEY, DRE.ethers.provider); 18 | } catch (e) { 19 | console.log(e); 20 | process.exit(1); 21 | } 22 | }; 23 | 24 | export const getMnemonicSigner = (indexNumber?: number): Signer => { 25 | try { 26 | let walletIndex; 27 | indexNumber ? (walletIndex = indexNumber) : (walletIndex = INDEX); 28 | const parentHdNode = utils.HDNode.fromMnemonic(MNEMONIC); 29 | const childHdNode = parentHdNode.derivePath(`m/44'/60'/0'/0/${walletIndex}`); 30 | return new Wallet(childHdNode.privateKey); 31 | } catch (e) { 32 | console.log(e); 33 | process.exit(1); 34 | } 35 | }; 36 | 37 | export const getDefenderSigner = (): Signer => { 38 | const credentials = { apiKey: DEFENDER_API_KEY, apiSecret: DEFENDER_SECRET_KEY }; 39 | try { 40 | const provider = new DefenderRelayProvider(credentials); 41 | return new DefenderRelaySigner(credentials, provider, { speed: 'fast' }); 42 | } catch (e) { 43 | console.log(e); 44 | process.exit(1); 45 | } 46 | }; 47 | 48 | export const getDefaultSigner = (signerName: string): Signer => { 49 | const signerNameLowerCase = signerName.toLowerCase(); 50 | if ( 51 | signerNameLowerCase === 'privatekey' || 52 | signerNameLowerCase === 'private key' || 53 | signerNameLowerCase === 'pk' 54 | ) { 55 | return getPrivateKeySigner(); 56 | } 57 | if (signerNameLowerCase.toLowerCase() === 'mnemonic' || signerNameLowerCase === 'mn') { 58 | return getMnemonicSigner(); 59 | } 60 | if (signerNameLowerCase.toLowerCase() === 'defender' || signerNameLowerCase === 'ozd') { 61 | return getDefenderSigner(); 62 | } 63 | throw new Error('Unrecognized Signer Type Selected'); 64 | }; 65 | -------------------------------------------------------------------------------- /contracts/bridges/OptimismBridgeExecutor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import {ICrossDomainMessenger} from '../dependencies/optimism/interfaces/ICrossDomainMessenger.sol'; 5 | import {L2BridgeExecutor} from './L2BridgeExecutor.sol'; 6 | 7 | /** 8 | * @title OptimismBridgeExecutor 9 | * @author Aave 10 | * @notice Implementation of the Optimism Bridge Executor, able to receive cross-chain transactions from Ethereum 11 | * @dev Queuing an ActionsSet into this Executor can only be done by the Optimism L2 Cross Domain Messenger and having 12 | * the EthereumGovernanceExecutor as xDomainMessageSender 13 | */ 14 | contract OptimismBridgeExecutor is L2BridgeExecutor { 15 | // Address of the Optimism L2 Cross Domain Messenger, in charge of redirecting cross-chain transactions in L2 16 | address public immutable OVM_L2_CROSS_DOMAIN_MESSENGER; 17 | 18 | /// @inheritdoc L2BridgeExecutor 19 | modifier onlyEthereumGovernanceExecutor() override { 20 | if ( 21 | msg.sender != OVM_L2_CROSS_DOMAIN_MESSENGER || 22 | ICrossDomainMessenger(OVM_L2_CROSS_DOMAIN_MESSENGER).xDomainMessageSender() != 23 | _ethereumGovernanceExecutor 24 | ) revert UnauthorizedEthereumExecutor(); 25 | _; 26 | } 27 | 28 | /** 29 | * @dev Constructor 30 | * 31 | * @param ovmL2CrossDomainMessenger The address of the Optimism L2CrossDomainMessenger 32 | * @param ethereumGovernanceExecutor The address of the EthereumGovernanceExecutor 33 | * @param delay The delay before which an actions set can be executed 34 | * @param gracePeriod The time period after a delay during which an actions set can be executed 35 | * @param minimumDelay The minimum bound a delay can be set to 36 | * @param maximumDelay The maximum bound a delay can be set to 37 | * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) 38 | */ 39 | constructor( 40 | address ovmL2CrossDomainMessenger, 41 | address ethereumGovernanceExecutor, 42 | uint256 delay, 43 | uint256 gracePeriod, 44 | uint256 minimumDelay, 45 | uint256 maximumDelay, 46 | address guardian 47 | ) 48 | L2BridgeExecutor( 49 | ethereumGovernanceExecutor, 50 | delay, 51 | gracePeriod, 52 | minimumDelay, 53 | maximumDelay, 54 | guardian 55 | ) 56 | { 57 | OVM_L2_CROSS_DOMAIN_MESSENGER = ovmL2CrossDomainMessenger; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /certora/munged/bridges/OptimismBridgeExecutor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import {ICrossDomainMessenger} from '../dependencies/optimism/interfaces/ICrossDomainMessenger.sol'; 5 | import {L2BridgeExecutor} from './L2BridgeExecutor.sol'; 6 | 7 | /** 8 | * @title OptimismBridgeExecutor 9 | * @author Aave 10 | * @notice Implementation of the Optimism Bridge Executor, able to receive cross-chain transactions from Ethereum 11 | * @dev Queuing an ActionsSet into this Executor can only be done by the Optimism L2 Cross Domain Messenger and having 12 | * the EthereumGovernanceExecutor as xDomainMessageSender 13 | */ 14 | contract OptimismBridgeExecutor is L2BridgeExecutor { 15 | // Address of the Optimism L2 Cross Domain Messenger, in charge of redirecting cross-chain transactions in L2 16 | address public immutable OVM_L2_CROSS_DOMAIN_MESSENGER; 17 | 18 | /// @inheritdoc L2BridgeExecutor 19 | modifier onlyEthereumGovernanceExecutor() override { 20 | if ( 21 | msg.sender != OVM_L2_CROSS_DOMAIN_MESSENGER || 22 | ICrossDomainMessenger(OVM_L2_CROSS_DOMAIN_MESSENGER).xDomainMessageSender() != 23 | _ethereumGovernanceExecutor 24 | ) revert UnauthorizedEthereumExecutor(); 25 | _; 26 | } 27 | 28 | /** 29 | * @dev Constructor 30 | * 31 | * @param ovmL2CrossDomainMessenger The address of the Optimism L2CrossDomainMessenger 32 | * @param ethereumGovernanceExecutor The address of the EthereumGovernanceExecutor 33 | * @param delay The delay before which an actions set can be executed 34 | * @param gracePeriod The time period after a delay during which an actions set can be executed 35 | * @param minimumDelay The minimum bound a delay can be set to 36 | * @param maximumDelay The maximum bound a delay can be set to 37 | * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) 38 | */ 39 | constructor( 40 | address ovmL2CrossDomainMessenger, 41 | address ethereumGovernanceExecutor, 42 | uint256 delay, 43 | uint256 gracePeriod, 44 | uint256 minimumDelay, 45 | uint256 maximumDelay, 46 | address guardian 47 | ) 48 | L2BridgeExecutor( 49 | ethereumGovernanceExecutor, 50 | delay, 51 | gracePeriod, 52 | minimumDelay, 53 | maximumDelay, 54 | guardian 55 | ) 56 | { 57 | OVM_L2_CROSS_DOMAIN_MESSENGER = ovmL2CrossDomainMessenger; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /tasks/verify/verify-template.ts: -------------------------------------------------------------------------------- 1 | import { task } from 'hardhat/config'; 2 | import { setDRE } from '../../helpers/misc-utils'; 3 | import { verifyContract } from '../../helpers/etherscan-verification'; 4 | import { 5 | printExpectedParams, 6 | parseParams, 7 | parseLibraries, 8 | defineParams, 9 | } from '../../helpers/task-helpers'; 10 | 11 | task('verify-template', 'verify contract on etherscan') 12 | .addParam('contract', 'Name of contract to deploy') 13 | .addParam('contractaddress', 'Address of deployed contract to verify') 14 | .addOptionalParam('params', 'JSON string of contract params - defaults to CLI') 15 | .addOptionalParam('paramsfile', 'Path to a TS file with params defined as default export') 16 | .addOptionalParam('libraries', 'json as string mapping of libraries to address') 17 | .addOptionalParam('librariesfile', 'file containing mapping of libraries to address') 18 | .addFlag('printparams', `Print constructor params`) 19 | .setAction( 20 | async ( 21 | { contract, contractaddress, params, paramsfile, libraries, librariesfile, printparams }, 22 | hre 23 | ) => { 24 | setDRE(hre); 25 | const { ethers } = hre; 26 | 27 | let parsedLibraries; 28 | if (libraries || librariesfile) { 29 | parsedLibraries = await parseLibraries(libraries, librariesfile); 30 | if (!parsedLibraries) return; 31 | } 32 | 33 | const ContractFactory = await ethers.getContractFactory(contract, parsedLibraries); 34 | const constructorInputs = ContractFactory.interface.deploy.inputs; 35 | 36 | let contractParams; 37 | if (params || paramsfile) { 38 | contractParams = await parseParams(params, paramsfile); 39 | if (!contractParams) return; 40 | } 41 | 42 | /** 43 | * uncomment following line to override params with hardcoded parameters 44 | */ 45 | // params = {} 46 | 47 | printExpectedParams(contract, constructorInputs); 48 | if (printparams) { 49 | return; 50 | } 51 | 52 | let paramsArray: any[] = []; 53 | if (constructorInputs.length > 0) { 54 | paramsArray = await defineParams(contractParams, constructorInputs); 55 | } 56 | 57 | const contractInstance = await ContractFactory.attach(contractaddress); 58 | let libs; 59 | if (parsedLibraries && parsedLibraries.libraries) { 60 | libs = JSON.stringify(parsedLibraries.libraries); 61 | } else { 62 | libs = ''; 63 | } 64 | await verifyContract(contractInstance.address, Object.values(paramsArray)); 65 | } 66 | ); 67 | -------------------------------------------------------------------------------- /tasks/deploy/deployPolygonGovernance.ts: -------------------------------------------------------------------------------- 1 | import { task } from 'hardhat/config'; 2 | import { Signer } from 'ethers'; 3 | import { DRE } from '../../helpers/misc-utils'; 4 | import { verifyContract } from '../../helpers/etherscan-verification'; 5 | import { getDefaultSigner } from '../../helpers/wallet-helpers'; 6 | 7 | import { printDefinedParams, defineParams, deployContract } from '../../helpers/task-helpers'; 8 | 9 | task('deployPolygonGovernance', 'deploy PolygonBridgeExecutor') 10 | .addFlag('verify', 'Verify contract on Etherscan') 11 | .setAction(async ({ verify }, hre) => { 12 | await hre.run('set-DRE'); 13 | const { ethers } = hre; 14 | 15 | let contractSigner: Signer = await (await DRE.ethers.getSigners())[0]; 16 | 17 | if (!DRE.network.name.includes('tenderly')) { 18 | console.log(`Using OpenZeppelin Defender`); 19 | contractSigner = getDefaultSigner('ozd'); 20 | } 21 | 22 | console.log(`Signer: ${await contractSigner.getAddress()}`); 23 | const ContractFactory = await ethers.getContractFactory('PolygonBridgeExecutor'); 24 | const constructorInputs = ContractFactory.interface.deploy.inputs; 25 | 26 | /** 27 | * uncomment following line to override params with hardcoded parameters 28 | */ 29 | const contractParams = { 30 | fxRootSender: '0xee56e2b3d491590b5b31738cc34d5232f378a8d5', 31 | fxChild: '0x8397259c983751DAf40400790063935a11afa28a', 32 | delay: '172800', 33 | gracePeriod: '259200', 34 | minimumDelay: '28800', 35 | maximumDelay: '604800', 36 | guardian: '0xbb2f3ba4a63982ed6d93c190c28b15cbba0b6af3', 37 | }; 38 | 39 | // MUMBAI Deployment Params 40 | // const contractParams = { 41 | // fxRootSender: '0x3d1d3E34f7fB6D26245E6640E1c50710eFFf15bA', 42 | // fxChild: '0xCf73231F28B7331BBe3124B907840A94851f9f11', 43 | // delay: '172800', 44 | // gracePeriod: '259200', 45 | // minimumDelay: '28800', 46 | // maximumDelay: '604800', 47 | // guardian: '0xbb2f3ba4a63982ed6d93c190c28b15cbba0b6af3', 48 | // }; 49 | 50 | let paramsArray: any[] = []; 51 | if (constructorInputs.length > 0) { 52 | paramsArray = await defineParams(contractParams, constructorInputs); 53 | printDefinedParams(constructorInputs, paramsArray); 54 | } 55 | 56 | console.log(' - Balance:', await contractSigner.getBalance()); 57 | 58 | const contractInstance = await deployContract(paramsArray, ContractFactory, contractSigner); 59 | 60 | if (verify) { 61 | const jsonLibs = '{}'; 62 | await verifyContract(contractInstance.address, paramsArray, jsonLibs); 63 | } 64 | }); 65 | -------------------------------------------------------------------------------- /helpers/types.ts: -------------------------------------------------------------------------------- 1 | export interface SymbolMap { 2 | [symbol: string]: T; 3 | } 4 | 5 | export type eNetwork = 6 | | eEthereumNetwork 7 | | ePolygonNetwork 8 | | eXDaiNetwork 9 | | eArbitrumNetwork 10 | | eOptimismNetwork; 11 | 12 | export enum eEthereumNetwork { 13 | kovan = 'kovan', 14 | ropsten = 'ropsten', 15 | rinkeby = 'rinkeby', 16 | goerli = 'goerli', 17 | main = 'main', 18 | coverage = 'coverage', 19 | hardhat = 'hardhat', 20 | tenderlyMain = 'tenderlyMain', 21 | } 22 | 23 | export enum ePolygonNetwork { 24 | matic = 'matic', 25 | mumbai = 'mumbai', 26 | } 27 | 28 | export enum eXDaiNetwork { 29 | xdai = 'xdai', 30 | } 31 | 32 | export enum eArbitrumNetwork { 33 | arbitrum = 'arbitrum', 34 | arbitrumTestnet = 'arbitrum-testnet', 35 | } 36 | 37 | export enum eOptimismNetwork { 38 | main = 'optimism', 39 | testnet = 'optimism-testnet', 40 | } 41 | 42 | export enum EthereumNetworkNames { 43 | kovan = 'kovan', 44 | ropsten = 'ropsten', 45 | rinkeby = 'rinkeby', 46 | goerli = 'goerli', 47 | main = 'main', 48 | matic = 'matic', 49 | mumbai = 'mumbai', 50 | xdai = 'xdai', 51 | } 52 | 53 | export type tEthereumAddress = string; 54 | 55 | export type iParamsPerNetwork = 56 | | iEthereumParamsPerNetwork 57 | | iPolygonParamsPerNetwork 58 | | iXDaiParamsPerNetwork 59 | | iArbitrumParamsPerNetwork 60 | | iOptimismParamsPerNetwork; 61 | 62 | export interface iParamsPerNetworkAll 63 | extends iEthereumParamsPerNetwork, 64 | iPolygonParamsPerNetwork, 65 | iXDaiParamsPerNetwork {} 66 | 67 | export interface iEthereumParamsPerNetwork { 68 | [eEthereumNetwork.coverage]: eNetwork; 69 | [eEthereumNetwork.kovan]: eNetwork; 70 | [eEthereumNetwork.ropsten]: eNetwork; 71 | [eEthereumNetwork.rinkeby]: eNetwork; 72 | [eEthereumNetwork.goerli]: eNetwork; 73 | [eEthereumNetwork.main]: eNetwork; 74 | [eEthereumNetwork.hardhat]: eNetwork; 75 | [eEthereumNetwork.tenderlyMain]: eNetwork; 76 | } 77 | 78 | export interface iPolygonParamsPerNetwork { 79 | [ePolygonNetwork.matic]: T; 80 | [ePolygonNetwork.mumbai]: T; 81 | } 82 | 83 | export interface iXDaiParamsPerNetwork { 84 | [eXDaiNetwork.xdai]: T; 85 | } 86 | 87 | export interface iArbitrumParamsPerNetwork { 88 | [eArbitrumNetwork.arbitrum]: T; 89 | [eArbitrumNetwork.arbitrumTestnet]: T; 90 | } 91 | 92 | export interface iOptimismParamsPerNetwork { 93 | [eOptimismNetwork.main]: T; 94 | [eOptimismNetwork.testnet]: T; 95 | } 96 | 97 | export interface ObjectString { 98 | [key: string]: string; 99 | } 100 | -------------------------------------------------------------------------------- /.github/workflows/certora-polygon.yaml: -------------------------------------------------------------------------------- 1 | name: certora-polygon 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | workflow_dispatch: 12 | 13 | jobs: 14 | verify: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - name: Check key 21 | env: 22 | CERTORAKEY: ${{ secrets.CERTORAKEY }} 23 | run: echo "key length" ${#CERTORAKEY} 24 | 25 | - name: Install python 26 | uses: actions/setup-python@v2 27 | with: { python-version: 3.9 } 28 | 29 | - name: Install java 30 | uses: actions/setup-java@v1 31 | with: { java-version: "11", java-package: jre } 32 | 33 | - name: Install certora cli 34 | run: pip install certora-cli 35 | 36 | - name: Install solc 37 | run: | 38 | wget https://github.com/ethereum/solidity/releases/download/v0.8.10/solc-static-linux 39 | chmod +x solc-static-linux 40 | sudo mv solc-static-linux /usr/local/bin/solc8.10 41 | 42 | - name: Verify rule ${{ matrix.rule }} 43 | run: | 44 | cd certora 45 | touch applyHarness.patch 46 | make munged 47 | cd .. 48 | echo "key length" ${#CERTORAKEY} 49 | sh certora/scripts/${{ matrix.rule }} 50 | env: 51 | CERTORAKEY: ${{ secrets.CERTORAKEY }} 52 | 53 | strategy: 54 | fail-fast: false 55 | max-parallel: 16 56 | matrix: 57 | rule: 58 | - verifyPolygon.sh executedValidTransition2 59 | - verifyPolygon.sh onlyCancelCanCancel 60 | - verifyPolygon.sh onlyQueuedAreExecuted 61 | - verifyPolygon.sh expiredForever 62 | - verifyPolygon.sh actionNotCanceledAndExecuted 63 | - verifyPolygon.sh properDelay 64 | - verifyPolygon.sh notCanceledNotExecuted 65 | - verifyPolygon.sh minDelayLtMaxDelay 66 | - verifyPolygon.sh whoChangedStateVariables 67 | - verifyPolygon.sh executeCannotCancel 68 | - verifyPolygon.sh whoChangesActionsSetState 69 | - verifyPolygon.sh canceledForever 70 | - verifyPolygon.sh executedForever 71 | - verifyPolygon.sh executeRevertsBeforeDelay 72 | - verifyPolygon.sh noIncarnations1 noIncarnations2 noIncarnations3 73 | - verifyPolygon.sh actionDuplicate holdYourHorses executeFailsIfExpired executedValidTransition1 queuePriviliged afterQueueHashQueued cancelPriviliged independentQueuedActions queueCannotCancel queueDoesntModifyStateVariables queuedStateConsistency queuedChangedCounter sameExecutionTimesReverts cancelExclusive 74 | -------------------------------------------------------------------------------- /.github/workflows/certora-arbitrum.yaml: -------------------------------------------------------------------------------- 1 | name: certora-arbitrum 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | workflow_dispatch: 12 | 13 | jobs: 14 | verify: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - name: Check key 21 | env: 22 | CERTORAKEY: ${{ secrets.CERTORAKEY }} 23 | run: echo "key length" ${#CERTORAKEY} 24 | 25 | - name: Install python 26 | uses: actions/setup-python@v2 27 | with: { python-version: 3.9 } 28 | 29 | - name: Install java 30 | uses: actions/setup-java@v1 31 | with: { java-version: "11", java-package: jre } 32 | 33 | - name: Install certora cli 34 | run: pip install certora-cli 35 | 36 | - name: Install solc 37 | run: | 38 | wget https://github.com/ethereum/solidity/releases/download/v0.8.10/solc-static-linux 39 | chmod +x solc-static-linux 40 | sudo mv solc-static-linux /usr/local/bin/solc8.10 41 | 42 | - name: Verify rule ${{ matrix.rule }} 43 | run: | 44 | cd certora 45 | touch applyHarness.patch 46 | make munged 47 | cd .. 48 | echo "key length" ${#CERTORAKEY} 49 | sh certora/scripts/${{ matrix.rule }} 50 | env: 51 | CERTORAKEY: ${{ secrets.CERTORAKEY }} 52 | 53 | strategy: 54 | fail-fast: false 55 | max-parallel: 16 56 | matrix: 57 | rule: 58 | - verifyArbitrum.sh executedValidTransition2 59 | - verifyArbitrum.sh onlyCancelCanCancel 60 | - verifyArbitrum.sh onlyQueuedAreExecuted 61 | - verifyArbitrum.sh expiredForever 62 | - verifyArbitrum.sh actionNotCanceledAndExecuted 63 | - verifyArbitrum.sh properDelay 64 | - verifyArbitrum.sh notCanceledNotExecuted 65 | - verifyArbitrum.sh minDelayLtMaxDelay 66 | - verifyArbitrum.sh whoChangedStateVariables 67 | - verifyArbitrum.sh executeCannotCancel 68 | - verifyArbitrum.sh whoChangesActionsSetState 69 | - verifyArbitrum.sh canceledForever 70 | - verifyArbitrum.sh executedForever 71 | - verifyArbitrum.sh executeRevertsBeforeDelay 72 | - verifyArbitrum.sh noIncarnations1 noIncarnations2 noIncarnations3 73 | - verifyArbitrum.sh actionDuplicate holdYourHorses executeFailsIfExpired executedValidTransition1 queuePriviliged afterQueueHashQueued queue2Reachability cancelPriviliged independentQueuedActions queueCannotCancel queueDoesntModifyStateVariables queuedStateConsistency queuedChangedCounter sameExecutionTimesReverts cancelExclusive 74 | -------------------------------------------------------------------------------- /.github/workflows/certora-optimism.yaml: -------------------------------------------------------------------------------- 1 | name: certora-optimism 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | branches: 9 | - master 10 | 11 | workflow_dispatch: 12 | 13 | jobs: 14 | verify: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | 20 | - name: Check key 21 | env: 22 | CERTORAKEY: ${{ secrets.CERTORAKEY }} 23 | run: echo "key length" ${#CERTORAKEY} 24 | 25 | - name: Install python 26 | uses: actions/setup-python@v2 27 | with: { python-version: 3.9 } 28 | 29 | - name: Install java 30 | uses: actions/setup-java@v1 31 | with: { java-version: "11", java-package: jre } 32 | 33 | - name: Install certora cli 34 | run: pip install certora-cli 35 | 36 | - name: Install solc 37 | run: | 38 | wget https://github.com/ethereum/solidity/releases/download/v0.8.10/solc-static-linux 39 | chmod +x solc-static-linux 40 | sudo mv solc-static-linux /usr/local/bin/solc8.10 41 | 42 | - name: Verify rule ${{ matrix.rule }} 43 | run: | 44 | cd certora 45 | touch applyHarness.patch 46 | make munged 47 | cd .. 48 | echo "key length" ${#CERTORAKEY} 49 | sh certora/scripts/${{ matrix.rule }} 50 | env: 51 | CERTORAKEY: ${{ secrets.CERTORAKEY }} 52 | 53 | strategy: 54 | fail-fast: false 55 | max-parallel: 16 56 | matrix: 57 | rule: 58 | - verifyOptimism.sh executedValidTransition2 59 | - verifyOptimism.sh onlyCancelCanCancel 60 | - verifyOptimism.sh onlyQueuedAreExecuted 61 | - verifyOptimism.sh expiredForever 62 | - verifyOptimism.sh actionNotCanceledAndExecuted 63 | - verifyOptimism.sh properDelay 64 | - verifyOptimism.sh notCanceledNotExecuted 65 | - verifyOptimism.sh minDelayLtMaxDelay 66 | - verifyOptimism.sh whoChangedStateVariables 67 | - verifyOptimism.sh executeCannotCancel 68 | - verifyOptimism.sh whoChangesActionsSetState 69 | - verifyOptimism.sh canceledForever 70 | - verifyOptimism.sh executedForever 71 | - verifyOptimism.sh executeRevertsBeforeDelay 72 | - verifyOptimism.sh noIncarnations1 noIncarnations2 noIncarnations3 73 | - verifyOptimism.sh actionDuplicate holdYourHorses executeFailsIfExpired executedValidTransition1 queuePriviliged afterQueueHashQueued queue2Reachability cancelPriviliged independentQueuedActions queueCannotCancel queueDoesntModifyStateVariables queuedStateConsistency queuedChangedCounter sameExecutionTimesReverts cancelExclusive 74 | -------------------------------------------------------------------------------- /contracts/bridges/L2BridgeExecutor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import {IL2BridgeExecutor} from '../interfaces/IL2BridgeExecutor.sol'; 5 | import {BridgeExecutorBase} from './BridgeExecutorBase.sol'; 6 | 7 | /** 8 | * @title L2BridgeExecutor 9 | * @author Aave 10 | * @notice Abstract contract that implements bridge executor functionality for L2 11 | * @dev It does not implement the `onlyEthereumGovernanceExecutor` modifier. This should instead be done in the inheriting 12 | * contract with proper configuration and adjustments depending on the L2 13 | */ 14 | abstract contract L2BridgeExecutor is BridgeExecutorBase, IL2BridgeExecutor { 15 | // Address of the Ethereum Governance Executor, which should be able to queue actions sets 16 | address internal _ethereumGovernanceExecutor; 17 | 18 | /** 19 | * @dev Only the Ethereum Governance Executor should be able to call functions marked by this modifier. 20 | **/ 21 | modifier onlyEthereumGovernanceExecutor() virtual; 22 | 23 | /** 24 | * @dev Constructor 25 | * 26 | * @param ethereumGovernanceExecutor The address of the EthereumGovernanceExecutor 27 | * @param delay The delay before which an actions set can be executed 28 | * @param gracePeriod The time period after a delay during which an actions set can be executed 29 | * @param minimumDelay The minimum bound a delay can be set to 30 | * @param maximumDelay The maximum bound a delay can be set to 31 | * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) 32 | */ 33 | constructor( 34 | address ethereumGovernanceExecutor, 35 | uint256 delay, 36 | uint256 gracePeriod, 37 | uint256 minimumDelay, 38 | uint256 maximumDelay, 39 | address guardian 40 | ) BridgeExecutorBase(delay, gracePeriod, minimumDelay, maximumDelay, guardian) { 41 | _ethereumGovernanceExecutor = ethereumGovernanceExecutor; 42 | } 43 | 44 | /// @inheritdoc IL2BridgeExecutor 45 | function queue( 46 | address[] memory targets, 47 | uint256[] memory values, 48 | string[] memory signatures, 49 | bytes[] memory calldatas, 50 | bool[] memory withDelegatecalls 51 | ) external onlyEthereumGovernanceExecutor { 52 | _queue(targets, values, signatures, calldatas, withDelegatecalls); 53 | } 54 | 55 | /// @inheritdoc IL2BridgeExecutor 56 | function updateEthereumGovernanceExecutor(address ethereumGovernanceExecutor) external onlyThis { 57 | emit EthereumGovernanceExecutorUpdate(_ethereumGovernanceExecutor, ethereumGovernanceExecutor); 58 | _ethereumGovernanceExecutor = ethereumGovernanceExecutor; 59 | } 60 | 61 | /// @inheritdoc IL2BridgeExecutor 62 | function getEthereumGovernanceExecutor() external view returns (address) { 63 | return _ethereumGovernanceExecutor; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /certora/munged/bridges/L2BridgeExecutor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import {IL2BridgeExecutor} from '../interfaces/IL2BridgeExecutor.sol'; 5 | import {BridgeExecutorBase} from './BridgeExecutorBase.sol'; 6 | 7 | /** 8 | * @title L2BridgeExecutor 9 | * @author Aave 10 | * @notice Abstract contract that implements bridge executor functionality for L2 11 | * @dev It does not implement the `onlyEthereumGovernanceExecutor` modifier. This should instead be done in the inheriting 12 | * contract with proper configuration and adjustments depending on the L2 13 | */ 14 | abstract contract L2BridgeExecutor is BridgeExecutorBase, IL2BridgeExecutor { 15 | // Address of the Ethereum Governance Executor, which should be able to queue actions sets 16 | address internal _ethereumGovernanceExecutor; 17 | 18 | /** 19 | * @dev Only the Ethereum Governance Executor should be able to call functions marked by this modifier. 20 | **/ 21 | modifier onlyEthereumGovernanceExecutor() virtual; 22 | 23 | /** 24 | * @dev Constructor 25 | * 26 | * @param ethereumGovernanceExecutor The address of the EthereumGovernanceExecutor 27 | * @param delay The delay before which an actions set can be executed 28 | * @param gracePeriod The time period after a delay during which an actions set can be executed 29 | * @param minimumDelay The minimum bound a delay can be set to 30 | * @param maximumDelay The maximum bound a delay can be set to 31 | * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) 32 | */ 33 | constructor( 34 | address ethereumGovernanceExecutor, 35 | uint256 delay, 36 | uint256 gracePeriod, 37 | uint256 minimumDelay, 38 | uint256 maximumDelay, 39 | address guardian 40 | ) BridgeExecutorBase(delay, gracePeriod, minimumDelay, maximumDelay, guardian) { 41 | _ethereumGovernanceExecutor = ethereumGovernanceExecutor; 42 | } 43 | 44 | /// @inheritdoc IL2BridgeExecutor 45 | // Certora harness : removed _queue() 46 | function queue( 47 | address[] memory targets, 48 | uint256[] memory values, 49 | string[] memory signatures, 50 | bytes[] memory calldatas, 51 | bool[] memory withDelegatecalls 52 | ) external onlyEthereumGovernanceExecutor { 53 | //_queue(targets, values, signatures, calldatas, withDelegatecalls); 54 | } 55 | 56 | /// @inheritdoc IL2BridgeExecutor 57 | function updateEthereumGovernanceExecutor(address ethereumGovernanceExecutor) external onlyThis { 58 | emit EthereumGovernanceExecutorUpdate(_ethereumGovernanceExecutor, ethereumGovernanceExecutor); 59 | _ethereumGovernanceExecutor = ethereumGovernanceExecutor; 60 | } 61 | 62 | /// @inheritdoc IL2BridgeExecutor 63 | function getEthereumGovernanceExecutor() external view returns (address) { 64 | return _ethereumGovernanceExecutor; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /certora/specs/complexity.spec: -------------------------------------------------------------------------------- 1 | import "erc20.spec" 2 | 3 | rule sanity(method f) 4 | { 5 | env e; 6 | calldataarg args; 7 | f(e,args); 8 | assert false; 9 | } 10 | 11 | 12 | /* 13 | This rule find which functions never reverts. 14 | 15 | */ 16 | 17 | 18 | rule noRevert(method f) 19 | description "$f has reverting paths" 20 | { 21 | env e; 22 | calldataarg arg; 23 | require e.msg.value == 0; 24 | f@withrevert(e, arg); 25 | assert !lastReverted, "${f.selector} can revert"; 26 | } 27 | 28 | 29 | rule alwaysRevert(method f) 30 | description "$f has reverting paths" 31 | { 32 | env e; 33 | calldataarg arg; 34 | f@withrevert(e, arg); 35 | assert lastReverted, "${f.selector} succeeds"; 36 | } 37 | 38 | 39 | /* 40 | This rule find which functions that can be called, may fail due to someone else calling a function right before. 41 | 42 | This is n expensive rule - might fail on the demo site on big contracts 43 | */ 44 | 45 | rule simpleFrontRunning(method f, address privileged) filtered { f-> !f.isView } 46 | description "$f can no longer be called after it had been called by someone else" 47 | { 48 | env e1; 49 | calldataarg arg; 50 | require e1.msg.sender == privileged; 51 | 52 | storage initialStorage = lastStorage; 53 | f(e1, arg); 54 | bool firstSucceeded = !lastReverted; 55 | 56 | env e2; 57 | calldataarg arg2; 58 | require e2.msg.sender != e1.msg.sender; 59 | f(e2, arg2) at initialStorage; 60 | f@withrevert(e1, arg); 61 | bool succeeded = !lastReverted; 62 | 63 | assert succeeded, "${f.selector} can be not be called if was called by someone else"; 64 | } 65 | 66 | 67 | /* 68 | This rule find which functions are privileged. 69 | A function is privileged if there is only one address that can call it. 70 | 71 | The rules finds this by finding which functions can be called by two different users. 72 | 73 | */ 74 | 75 | 76 | rule privilegedOperation(method f, address privileged) 77 | description "$f can be called by more than one user without reverting" 78 | { 79 | env e1; 80 | calldataarg arg; 81 | require e1.msg.sender == privileged; 82 | 83 | storage initialStorage = lastStorage; 84 | f@withrevert(e1, arg); // privileged succeeds executing candidate privileged operation. 85 | bool firstSucceeded = !lastReverted; 86 | 87 | env e2; 88 | calldataarg arg2; 89 | require e2.msg.sender != privileged; 90 | f@withrevert(e2, arg2) at initialStorage; // unprivileged 91 | bool secondSucceeded = !lastReverted; 92 | 93 | assert !(firstSucceeded && secondSucceeded), "${f.selector} can be called by both ${e1.msg.sender} and ${e2.msg.sender}, so it is not privileged"; 94 | } 95 | 96 | rule whoChangedBalanceOf(method f, address u) { 97 | env eB; 98 | env eF; 99 | calldataarg args; 100 | uint256 before = balanceOf(eB, u); 101 | f(eF,args); 102 | assert balanceOf(eB, u) == before, "balanceOf changed"; 103 | } -------------------------------------------------------------------------------- /tasks/deploy/deploy.ts: -------------------------------------------------------------------------------- 1 | import { task } from 'hardhat/config'; 2 | import { Signer } from 'ethers'; 3 | import { setDRE } from '../../helpers/misc-utils'; 4 | import { verifyContract } from '../../helpers/etherscan-verification'; 5 | import { getDefaultSigner } from '../../helpers/wallet-helpers'; 6 | 7 | import { 8 | printExpectedParams, 9 | printDefinedParams, 10 | parseParams, 11 | parseLibraries, 12 | defineParams, 13 | deployContract, 14 | } from '../../helpers/task-helpers'; 15 | 16 | task('deploy-contract', 'deploy contract - add contract name and params as arguments') 17 | .addParam('contract', 'Name of contract to deploy') 18 | .addOptionalParam('params', 'JSON string of contract params - defaults to CLI') 19 | .addOptionalParam('paramsfile', 'Path to a TS file with params defined as default export') 20 | .addOptionalParam( 21 | 'signer', 22 | 'Define signer - private key(pk), mnemonic(mn), defender(ozd) - defaults to ethers signer' 23 | ) 24 | .addOptionalParam('libraries', 'json as string mapping of libraries to address') 25 | .addOptionalParam('librariesfile', 'file containing mapping of libraries to address') 26 | .addFlag('verify', 'Verify contract on Etherscan') 27 | .addFlag('printparams', `Print constructor params`) 28 | .setAction( 29 | async ( 30 | { contract, params, paramsfile, signer, libraries, librariesfile, verify, printparams }, 31 | hre 32 | ) => { 33 | const { ethers } = hre; 34 | setDRE(hre); 35 | let contractSigner: Signer | null = ethers.provider.getSigner(); 36 | 37 | if (signer) { 38 | contractSigner = getDefaultSigner(signer); 39 | } 40 | 41 | let parsedLibraries; 42 | if (libraries || librariesfile) { 43 | parsedLibraries = await parseLibraries(libraries, librariesfile); 44 | if (!parsedLibraries) return; 45 | } 46 | 47 | const ContractFactory = await ethers.getContractFactory(contract, parsedLibraries); 48 | const constructorInputs = ContractFactory.interface.deploy.inputs; 49 | 50 | if (printparams) { 51 | printExpectedParams(contract, constructorInputs); 52 | return; 53 | } 54 | 55 | let contractParams; 56 | if (params || paramsfile) { 57 | contractParams = await parseParams(params, paramsfile); 58 | if (!contractParams) return; 59 | } 60 | /** 61 | * uncomment following line to override params with hardcoded parameters 62 | */ 63 | // contractParams = {} 64 | 65 | let paramsArray: any[] = []; 66 | if (constructorInputs.length > 0) { 67 | paramsArray = await defineParams(contractParams, constructorInputs); 68 | printDefinedParams(constructorInputs, paramsArray); 69 | } 70 | 71 | const contractInstance = await deployContract(paramsArray, ContractFactory, contractSigner); 72 | if (verify) { 73 | await verifyContract( 74 | contractInstance.address, 75 | paramsArray, 76 | parsedLibraries.libraries ? JSON.stringify(parsedLibraries.libraries) : '' 77 | ); 78 | } 79 | } 80 | ); 81 | -------------------------------------------------------------------------------- /certora/harness/ArbitrumHarness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import {AddressAliasHelper} from '../../contracts/dependencies/arbitrum/AddressAliasHelper.sol'; 5 | import {L2BridgeExecutorHarness} from './L2BridgeExecutorHarness.sol'; 6 | import {mockTarget} from './mockTarget.sol'; 7 | 8 | 9 | /** 10 | * @title ArbitrumBridgeExecutor 11 | * @author Aave 12 | * @notice Implementation of the Arbitrum Bridge Executor, able to receive cross-chain transactions from Ethereum 13 | * @dev Queuing an ActionsSet into this Executor can only be done by the L2 Address Alias of the L1 EthereumGovernanceExecutor 14 | */ 15 | contract ArbitrumHarness is L2BridgeExecutorHarness { 16 | // Address of the Optimism L2 Cross Domain Messenger, in charge of redirecting cross-chain transactions in L2 17 | mockTarget public _mock; 18 | 19 | /// @inheritdoc L2BridgeExecutorHarness 20 | modifier onlyEthereumGovernanceExecutor() override { 21 | if (AddressAliasHelper.undoL1ToL2Alias(msg.sender) != _ethereumGovernanceExecutor) 22 | revert UnauthorizedEthereumExecutor(); 23 | _; 24 | } 25 | 26 | /** 27 | * @dev Constructor 28 | * 29 | * @param ethereumGovernanceExecutor The address of the EthereumGovernanceExecutor 30 | * @param delay The delay before which an actions set can be executed 31 | * @param gracePeriod The time period after a delay during which an actions set can be executed 32 | * @param minimumDelay The minimum bound a delay can be set to 33 | * @param maximumDelay The maximum bound a delay can be set to 34 | * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) 35 | */ 36 | constructor( 37 | address ethereumGovernanceExecutor, 38 | uint256 delay, 39 | uint256 gracePeriod, 40 | uint256 minimumDelay, 41 | uint256 maximumDelay, 42 | address guardian 43 | ) 44 | L2BridgeExecutorHarness( 45 | ethereumGovernanceExecutor, 46 | delay, 47 | gracePeriod, 48 | minimumDelay, 49 | maximumDelay, 50 | guardian 51 | ) 52 | { 53 | } 54 | 55 | function _executeTransaction( 56 | address target, 57 | uint256 value, 58 | string memory signature, 59 | bytes memory data, 60 | uint256 executionTime, 61 | bool withDelegatecall 62 | ) internal override returns (bytes memory) { 63 | if (address(this).balance < value) revert InsufficientBalance(); 64 | 65 | bytes32 actionHash = keccak256( 66 | abi.encode(target, value, executionTime) 67 | ); 68 | _queuedActions[actionHash] = false; 69 | 70 | bool success; 71 | bytes memory resultData; 72 | if (withDelegatecall) { 73 | (success, resultData) = this.executeDelegateCall{value: value}(target, data); 74 | } else { 75 | // solium-disable-next-line security/no-call-value 76 | success = _mock.targetCall(data); 77 | } 78 | return _verifyCallResult(success, resultData); 79 | } 80 | 81 | function executeDelegateCall(address target, bytes calldata data) 82 | external 83 | payable 84 | override 85 | onlyThis 86 | returns (bool, bytes memory) 87 | { 88 | bool success; 89 | bytes memory resultData; 90 | // solium-disable-next-line security/no-call-value 91 | success = _mock.targetCall(data); 92 | return (success, resultData); 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /contracts/dependencies/arbitrum/interfaces/ArbRetryableTx.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | /** 4 | * @title precompiled contract in every Arbitrum chain for retryable transaction related data retrieval and interactions. Exists at 0x000000000000000000000000000000000000006E 5 | */ 6 | interface ArbRetryableTx { 7 | /** 8 | * @notice Redeem a redeemable tx. 9 | * Revert if called by an L2 contract, or if userTxHash does not exist, or if userTxHash reverts. 10 | * If this returns, userTxHash has been completed and is no longer available for redemption. 11 | * If this reverts, userTxHash is still available for redemption (until it times out or is canceled). 12 | * @param userTxHash unique identifier of retryable message: keccak256(keccak256(ArbchainId, inbox-sequence-number), uint(0) ) 13 | */ 14 | function redeem(bytes32 userTxHash) external; 15 | 16 | /** 17 | * @notice Return the minimum lifetime of redeemable txn. 18 | * @return lifetime in seconds 19 | */ 20 | function getLifetime() external view returns (uint256); 21 | 22 | /** 23 | * @notice Return the timestamp when userTxHash will age out, or zero if userTxHash does not exist. 24 | * The timestamp could be in the past, because aged-out tickets might not be discarded immediately. 25 | * @param userTxHash unique ticket identifier 26 | * @return timestamp for ticket's deadline 27 | */ 28 | function getTimeout(bytes32 userTxHash) external view returns (uint256); 29 | 30 | /** 31 | * @notice Return the price, in wei, of submitting a new retryable tx with a given calldata size. 32 | * @param calldataSize call data size to get price of (in wei) 33 | * @return (price, nextUpdateTimestamp). Price is guaranteed not to change until nextUpdateTimestamp. 34 | */ 35 | function getSubmissionPrice(uint256 calldataSize) external view returns (uint256, uint256); 36 | 37 | /** 38 | * @notice Return the price, in wei, of extending the lifetime of userTxHash by an additional lifetime period. Revert if userTxHash doesn't exist. 39 | * @param userTxHash unique ticket identifier 40 | * @return (price, nextUpdateTimestamp). Price is guaranteed not to change until nextUpdateTimestamp. 41 | */ 42 | function getKeepalivePrice(bytes32 userTxHash) external view returns (uint256, uint256); 43 | 44 | /** 45 | @notice Deposits callvalue into the sender's L2 account, then adds one lifetime period to the life of userTxHash. 46 | * If successful, emits LifetimeExtended event. 47 | * Revert if userTxHash does not exist, or if the timeout of userTxHash is already at least one lifetime period in the future, or if the sender has insufficient funds (after the deposit). 48 | * @param userTxHash unique ticket identifier 49 | * @return New timeout of userTxHash. 50 | */ 51 | function keepalive(bytes32 userTxHash) external payable returns (uint256); 52 | 53 | /** 54 | * @notice Return the beneficiary of userTxHash. 55 | * Revert if userTxHash doesn't exist. 56 | * @param userTxHash unique ticket identifier 57 | * @return address of beneficiary for ticket 58 | */ 59 | function getBeneficiary(bytes32 userTxHash) external view returns (address); 60 | 61 | /** 62 | * @notice Cancel userTxHash and refund its callvalue to its beneficiary. 63 | * Revert if userTxHash doesn't exist, or if called by anyone other than userTxHash's beneficiary. 64 | * @param userTxHash unique ticket identifier 65 | */ 66 | function cancel(bytes32 userTxHash) external; 67 | 68 | event TicketCreated(bytes32 indexed userTxHash); 69 | event LifetimeExtended(bytes32 indexed userTxHash, uint256 newTimeout); 70 | event Redeemed(bytes32 indexed userTxHash); 71 | event Canceled(bytes32 indexed userTxHash); 72 | } 73 | -------------------------------------------------------------------------------- /certora/munged/dependencies/arbitrum/interfaces/ArbRetryableTx.sol: -------------------------------------------------------------------------------- 1 | pragma solidity >=0.4.21 <0.9.0; 2 | 3 | /** 4 | * @title precompiled contract in every Arbitrum chain for retryable transaction related data retrieval and interactions. Exists at 0x000000000000000000000000000000000000006E 5 | */ 6 | interface ArbRetryableTx { 7 | /** 8 | * @notice Redeem a redeemable tx. 9 | * Revert if called by an L2 contract, or if userTxHash does not exist, or if userTxHash reverts. 10 | * If this returns, userTxHash has been completed and is no longer available for redemption. 11 | * If this reverts, userTxHash is still available for redemption (until it times out or is canceled). 12 | * @param userTxHash unique identifier of retryable message: keccak256(keccak256(ArbchainId, inbox-sequence-number), uint(0) ) 13 | */ 14 | function redeem(bytes32 userTxHash) external; 15 | 16 | /** 17 | * @notice Return the minimum lifetime of redeemable txn. 18 | * @return lifetime in seconds 19 | */ 20 | function getLifetime() external view returns (uint256); 21 | 22 | /** 23 | * @notice Return the timestamp when userTxHash will age out, or zero if userTxHash does not exist. 24 | * The timestamp could be in the past, because aged-out tickets might not be discarded immediately. 25 | * @param userTxHash unique ticket identifier 26 | * @return timestamp for ticket's deadline 27 | */ 28 | function getTimeout(bytes32 userTxHash) external view returns (uint256); 29 | 30 | /** 31 | * @notice Return the price, in wei, of submitting a new retryable tx with a given calldata size. 32 | * @param calldataSize call data size to get price of (in wei) 33 | * @return (price, nextUpdateTimestamp). Price is guaranteed not to change until nextUpdateTimestamp. 34 | */ 35 | function getSubmissionPrice(uint256 calldataSize) external view returns (uint256, uint256); 36 | 37 | /** 38 | * @notice Return the price, in wei, of extending the lifetime of userTxHash by an additional lifetime period. Revert if userTxHash doesn't exist. 39 | * @param userTxHash unique ticket identifier 40 | * @return (price, nextUpdateTimestamp). Price is guaranteed not to change until nextUpdateTimestamp. 41 | */ 42 | function getKeepalivePrice(bytes32 userTxHash) external view returns (uint256, uint256); 43 | 44 | /** 45 | @notice Deposits callvalue into the sender's L2 account, then adds one lifetime period to the life of userTxHash. 46 | * If successful, emits LifetimeExtended event. 47 | * Revert if userTxHash does not exist, or if the timeout of userTxHash is already at least one lifetime period in the future, or if the sender has insufficient funds (after the deposit). 48 | * @param userTxHash unique ticket identifier 49 | * @return New timeout of userTxHash. 50 | */ 51 | function keepalive(bytes32 userTxHash) external payable returns (uint256); 52 | 53 | /** 54 | * @notice Return the beneficiary of userTxHash. 55 | * Revert if userTxHash doesn't exist. 56 | * @param userTxHash unique ticket identifier 57 | * @return address of beneficiary for ticket 58 | */ 59 | function getBeneficiary(bytes32 userTxHash) external view returns (address); 60 | 61 | /** 62 | * @notice Cancel userTxHash and refund its callvalue to its beneficiary. 63 | * Revert if userTxHash doesn't exist, or if called by anyone other than userTxHash's beneficiary. 64 | * @param userTxHash unique ticket identifier 65 | */ 66 | function cancel(bytes32 userTxHash) external; 67 | 68 | event TicketCreated(bytes32 indexed userTxHash); 69 | event LifetimeExtended(bytes32 indexed userTxHash, uint256 newTimeout); 70 | event Redeemed(bytes32 indexed userTxHash); 71 | event Canceled(bytes32 indexed userTxHash); 72 | } 73 | -------------------------------------------------------------------------------- /contracts/mocks/MockInbox.sol: -------------------------------------------------------------------------------- 1 | //SPDX-License-Identifier: Unlicense 2 | pragma solidity ^0.8.10; 3 | 4 | import {IInbox} from '../dependencies/arbitrum/interfaces/IInbox.sol'; 5 | import {IBridge} from '../dependencies/arbitrum/interfaces/IBridge.sol'; 6 | import {ISequencerInbox} from '../dependencies/arbitrum/interfaces/ISequencerInbox.sol'; 7 | 8 | contract MockInbox is IInbox { 9 | uint256 public messageNum; 10 | 11 | function setMessageNum(uint256 msgNum) external { 12 | messageNum = msgNum; 13 | } 14 | 15 | function bridge() external view override returns (IBridge) { 16 | return IBridge(address(1)); 17 | } 18 | 19 | function sequencerInbox() external view override returns (ISequencerInbox) { 20 | return ISequencerInbox(address(1)); 21 | } 22 | 23 | function sendL2MessageFromOrigin( 24 | bytes calldata // messageData 25 | ) external override returns (uint256) { 26 | return messageNum; 27 | } 28 | 29 | function sendL2Message( 30 | bytes calldata // messageData 31 | ) external override returns (uint256) { 32 | return messageNum; 33 | } 34 | 35 | function sendL1FundedUnsignedTransaction( 36 | uint256, // gasLimit 37 | uint256, // maxFeePerGas 38 | uint256, // nonce 39 | address, // to 40 | bytes calldata // data 41 | ) external payable override returns (uint256) { 42 | return messageNum; 43 | } 44 | 45 | function sendL1FundedContractTransaction( 46 | uint256, // gasLimit 47 | uint256, // maxFeePerGas 48 | address, // to 49 | bytes calldata // data 50 | ) external payable override returns (uint256) { 51 | return messageNum; 52 | } 53 | 54 | function sendUnsignedTransaction( 55 | uint256, // gasLimit 56 | uint256, // maxFeePerGas 57 | uint256, // nonce 58 | address, // to 59 | uint256, // value 60 | bytes calldata // data 61 | ) external override returns (uint256) { 62 | return messageNum; 63 | } 64 | 65 | function sendContractTransaction( 66 | uint256, // gasLimit 67 | uint256, // maxFeePerGas 68 | address, // to 69 | uint256, // value 70 | bytes calldata // data 71 | ) external override returns (uint256) { 72 | return messageNum; 73 | } 74 | 75 | function calculateRetryableSubmissionFee( 76 | uint256 dataLength, 77 | uint256 // baseFee 78 | ) external view override returns (uint256) { 79 | return 1_000_000; 80 | } 81 | 82 | function depositEth() external payable override returns (uint256) { 83 | return messageNum; 84 | } 85 | 86 | function createRetryableTicket( 87 | address to, // to 88 | uint256, // l2CallValue 89 | uint256, // maxSubmissionCost 90 | address, // excessFeeRefundAddress 91 | address, // callValueRefundAddress 92 | uint256 gasLimit, 93 | uint256, // maxFeePerGas 94 | bytes calldata data 95 | ) external payable override returns (uint256) { 96 | bool success; 97 | (success, ) = to.call{gas: gasLimit}(data); 98 | return messageNum; 99 | } 100 | 101 | function unsafeCreateRetryableTicket( 102 | address, // to 103 | uint256, // l2CallValue 104 | uint256, // maxSubmissionCost 105 | address, // excessFeeRefundAddress 106 | address, // callValueRefundAddress 107 | uint256, // gasLimit 108 | uint256, // maxFeePerGas 109 | bytes calldata // data 110 | ) external payable override returns (uint256) {} 111 | 112 | function pause() external override {} 113 | 114 | function unpause() external override {} 115 | 116 | function postUpgradeInit(IBridge _bridge) external override {} 117 | 118 | function initialize(IBridge _bridge, ISequencerInbox _sequencerInbox) external override {} 119 | } 120 | -------------------------------------------------------------------------------- /helpers/contract-getters.ts: -------------------------------------------------------------------------------- 1 | import { Signer, BigNumber } from 'ethers'; 2 | import { 3 | AaveGovernanceV2, 4 | AaveGovernanceV2__factory, 5 | CustomPolygonMapping, 6 | Executor, 7 | Executor__factory, 8 | CustomPolygonMapping__factory, 9 | FxRoot, 10 | FxRoot__factory, 11 | FxChild, 12 | FxChild__factory, 13 | PolygonBridgeExecutor, 14 | PolygonBridgeExecutor__factory, 15 | PolygonMarketUpdate, 16 | PolygonMarketUpdate__factory, 17 | } from '../typechain'; 18 | import { tEthereumAddress } from './types'; 19 | 20 | export const getAaveGovContract = async ( 21 | address: tEthereumAddress, 22 | signer: Signer 23 | ): Promise => { 24 | const aaveGovContract = await AaveGovernanceV2__factory.connect(address, signer); 25 | return aaveGovContract; 26 | }; 27 | 28 | export const deployExecutorContract = async (signer: Signer): Promise => { 29 | const executorFactory = new Executor__factory(signer); 30 | const admin = '0xec568fffba86c094cf06b22134b23074dfe2252c'; 31 | const delay = 86400; 32 | const gracePeriod = 432000; 33 | const minimumDelay = 86400; 34 | const maximumDelay = 864000; 35 | const propositionThreshold = 50; 36 | const voteDuration = 60; 37 | const voteDifferential = 50; 38 | const minimumQuorum = 200; 39 | const shortExecutor = await executorFactory.deploy( 40 | admin, 41 | delay, 42 | gracePeriod, 43 | minimumDelay, 44 | maximumDelay, 45 | propositionThreshold, 46 | voteDuration, 47 | voteDifferential, 48 | minimumQuorum 49 | ); 50 | await shortExecutor.deployTransaction.wait(); 51 | return shortExecutor; 52 | }; 53 | 54 | export const deployCustomPolygonMapping = async (signer: Signer): Promise => { 55 | const customPolygonMappingFactory = new CustomPolygonMapping__factory(signer); 56 | const customPolygonMapping = await customPolygonMappingFactory.deploy(); 57 | await customPolygonMapping.deployTransaction.wait(); 58 | return customPolygonMapping; 59 | }; 60 | 61 | export const deployPolygonMarketUpdate = async (signer: Signer): Promise => { 62 | const polygonMarketUpdateFactory = new PolygonMarketUpdate__factory(signer); 63 | const polygonMarketUpdate = await polygonMarketUpdateFactory.deploy(); 64 | await polygonMarketUpdate.deployTransaction.wait(); 65 | return polygonMarketUpdate; 66 | }; 67 | 68 | export const deployFxRoot = async ( 69 | stateSenderAddress: tEthereumAddress, 70 | signer: Signer 71 | ): Promise => { 72 | const fxRootFactory = new FxRoot__factory(signer); 73 | const fxRoot = await fxRootFactory.deploy(stateSenderAddress); 74 | await fxRoot.deployTransaction.wait(); 75 | return fxRoot; 76 | }; 77 | 78 | export const deployFxChild = async (signer: Signer): Promise => { 79 | const fxChildFactory = new FxChild__factory(signer); 80 | const fxChild = await fxChildFactory.deploy(); 81 | await fxChild.deployTransaction.wait(); 82 | return fxChild; 83 | }; 84 | 85 | export const deployPolygonBridgeExecutor = async ( 86 | rootSenderAddress: tEthereumAddress, 87 | childAddress: tEthereumAddress, 88 | delay: BigNumber, 89 | gracePeriod: BigNumber, 90 | minimumDelay: BigNumber, 91 | maximumDelay: BigNumber, 92 | guardian: tEthereumAddress, 93 | signer: Signer 94 | ): Promise => { 95 | const polygonBridgeExecutorFactory = new PolygonBridgeExecutor__factory(signer); 96 | const polygonBridgeExecutor = await polygonBridgeExecutorFactory.deploy( 97 | rootSenderAddress, 98 | childAddress, 99 | delay, 100 | gracePeriod, 101 | minimumDelay, 102 | maximumDelay, 103 | guardian 104 | ); 105 | await polygonBridgeExecutor.deployTransaction.wait(); 106 | return polygonBridgeExecutor; 107 | }; 108 | -------------------------------------------------------------------------------- /contracts/dependencies/arbitrum/interfaces/IBridge.sol: -------------------------------------------------------------------------------- 1 | // Copyright 2021-2022, Offchain Labs, Inc. 2 | // For license information, see https://github.com/nitro/blob/master/LICENSE 3 | // SPDX-License-Identifier: BUSL-1.1 4 | 5 | // solhint-disable-next-line compiler-version 6 | pragma solidity >=0.6.9 <0.9.0; 7 | 8 | import './IOwnable.sol'; 9 | 10 | interface IBridge { 11 | event MessageDelivered( 12 | uint256 indexed messageIndex, 13 | bytes32 indexed beforeInboxAcc, 14 | address inbox, 15 | uint8 kind, 16 | address sender, 17 | bytes32 messageDataHash, 18 | uint256 baseFeeL1, 19 | uint64 timestamp 20 | ); 21 | 22 | event BridgeCallTriggered(address indexed outbox, address indexed to, uint256 value, bytes data); 23 | 24 | event InboxToggle(address indexed inbox, bool enabled); 25 | 26 | event OutboxToggle(address indexed outbox, bool enabled); 27 | 28 | event SequencerInboxUpdated(address newSequencerInbox); 29 | 30 | function allowedDelayedInboxList(uint256) external returns (address); 31 | 32 | function allowedOutboxList(uint256) external returns (address); 33 | 34 | /// @dev Accumulator for delayed inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. 35 | function delayedInboxAccs(uint256) external view returns (bytes32); 36 | 37 | /// @dev Accumulator for sequencer inbox messages; tail represents hash of the current state; each element represents the inclusion of a new message. 38 | function sequencerInboxAccs(uint256) external view returns (bytes32); 39 | 40 | function rollup() external view returns (IOwnable); 41 | 42 | function sequencerInbox() external view returns (address); 43 | 44 | function activeOutbox() external view returns (address); 45 | 46 | function allowedDelayedInboxes(address inbox) external view returns (bool); 47 | 48 | function allowedOutboxes(address outbox) external view returns (bool); 49 | 50 | /** 51 | * @dev Enqueue a message in the delayed inbox accumulator. 52 | * These messages are later sequenced in the SequencerInbox, either 53 | * by the sequencer as part of a normal batch, or by force inclusion. 54 | */ 55 | function enqueueDelayedMessage( 56 | uint8 kind, 57 | address sender, 58 | bytes32 messageDataHash 59 | ) external payable returns (uint256); 60 | 61 | function executeCall( 62 | address to, 63 | uint256 value, 64 | bytes calldata data 65 | ) external returns (bool success, bytes memory returnData); 66 | 67 | function delayedMessageCount() external view returns (uint256); 68 | 69 | function sequencerMessageCount() external view returns (uint256); 70 | 71 | // ---------- onlySequencerInbox functions ---------- 72 | 73 | function enqueueSequencerMessage(bytes32 dataHash, uint256 afterDelayedMessagesRead) 74 | external 75 | returns ( 76 | uint256 seqMessageIndex, 77 | bytes32 beforeAcc, 78 | bytes32 delayedAcc, 79 | bytes32 acc 80 | ); 81 | 82 | /** 83 | * @dev Allows the sequencer inbox to submit a delayed message of the batchPostingReport type 84 | * This is done through a separate function entrypoint instead of allowing the sequencer inbox 85 | * to call `enqueueDelayedMessage` to avoid the gas overhead of an extra SLOAD in either 86 | * every delayed inbox or every sequencer inbox call. 87 | */ 88 | function submitBatchSpendingReport(address batchPoster, bytes32 dataHash) 89 | external 90 | returns (uint256 msgNum); 91 | 92 | // ---------- onlyRollupOrOwner functions ---------- 93 | 94 | function setSequencerInbox(address _sequencerInbox) external; 95 | 96 | function setDelayedInbox(address inbox, bool enabled) external; 97 | 98 | function setOutbox(address inbox, bool enabled) external; 99 | 100 | // ---------- initializer ---------- 101 | 102 | function initialize(IOwnable rollup_) external; 103 | } 104 | -------------------------------------------------------------------------------- /certora/harness/OptimismHarness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import {ICrossDomainMessenger} from '../munged/dependencies/optimism/interfaces/ICrossDomainMessenger.sol'; 5 | import {L2BridgeExecutorHarness} from './L2BridgeExecutorHarness.sol'; 6 | import {mockTarget} from './mockTarget.sol'; 7 | 8 | 9 | /** 10 | * @title OptimismBridgeExecutor 11 | * @author Aave 12 | * @notice Implementation of the Optimism Bridge Executor, able to receive cross-chain transactions from Ethereum 13 | * @dev Queuing an ActionsSet into this Executor can only be done by the Optimism L2 Cross Domain Messenger and having 14 | * the EthereumGovernanceExecutor as xDomainMessageSender 15 | */ 16 | contract OptimismHarness is L2BridgeExecutorHarness { 17 | // Address of the Optimism L2 Cross Domain Messenger, in charge of redirecting cross-chain transactions in L2 18 | address public immutable OVM_L2_CROSS_DOMAIN_MESSENGER; 19 | // Certora : replace xDomainMessageSender by a known address. 20 | address private _domainMsgSender; 21 | mockTarget public _mock; 22 | 23 | /// @inheritdoc L2BridgeExecutorHarness 24 | // Certora: removed call to domainMessageSender and replaced with address variable. 25 | modifier onlyEthereumGovernanceExecutor() override { 26 | if ( 27 | msg.sender != OVM_L2_CROSS_DOMAIN_MESSENGER || 28 | _domainMsgSender !=_ethereumGovernanceExecutor 29 | ) revert UnauthorizedEthereumExecutor(); 30 | _; 31 | } 32 | 33 | /** 34 | * @dev Constructor 35 | * 36 | * @param ovmL2CrossDomainMessenger The address of the Optimism L2CrossDomainMessenger 37 | * @param ethereumGovernanceExecutor The address of the EthereumGovernanceExecutor 38 | * @param delay The delay before which an actions set can be executed 39 | * @param gracePeriod The time period after a delay during which an actions set can be executed 40 | * @param minimumDelay The minimum bound a delay can be set to 41 | * @param maximumDelay The maximum bound a delay can be set to 42 | * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) 43 | */ 44 | constructor( 45 | address ovmL2CrossDomainMessenger, 46 | address ethereumGovernanceExecutor, 47 | uint256 delay, 48 | uint256 gracePeriod, 49 | uint256 minimumDelay, 50 | uint256 maximumDelay, 51 | address guardian 52 | ) 53 | L2BridgeExecutorHarness( 54 | ethereumGovernanceExecutor, 55 | delay, 56 | gracePeriod, 57 | minimumDelay, 58 | maximumDelay, 59 | guardian 60 | ) 61 | { 62 | OVM_L2_CROSS_DOMAIN_MESSENGER = ovmL2CrossDomainMessenger; 63 | } 64 | 65 | function _executeTransaction( 66 | address target, 67 | uint256 value, 68 | string memory signature, 69 | bytes memory data, 70 | uint256 executionTime, 71 | bool withDelegatecall 72 | ) internal override returns (bytes memory) { 73 | if (address(this).balance < value) revert InsufficientBalance(); 74 | 75 | bytes32 actionHash = keccak256( 76 | abi.encode(target, value, executionTime) 77 | ); 78 | _queuedActions[actionHash] = false; 79 | 80 | bool success; 81 | bytes memory resultData; 82 | if (withDelegatecall) { 83 | (success, resultData) = this.executeDelegateCall{value: value}(target, data); 84 | } else { 85 | // solium-disable-next-line security/no-call-value 86 | success = _mock.targetCall(data); 87 | } 88 | return _verifyCallResult(success, resultData); 89 | } 90 | 91 | function executeDelegateCall(address target, bytes calldata data) 92 | external 93 | payable 94 | override 95 | onlyThis 96 | returns (bool, bytes memory) 97 | { 98 | bool success; 99 | bytes memory resultData; 100 | // solium-disable-next-line security/no-call-value 101 | success = _mock.targetCall(data); 102 | return (success, resultData); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@aave/governance-crosschain-bridges", 3 | "version": "1.0.1", 4 | "description": "Contracts of Aave Governance Crosschain Bridges", 5 | "files": [ 6 | "contracts", 7 | "artifacts", 8 | "types" 9 | ], 10 | "scripts": { 11 | "run-env": "npm i && tail -f /dev/null", 12 | "hardhat": "hardhat", 13 | "compile": "SKIP_LOAD=true hardhat clean && SKIP_LOAD=true hardhat compile", 14 | "clean": "SKIP_LOAD=true hardhat clean", 15 | "console:fork": "MAINNET_FORK=true hardhat console", 16 | "prettier:check": "npx prettier -c 'tasks/**/*.ts' 'contracts/**/*.sol' 'helpers/*.ts' 'test/*.ts' 'test/**/*.ts'", 17 | "prettier:write": "prettier --write 'tasks/**/*.ts' 'contracts/**/*.sol' 'helpers/*.ts' 'test/*.ts' 'test/**/*.ts'", 18 | "coverage": "npx hardhat coverage --testfiles 'test/emptyrun.coverage.ts' && npx hardhat coverage --testfiles 'test/*.spec.ts'", 19 | "test": "npm run compile && hardhat test test/*.spec.ts", 20 | "test-fork": "npm run compile && MAINNET_FORK=true FORKING_BLOCK_NUMBER=14340480 hardhat test test/fork/crosschain-bridges.spec.ts", 21 | "hardhat:kovan": "hardhat --network kovan", 22 | "hardhat:tenderlyMain": "hardhat --network tenderlyMain", 23 | "hardhat:ropsten": "hardhat --network ropsten", 24 | "hardhat:rinkeby": "hardhat --network rinkeby", 25 | "hardhat:goerli": "hardhat --network goerli", 26 | "hardhat:main": "hardhat --network main", 27 | "hardhat:docker": "hardhat --network hardhatevm_docker", 28 | "hardhat:mumbai": "hardhat --network mumbai", 29 | "hardhat:matic": "hardhat --network matic", 30 | "hardhat:optimism": "hardhat --network optimism", 31 | "hardhat:optimism-testnet": "hardhat --network optimism-testnet", 32 | "hardhat:arbitrum": "hardhat --network arbitrum", 33 | "hardhat:arbitrum-testnet": "hardhat --network arbitrum-testnet", 34 | "deploy:gov-bridge:optimism-testnet": "npm run hardhat:optimism-testnet -- --tags 'OptimisticGov,Greeter'", 35 | "deploy:gov-bridge:arbitrum-testnet": "npm run hardhat:arbitrum-testnet -- --tags 'ArbitrumGov,Greeter'" 36 | }, 37 | "devDependencies": { 38 | "@aave/governance-v2": "^1.0.0", 39 | "@ethersproject/abi": "^5.1.0", 40 | "@nomiclabs/hardhat-ethers": "npm:hardhat-deploy-ethers@^0.3.0-beta.13", 41 | "@nomiclabs/hardhat-etherscan": "^3.0.3", 42 | "@nomiclabs/hardhat-waffle": "^2.0.3", 43 | "@openzeppelin/contracts": "^4.3.2", 44 | "@tenderly/hardhat-tenderly": "^1.1.0-beta.5", 45 | "@typechain/ethers-v5": "^7.0.1", 46 | "@typechain/hardhat": "^2.0.2", 47 | "@types/bluebird": "^3.5.36", 48 | "@types/chai": "^4.2.21", 49 | "@types/mocha": "^9.1.1", 50 | "@types/node": "^16.11.36", 51 | "bluebird": "^3.7.2", 52 | "chai": "^4.3.4", 53 | "defender-relay-client": "^1.4.2", 54 | "dotenv": "^10.0.0", 55 | "ethereum-waffle": "^3.4.0", 56 | "ethers": "^5.6.1", 57 | "hardhat": "^2.9.5", 58 | "hardhat-contract-sizer": "^2.0.3", 59 | "hardhat-dependency-compiler": "^1.1.2", 60 | "hardhat-deploy": "^0.10.6", 61 | "hardhat-gas-reporter": "^1.0.8", 62 | "prettier": "^2.3.2", 63 | "prettier-plugin-solidity": "^1.0.0-beta.19", 64 | "solidity-coverage": "^0.7.17", 65 | "tmp-promise": "^3.0.2", 66 | "ts-node": "^10.2.1", 67 | "tslint": "^6.1.3", 68 | "tslint-config-prettier": "^1.18.0", 69 | "tslint-plugin-prettier": "^2.3.0", 70 | "typechain": "^5.1.2", 71 | "typescript": "^4.6.4" 72 | }, 73 | "husky": { 74 | "hooks": { 75 | "pre-commit": "pretty-quick --staged --pattern 'contracts/**/*.sol' --pattern 'helpers/**/*.ts' --pattern 'test/**/*.ts' --pattern 'tasks/**/*.ts'" 76 | } 77 | }, 78 | "author": "Aave", 79 | "contributors": [ 80 | "Steven Valeri ", 81 | "Lasse Herskind ", 82 | "Miguel Martinez ", 83 | "Peter Michael " 84 | ], 85 | "license": "BSD-3-Clause", 86 | "keywords": [ 87 | "aave", 88 | "governance", 89 | "crosschain", 90 | "bridges", 91 | "ethereum", 92 | "solidity" 93 | ], 94 | "publishConfig": { 95 | "registry": "https://npm.pkg.github.com" 96 | }, 97 | "repository": { 98 | "type": "git", 99 | "url": "git://github.com/aave/governance-crosschain-bridges" 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /tasks/l2/optimism.ts: -------------------------------------------------------------------------------- 1 | import { BigNumber } from 'ethers'; 2 | import { formatUnits } from 'ethers/lib/utils'; 3 | import { task } from 'hardhat/config'; 4 | import { ADDRESSES, CONSTANTS } from '../../helpers/gov-constants'; 5 | 6 | import { DRE } from '../../helpers/misc-utils'; 7 | import { eEthereumNetwork, eOptimismNetwork } from '../../helpers/types'; 8 | import { 9 | Greeter__factory, 10 | ICrossDomainMessenger__factory, 11 | OptimismBridgeExecutor__factory, 12 | } from '../../typechain'; 13 | 14 | task( 15 | 'optimism:initiate-greeting', 16 | 'Queue a greeting in the governance executor on Optimism by transacting on L1' 17 | ).setAction(async (_, hre) => { 18 | await hre.run('set-DRE'); 19 | 20 | if (DRE.network.name != eEthereumNetwork.kovan && DRE.network.name != eEthereumNetwork.main) { 21 | throw new Error('Only applicable on mainnet or kovan where optimism L2 exist'); 22 | } 23 | 24 | const GAS_LIMIT = 1500000; 25 | const MESSAGE = 'Miguel was also here'; 26 | 27 | let OVM_L1_MESSENGER = ADDRESSES['OVM_L1_MESSENGER_MAIN']; 28 | if (DRE.network.name == eEthereumNetwork.kovan) { 29 | OVM_L1_MESSENGER = ADDRESSES['OVM_L1_MESSENGER_KOVAN']; 30 | } 31 | 32 | const l2 = DRE.companionNetworks['optimism']; 33 | 34 | const { deployer: deployerAddress } = await DRE.getNamedAccounts(); 35 | const deployer = await DRE.ethers.getSigner(deployerAddress); 36 | console.log( 37 | `Deployer address: ${deployer.address} (${formatUnits(await deployer.getBalance())})` 38 | ); 39 | 40 | // Note, the contract is on the optimism network, but only used to encode so no issue 41 | const optimisticGov = OptimismBridgeExecutor__factory.connect( 42 | (await l2.deployments.get('OptimisticGov')).address, 43 | deployer 44 | ); 45 | console.log(`Optimistic Gov at ${optimisticGov.address}`); 46 | 47 | // Note, the contract is on the optimism network, but only used to encode so no issue 48 | const greeter = Greeter__factory.connect((await l2.deployments.get('Greeter')).address, deployer); 49 | console.log(`Greeter at ${greeter.address}`); 50 | 51 | const messenger = ICrossDomainMessenger__factory.connect(OVM_L1_MESSENGER, deployer); 52 | console.log(`OVM_L1_MESSENGER at: ${messenger.address}`); 53 | 54 | const encodedGreeting = greeter.interface.encodeFunctionData('setMessage', [MESSAGE]); 55 | 56 | const targets: string[] = [greeter.address]; 57 | const values: BigNumber[] = [BigNumber.from(0)]; 58 | const signatures: string[] = ['']; 59 | const calldatas: string[] = [encodedGreeting]; 60 | const withDelegatecalls: boolean[] = [false]; 61 | 62 | const encodedQueue = optimisticGov.interface.encodeFunctionData('queue', [ 63 | targets, 64 | values, 65 | signatures, 66 | calldatas, 67 | withDelegatecalls, 68 | ]); 69 | 70 | const tx = await messenger.sendMessage(optimisticGov.address, encodedQueue, GAS_LIMIT); 71 | console.log(`Transaction initiated: ${tx.hash}`); 72 | }); 73 | 74 | task('optimism:execute-greeting', '') 75 | .addParam('id', 'Id of the proposal to execute') 76 | .setAction(async (taskArg, hre) => { 77 | await hre.run('set-DRE'); 78 | 79 | if (DRE.network.name != eOptimismNetwork.main && DRE.network.name != eOptimismNetwork.testnet) { 80 | throw new Error('Only applicable on optimism L2'); 81 | } 82 | 83 | const id = taskArg.id; 84 | 85 | const { deployer: deployerAddress } = await DRE.getNamedAccounts(); 86 | const deployer = await DRE.ethers.getSigner(deployerAddress); 87 | console.log( 88 | `Deployer address: ${deployer.address} (${formatUnits(await deployer.getBalance())})` 89 | ); 90 | 91 | // Note, the contract is on the optimism network, but only used to encode so no issue 92 | const optimisticGov = OptimismBridgeExecutor__factory.connect( 93 | (await DRE.deployments.get('OptimisticGov')).address, 94 | deployer 95 | ); 96 | console.log(`Optimistic Gov at ${optimisticGov.address}`); 97 | 98 | // Note, the contract is on the optimism network, but only used to encode so no issue 99 | const greeter = Greeter__factory.connect( 100 | (await DRE.deployments.get('Greeter')).address, 101 | deployer 102 | ); 103 | console.log(`Greeter at ${greeter.address}`); 104 | 105 | const tx = await optimisticGov.execute(id); 106 | 107 | console.log(`Transaction initiated: ${tx.hash}`); 108 | }); 109 | -------------------------------------------------------------------------------- /contracts/bridges/PolygonBridgeExecutor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import {IFxMessageProcessor} from '../dependencies/polygon/fxportal/interfaces/IFxMessageProcessor.sol'; 5 | import {BridgeExecutorBase} from './BridgeExecutorBase.sol'; 6 | 7 | /** 8 | * @title PolygonBridgeExecutor 9 | * @author Aave 10 | * @notice Implementation of the Polygon Bridge Executor, able to receive cross-chain transactions from Ethereum 11 | * @dev Queuing an ActionsSet into this Executor can only be done by the FxChild and after passing the EthereumGovernanceExecutor check 12 | * as the FxRoot sender 13 | */ 14 | contract PolygonBridgeExecutor is BridgeExecutorBase, IFxMessageProcessor { 15 | error UnauthorizedChildOrigin(); 16 | error UnauthorizedRootOrigin(); 17 | 18 | /** 19 | * @dev Emitted when the FxRoot Sender is updated 20 | * @param oldFxRootSender The address of the old FxRootSender 21 | * @param newFxRootSender The address of the new FxRootSender 22 | **/ 23 | event FxRootSenderUpdate(address oldFxRootSender, address newFxRootSender); 24 | 25 | /** 26 | * @dev Emitted when the FxChild is updated 27 | * @param oldFxChild The address of the old FxChild 28 | * @param newFxChild The address of the new FxChild 29 | **/ 30 | event FxChildUpdate(address oldFxChild, address newFxChild); 31 | 32 | // Address of the FxRoot Sender, sending the cross-chain transaction from Ethereum 33 | address private _fxRootSender; 34 | // Address of the FxChild, in charge of redirecting cross-chain transactions in Polygon 35 | address private _fxChild; 36 | 37 | /** 38 | * @dev Only FxChild can call functions marked by this modifier. 39 | **/ 40 | modifier onlyFxChild() { 41 | if (msg.sender != _fxChild) revert UnauthorizedChildOrigin(); 42 | _; 43 | } 44 | 45 | /** 46 | * @dev Constructor 47 | * 48 | * @param fxRootSender The address of the transaction sender in FxRoot 49 | * @param fxChild The address of the FxChild 50 | * @param delay The delay before which an actions set can be executed 51 | * @param gracePeriod The time period after a delay during which an actions set can be executed 52 | * @param minimumDelay The minimum bound a delay can be set to 53 | * @param maximumDelay The maximum bound a delay can be set to 54 | * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) 55 | */ 56 | constructor( 57 | address fxRootSender, 58 | address fxChild, 59 | uint256 delay, 60 | uint256 gracePeriod, 61 | uint256 minimumDelay, 62 | uint256 maximumDelay, 63 | address guardian 64 | ) BridgeExecutorBase(delay, gracePeriod, minimumDelay, maximumDelay, guardian) { 65 | _fxRootSender = fxRootSender; 66 | _fxChild = fxChild; 67 | } 68 | 69 | /// @inheritdoc IFxMessageProcessor 70 | function processMessageFromRoot( 71 | uint256 stateId, 72 | address rootMessageSender, 73 | bytes calldata data 74 | ) external override onlyFxChild { 75 | if (rootMessageSender != _fxRootSender) revert UnauthorizedRootOrigin(); 76 | 77 | address[] memory targets; 78 | uint256[] memory values; 79 | string[] memory signatures; 80 | bytes[] memory calldatas; 81 | bool[] memory withDelegatecalls; 82 | 83 | (targets, values, signatures, calldatas, withDelegatecalls) = abi.decode( 84 | data, 85 | (address[], uint256[], string[], bytes[], bool[]) 86 | ); 87 | 88 | _queue(targets, values, signatures, calldatas, withDelegatecalls); 89 | } 90 | 91 | /** 92 | * @notice Update the address of the FxRoot Sender 93 | * @param fxRootSender The address of the new FxRootSender 94 | **/ 95 | function updateFxRootSender(address fxRootSender) external onlyThis { 96 | emit FxRootSenderUpdate(_fxRootSender, fxRootSender); 97 | _fxRootSender = fxRootSender; 98 | } 99 | 100 | /** 101 | * @notice Update the address of the FxChild 102 | * @param fxChild The address of the new FxChild 103 | **/ 104 | function updateFxChild(address fxChild) external onlyThis { 105 | emit FxChildUpdate(_fxChild, fxChild); 106 | _fxChild = fxChild; 107 | } 108 | 109 | /** 110 | * @notice Returns the address of the FxRoot Sender 111 | * @return The address of the FxRootSender 112 | **/ 113 | function getFxRootSender() external view returns (address) { 114 | return _fxRootSender; 115 | } 116 | 117 | /** 118 | * @notice Returns the address of the FxChild 119 | * @return fxChild The address of FxChild 120 | **/ 121 | function getFxChild() external view returns (address) { 122 | return _fxChild; 123 | } 124 | } 125 | -------------------------------------------------------------------------------- /certora/munged/bridges/PolygonBridgeExecutor.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import {IFxMessageProcessor} from '../dependencies/polygon/fxportal/interfaces/IFxMessageProcessor.sol'; 5 | import {BridgeExecutorBase} from './BridgeExecutorBase.sol'; 6 | 7 | /** 8 | * @title PolygonBridgeExecutor 9 | * @author Aave 10 | * @notice Implementation of the Polygon Bridge Executor, able to receive cross-chain transactions from Ethereum 11 | * @dev Queuing an ActionsSet into this Executor can only be done by the FxChild and after passing the EthereumGovernanceExecutor check 12 | * as the FxRoot sender 13 | */ 14 | contract PolygonBridgeExecutor is BridgeExecutorBase, IFxMessageProcessor { 15 | error UnauthorizedChildOrigin(); 16 | error UnauthorizedRootOrigin(); 17 | 18 | /** 19 | * @dev Emitted when the FxRoot Sender is updated 20 | * @param oldFxRootSender The address of the old FxRootSender 21 | * @param newFxRootSender The address of the new FxRootSender 22 | **/ 23 | event FxRootSenderUpdate(address oldFxRootSender, address newFxRootSender); 24 | 25 | /** 26 | * @dev Emitted when the FxChild is updated 27 | * @param oldFxChild The address of the old FxChild 28 | * @param newFxChild The address of the new FxChild 29 | **/ 30 | event FxChildUpdate(address oldFxChild, address newFxChild); 31 | 32 | // Address of the FxRoot Sender, sending the cross-chain transaction from Ethereum 33 | address internal _fxRootSender; // Certora harness: private -> internal 34 | // Address of the FxChild, in charge of redirecting cross-chain transactions in Polygon 35 | address internal _fxChild; // Certora harness: private -> internal 36 | 37 | /** 38 | * @dev Only FxChild can call functions marked by this modifier. 39 | **/ 40 | modifier onlyFxChild() { 41 | if (msg.sender != _fxChild) revert UnauthorizedChildOrigin(); 42 | _; 43 | } 44 | 45 | /** 46 | * @dev Constructor 47 | * 48 | * @param fxRootSender The address of the transaction sender in FxRoot 49 | * @param fxChild The address of the FxChild 50 | * @param delay The delay before which an actions set can be executed 51 | * @param gracePeriod The time period after a delay during which an actions set can be executed 52 | * @param minimumDelay The minimum bound a delay can be set to 53 | * @param maximumDelay The maximum bound a delay can be set to 54 | * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) 55 | */ 56 | constructor( 57 | address fxRootSender, 58 | address fxChild, 59 | uint256 delay, 60 | uint256 gracePeriod, 61 | uint256 minimumDelay, 62 | uint256 maximumDelay, 63 | address guardian 64 | ) BridgeExecutorBase(delay, gracePeriod, minimumDelay, maximumDelay, guardian) { 65 | _fxRootSender = fxRootSender; 66 | _fxChild = fxChild; 67 | } 68 | 69 | /// @inheritdoc IFxMessageProcessor 70 | // Certora harness: added virtual 71 | function processMessageFromRoot( 72 | uint256 stateId, 73 | address rootMessageSender, 74 | bytes calldata data 75 | ) external override virtual onlyFxChild { 76 | if (rootMessageSender != _fxRootSender) revert UnauthorizedRootOrigin(); 77 | 78 | address[] memory targets; 79 | uint256[] memory values; 80 | string[] memory signatures; 81 | bytes[] memory calldatas; 82 | bool[] memory withDelegatecalls; 83 | 84 | (targets, values, signatures, calldatas, withDelegatecalls) = abi.decode( 85 | data, 86 | (address[], uint256[], string[], bytes[], bool[]) 87 | ); 88 | 89 | _queue(targets, values, signatures, calldatas, withDelegatecalls); 90 | } 91 | 92 | /** 93 | * @notice Update the address of the FxRoot Sender 94 | * @param fxRootSender The address of the new FxRootSender 95 | **/ 96 | function updateFxRootSender(address fxRootSender) external onlyThis { 97 | emit FxRootSenderUpdate(_fxRootSender, fxRootSender); 98 | _fxRootSender = fxRootSender; 99 | } 100 | 101 | /** 102 | * @notice Update the address of the FxChild 103 | * @param fxChild The address of the new FxChild 104 | **/ 105 | function updateFxChild(address fxChild) external onlyThis { 106 | emit FxChildUpdate(_fxChild, fxChild); 107 | _fxChild = fxChild; 108 | } 109 | 110 | /** 111 | * @notice Returns the address of the FxRoot Sender 112 | * @return The address of the FxRootSender 113 | **/ 114 | function getFxRootSender() external view returns (address) { 115 | return _fxRootSender; 116 | } 117 | 118 | /** 119 | * @notice Returns the address of the FxChild 120 | * @return fxChild The address of FxChild 121 | **/ 122 | function getFxChild() external view returns (address) { 123 | return _fxChild; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /helpers/etherscan-verification.ts: -------------------------------------------------------------------------------- 1 | import { exit } from 'process'; 2 | import fs from 'fs'; 3 | import { file } from 'tmp-promise'; 4 | import { DRE } from './misc-utils'; 5 | 6 | const fatalErrors = [ 7 | `The address provided as argument contains a contract, but its bytecode`, 8 | `Daily limit of 100 source code submissions reached`, 9 | `has no bytecode. Is the contract deployed to this network`, 10 | `The constructor for`, 11 | ]; 12 | 13 | const okErrors = [`Contract source code already verified`]; 14 | 15 | const unableVerifyError = 'Fail - Unable to verify'; 16 | 17 | export const SUPPORTED_ETHERSCAN_NETWORKS = [ 18 | 'main', 19 | 'ropsten', 20 | 'kovan', 21 | 'goerli', 22 | 'matic', 23 | 'mumbai', 24 | ]; 25 | 26 | function delay(ms: number) { 27 | return new Promise((resolve) => setTimeout(resolve, ms)); 28 | } 29 | 30 | export const verifyContract = async ( 31 | address: string, 32 | constructorArguments: (string | string[])[], 33 | libraries?: string 34 | ) => { 35 | const currentNetwork = DRE.network.name; 36 | 37 | if (!process.env.ETHERSCAN_KEY) { 38 | throw Error('Missing process.env.ETHERSCAN_KEY.'); 39 | } 40 | if (!SUPPORTED_ETHERSCAN_NETWORKS.includes(currentNetwork)) { 41 | throw Error( 42 | `Current network ${currentNetwork} not supported. Please change to one of the next networks: ${SUPPORTED_ETHERSCAN_NETWORKS.toString()}` 43 | ); 44 | } 45 | 46 | try { 47 | console.log( 48 | '[ETHERSCAN][WARNING] Delaying Etherscan verification due their API can not find newly deployed contracts' 49 | ); 50 | const msDelay = 3000; 51 | const times = 4; 52 | // Write a temporal file to host complex parameters for hardhat-etherscan https://github.com/nomiclabs/hardhat/tree/development/packages/hardhat-etherscan#complex-arguments 53 | const paramsFile = await file({ 54 | prefix: 'verify-params-', 55 | postfix: '.js', 56 | }); 57 | fs.writeSync(paramsFile.fd, `module.exports = ${JSON.stringify([...constructorArguments])};`); 58 | 59 | // Write a temporal file for library args 60 | const librariesFile = await file({ 61 | prefix: 'verify-libraries-', 62 | postfix: '.js', 63 | }); 64 | fs.writeSync(librariesFile.fd, `module.exports = ${libraries};`); 65 | 66 | const params = { 67 | address: address, 68 | libraries: librariesFile.path, 69 | constructorArgs: paramsFile.path, 70 | relatedSources: true, 71 | }; 72 | await runTaskWithRetry('verify', params, times, msDelay, () => { 73 | paramsFile.cleanup(); 74 | librariesFile.cleanup(); 75 | }); 76 | } catch (error) {} 77 | }; 78 | 79 | export const runTaskWithRetry = async ( 80 | task: string, 81 | params: any, 82 | times: number, 83 | msDelay: number, 84 | cleanup: () => void 85 | ) => { 86 | let counter = times; 87 | await delay(msDelay); 88 | 89 | try { 90 | if (times > 1) { 91 | await DRE.run(task, params); 92 | cleanup(); 93 | } else if (times === 1) { 94 | console.log('[ETHERSCAN][WARNING] Trying to verify via uploading all sources.'); 95 | delete params.relatedSources; 96 | await DRE.run(task, params); 97 | cleanup(); 98 | } else { 99 | cleanup(); 100 | console.error( 101 | '[ETHERSCAN][ERROR] Errors after all the retries, check the logs for more information.' 102 | ); 103 | } 104 | } catch (error) { 105 | counter--; 106 | 107 | if (okErrors.some((okReason) => error.message.includes(okReason))) { 108 | console.info('[ETHERSCAN][INFO] Skipping due OK response: ', error.message); 109 | return; 110 | } 111 | 112 | if (fatalErrors.some((fatalError) => error.message.includes(fatalError))) { 113 | console.error( 114 | '[ETHERSCAN][ERROR] Fatal error detected, skip retries and resume deployment.', 115 | error.message 116 | ); 117 | return; 118 | } 119 | console.error('[ETHERSCAN][ERROR]', error.message); 120 | console.log(); 121 | console.info(`[ETHERSCAN][[INFO] Retrying attempts: ${counter}.`); 122 | if (error.message.includes(unableVerifyError)) { 123 | console.log('[ETHERSCAN][WARNING] Trying to verify via uploading all sources.'); 124 | delete params.relatedSources; 125 | } 126 | await runTaskWithRetry(task, params, counter, msDelay, cleanup); 127 | } 128 | }; 129 | 130 | export const checkVerification = () => { 131 | const currentNetwork = DRE.network.name; 132 | if (!process.env.ETHERSCAN_KEY) { 133 | console.error('Missing process.env.ETHERSCAN_KEY.'); 134 | exit(3); 135 | } 136 | if (!SUPPORTED_ETHERSCAN_NETWORKS.includes(currentNetwork)) { 137 | console.error( 138 | `Current network ${currentNetwork} not supported. Please change to one of the next networks: ${SUPPORTED_ETHERSCAN_NETWORKS.toString()}` 139 | ); 140 | exit(5); 141 | } 142 | }; 143 | -------------------------------------------------------------------------------- /certora/applyHarness.patch: -------------------------------------------------------------------------------- 1 | diff -ruN .gitignore .gitignore 2 | --- .gitignore 1970-01-01 02:00:00.000000000 +0200 3 | +++ .gitignore 2022-08-03 12:50:10.000000000 +0300 4 | @@ -0,0 +1,2 @@ 5 | +* 6 | +!.gitignore 7 | diff -ruN bridges/BridgeExecutorBase.sol bridges/BridgeExecutorBase.sol 8 | --- bridges/BridgeExecutorBase.sol 2022-07-31 16:38:17.000000000 +0300 9 | +++ bridges/BridgeExecutorBase.sol 2022-07-31 16:35:45.000000000 +0300 10 | @@ -15,7 +15,7 @@ 11 | uint256 constant MINIMUM_GRACE_PERIOD = 10 minutes; 12 | 13 | // Time between queuing and execution 14 | - uint256 private _delay; 15 | + uint256 internal _delay; // harness: private -> internal 16 | // Time after the execution time during which the actions set can be executed 17 | uint256 private _gracePeriod; 18 | // Minimum allowed delay 19 | @@ -24,13 +24,12 @@ 20 | uint256 private _maximumDelay; 21 | // Address with the ability of canceling actions sets 22 | address private _guardian; 23 | - 24 | // Number of actions sets 25 | - uint256 private _actionsSetCounter; 26 | + uint256 internal _actionsSetCounter; // harness: private -> internal 27 | // Map of registered actions sets (id => ActionsSet) 28 | - mapping(uint256 => ActionsSet) private _actionsSets; 29 | - // Map of queued actions (actionHash => isQueued) 30 | - mapping(bytes32 => bool) private _queuedActions; 31 | + mapping(uint256 => ActionsSet) internal _actionsSets; // harness: private -> internal 32 | + // Map of queued actions sets (actionHash => isQueued) 33 | + mapping(bytes32 => bool) internal _queuedActions; // harness: private -> internal 34 | 35 | /** 36 | * @dev Only guardian can call functions marked by this modifier. 37 | @@ -162,11 +161,13 @@ 38 | _validateDelay(_delay); 39 | } 40 | 41 | + // Certora : added virtual 42 | /// @inheritdoc IExecutorBase 43 | function executeDelegateCall(address target, bytes calldata data) 44 | external 45 | payable 46 | override 47 | + virtual 48 | onlyThis 49 | returns (bool, bytes memory) 50 | { 51 | @@ -312,7 +313,7 @@ 52 | unchecked { 53 | ++i; 54 | } 55 | - } 56 | + } 57 | 58 | ActionsSet storage actionsSet = _actionsSets[actionsSetId]; 59 | actionsSet.targets = targets; 60 | @@ -333,6 +334,7 @@ 61 | ); 62 | } 63 | 64 | + // harness : added virtual 65 | function _executeTransaction( 66 | address target, 67 | uint256 value, 68 | @@ -340,7 +342,7 @@ 69 | bytes memory data, 70 | uint256 executionTime, 71 | bool withDelegatecall 72 | - ) internal returns (bytes memory) { 73 | + ) internal virtual returns (bytes memory) { 74 | if (address(this).balance < value) revert InsufficientBalance(); 75 | 76 | bytes32 actionHash = keccak256( 77 | @@ -385,8 +387,9 @@ 78 | if (delay > _maximumDelay) revert DelayLongerThanMax(); 79 | } 80 | 81 | + // harness: private to internal 82 | function _verifyCallResult(bool success, bytes memory returnData) 83 | - private 84 | + internal 85 | pure 86 | returns (bytes memory) 87 | { 88 | diff -ruN bridges/L2BridgeExecutor.sol bridges/L2BridgeExecutor.sol 89 | --- bridges/L2BridgeExecutor.sol 2022-05-25 20:17:05.000000000 +0300 90 | +++ bridges/L2BridgeExecutor.sol 2022-07-31 16:35:45.000000000 +0300 91 | @@ -42,6 +42,7 @@ 92 | } 93 | 94 | /// @inheritdoc IL2BridgeExecutor 95 | + // Certora harness : removed _queue() 96 | function queue( 97 | address[] memory targets, 98 | uint256[] memory values, 99 | @@ -49,7 +50,7 @@ 100 | bytes[] memory calldatas, 101 | bool[] memory withDelegatecalls 102 | ) external onlyEthereumGovernanceExecutor { 103 | - _queue(targets, values, signatures, calldatas, withDelegatecalls); 104 | + //_queue(targets, values, signatures, calldatas, withDelegatecalls); 105 | } 106 | 107 | /// @inheritdoc IL2BridgeExecutor 108 | diff -ruN bridges/PolygonBridgeExecutor.sol bridges/PolygonBridgeExecutor.sol 109 | --- bridges/PolygonBridgeExecutor.sol 2022-05-25 20:17:05.000000000 +0300 110 | +++ bridges/PolygonBridgeExecutor.sol 2022-07-31 16:35:45.000000000 +0300 111 | @@ -30,9 +30,9 @@ 112 | event FxChildUpdate(address oldFxChild, address newFxChild); 113 | 114 | // Address of the FxRoot Sender, sending the cross-chain transaction from Ethereum 115 | - address private _fxRootSender; 116 | + address internal _fxRootSender; // Certora harness: private -> internal 117 | // Address of the FxChild, in charge of redirecting cross-chain transactions in Polygon 118 | - address private _fxChild; 119 | + address internal _fxChild; // Certora harness: private -> internal 120 | 121 | /** 122 | * @dev Only FxChild can call functions marked by this modifier. 123 | @@ -67,11 +67,12 @@ 124 | } 125 | 126 | /// @inheritdoc IFxMessageProcessor 127 | + // Certora harness: added virtual 128 | function processMessageFromRoot( 129 | uint256 stateId, 130 | address rootMessageSender, 131 | bytes calldata data 132 | - ) external override onlyFxChild { 133 | + ) external override virtual onlyFxChild { 134 | if (rootMessageSender != _fxRootSender) revert UnauthorizedRootOrigin(); 135 | 136 | address[] memory targets; 137 | -------------------------------------------------------------------------------- /helpers/task-helpers.ts: -------------------------------------------------------------------------------- 1 | import { concat } from '@ethersproject/bytes'; 2 | import readline from 'readline'; 3 | import util from 'util'; 4 | import { Contract, ContractFactory, Signer } from 'ethers'; 5 | import { ParamType } from '@ethersproject/abi'; 6 | 7 | export const printExpectedParams = (contract: string, constructorInputs: ParamType[]) => { 8 | console.log(`\n${contract} Constructor Parameters\n`); 9 | const params = {}; 10 | for (const input in constructorInputs) { 11 | console.log(`${constructorInputs[input].name} (${constructorInputs[input].type})`); 12 | params[constructorInputs[input].name] = ''; 13 | } 14 | console.log(`\n${JSON.stringify(params, null, 2)}`); 15 | }; 16 | 17 | export const printDefinedParams = (constructorInputs: ParamType[], paramsArray: any[]) => { 18 | for (const input in constructorInputs) { 19 | console.log( 20 | `${constructorInputs[input].name} (${constructorInputs[input].type}): ${paramsArray[input]}` 21 | ); 22 | } 23 | }; 24 | 25 | export const parseParams = async (params: string, paramsFile: string) => { 26 | if (params && paramsFile) { 27 | console.log(`\nYou can not use both parameters --params and --paramsFile \n`); 28 | return; 29 | } 30 | 31 | if (params) { 32 | try { 33 | return JSON.parse(params); 34 | } catch (err) { 35 | console.log(`\nParams provided in an incorrect format\n`); 36 | } 37 | } 38 | 39 | if (paramsFile) { 40 | try { 41 | const values = await import(`${process.cwd()}/${paramsFile}`); 42 | return values.default; 43 | } catch (err) { 44 | console.log(`\nCould not find linked params files`); 45 | console.log(`Path Provided: ${process.cwd()}/${paramsFile}\n`); 46 | } 47 | } 48 | }; 49 | 50 | export const parseLibraries = async (libraries: string, librariesFile: string) => { 51 | if (libraries && librariesFile) { 52 | console.log(`\nYou can not use both parameters --libraries and --librariesFile \n`); 53 | return; 54 | } 55 | 56 | const parsedLibraries = {}; 57 | if (libraries) { 58 | try { 59 | parsedLibraries['libraries'] = JSON.parse(libraries); 60 | return parsedLibraries; 61 | } catch (err) { 62 | console.log(`\nLibraries provided in an incorrect format\n`); 63 | } 64 | } 65 | 66 | if (librariesFile) { 67 | try { 68 | const exportedLibraries = await import(`${process.cwd()}/${librariesFile}`); 69 | parsedLibraries['libraries'] = exportedLibraries.default; 70 | return parsedLibraries; 71 | } catch (err) { 72 | console.log(`\nCould not find linked libraries files`); 73 | console.log(`Path Provided: ${process.cwd()}/${librariesFile}\n`); 74 | } 75 | } 76 | }; 77 | 78 | export const defineParams = async ( 79 | contractParams: any, 80 | constructorInputs: ParamType[] 81 | ): Promise => { 82 | console.log(`\nDefining Parameters...\n`); 83 | let params: any[] = []; 84 | if (contractParams) { 85 | params = await getParamsArray(contractParams, constructorInputs); 86 | } else { 87 | params = await cliParams(constructorInputs); 88 | } 89 | return params; 90 | }; 91 | 92 | export const deployContract = async ( 93 | params: any[], 94 | contractFactory: ContractFactory, 95 | contractSigner: Signer 96 | ): Promise => { 97 | console.log(`\nDeploying...\n`); 98 | const encodedParams = contractFactory.interface.encodeDeploy(params); 99 | const tx = { 100 | data: concat([contractFactory.bytecode, encodedParams]), 101 | }; 102 | const sentTx = await contractSigner.sendTransaction(tx); 103 | console.log(`Tx submitted - txHash: ${sentTx.hash}`); 104 | const contractTransaction = await sentTx.wait(); 105 | const contractInstance = await contractFactory.attach(contractTransaction.contractAddress); 106 | 107 | console.log(`Contract address: ${contractTransaction.contractAddress}`); 108 | console.log(`Contract deployment tx: ${contractTransaction.transactionHash}`); 109 | console.log(`Contract deployed from: ${contractTransaction.from}`); 110 | console.log(``); 111 | return contractInstance; 112 | }; 113 | 114 | const getParamsArray = async ( 115 | contractParams: any, 116 | constructorInputs: ParamType[] 117 | ): Promise => { 118 | const parsedParams = contractParams; 119 | const params: any[] = []; 120 | for (const input in constructorInputs) { 121 | const inputName = parsedParams[constructorInputs[input].name]; 122 | if (inputName) { 123 | params.push(inputName); 124 | } else { 125 | console.log(`${constructorInputs[input].name} not included as an input param\n`); 126 | return []; 127 | } 128 | } 129 | console.log(); 130 | return params; 131 | }; 132 | 133 | const cliParams = async (constructorInputs: ParamType[]): Promise => { 134 | const rl = readline.createInterface({ 135 | input: process.stdin, 136 | output: process.stdout, 137 | }); 138 | const prompt = util.promisify(rl.question).bind(rl); 139 | 140 | console.log('Enter Constructor Parameters:'); 141 | const params: any[] = []; 142 | for (const input in constructorInputs) { 143 | params.push( 144 | await prompt(`${constructorInputs[input].name} (${constructorInputs[input].type}): `) 145 | ); 146 | } 147 | return params; 148 | }; 149 | -------------------------------------------------------------------------------- /helpers/misc-utils.ts: -------------------------------------------------------------------------------- 1 | import { Wallet, ContractTransaction, Signer, BigNumber } from 'ethers'; 2 | import { HardhatRuntimeEnvironment } from 'hardhat/types'; 3 | import { tEthereumAddress } from './types'; 4 | import { isAddress } from 'ethers/lib/utils'; 5 | import { isZeroAddress } from 'ethereumjs-util'; 6 | 7 | export let DRE: HardhatRuntimeEnvironment; 8 | 9 | export const setDRE = (_DRE: HardhatRuntimeEnvironment) => { 10 | DRE = _DRE; 11 | }; 12 | 13 | export const sleep = (milliseconds: number) => { 14 | return new Promise((resolve) => setTimeout(resolve, milliseconds)); 15 | }; 16 | 17 | export const createRandomAddress = () => Wallet.createRandom().address; 18 | 19 | export const evmSnapshot = async () => await DRE.ethers.provider.send('evm_snapshot', []); 20 | 21 | export const evmRevert = async (id: string) => DRE.ethers.provider.send('evm_revert', [id]); 22 | 23 | export const timeLatest = async () => { 24 | const block = await DRE.ethers.provider.getBlock('latest'); 25 | return BigNumber.from(block.timestamp); 26 | }; 27 | 28 | export const setBlocktime = async (time: number) => { 29 | await DRE.ethers.provider.send('evm_setNextBlockTimestamp', [time]); 30 | }; 31 | export const advanceBlocks = async (numberOfBlocks: number, timeBetweenBlocks: number = 0) => 32 | await DRE.ethers.provider.send('hardhat_mine', [ 33 | `0x${numberOfBlocks.toString(16)}`, 34 | `0x${timeBetweenBlocks.toString(16)}`, 35 | ]); 36 | 37 | export const advanceBlock = async (timestamp: number) => 38 | await DRE.ethers.provider.send('evm_mine', [timestamp]); 39 | 40 | export const increaseTime = async (secondsToIncrease: number) => { 41 | await DRE.ethers.provider.send('evm_increaseTime', [secondsToIncrease]); 42 | await DRE.ethers.provider.send('evm_mine', []); 43 | }; 44 | 45 | // Workaround for time travel tests bug: https://github.com/Tonyhaenn/hh-time-travel/blob/0161d993065a0b7585ec5a043af2eb4b654498b8/test/test.js#L12 46 | export const advanceTimeAndBlock = async function (forwardTime: number) { 47 | const currentBlockNumber = await DRE.ethers.provider.getBlockNumber(); 48 | const currentBlock = await DRE.ethers.provider.getBlock(currentBlockNumber); 49 | 50 | if (currentBlock === null) { 51 | /* Workaround for https://github.com/nomiclabs/hardhat/issues/1183 52 | */ 53 | await DRE.ethers.provider.send('evm_increaseTime', [forwardTime]); 54 | await DRE.ethers.provider.send('evm_mine', []); 55 | //Set the next blocktime back to 15 seconds 56 | await DRE.ethers.provider.send('evm_increaseTime', [15]); 57 | return; 58 | } 59 | const currentTime = currentBlock.timestamp; 60 | const futureTime = currentTime + forwardTime; 61 | await DRE.ethers.provider.send('evm_setNextBlockTimestamp', [futureTime]); 62 | await DRE.ethers.provider.send('evm_mine', []); 63 | }; 64 | 65 | export const latestBlock = async () => await DRE.ethers.provider.getBlockNumber(); 66 | 67 | export const advanceBlockTo = async (target: number) => { 68 | const currentBlock = await latestBlock(); 69 | const diff = DRE.ethers.BigNumber.from(target - currentBlock).toHexString(); 70 | const diff_clean = `0x${diff.substring(2).replace(/^0+/, '')}`; 71 | await DRE.network.provider.send('hardhat_mine', [diff_clean]); 72 | }; 73 | 74 | export const waitForTx = async (tx: ContractTransaction) => await tx.wait(1); 75 | 76 | export const filterMapBy = (raw: { [key: string]: any }, fn: (key: string) => boolean) => 77 | Object.keys(raw) 78 | .filter(fn) 79 | .reduce<{ [key: string]: any }>((obj, key) => { 80 | obj[key] = raw[key]; 81 | return obj; 82 | }, {}); 83 | 84 | export const chunk = (arr: Array, chunkSize: number): Array> => { 85 | return arr.reduce( 86 | (prevVal: any, currVal: any, currIndx: number, array: Array) => 87 | !(currIndx % chunkSize) 88 | ? prevVal.concat([array.slice(currIndx, currIndx + chunkSize)]) 89 | : prevVal, 90 | [] 91 | ); 92 | }; 93 | 94 | interface DbEntry { 95 | [network: string]: { 96 | deployer: string; 97 | address: string; 98 | }; 99 | } 100 | 101 | export const getImpersonatedSigner = async (address: tEthereumAddress): Promise => { 102 | await DRE.network.provider.request({ 103 | method: 'hardhat_impersonateAccount', 104 | params: [address], 105 | }); 106 | return await DRE.ethers.getSigner(address); 107 | }; 108 | // export const printContracts = () => { 109 | // const network = DRE.network.name; 110 | // const db = getDb(); 111 | // console.log('Contracts deployed at', network); 112 | // console.log('---------------------------------'); 113 | 114 | // const entries = Object.entries(db.getState()).filter(([_k, value]) => !!value[network]); 115 | 116 | // const contractsPrint = entries.map( 117 | // ([key, value]: [string, DbEntry]) => `${key}: ${value[network].address}` 118 | // ); 119 | 120 | // console.log('N# Contracts:', entries.length); 121 | // console.log(contractsPrint.join('\n'), '\n'); 122 | // }; 123 | 124 | export const notFalsyOrZeroAddress = (address: tEthereumAddress | null | undefined): boolean => { 125 | if (!address) { 126 | return false; 127 | } 128 | return isAddress(address) && !isZeroAddress(address); 129 | }; 130 | 131 | export const setCode = async (address: tEthereumAddress, bytecode: string): Promise => { 132 | await DRE.network.provider.request({ 133 | method: 'hardhat_setCode', 134 | params: [address, bytecode], 135 | }); 136 | }; 137 | -------------------------------------------------------------------------------- /contracts/dependencies/polygon/CustomPolygonMapping.sol: -------------------------------------------------------------------------------- 1 | /** 2 | *Submitted for verification at Etherscan.io on 2020-05-30 3 | */ 4 | 5 | /** 6 | Matic network contracts 7 | */ 8 | 9 | pragma solidity ^0.5.2; 10 | 11 | contract Ownable { 12 | address private _owner; 13 | 14 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 15 | 16 | /** 17 | * @dev The Ownable constructor sets the original `owner` of the contract to the sender 18 | * account. 19 | */ 20 | constructor() internal { 21 | _owner = msg.sender; 22 | emit OwnershipTransferred(address(0), _owner); 23 | } 24 | 25 | /** 26 | * @return the address of the owner. 27 | */ 28 | function owner() public view returns (address) { 29 | return _owner; 30 | } 31 | 32 | /** 33 | * @dev Throws if called by any account other than the owner. 34 | */ 35 | modifier onlyOwner() { 36 | require(isOwner()); 37 | _; 38 | } 39 | 40 | /** 41 | * @return true if `msg.sender` is the owner of the contract. 42 | */ 43 | function isOwner() public view returns (bool) { 44 | return msg.sender == _owner; 45 | } 46 | 47 | /** 48 | * @dev Allows the current owner to relinquish control of the contract. 49 | * It will not be possible to call the functions with the `onlyOwner` 50 | * modifier anymore. 51 | * @notice Renouncing ownership will leave the contract without an owner, 52 | * thereby removing any functionality that is only available to the owner. 53 | */ 54 | function renounceOwnership() public onlyOwner { 55 | emit OwnershipTransferred(_owner, address(0)); 56 | _owner = address(0); 57 | } 58 | 59 | /** 60 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 61 | * @param newOwner The address to transfer ownership to. 62 | */ 63 | function transferOwnership(address newOwner) public onlyOwner { 64 | _transferOwnership(newOwner); 65 | } 66 | 67 | /** 68 | * @dev Transfers control of the contract to a newOwner. 69 | * @param newOwner The address to transfer ownership to. 70 | */ 71 | function _transferOwnership(address newOwner) internal { 72 | require(newOwner != address(0)); 73 | emit OwnershipTransferred(_owner, newOwner); 74 | _owner = newOwner; 75 | } 76 | } 77 | 78 | library SafeMath { 79 | /** 80 | * @dev Multiplies two unsigned integers, reverts on overflow. 81 | */ 82 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 83 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 84 | // benefit is lost if 'b' is also tested. 85 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 86 | if (a == 0) { 87 | return 0; 88 | } 89 | 90 | uint256 c = a * b; 91 | require(c / a == b); 92 | 93 | return c; 94 | } 95 | 96 | /** 97 | * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. 98 | */ 99 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 100 | // Solidity only automatically asserts when dividing by 0 101 | require(b > 0); 102 | uint256 c = a / b; 103 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 104 | 105 | return c; 106 | } 107 | 108 | /** 109 | * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). 110 | */ 111 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 112 | require(b <= a); 113 | uint256 c = a - b; 114 | 115 | return c; 116 | } 117 | 118 | /** 119 | * @dev Adds two unsigned integers, reverts on overflow. 120 | */ 121 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 122 | uint256 c = a + b; 123 | require(c >= a); 124 | 125 | return c; 126 | } 127 | 128 | /** 129 | * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), 130 | * reverts when dividing by zero. 131 | */ 132 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 133 | require(b != 0); 134 | return a % b; 135 | } 136 | } 137 | 138 | contract CustomPolygonMapping is Ownable { 139 | using SafeMath for uint256; 140 | 141 | uint256 public counter; 142 | mapping(address => address) public registrations; 143 | 144 | event NewRegistration(address indexed user, address indexed sender, address indexed receiver); 145 | event RegistrationUpdated(address indexed user, address indexed sender, address indexed receiver); 146 | event StateSynced(uint256 indexed id, address indexed contractAddress, bytes data); 147 | 148 | modifier onlyRegistered(address receiver) { 149 | require(registrations[receiver] == msg.sender, 'Invalid sender'); 150 | _; 151 | } 152 | 153 | function syncState(address receiver, bytes calldata data) external onlyRegistered(receiver) { 154 | counter = counter.add(1); 155 | emit StateSynced(counter, receiver, data); 156 | // THIS IS THE ONLY CUSTOM PART 157 | bool success; 158 | bytes memory resultData; 159 | (success, resultData) = receiver.call( 160 | abi.encodeWithSignature('onStateReceive(uint256,bytes)', counter, data) 161 | ); 162 | require(success, 'FAILED_ACTION_EXECUTION_CUSTOM_MAPPING'); 163 | // END THE CUSTOM PART 164 | } 165 | 166 | // register new contract for state sync 167 | function register(address sender, address receiver) public { 168 | require( 169 | isOwner() || registrations[receiver] == msg.sender, 170 | 'StateSender.register: Not authorized to register' 171 | ); 172 | registrations[receiver] = sender; 173 | if (registrations[receiver] == address(0)) { 174 | emit NewRegistration(msg.sender, sender, receiver); 175 | } else { 176 | emit RegistrationUpdated(msg.sender, sender, receiver); 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /certora/munged/dependencies/polygon/CustomPolygonMapping.sol: -------------------------------------------------------------------------------- 1 | /** 2 | *Submitted for verification at Etherscan.io on 2020-05-30 3 | */ 4 | 5 | /** 6 | Matic network contracts 7 | */ 8 | 9 | pragma solidity ^0.5.2; 10 | 11 | contract Ownable { 12 | address private _owner; 13 | 14 | event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); 15 | 16 | /** 17 | * @dev The Ownable constructor sets the original `owner` of the contract to the sender 18 | * account. 19 | */ 20 | constructor() internal { 21 | _owner = msg.sender; 22 | emit OwnershipTransferred(address(0), _owner); 23 | } 24 | 25 | /** 26 | * @return the address of the owner. 27 | */ 28 | function owner() public view returns (address) { 29 | return _owner; 30 | } 31 | 32 | /** 33 | * @dev Throws if called by any account other than the owner. 34 | */ 35 | modifier onlyOwner() { 36 | require(isOwner()); 37 | _; 38 | } 39 | 40 | /** 41 | * @return true if `msg.sender` is the owner of the contract. 42 | */ 43 | function isOwner() public view returns (bool) { 44 | return msg.sender == _owner; 45 | } 46 | 47 | /** 48 | * @dev Allows the current owner to relinquish control of the contract. 49 | * It will not be possible to call the functions with the `onlyOwner` 50 | * modifier anymore. 51 | * @notice Renouncing ownership will leave the contract without an owner, 52 | * thereby removing any functionality that is only available to the owner. 53 | */ 54 | function renounceOwnership() public onlyOwner { 55 | emit OwnershipTransferred(_owner, address(0)); 56 | _owner = address(0); 57 | } 58 | 59 | /** 60 | * @dev Allows the current owner to transfer control of the contract to a newOwner. 61 | * @param newOwner The address to transfer ownership to. 62 | */ 63 | function transferOwnership(address newOwner) public onlyOwner { 64 | _transferOwnership(newOwner); 65 | } 66 | 67 | /** 68 | * @dev Transfers control of the contract to a newOwner. 69 | * @param newOwner The address to transfer ownership to. 70 | */ 71 | function _transferOwnership(address newOwner) internal { 72 | require(newOwner != address(0)); 73 | emit OwnershipTransferred(_owner, newOwner); 74 | _owner = newOwner; 75 | } 76 | } 77 | 78 | library SafeMath { 79 | /** 80 | * @dev Multiplies two unsigned integers, reverts on overflow. 81 | */ 82 | function mul(uint256 a, uint256 b) internal pure returns (uint256) { 83 | // Gas optimization: this is cheaper than requiring 'a' not being zero, but the 84 | // benefit is lost if 'b' is also tested. 85 | // See: https://github.com/OpenZeppelin/openzeppelin-solidity/pull/522 86 | if (a == 0) { 87 | return 0; 88 | } 89 | 90 | uint256 c = a * b; 91 | require(c / a == b); 92 | 93 | return c; 94 | } 95 | 96 | /** 97 | * @dev Integer division of two unsigned integers truncating the quotient, reverts on division by zero. 98 | */ 99 | function div(uint256 a, uint256 b) internal pure returns (uint256) { 100 | // Solidity only automatically asserts when dividing by 0 101 | require(b > 0); 102 | uint256 c = a / b; 103 | // assert(a == b * c + a % b); // There is no case in which this doesn't hold 104 | 105 | return c; 106 | } 107 | 108 | /** 109 | * @dev Subtracts two unsigned integers, reverts on overflow (i.e. if subtrahend is greater than minuend). 110 | */ 111 | function sub(uint256 a, uint256 b) internal pure returns (uint256) { 112 | require(b <= a); 113 | uint256 c = a - b; 114 | 115 | return c; 116 | } 117 | 118 | /** 119 | * @dev Adds two unsigned integers, reverts on overflow. 120 | */ 121 | function add(uint256 a, uint256 b) internal pure returns (uint256) { 122 | uint256 c = a + b; 123 | require(c >= a); 124 | 125 | return c; 126 | } 127 | 128 | /** 129 | * @dev Divides two unsigned integers and returns the remainder (unsigned integer modulo), 130 | * reverts when dividing by zero. 131 | */ 132 | function mod(uint256 a, uint256 b) internal pure returns (uint256) { 133 | require(b != 0); 134 | return a % b; 135 | } 136 | } 137 | 138 | contract CustomPolygonMapping is Ownable { 139 | using SafeMath for uint256; 140 | 141 | uint256 public counter; 142 | mapping(address => address) public registrations; 143 | 144 | event NewRegistration(address indexed user, address indexed sender, address indexed receiver); 145 | event RegistrationUpdated(address indexed user, address indexed sender, address indexed receiver); 146 | event StateSynced(uint256 indexed id, address indexed contractAddress, bytes data); 147 | 148 | modifier onlyRegistered(address receiver) { 149 | require(registrations[receiver] == msg.sender, 'Invalid sender'); 150 | _; 151 | } 152 | 153 | function syncState(address receiver, bytes calldata data) external onlyRegistered(receiver) { 154 | counter = counter.add(1); 155 | emit StateSynced(counter, receiver, data); 156 | // THIS IS THE ONLY CUSTOM PART 157 | bool success; 158 | bytes memory resultData; 159 | (success, resultData) = receiver.call( 160 | abi.encodeWithSignature('onStateReceive(uint256,bytes)', counter, data) 161 | ); 162 | require(success, 'FAILED_ACTION_EXECUTION_CUSTOM_MAPPING'); 163 | // END THE CUSTOM PART 164 | } 165 | 166 | // register new contract for state sync 167 | function register(address sender, address receiver) public { 168 | require( 169 | isOwner() || registrations[receiver] == msg.sender, 170 | 'StateSender.register: Not authorized to register' 171 | ); 172 | registrations[receiver] = sender; 173 | if (registrations[receiver] == address(0)) { 174 | emit NewRegistration(msg.sender, sender, receiver); 175 | } else { 176 | emit RegistrationUpdated(msg.sender, sender, receiver); 177 | } 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /helpers/polygon-helpers.ts: -------------------------------------------------------------------------------- 1 | import { DRE } from './misc-utils'; 2 | import { BigNumber } from 'ethers'; 3 | import { 4 | PolygonBridgeExecutor, 5 | PolygonBridgeExecutor__factory, 6 | PolygonMarketUpdate, 7 | PolygonMarketUpdate__factory, 8 | } from '../typechain'; 9 | import ContractAddresses from '../contractAddresses.json'; 10 | import { getMnemonicSigner } from './wallet-helpers'; 11 | 12 | let actionsSet; 13 | 14 | export const getMumbaiBlocktime = async (): Promise => { 15 | const net: any = DRE.config.networks.mumbai; 16 | const mumbaiProvider = new DRE.ethers.providers.JsonRpcProvider(net.url); 17 | const blockNumber = await mumbaiProvider.getBlockNumber(); 18 | const block = await mumbaiProvider.getBlock(blockNumber); 19 | return block.timestamp; 20 | }; 21 | 22 | export const getMumbaiBlocknumber = async (): Promise => { 23 | const net: any = DRE.config.networks.mumbai; 24 | const mumbaiProvider = new DRE.ethers.providers.JsonRpcProvider(net.url); 25 | return mumbaiProvider.getBlockNumber(); 26 | }; 27 | 28 | export const initPolygonMarketUpdateContract = async (): Promise => { 29 | let aaveWhaleSigner = getMnemonicSigner(1); 30 | const net: any = DRE.config.networks.mumbai; 31 | const mumbaiProvider = new DRE.ethers.providers.JsonRpcProvider(net.url); 32 | aaveWhaleSigner = aaveWhaleSigner.connect(mumbaiProvider); 33 | const polygonMarketUpdateContract = PolygonMarketUpdate__factory.connect( 34 | ContractAddresses.marketUpdate, 35 | aaveWhaleSigner 36 | ); 37 | return polygonMarketUpdateContract; 38 | }; 39 | 40 | export const initPolygonBridgeExecutor = async (): Promise => { 41 | let aaveWhaleSigner = getMnemonicSigner(1); 42 | const net: any = DRE.config.networks.mumbai; 43 | const mumbaiProvider = new DRE.ethers.providers.JsonRpcProvider(net.url); 44 | aaveWhaleSigner = aaveWhaleSigner.connect(mumbaiProvider); 45 | const polygonBridgeExecutor = PolygonBridgeExecutor__factory.connect( 46 | ContractAddresses.polygonBridgeExecutor, 47 | aaveWhaleSigner 48 | ); 49 | return polygonBridgeExecutor; 50 | }; 51 | 52 | export const listenForActionsQueued = async ( 53 | polygonBridgeExecutor: PolygonBridgeExecutor 54 | ): Promise => { 55 | console.log(`Creating ActionsQueued Listener...`); 56 | polygonBridgeExecutor.once( 57 | 'ActionsSetQueued', 58 | async (id, targets, values, signatures, calldatas, withDelegatecalls, executionTime) => { 59 | console.log(`\n\nActionsSetQueued Event Received`); 60 | console.log(` --- From Event --- `); 61 | console.log(`actionsSetId: ${id.toString()}`); 62 | console.log(`targets: ${targets}`); 63 | console.log(`values: ${values}`); 64 | console.log(`signatures: ${signatures}`); 65 | console.log(`calldatas: ${calldatas}`); 66 | console.log(`withDelegatecalls ${withDelegatecalls}`); 67 | console.log(`executionTime: ${executionTime.toString()}`); 68 | console.log(`------------------- \n`); 69 | 70 | console.log(`Checking Polygon Contract State....`); 71 | await getActionsSetById(polygonBridgeExecutor, id); 72 | } 73 | ); 74 | }; 75 | 76 | export const getActionsSetById = async ( 77 | polygonBridgeExecutor: PolygonBridgeExecutor, 78 | actionsSetId: BigNumber 79 | ): Promise => { 80 | actionsSet = await polygonBridgeExecutor.getActionsSetById(actionsSetId); 81 | console.log(` --- From Contract State --- `); 82 | console.log(`actionsSetId: ${actionsSet.id.toString()}`); 83 | console.log(`targets: ${actionsSet.targets}`); 84 | console.log(`values: ${actionsSet[2]}`); 85 | console.log(`signatures: ${actionsSet.signatures}`); 86 | console.log(`calldatas: ${actionsSet.calldatas}`); 87 | console.log(`withDelegatecalls ${actionsSet.withDelegatecalls}`); 88 | console.log(`executionTime: ${actionsSet.executionTime.toString()}`); 89 | console.log(`canceled: ${actionsSet.canceled}`); 90 | console.log(`executed: ${actionsSet.executed}`); 91 | console.log(`------------------- \n`); 92 | }; 93 | 94 | export const listenForUpdateExecuted = async ( 95 | polygonMarketUpdate: PolygonMarketUpdate 96 | ): Promise => { 97 | console.log(`Creating Polygon Market Update Listener...`); 98 | polygonMarketUpdate.once('UpdateExecuted', async (counter, testInt, testAddress) => { 99 | console.log(`\n\nUpdateExecuted Event Received`); 100 | console.log(` --- From Event --- `); 101 | console.log(`counter: ${counter.toString()}`); 102 | console.log(`testInt: ${testInt.toString()}`); 103 | console.log(`testAddress: ${testAddress}`); 104 | console.log(`------------------- \n`); 105 | 106 | console.log(`Checking Polygon Contract State....`); 107 | console.log(`Contract Counter Value: ${(await polygonMarketUpdate.getCounter()).toString()}`); 108 | console.log(`Contract Integer Value: ${(await polygonMarketUpdate.getTestInt()).toString()}`); 109 | }); 110 | }; 111 | 112 | export const listenForNewDelay = async ( 113 | polygonBridgeExecutor: PolygonBridgeExecutor 114 | ): Promise => { 115 | console.log(`Creating Polygon Market Update Listener...`); 116 | polygonBridgeExecutor.once('NewDelay', async (delay) => { 117 | console.log(`\n\nNewDelay Event Received`); 118 | console.log(` --- From Event --- `); 119 | console.log(`delay: ${delay.toString()}`); 120 | console.log(`------------------- \n`); 121 | 122 | console.log(`Checking Polygon Contract State....`); 123 | console.log(`Contract Counter Value: ${(await polygonBridgeExecutor.getDelay()).toString()}`); 124 | }); 125 | }; 126 | 127 | export const getActionsSetId = (): BigNumber => { 128 | return actionsSet.id; 129 | }; 130 | export const getActionsExecutionTime = (): BigNumber => { 131 | return actionsSet.executionTime; 132 | }; 133 | -------------------------------------------------------------------------------- /contracts/dependencies/arbitrum/interfaces/ISequencerInbox.sol: -------------------------------------------------------------------------------- 1 | // Copyright 2021-2022, Offchain Labs, Inc. 2 | // For license information, see https://github.com/nitro/blob/master/LICENSE 3 | // SPDX-License-Identifier: BUSL-1.1 4 | 5 | // solhint-disable-next-line compiler-version 6 | pragma solidity >=0.6.9 <0.9.0; 7 | pragma experimental ABIEncoderV2; 8 | 9 | import '../libraries/IGasRefunder.sol'; 10 | import './IDelayedMessageProvider.sol'; 11 | import './IBridge.sol'; 12 | 13 | interface ISequencerInbox is IDelayedMessageProvider { 14 | struct MaxTimeVariation { 15 | uint256 delayBlocks; 16 | uint256 futureBlocks; 17 | uint256 delaySeconds; 18 | uint256 futureSeconds; 19 | } 20 | 21 | struct TimeBounds { 22 | uint64 minTimestamp; 23 | uint64 maxTimestamp; 24 | uint64 minBlockNumber; 25 | uint64 maxBlockNumber; 26 | } 27 | 28 | enum BatchDataLocation { 29 | TxInput, 30 | SeparateBatchEvent, 31 | NoData 32 | } 33 | 34 | event SequencerBatchDelivered( 35 | uint256 indexed batchSequenceNumber, 36 | bytes32 indexed beforeAcc, 37 | bytes32 indexed afterAcc, 38 | bytes32 delayedAcc, 39 | uint256 afterDelayedMessagesRead, 40 | TimeBounds timeBounds, 41 | BatchDataLocation dataLocation 42 | ); 43 | 44 | event OwnerFunctionCalled(uint256 indexed id); 45 | 46 | /// @dev a separate event that emits batch data when this isn't easily accessible in the tx.input 47 | event SequencerBatchData(uint256 indexed batchSequenceNumber, bytes data); 48 | 49 | /// @dev a valid keyset was added 50 | event SetValidKeyset(bytes32 indexed keysetHash, bytes keysetBytes); 51 | 52 | /// @dev a keyset was invalidated 53 | event InvalidateKeyset(bytes32 indexed keysetHash); 54 | 55 | function totalDelayedMessagesRead() external view returns (uint256); 56 | 57 | function bridge() external view returns (IBridge); 58 | 59 | /// @dev The size of the batch header 60 | // solhint-disable-next-line func-name-mixedcase 61 | function HEADER_LENGTH() external view returns (uint256); 62 | 63 | /// @dev If the first batch data byte after the header has this bit set, 64 | /// the sequencer inbox has authenticated the data. Currently not used. 65 | // solhint-disable-next-line func-name-mixedcase 66 | function DATA_AUTHENTICATED_FLAG() external view returns (bytes1); 67 | 68 | function rollup() external view returns (IOwnable); 69 | 70 | function isBatchPoster(address) external view returns (bool); 71 | 72 | struct DasKeySetInfo { 73 | bool isValidKeyset; 74 | uint64 creationBlock; 75 | } 76 | 77 | // https://github.com/ethereum/solidity/issues/11826 78 | // function maxTimeVariation() external view returns (MaxTimeVariation calldata); 79 | // function dasKeySetInfo(bytes32) external view returns (DasKeySetInfo calldata); 80 | 81 | /// @notice Force messages from the delayed inbox to be included in the chain 82 | /// Callable by any address, but message can only be force-included after maxTimeVariation.delayBlocks and 83 | /// maxTimeVariation.delaySeconds has elapsed. As part of normal behaviour the sequencer will include these 84 | /// messages so it's only necessary to call this if the sequencer is down, or not including any delayed messages. 85 | /// @param _totalDelayedMessagesRead The total number of messages to read up to 86 | /// @param kind The kind of the last message to be included 87 | /// @param l1BlockAndTime The l1 block and the l1 timestamp of the last message to be included 88 | /// @param baseFeeL1 The l1 gas price of the last message to be included 89 | /// @param sender The sender of the last message to be included 90 | /// @param messageDataHash The messageDataHash of the last message to be included 91 | function forceInclusion( 92 | uint256 _totalDelayedMessagesRead, 93 | uint8 kind, 94 | uint64[2] calldata l1BlockAndTime, 95 | uint256 baseFeeL1, 96 | address sender, 97 | bytes32 messageDataHash 98 | ) external; 99 | 100 | function inboxAccs(uint256 index) external view returns (bytes32); 101 | 102 | function batchCount() external view returns (uint256); 103 | 104 | function isValidKeysetHash(bytes32 ksHash) external view returns (bool); 105 | 106 | /// @notice the creation block is intended to still be available after a keyset is deleted 107 | function getKeysetCreationBlock(bytes32 ksHash) external view returns (uint256); 108 | 109 | // ---------- BatchPoster functions ---------- 110 | 111 | function addSequencerL2BatchFromOrigin( 112 | uint256 sequenceNumber, 113 | bytes calldata data, 114 | uint256 afterDelayedMessagesRead, 115 | IGasRefunder gasRefunder 116 | ) external; 117 | 118 | function addSequencerL2Batch( 119 | uint256 sequenceNumber, 120 | bytes calldata data, 121 | uint256 afterDelayedMessagesRead, 122 | IGasRefunder gasRefunder 123 | ) external; 124 | 125 | // ---------- onlyRollupOrOwner functions ---------- 126 | 127 | /** 128 | * @notice Set max delay for sequencer inbox 129 | * @param maxTimeVariation_ the maximum time variation parameters 130 | */ 131 | function setMaxTimeVariation(MaxTimeVariation memory maxTimeVariation_) external; 132 | 133 | /** 134 | * @notice Updates whether an address is authorized to be a batch poster at the sequencer inbox 135 | * @param addr the address 136 | * @param isBatchPoster_ if the specified address should be authorized as a batch poster 137 | */ 138 | function setIsBatchPoster(address addr, bool isBatchPoster_) external; 139 | 140 | /** 141 | * @notice Makes Data Availability Service keyset valid 142 | * @param keysetBytes bytes of the serialized keyset 143 | */ 144 | function setValidKeyset(bytes calldata keysetBytes) external; 145 | 146 | /** 147 | * @notice Invalidates a Data Availability Service keyset 148 | * @param ksHash hash of the keyset 149 | */ 150 | function invalidateKeysetHash(bytes32 ksHash) external; 151 | 152 | // ---------- initializer ---------- 153 | 154 | function initialize(IBridge bridge_, MaxTimeVariation calldata maxTimeVariation_) external; 155 | } 156 | -------------------------------------------------------------------------------- /hardhat.config.ts: -------------------------------------------------------------------------------- 1 | import fs from 'fs'; 2 | import path from 'path'; 3 | import { config } from 'dotenv'; 4 | import { HardhatUserConfig } from 'hardhat/types'; 5 | import '@typechain/hardhat'; 6 | import '@typechain/ethers-v5'; 7 | import '@nomiclabs/hardhat-ethers'; 8 | import '@nomiclabs/hardhat-waffle'; 9 | import '@nomiclabs/hardhat-etherscan'; 10 | import '@tenderly/hardhat-tenderly'; 11 | import 'hardhat-deploy'; 12 | import 'hardhat-gas-reporter'; 13 | import 'hardhat-dependency-compiler'; 14 | import 'hardhat-contract-sizer'; 15 | import 'solidity-coverage'; 16 | 17 | config(); 18 | 19 | import { accounts } from './helpers/test-wallets'; 20 | import { 21 | eArbitrumNetwork, 22 | eEthereumNetwork, 23 | eNetwork, 24 | eOptimismNetwork, 25 | ePolygonNetwork, 26 | eXDaiNetwork, 27 | } from './helpers/types'; 28 | import { NETWORKS_RPC_URL } from './helper-hardhat-config'; 29 | 30 | const SKIP_LOAD = process.env.SKIP_LOAD === 'true'; 31 | const TASK_FOLDERS = ['deploy', 'governance', 'l2', 'misc', 'setup', 'verify']; 32 | if (!SKIP_LOAD) { 33 | TASK_FOLDERS.forEach((folder) => { 34 | const tasksPath = path.join(__dirname, './tasks', folder); 35 | fs.readdirSync(tasksPath) 36 | .filter((pth) => pth.includes('.ts') || pth.includes('.js')) 37 | .forEach((task) => { 38 | require(`${tasksPath}/${task}`); 39 | }); 40 | }); 41 | } 42 | 43 | const MNEMONIC_PATH = "m/44'/60'/0'/0"; 44 | const MNEMONIC = process.env.MNEMONIC || ''; 45 | const PRIVATE_KEY = process.env.PRIVATE_KEY || ''; 46 | const MAINNET_FORK = process.env.MAINNET_FORK === 'true'; 47 | const FORKING_BLOCK_NUMBER = process.env.FORKING_BLOCK_NUMBER; 48 | const ARBISCAN_KEY = process.env.ARBISCAN_KEY || ''; 49 | const OPTIMISTIC_ETHERSCAN_KEY = process.env.OPTIMISTIC_ETHERSCAN_KEY || ''; 50 | const TENDERLY_PROJECT = process.env.TENDERLY_PROJECT || ''; 51 | const TENDERLY_USERNAME = process.env.TENDERLY_USERNAME || ''; 52 | 53 | // Use of mnemonic over private key if mnemonic is provided 54 | const accountsToUse = 55 | PRIVATE_KEY == '' 56 | ? { 57 | mnemonic: MNEMONIC, 58 | path: MNEMONIC_PATH, 59 | initialIndex: 0, 60 | count: 20, 61 | } 62 | : [PRIVATE_KEY]; 63 | 64 | const getCommonNetworkConfig = (networkName: eNetwork, chainId: number) => ({ 65 | url: NETWORKS_RPC_URL[networkName], 66 | chainId, 67 | accounts: accountsToUse, 68 | }); 69 | 70 | const mainnetFork = MAINNET_FORK 71 | ? { 72 | blockNumber: FORKING_BLOCK_NUMBER ? Number.parseInt(FORKING_BLOCK_NUMBER) : 14340480, 73 | url: NETWORKS_RPC_URL['main'], 74 | } 75 | : undefined; 76 | 77 | const hardhatConfig: HardhatUserConfig = { 78 | typechain: { 79 | outDir: 'typechain', 80 | target: 'ethers-v5', 81 | }, 82 | namedAccounts: { 83 | deployer: 0, 84 | }, 85 | gasReporter: { 86 | enabled: process.env.REPORT_GAS ? true : false, 87 | }, 88 | solidity: { 89 | compilers: [ 90 | { 91 | version: '0.8.10', 92 | settings: { optimizer: { enabled: true, runs: 200 } }, 93 | }, 94 | { 95 | version: '0.7.5', 96 | settings: { 97 | optimizer: { 98 | enabled: true, 99 | runs: 200, 100 | details: { 101 | yul: true, 102 | }, 103 | }, 104 | }, 105 | }, 106 | { version: '0.7.3', settings: { optimizer: { enabled: true, runs: 200 } } }, 107 | { version: '0.5.2', settings: { optimizer: { enabled: true, runs: 200 } } }, 108 | ], 109 | }, 110 | etherscan: { 111 | apiKey: { 112 | optimisticEthereum: OPTIMISTIC_ETHERSCAN_KEY, 113 | arbitrumOne: ARBISCAN_KEY, 114 | }, 115 | }, 116 | tenderly: { 117 | project: TENDERLY_PROJECT, 118 | username: TENDERLY_USERNAME, 119 | forkNetwork: '137', 120 | }, 121 | mocha: { 122 | timeout: 100000, 123 | }, 124 | networks: { 125 | kovan: { 126 | ...getCommonNetworkConfig(eEthereumNetwork.kovan, 42), 127 | companionNetworks: { 128 | optimism: eOptimismNetwork.testnet, 129 | }, 130 | }, 131 | ropsten: getCommonNetworkConfig(eEthereumNetwork.ropsten, 3), 132 | rinkeby: { 133 | ...getCommonNetworkConfig(eEthereumNetwork.rinkeby, 4), 134 | companionNetworks: { 135 | arbitrum: eArbitrumNetwork.arbitrumTestnet, 136 | }, 137 | }, 138 | goerli: getCommonNetworkConfig(eEthereumNetwork.goerli, 5), 139 | main: { 140 | ...getCommonNetworkConfig(eEthereumNetwork.main, 1), 141 | companionNetworks: { 142 | optimism: eOptimismNetwork.main, 143 | arbitrum: eArbitrumNetwork.arbitrum, 144 | }, 145 | }, 146 | tenderlyMain: getCommonNetworkConfig(eEthereumNetwork.tenderlyMain, 5), 147 | matic: getCommonNetworkConfig(ePolygonNetwork.matic, 137), 148 | mumbai: getCommonNetworkConfig(ePolygonNetwork.mumbai, 80001), 149 | xdai: getCommonNetworkConfig(eXDaiNetwork.xdai, 100), 150 | [eArbitrumNetwork.arbitrum]: getCommonNetworkConfig(eArbitrumNetwork.arbitrum, 42161), 151 | [eArbitrumNetwork.arbitrumTestnet]: { 152 | ...getCommonNetworkConfig(eArbitrumNetwork.arbitrumTestnet, 421611), 153 | companionNetworks: { 154 | l1: 'rinkeby', 155 | }, 156 | }, 157 | [eOptimismNetwork.main]: getCommonNetworkConfig(eOptimismNetwork.main, 10), 158 | [eOptimismNetwork.testnet]: { 159 | ...getCommonNetworkConfig(eOptimismNetwork.testnet, 69), 160 | companionNetworks: { 161 | l1: 'kovan', 162 | }, 163 | }, 164 | hardhat: { 165 | accounts: accounts.map(({ secretKey, balance }: { secretKey: string; balance: string }) => ({ 166 | privateKey: secretKey, 167 | balance, 168 | })), 169 | throwOnTransactionFailures: true, 170 | throwOnCallFailures: true, 171 | forking: mainnetFork, 172 | }, 173 | }, 174 | dependencyCompiler: { 175 | paths: [ 176 | '@aave/governance-v2/contracts/governance/AaveGovernanceV2.sol', 177 | '@aave/governance-v2/contracts/governance/Executor.sol', 178 | ], 179 | }, 180 | }; 181 | 182 | export default hardhatConfig; 183 | -------------------------------------------------------------------------------- /certora/harness/L2BridgeExecutorHarness.sol: -------------------------------------------------------------------------------- 1 | // SPDX-License-Identifier: AGPL-3.0 2 | pragma solidity ^0.8.10; 3 | 4 | import {L2BridgeExecutor} from '../munged/bridges/L2BridgeExecutor.sol'; 5 | 6 | /** 7 | * @title BridgeExecutorBase 8 | * @author Aave 9 | * @notice Abstract contract that implements basic executor functionality 10 | * @dev It does not implement an external `queue` function. This should instead be done in the inheriting 11 | * contract with proper access control 12 | */ 13 | abstract contract L2BridgeExecutorHarness is L2BridgeExecutor { 14 | modifier onlyEthereumGovernanceExecutor() override virtual; 15 | /** 16 | * @dev Constructor 17 | * 18 | * @param ethereumGovernanceExecutor The address of the EthereumGovernanceExecutor 19 | * @param delay The delay before which an actions set can be executed 20 | * @param gracePeriod The time period after a delay during which an actions set can be executed 21 | * @param minimumDelay The minimum bound a delay can be set to 22 | * @param maximumDelay The maximum bound a delay can be set to 23 | * @param guardian The address of the guardian, which can cancel queued proposals (can be zero) 24 | */ 25 | constructor( 26 | address ethereumGovernanceExecutor, 27 | uint256 delay, 28 | uint256 gracePeriod, 29 | uint256 minimumDelay, 30 | uint256 maximumDelay, 31 | address guardian 32 | ) L2BridgeExecutor(ethereumGovernanceExecutor, delay, gracePeriod, 33 | minimumDelay, maximumDelay, guardian){} 34 | 35 | /* 36 | * @notice Queue an ActionsSet 37 | * @dev If a signature is empty, calldata is used for the execution, calldata is appended to signature otherwise 38 | * @param targets Array of targets to be called by the actions set 39 | * @param values Array of values to pass in each call by the actions set 40 | * @param signatures Array of function signatures to encode in each call (can be empty) 41 | * @param calldatas Array of calldata to pass in each call (can be empty) 42 | * @param withDelegatecalls Array of whether to delegatecall for each call 43 | **/ 44 | 45 | function queue2( 46 | address[2] memory targets, 47 | uint256[2] memory values, 48 | string[2] memory signatures, 49 | bytes[2] memory calldatas, 50 | bool[2] memory withDelegatecalls 51 | ) external onlyEthereumGovernanceExecutor { 52 | _queue2(targets, values, signatures, calldatas, withDelegatecalls); 53 | } 54 | 55 | function _queue2( 56 | address[2] memory targets, 57 | uint256[2] memory values, 58 | string[2] memory signatures, 59 | bytes[2] memory calldatas, 60 | bool[2] memory withDelegatecalls 61 | ) internal { 62 | if (targets.length == 0) revert EmptyTargets(); 63 | uint256 targetsLength = targets.length; 64 | if ( 65 | targetsLength != values.length || 66 | targetsLength != signatures.length || 67 | targetsLength != calldatas.length || 68 | targetsLength != withDelegatecalls.length 69 | ) revert InconsistentParamsLength(); 70 | 71 | uint256 actionsSetId = _actionsSetCounter; 72 | uint256 executionTime = block.timestamp + _delay; 73 | unchecked { 74 | ++_actionsSetCounter; 75 | } 76 | 77 | for (uint256 i = 0; i < targetsLength; ) { 78 | bytes32 actionHash = keccak256( 79 | abi.encode( 80 | targets[i], 81 | values[i], 82 | executionTime 83 | ) 84 | ); 85 | if (isActionQueued(actionHash)) revert DuplicateAction(); 86 | _queuedActions[actionHash] = true; 87 | unchecked { 88 | ++i; 89 | } 90 | } 91 | 92 | ActionsSet storage actionsSet = _actionsSets[actionsSetId]; 93 | actionsSet.targets = targets; 94 | actionsSet.values = values; 95 | //actionsSet.signatures = signatures; 96 | //actionsSet.calldatas = calldatas; 97 | //actionsSet.withDelegatecalls = withDelegatecalls; 98 | actionsSet.executionTime = executionTime; 99 | 100 | for (uint256 j = 0; j < targetsLength; ) { 101 | actionsSet.withDelegatecalls[j] = withDelegatecalls[j]; 102 | unchecked { 103 | ++j; 104 | } 105 | } 106 | } 107 | 108 | // Certora : add getters 109 | function getActionsSetLength(uint256 actionsSetId) 110 | public view returns (uint256) 111 | { 112 | return _actionsSets[actionsSetId].targets.length; 113 | } 114 | 115 | function getActionsSetExecutionTime(uint256 actionsSetId) 116 | public view returns (uint256) 117 | { 118 | return _actionsSets[actionsSetId].executionTime; 119 | } 120 | 121 | function getActionsSetTarget(uint256 actionsSetId, uint256 i) 122 | public view returns (address) 123 | { 124 | return _actionsSets[actionsSetId].targets[i]; 125 | } 126 | 127 | function getActionSetWithDelegate(uint256 actionsSetId, uint256 i) 128 | public view returns (bool) 129 | { 130 | return _actionsSets[actionsSetId].withDelegatecalls[i]; 131 | } 132 | 133 | function getActionsSetCalldata(uint256 actionsSetId, uint256 i) 134 | public view returns (bytes memory) 135 | { 136 | return _actionsSets[actionsSetId].calldatas[i]; 137 | } 138 | 139 | function getActionsSetCanceled(uint256 actionsSetId) 140 | public view returns(bool) 141 | { 142 | return _actionsSets[actionsSetId].canceled; 143 | } 144 | 145 | function getActionsSetExecuted(uint256 actionsSetId) 146 | public view returns(bool) 147 | { 148 | return _actionsSets[actionsSetId].executed; 149 | } 150 | 151 | function noDelegateCalls(uint256 actionsSetId) external onlyThis 152 | { 153 | uint256 length = getActionsSetLength(actionsSetId); 154 | for (uint256 i = 0; i < length; ) { 155 | _actionsSets[actionsSetId].withDelegatecalls[i] = false; 156 | unchecked { 157 | ++i; 158 | } 159 | } 160 | } 161 | 162 | function ID2actionHash(uint256 actionsSetId, uint i) public view returns (bytes32) 163 | { 164 | ActionsSet storage actionsSet = _actionsSets[actionsSetId]; 165 | return keccak256( 166 | abi.encode(actionsSet.targets[i], actionsSet.values[i], 167 | actionsSet.executionTime)); 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /contracts/dependencies/arbitrum/interfaces/IInbox.sol: -------------------------------------------------------------------------------- 1 | // Copyright 2021-2022, Offchain Labs, Inc. 2 | // For license information, see https://github.com/nitro/blob/master/LICENSE 3 | // SPDX-License-Identifier: BUSL-1.1 4 | 5 | // solhint-disable-next-line compiler-version 6 | pragma solidity >=0.6.9 <0.9.0; 7 | 8 | import './IBridge.sol'; 9 | import './IDelayedMessageProvider.sol'; 10 | import './ISequencerInbox.sol'; 11 | 12 | interface IInbox is IDelayedMessageProvider { 13 | function bridge() external view returns (IBridge); 14 | 15 | function sequencerInbox() external view returns (ISequencerInbox); 16 | 17 | /** 18 | * @notice Send a generic L2 message to the chain 19 | * @dev This method is an optimization to avoid having to emit the entirety of the messageData in a log. Instead validators are expected to be able to parse the data from the transaction's input 20 | * @param messageData Data of the message being sent 21 | */ 22 | function sendL2MessageFromOrigin(bytes calldata messageData) external returns (uint256); 23 | 24 | /** 25 | * @notice Send a generic L2 message to the chain 26 | * @dev This method can be used to send any type of message that doesn't require L1 validation 27 | * @param messageData Data of the message being sent 28 | */ 29 | function sendL2Message(bytes calldata messageData) external returns (uint256); 30 | 31 | function sendL1FundedUnsignedTransaction( 32 | uint256 gasLimit, 33 | uint256 maxFeePerGas, 34 | uint256 nonce, 35 | address to, 36 | bytes calldata data 37 | ) external payable returns (uint256); 38 | 39 | function sendL1FundedContractTransaction( 40 | uint256 gasLimit, 41 | uint256 maxFeePerGas, 42 | address to, 43 | bytes calldata data 44 | ) external payable returns (uint256); 45 | 46 | function sendUnsignedTransaction( 47 | uint256 gasLimit, 48 | uint256 maxFeePerGas, 49 | uint256 nonce, 50 | address to, 51 | uint256 value, 52 | bytes calldata data 53 | ) external returns (uint256); 54 | 55 | function sendContractTransaction( 56 | uint256 gasLimit, 57 | uint256 maxFeePerGas, 58 | address to, 59 | uint256 value, 60 | bytes calldata data 61 | ) external returns (uint256); 62 | 63 | /** 64 | * @notice Get the L1 fee for submitting a retryable 65 | * @dev This fee can be paid by funds already in the L2 aliased address or by the current message value 66 | * @dev This formula may change in the future, to future proof your code query this method instead of inlining!! 67 | * @param dataLength The length of the retryable's calldata, in bytes 68 | * @param baseFee The block basefee when the retryable is included in the chain, if 0 current block.basefee will be used 69 | */ 70 | function calculateRetryableSubmissionFee(uint256 dataLength, uint256 baseFee) 71 | external 72 | view 73 | returns (uint256); 74 | 75 | /** 76 | * @notice Deposit eth from L1 to L2 77 | * @dev This does not trigger the fallback function when receiving in the L2 side. 78 | * Look into retryable tickets if you are interested in this functionality. 79 | * @dev This function should not be called inside contract constructors 80 | */ 81 | function depositEth() external payable returns (uint256); 82 | 83 | /** 84 | * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts 85 | * @dev all msg.value will deposited to callValueRefundAddress on L2 86 | * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error 87 | * @param to destination L2 contract address 88 | * @param l2CallValue call value for retryable L2 message 89 | * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee 90 | * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance 91 | * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled 92 | * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) 93 | * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) 94 | * @param data ABI encoded data of L2 message 95 | * @return unique message number of the retryable transaction 96 | */ 97 | function createRetryableTicket( 98 | address to, 99 | uint256 l2CallValue, 100 | uint256 maxSubmissionCost, 101 | address excessFeeRefundAddress, 102 | address callValueRefundAddress, 103 | uint256 gasLimit, 104 | uint256 maxFeePerGas, 105 | bytes calldata data 106 | ) external payable returns (uint256); 107 | 108 | /** 109 | * @notice Put a message in the L2 inbox that can be reexecuted for some fixed amount of time if it reverts 110 | * @dev Same as createRetryableTicket, but does not guarantee that submission will succeed by requiring the needed funds 111 | * come from the deposit alone, rather than falling back on the user's L2 balance 112 | * @dev Advanced usage only (does not rewrite aliases for excessFeeRefundAddress and callValueRefundAddress). 113 | * createRetryableTicket method is the recommended standard. 114 | * @dev Gas limit and maxFeePerGas should not be set to 1 as that is used to trigger the RetryableData error 115 | * @param to destination L2 contract address 116 | * @param l2CallValue call value for retryable L2 message 117 | * @param maxSubmissionCost Max gas deducted from user's L2 balance to cover base submission fee 118 | * @param excessFeeRefundAddress gasLimit x maxFeePerGas - execution cost gets credited here on L2 balance 119 | * @param callValueRefundAddress l2Callvalue gets credited here on L2 if retryable txn times out or gets cancelled 120 | * @param gasLimit Max gas deducted from user's L2 balance to cover L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) 121 | * @param maxFeePerGas price bid for L2 execution. Should not be set to 1 (magic value used to trigger the RetryableData error) 122 | * @param data ABI encoded data of L2 message 123 | * @return unique message number of the retryable transaction 124 | */ 125 | function unsafeCreateRetryableTicket( 126 | address to, 127 | uint256 l2CallValue, 128 | uint256 maxSubmissionCost, 129 | address excessFeeRefundAddress, 130 | address callValueRefundAddress, 131 | uint256 gasLimit, 132 | uint256 maxFeePerGas, 133 | bytes calldata data 134 | ) external payable returns (uint256); 135 | 136 | // ---------- onlyRollupOrOwner functions ---------- 137 | 138 | /// @notice pauses all inbox functionality 139 | function pause() external; 140 | 141 | /// @notice unpauses all inbox functionality 142 | function unpause() external; 143 | 144 | // ---------- initializer ---------- 145 | 146 | /** 147 | * @dev function to be called one time during the inbox upgrade process 148 | * this is used to fix the storage slots 149 | */ 150 | function postUpgradeInit(IBridge _bridge) external; 151 | 152 | function initialize(IBridge _bridge, ISequencerInbox _sequencerInbox) external; 153 | } 154 | --------------------------------------------------------------------------------