Commit 3bc8c737 authored by mergify[bot]'s avatar mergify[bot] Committed by GitHub

Merge branch 'develop' into jm/monitoring/package

parents 2b931bc3 bd907f6c
...@@ -513,8 +513,9 @@ jobs: ...@@ -513,8 +513,9 @@ jobs:
command: | command: |
# Note: We don't use circle CI test splits because we need to split by test name, not by package. There is an additional # Note: We don't use circle CI test splits because we need to split by test name, not by package. There is an additional
# constraint that gotestsum does not currently (nor likely will) accept files from different pacakges when building. # constraint that gotestsum does not currently (nor likely will) accept files from different pacakges when building.
OP_TESTLOG_DISABLE_COLOR=true OP_E2E_DISABLE_PARALLEL=true OP_E2E_USE_HTTP=<<parameters.use_http>> gotestsum \ OP_TESTLOG_DISABLE_COLOR=true OP_E2E_DISABLE_PARALLEL=false OP_E2E_USE_HTTP=<<parameters.use_http>> gotestsum \
--format=standard-verbose --junitfile=/tmp/test-results/<<parameters.module>>_http_<<parameters.use_http>>.xml ./... --format=standard-verbose --junitfile=/tmp/test-results/<<parameters.module>>_http_<<parameters.use_http>>.xml \
-- -timeout=20m
working_directory: <<parameters.module>> working_directory: <<parameters.module>>
- store_test_results: - store_test_results:
path: /tmp/test-results path: /tmp/test-results
...@@ -830,45 +831,6 @@ workflows: ...@@ -830,45 +831,6 @@ workflows:
- contracts-bedrock-tests: - contracts-bedrock-tests:
requires: requires:
- yarn-monorepo - yarn-monorepo
- bedrock-echidna-build:
requires:
- yarn-monorepo
- bedrock-echidna-run:
name: Bedrock Echidna Alias Test
echidna_target: aliasing
requires:
- bedrock-echidna-build
- bedrock-echidna-run:
name: Bedrock Echidna Eth Burn Test
echidna_target: "burn:eth"
requires:
- bedrock-echidna-build
- bedrock-echidna-run:
name: Bedrock Echidna Gas Burn Test
echidna_target: "burn:gas"
size: 2xlarge
requires:
- bedrock-echidna-build
- bedrock-echidna-run:
name: Bedrock Echidna Encoding Test
echidna_target: encoding
requires:
- bedrock-echidna-build
- bedrock-echidna-run:
name: Bedrock Echidna Metering Test
echidna_target: metering
requires:
- bedrock-echidna-build
- bedrock-echidna-run:
name: Bedrock Echidna Hashing Test
echidna_target: hashing
requires:
- bedrock-echidna-build
- bedrock-echidna-run:
name: Bedrock Echidna Portal Test
echidna_target: portal
requires:
- bedrock-echidna-build
- op-bindings-build: - op-bindings-build:
requires: requires:
- yarn-monorepo - yarn-monorepo
......
queue_rules: queue_rules:
- name: default - name: default
speculative_checks: 3
conditions: [] conditions: []
pull_request_rules: pull_request_rules:
...@@ -11,6 +10,7 @@ pull_request_rules: ...@@ -11,6 +10,7 @@ pull_request_rules:
- "#approved-reviews-by>=1" - "#approved-reviews-by>=1"
- "#changes-requested-reviews-by=0" - "#changes-requested-reviews-by=0"
- "label!=do-not-merge" - "label!=do-not-merge"
- "label!=multiple-reviewers"
- "label!=mergify-ignore" - "label!=mergify-ignore"
- "base=develop" - "base=develop"
- or: - or:
......
...@@ -11,18 +11,18 @@ use ( ...@@ -11,18 +11,18 @@ use (
./op-batcher ./op-batcher
./op-bindings ./op-bindings
./op-chain-ops ./op-chain-ops
./op-wheel
./op-e2e ./op-e2e
./op-exporter ./op-exporter
./op-heartbeat ./op-heartbeat
./op-node ./op-node
./op-proposer ./op-proposer
./op-service ./op-service
./proxyd ./op-wheel
./packages/contracts-bedrock/test-case-generator ./packages/contracts-bedrock/test-case-generator
./proxyd
) )
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468
// For local debugging: // For local debugging:
//replace github.com/ethereum/go-ethereum v1.10.26 => ../go-ethereum //replace github.com/ethereum/go-ethereum v1.10.26 => ../go-ethereum
...@@ -50,7 +50,6 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV ...@@ -50,7 +50,6 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398 h1:WDC6ySpJzbxGWFh4aMxFFC28wwGp5pEuoTtvA4q/qQ4= github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398 h1:WDC6ySpJzbxGWFh4aMxFFC28wwGp5pEuoTtvA4q/qQ4=
github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s= github.com/Shopify/sarama v1.19.0 h1:9oksLxC6uxVPHPVYUmq6xhr1BOF/hHobWH2UzO67z1s=
github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc= github.com/Shopify/toxiproxy v2.1.4+incompatible h1:TKdv8HiTLgE5wdJuEML90aBgNWsokNbMijUGhmcoBJc=
github.com/StackExchange/wmi v0.0.0-20180116203802-5d049714c4a6 h1:fLjPD/aNc3UIOA6tDi6QXUemppXK3P9BI7mr2hd6gx8=
github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE= github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5 h1:rFw4nCn9iMW+Vajsk51NtYIcwSTkXr+JGrMd36kTDJw=
github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
...@@ -156,6 +155,8 @@ github.com/ethereum-optimism/op-geth v0.0.0-20221028165624-03aeaf2c39a5 h1:b0ySU ...@@ -156,6 +155,8 @@ github.com/ethereum-optimism/op-geth v0.0.0-20221028165624-03aeaf2c39a5 h1:b0ySU
github.com/ethereum-optimism/op-geth v0.0.0-20221028165624-03aeaf2c39a5/go.mod h1:/6CsT5Ceen2WPLI/oCA3xMcZ5sWMF/D46SjM/ayY0Oo= github.com/ethereum-optimism/op-geth v0.0.0-20221028165624-03aeaf2c39a5/go.mod h1:/6CsT5Ceen2WPLI/oCA3xMcZ5sWMF/D46SjM/ayY0Oo=
github.com/ethereum-optimism/op-geth v0.0.0-20221104125741-d6c1bb9a110d h1:rDRvYVDftip53l1igBiLa3+CLPmIH2r0of2GTArMC60= github.com/ethereum-optimism/op-geth v0.0.0-20221104125741-d6c1bb9a110d h1:rDRvYVDftip53l1igBiLa3+CLPmIH2r0of2GTArMC60=
github.com/ethereum-optimism/op-geth v0.0.0-20221104125741-d6c1bb9a110d/go.mod h1:/6CsT5Ceen2WPLI/oCA3xMcZ5sWMF/D46SjM/ayY0Oo= github.com/ethereum-optimism/op-geth v0.0.0-20221104125741-d6c1bb9a110d/go.mod h1:/6CsT5Ceen2WPLI/oCA3xMcZ5sWMF/D46SjM/ayY0Oo=
github.com/ethereum-optimism/op-geth v0.0.0-20221215174217-c69b1f12761e h1:kdpBVWv7Rs/LbM8o8QyJlEBNiA2sw1GEhGyn4pkpesw=
github.com/ethereum-optimism/op-geth v0.0.0-20221215174217-c69b1f12761e/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY=
github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses= github.com/evanphx/json-patch v4.9.0+incompatible h1:kLcOMZeuLAJvL2BPWLMIj5oaZQobrkAqrL+WFZwQses=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 h1:DddqAaWDpywytcG8w/qoQ5sAN8X12d3Z3koB0C3Rxsc= github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072 h1:DddqAaWDpywytcG8w/qoQ5sAN8X12d3Z3koB0C3Rxsc=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo= github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
...@@ -310,7 +311,6 @@ github.com/klauspost/cpuid/v2 v2.2.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8t ...@@ -310,7 +311,6 @@ github.com/klauspost/cpuid/v2 v2.2.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8t
github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6 h1:KAZ1BW2TCmT6PRihDPpocIy1QTtsAsrx6TneU/4+CMg= github.com/klauspost/crc32 v0.0.0-20161016154125-cb6bfca970f6 h1:KAZ1BW2TCmT6PRihDPpocIy1QTtsAsrx6TneU/4+CMg=
github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada h1:3L+neHp83cTjegPdCiOxVOJtRIy7/8RldvMTsyPYH10= github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada h1:3L+neHp83cTjegPdCiOxVOJtRIy7/8RldvMTsyPYH10=
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4= github.com/kr/pty v1.1.5 h1:hyz3dwM5QLc1Rfoz4FuWJQG5BN7tc6K1MndAUnGpQr4=
github.com/labstack/echo/v4 v4.5.0 h1:JXk6H5PAw9I3GwizqUHhYyS4f45iyGebR/c1xNCeOCY= github.com/labstack/echo/v4 v4.5.0 h1:JXk6H5PAw9I3GwizqUHhYyS4f45iyGebR/c1xNCeOCY=
github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0= github.com/labstack/gommon v0.3.0 h1:JEeO0bvc78PKdyHxloTKiF8BD5iGrH8T6MSeGvSgob0=
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
pragma solidity ^0.8.9; pragma solidity ^0.8.9;
import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import { OptimismMintableERC721 } from "@eth-optimism/contracts-periphery/contracts/universal/op-erc721/OptimismMintableERC721.sol"; import { OptimismMintableERC721 } from "@eth-optimism/contracts-bedrock/contracts/universal/OptimismMintableERC721.sol";
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
contract FakeOptimismMintableERC721 is OptimismMintableERC721 { contract FakeOptimismMintableERC721 is OptimismMintableERC721 {
......
...@@ -30,6 +30,7 @@ ...@@ -30,6 +30,7 @@
"devDependencies": { "devDependencies": {
"@babel/eslint-parser": "^7.5.4", "@babel/eslint-parser": "^7.5.4",
"@eth-optimism/contracts": "^0.5.39", "@eth-optimism/contracts": "^0.5.39",
"@eth-optimism/contracts-bedrock": "0.11.0",
"@eth-optimism/contracts-periphery": "^1.0.4", "@eth-optimism/contracts-periphery": "^1.0.4",
"@eth-optimism/core-utils": "0.12.0", "@eth-optimism/core-utils": "0.12.0",
"@eth-optimism/sdk": "1.8.0", "@eth-optimism/sdk": "1.8.0",
......
...@@ -4,10 +4,10 @@ import { ethers } from 'hardhat' ...@@ -4,10 +4,10 @@ import { ethers } from 'hardhat'
import { getChainId } from '@eth-optimism/core-utils' import { getChainId } from '@eth-optimism/core-utils'
import { predeploys } from '@eth-optimism/contracts' import { predeploys } from '@eth-optimism/contracts'
import Artifact__TestERC721 from '@eth-optimism/contracts-periphery/artifacts/contracts/testing/helpers/TestERC721.sol/TestERC721.json' import Artifact__TestERC721 from '@eth-optimism/contracts-periphery/artifacts/contracts/testing/helpers/TestERC721.sol/TestERC721.json'
import Artifact__L1ERC721Bridge from '@eth-optimism/contracts-periphery/artifacts/contracts/L1/L1ERC721Bridge.sol/L1ERC721Bridge.json' import Artifact__L1ERC721Bridge from '@eth-optimism/contracts-bedrock/artifacts/contracts/L1/L1ERC721Bridge.sol/L1ERC721Bridge.json'
import Artifact__L2ERC721Bridge from '@eth-optimism/contracts-periphery/artifacts/contracts/L2/L2ERC721Bridge.sol/L2ERC721Bridge.json' import Artifact__L2ERC721Bridge from '@eth-optimism/contracts-bedrock/artifacts/contracts/L2/L2ERC721Bridge.sol/L2ERC721Bridge.json'
import Artifact__OptimismMintableERC721Factory from '@eth-optimism/contracts-periphery/artifacts/contracts/universal/op-erc721/OptimismMintableERC721Factory.sol/OptimismMintableERC721Factory.json' import Artifact__OptimismMintableERC721Factory from '@eth-optimism/contracts-bedrock/artifacts/contracts/universal/OptimismMintableERC721Factory.sol/OptimismMintableERC721Factory.json'
import Artifact__OptimismMintableERC721 from '@eth-optimism/contracts-periphery/artifacts/contracts/universal/op-erc721/OptimismMintableERC721.sol/OptimismMintableERC721.json' import Artifact__OptimismMintableERC721 from '@eth-optimism/contracts-bedrock/artifacts/contracts/universal/OptimismMintableERC721.sol/OptimismMintableERC721.json'
/* Imports: Internal */ /* Imports: Internal */
import { expect } from './shared/setup' import { expect } from './shared/setup'
......
...@@ -93,4 +93,4 @@ require ( ...@@ -93,4 +93,4 @@ require (
lukechampine.com/blake3 v1.1.7 // indirect lukechampine.com/blake3 v1.1.7 // indirect
) )
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468
...@@ -104,8 +104,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m ...@@ -104,8 +104,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc=
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs=
github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 h1:QJL/gtfxGe11tApZIPCeKERQHrLZMAG0RwGV9eTgtvE= github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468 h1:7KgjBYDji5AKi42eRYI+n8Gs+ZJVilSASL3WBu82c3M=
github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY= github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY=
github.com/ethereum-optimism/optimism/op-bindings v0.10.4 h1:CFn4+t0FUrBG5DmkKyYrLbGmzHWLdLv8QdUnlklvozc= github.com/ethereum-optimism/optimism/op-bindings v0.10.4 h1:CFn4+t0FUrBG5DmkKyYrLbGmzHWLdLv8QdUnlklvozc=
github.com/ethereum-optimism/optimism/op-bindings v0.10.4/go.mod h1:philKV8erP02ggjk2mRIdvJd2ZjMzpmqu0+zwwzKmNw= github.com/ethereum-optimism/optimism/op-bindings v0.10.4/go.mod h1:philKV8erP02ggjk2mRIdvJd2ZjMzpmqu0+zwwzKmNw=
github.com/ethereum-optimism/optimism/op-node v0.10.4 h1:ZXqfrFKgb6W4ZLbkfO9NlgaQ1djBCCPzNGbd6TgehVI= github.com/ethereum-optimism/optimism/op-node v0.10.4 h1:ZXqfrFKgb6W4ZLbkfO9NlgaQ1djBCCPzNGbd6TgehVI=
......
...@@ -30,8 +30,8 @@ var ( ...@@ -30,8 +30,8 @@ var (
// SystemConfigMetaData contains all meta data concerning the SystemConfig contract. // SystemConfigMetaData contains all meta data concerning the SystemConfig contract.
var SystemConfigMetaData = &bind.MetaData{ var SystemConfigMetaData = &bind.MetaData{
ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_overhead\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_scalar\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"_batcherHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"_gasLimit\",\"type\":\"uint64\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"version\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"enumSystemConfig.UpdateType\",\"name\":\"updateType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"ConfigUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"MINIMUM_GAS_LIMIT\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"VERSION\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batcherHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gasLimit\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_overhead\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_scalar\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"_batcherHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"_gasLimit\",\"type\":\"uint64\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"overhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"scalar\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_batcherHash\",\"type\":\"bytes32\"}],\"name\":\"setBatcherHash\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_overhead\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_scalar\",\"type\":\"uint256\"}],\"name\":\"setGasConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_gasLimit\",\"type\":\"uint64\"}],\"name\":\"setGasLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]", ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_overhead\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_scalar\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"_batcherHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"_gasLimit\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"_unsafeBlockSigner\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"version\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"enumSystemConfig.UpdateType\",\"name\":\"updateType\",\"type\":\"uint8\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"ConfigUpdate\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint8\",\"name\":\"version\",\"type\":\"uint8\"}],\"name\":\"Initialized\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"previousOwner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"MINIMUM_GAS_LIMIT\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"VERSION\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"batcherHash\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"\",\"type\":\"bytes32\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"gasLimit\",\"outputs\":[{\"internalType\":\"uint64\",\"name\":\"\",\"type\":\"uint64\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_owner\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"_overhead\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_scalar\",\"type\":\"uint256\"},{\"internalType\":\"bytes32\",\"name\":\"_batcherHash\",\"type\":\"bytes32\"},{\"internalType\":\"uint64\",\"name\":\"_gasLimit\",\"type\":\"uint64\"},{\"internalType\":\"address\",\"name\":\"_unsafeBlockSigner\",\"type\":\"address\"}],\"name\":\"initialize\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"overhead\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"renounceOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"scalar\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"_batcherHash\",\"type\":\"bytes32\"}],\"name\":\"setBatcherHash\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"_overhead\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"_scalar\",\"type\":\"uint256\"}],\"name\":\"setGasConfig\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint64\",\"name\":\"_gasLimit\",\"type\":\"uint64\"}],\"name\":\"setGasLimit\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"_unsafeBlockSigner\",\"type\":\"address\"}],\"name\":\"setUnsafeBlockSigner\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"newOwner\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"unsafeBlockSigner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"version\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"}]",
Bin: "0x60e06040523480156200001157600080fd5b506040516200139238038062001392833981016040819052620000349162000432565b6001608052600060a081905260c0526200005285858585856200005d565b5050505050620004a5565b600054610100900460ff16158080156200007e5750600054600160ff909116105b80620000ae57506200009b306200022760201b620007a91760201c565b158015620000ae575060005460ff166001145b620001175760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff1916600117905580156200013b576000805461ff0019166101001790555b627a12006001600160401b0383161015620001995760405162461bcd60e51b815260206004820152601f60248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f206c6f770060448201526064016200010e565b620001a362000236565b620001ae866200029e565b606585905560668490556067839055606880546001600160401b0319166001600160401b03841617905580156200021f576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b6001600160a01b03163b151590565b600054610100900460ff16620002925760405162461bcd60e51b815260206004820152602b60248201526000805160206200137283398151915260448201526a6e697469616c697a696e6760a81b60648201526084016200010e565b6200029c6200031d565b565b620002a862000384565b6001600160a01b0381166200030f5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016200010e565b6200031a81620003e0565b50565b600054610100900460ff16620003795760405162461bcd60e51b815260206004820152602b60248201526000805160206200137283398151915260448201526a6e697469616c697a696e6760a81b60648201526084016200010e565b6200029c33620003e0565b6033546001600160a01b031633146200029c5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016200010e565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600080600080600060a086880312156200044b57600080fd5b85516001600160a01b03811681146200046357600080fd5b60208701516040880151606089015160808a0151939850919650945092506001600160401b03811681146200049757600080fd5b809150509295509295909350565b60805160a05160c051610e9d620004d5600039600061025b01526000610232015260006102090152610e9d6000f3fe608060405234801561001057600080fd5b50600436106100ea5760003560e01c8063b40a817c1161008c578063f2fde38b11610066578063f2fde38b146101ca578063f45e65d8146101dd578063f68016b7146101e6578063ffa1ad74146101fa57600080fd5b8063b40a817c1461019b578063c9b26f61146101ae578063e81b2c6d146101c157600080fd5b806370bde19c116100c857806370bde19c14610143578063715018a6146101585780638da5cb5b14610160578063935f029e1461018857600080fd5b80630c18c162146100ef57806329477e861461010b57806354fd4d501461012e575b600080fd5b6100f860655481565b6040519081526020015b60405180910390f35b610115627a120081565b60405167ffffffffffffffff9091168152602001610102565b610136610202565b6040516101029190610bb3565b610156610151366004610c0e565b6102a5565b005b610156610504565b60335460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610102565b610156610196366004610c5e565b610518565b6101566101a9366004610c80565b6105b1565b6101566101bc366004610c9b565b6106c2565b6100f860675481565b6101566101d8366004610cb4565b6106f2565b6100f860665481565b6068546101159067ffffffffffffffff1681565b6100f8600081565b606061022d7f00000000000000000000000000000000000000000000000000000000000000006107c5565b6102567f00000000000000000000000000000000000000000000000000000000000000006107c5565b61027f7f00000000000000000000000000000000000000000000000000000000000000006107c5565b60405160200161029193929190610ccf565b604051602081830303815290604052905090565b600054610100900460ff16158080156102c55750600054600160ff909116105b806102df5750303b1580156102df575060005460ff166001145b610370576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156103ce57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b627a120067ffffffffffffffff83161015610445576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f206c6f77006044820152606401610367565b61044d610902565b610456866106f2565b606585905560668490556067839055606880547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff841617905580156104fc57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b61050c6109a1565b6105166000610a22565b565b6105206109a1565b606582905560668190556040805160208101849052908101829052600090606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190529050600160007f1d2b0bda21d56b8bd12d4f94ebacffdfb35f5e226f84b461103bb8beab6353be836040516105a49190610bb3565b60405180910390a3505050565b6105b96109a1565b627a120067ffffffffffffffff82161015610630576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f206c6f77006044820152606401610367565b606880547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff831690811790915560408051602080820193909352815180820390930183528101905260025b60007f1d2b0bda21d56b8bd12d4f94ebacffdfb35f5e226f84b461103bb8beab6353be836040516106b69190610bb3565b60405180910390a35050565b6106ca6109a1565b6067819055604080516020808201849052825180830390910181529082019091526000610685565b6106fa6109a1565b73ffffffffffffffffffffffffffffffffffffffff811661079d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610367565b6107a681610a22565b50565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b60608160000361080857505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115610832578061081c81610d74565b915061082b9050600a83610ddb565b915061080c565b60008167ffffffffffffffff81111561084d5761084d610def565b6040519080825280601f01601f191660200182016040528015610877576020820181803683370190505b5090505b84156108fa5761088c600183610e1e565b9150610899600a86610e35565b6108a4906030610e49565b60f81b8183815181106108b9576108b9610e61565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506108f3600a86610ddb565b945061087b565b949350505050565b600054610100900460ff16610999576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610367565b610516610a99565b60335473ffffffffffffffffffffffffffffffffffffffff163314610516576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610367565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16610b30576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610367565b61051633610a22565b60005b83811015610b54578181015183820152602001610b3c565b83811115610b63576000848401525b50505050565b60008151808452610b81816020860160208601610b39565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610bc66020830184610b69565b9392505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610bf157600080fd5b919050565b803567ffffffffffffffff81168114610bf157600080fd5b600080600080600060a08688031215610c2657600080fd5b610c2f86610bcd565b9450602086013593506040860135925060608601359150610c5260808701610bf6565b90509295509295909350565b60008060408385031215610c7157600080fd5b50508035926020909101359150565b600060208284031215610c9257600080fd5b610bc682610bf6565b600060208284031215610cad57600080fd5b5035919050565b600060208284031215610cc657600080fd5b610bc682610bcd565b60008451610ce1818460208901610b39565b80830190507f2e000000000000000000000000000000000000000000000000000000000000008082528551610d1d816001850160208a01610b39565b60019201918201528351610d38816002840160208801610b39565b0160020195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610da557610da5610d45565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082610dea57610dea610dac565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082821015610e3057610e30610d45565b500390565b600082610e4457610e44610dac565b500690565b60008219821115610e5c57610e5c610d45565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea164736f6c634300080f000a496e697469616c697a61626c653a20636f6e7472616374206973206e6f742069", Bin: "0x60e06040523480156200001157600080fd5b50604051620014eb380380620014eb83398101604081905262000034916200046d565b6001608052600060a081905260c052620000538686868686866200005f565b505050505050620004e2565b600054610100900460ff1615808015620000805750600054600160ff909116105b80620000b057506200009d306200024560201b620008bb1760201c565b158015620000b0575060005460ff166001145b620001195760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff1916600117905580156200013d576000805461ff0019166101001790555b627a12006001600160401b03841610156200019b5760405162461bcd60e51b815260206004820152601f60248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f206c6f7700604482015260640162000110565b620001a562000254565b620001b087620002bc565b606586905560668590556068849055606980546001600160401b0319166001600160401b038516179055606780546001600160a01b0319166001600160a01b03841617905580156200023c576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b6001600160a01b03163b151590565b600054610100900460ff16620002b05760405162461bcd60e51b815260206004820152602b6024820152600080516020620014cb83398151915260448201526a6e697469616c697a696e6760a81b606482015260840162000110565b620002ba6200033b565b565b620002c6620003a2565b6001600160a01b0381166200032d5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b606482015260840162000110565b6200033881620003fe565b50565b600054610100900460ff16620003975760405162461bcd60e51b815260206004820152602b6024820152600080516020620014cb83398151915260448201526a6e697469616c697a696e6760a81b606482015260840162000110565b620002ba33620003fe565b6033546001600160a01b03163314620002ba5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640162000110565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b80516001600160a01b03811681146200046857600080fd5b919050565b60008060008060008060c087890312156200048757600080fd5b620004928762000450565b6020880151604089015160608a015160808b0151939950919750955093506001600160401b0381168114620004c657600080fd5b9150620004d660a0880162000450565b90509295509295509295565b60805160a05160c051610fb96200051260003960006103650152600061033c015260006103130152610fb96000f3fe608060405234801561001057600080fd5b50600436106101005760003560e01c8063935f029e11610097578063f2fde38b11610066578063f2fde38b1461022e578063f45e65d814610241578063f68016b71461024a578063ffa1ad741461025e57600080fd5b8063935f029e146101ec578063b40a817c146101ff578063c9b26f6114610212578063e81b2c6d1461022557600080fd5b806354fd4d50116100d357806354fd4d501461019e578063715018a6146101b35780638da5cb5b146101bb5780638f974d7f146101d957600080fd5b80630c18c1621461010557806318d13918146101215780631fd19ee11461013657806329477e861461017b575b600080fd5b61010e60655481565b6040519081526020015b60405180910390f35b61013461012f366004610c6f565b610266565b005b6067546101569073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610118565b610185627a120081565b60405167ffffffffffffffff9091168152602001610118565b6101a661030c565b6040516101189190610d0b565b6101346103af565b60335473ffffffffffffffffffffffffffffffffffffffff16610156565b6101346101e7366004610d36565b6103c3565b6101346101fa366004610d95565b610663565b61013461020d366004610db7565b6106fc565b610134610220366004610dd2565b6107d4565b61010e60685481565b61013461023c366004610c6f565b610804565b61010e60665481565b6069546101859067ffffffffffffffff1681565b61010e600081565b61026e6108d7565b606780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560408051602080820193909352815180820390930183528101905260035b60007f1d2b0bda21d56b8bd12d4f94ebacffdfb35f5e226f84b461103bb8beab6353be836040516103009190610d0b565b60405180910390a35050565b60606103377f0000000000000000000000000000000000000000000000000000000000000000610958565b6103607f0000000000000000000000000000000000000000000000000000000000000000610958565b6103897f0000000000000000000000000000000000000000000000000000000000000000610958565b60405160200161039b93929190610deb565b604051602081830303815290604052905090565b6103b76108d7565b6103c16000610a95565b565b600054610100900460ff16158080156103e35750600054600160ff909116105b806103fd5750303b1580156103fd575060005460ff166001145b61048e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156104ec57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b627a120067ffffffffffffffff84161015610563576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f206c6f77006044820152606401610485565b61056b610b0c565b61057487610804565b606586905560668590556068849055606980547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff8516179055606780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8416179055801561065a57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b61066b6108d7565b606582905560668190556040805160208101849052908101829052600090606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190529050600160007f1d2b0bda21d56b8bd12d4f94ebacffdfb35f5e226f84b461103bb8beab6353be836040516106ef9190610d0b565b60405180910390a3505050565b6107046108d7565b627a120067ffffffffffffffff8216101561077b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f206c6f77006044820152606401610485565b606980547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff831690811790915560408051602080820193909352815180820390930183528101905260026102cf565b6107dc6108d7565b60688190556040805160208082018490528251808303909101815290820190915260006102cf565b61080c6108d7565b73ffffffffffffffffffffffffffffffffffffffff81166108af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610485565b6108b881610a95565b50565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b60335473ffffffffffffffffffffffffffffffffffffffff1633146103c1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610485565b60608160000361099b57505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b81156109c557806109af81610e90565b91506109be9050600a83610ef7565b915061099f565b60008167ffffffffffffffff8111156109e0576109e0610f0b565b6040519080825280601f01601f191660200182016040528015610a0a576020820181803683370190505b5090505b8415610a8d57610a1f600183610f3a565b9150610a2c600a86610f51565b610a37906030610f65565b60f81b818381518110610a4c57610a4c610f7d565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610a86600a86610ef7565b9450610a0e565b949350505050565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16610ba3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610485565b6103c1600054610100900460ff16610c3d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610485565b6103c133610a95565b803573ffffffffffffffffffffffffffffffffffffffff81168114610c6a57600080fd5b919050565b600060208284031215610c8157600080fd5b610c8a82610c46565b9392505050565b60005b83811015610cac578181015183820152602001610c94565b83811115610cbb576000848401525b50505050565b60008151808452610cd9816020860160208601610c91565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610c8a6020830184610cc1565b803567ffffffffffffffff81168114610c6a57600080fd5b60008060008060008060c08789031215610d4f57600080fd5b610d5887610c46565b9550602087013594506040870135935060608701359250610d7b60808801610d1e565b9150610d8960a08801610c46565b90509295509295509295565b60008060408385031215610da857600080fd5b50508035926020909101359150565b600060208284031215610dc957600080fd5b610c8a82610d1e565b600060208284031215610de457600080fd5b5035919050565b60008451610dfd818460208901610c91565b80830190507f2e000000000000000000000000000000000000000000000000000000000000008082528551610e39816001850160208a01610c91565b60019201918201528351610e54816002840160208801610c91565b0160020195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610ec157610ec1610e61565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082610f0657610f06610ec8565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082821015610f4c57610f4c610e61565b500390565b600082610f6057610f60610ec8565b500690565b60008219821115610f7857610f78610e61565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea164736f6c634300080f000a496e697469616c697a61626c653a20636f6e7472616374206973206e6f742069",
} }
// SystemConfigABI is the input ABI used to generate the binding from. // SystemConfigABI is the input ABI used to generate the binding from.
...@@ -43,7 +43,7 @@ var SystemConfigABI = SystemConfigMetaData.ABI ...@@ -43,7 +43,7 @@ var SystemConfigABI = SystemConfigMetaData.ABI
var SystemConfigBin = SystemConfigMetaData.Bin var SystemConfigBin = SystemConfigMetaData.Bin
// DeploySystemConfig deploys a new Ethereum contract, binding an instance of SystemConfig to it. // DeploySystemConfig deploys a new Ethereum contract, binding an instance of SystemConfig to it.
func DeploySystemConfig(auth *bind.TransactOpts, backend bind.ContractBackend, _owner common.Address, _overhead *big.Int, _scalar *big.Int, _batcherHash [32]byte, _gasLimit uint64) (common.Address, *types.Transaction, *SystemConfig, error) { func DeploySystemConfig(auth *bind.TransactOpts, backend bind.ContractBackend, _owner common.Address, _overhead *big.Int, _scalar *big.Int, _batcherHash [32]byte, _gasLimit uint64, _unsafeBlockSigner common.Address) (common.Address, *types.Transaction, *SystemConfig, error) {
parsed, err := SystemConfigMetaData.GetAbi() parsed, err := SystemConfigMetaData.GetAbi()
if err != nil { if err != nil {
return common.Address{}, nil, nil, err return common.Address{}, nil, nil, err
...@@ -52,7 +52,7 @@ func DeploySystemConfig(auth *bind.TransactOpts, backend bind.ContractBackend, _ ...@@ -52,7 +52,7 @@ func DeploySystemConfig(auth *bind.TransactOpts, backend bind.ContractBackend, _
return common.Address{}, nil, nil, errors.New("GetABI returned nil") return common.Address{}, nil, nil, errors.New("GetABI returned nil")
} }
address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(SystemConfigBin), backend, _owner, _overhead, _scalar, _batcherHash, _gasLimit) address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(SystemConfigBin), backend, _owner, _overhead, _scalar, _batcherHash, _gasLimit, _unsafeBlockSigner)
if err != nil { if err != nil {
return common.Address{}, nil, nil, err return common.Address{}, nil, nil, err
} }
...@@ -418,6 +418,37 @@ func (_SystemConfig *SystemConfigCallerSession) Scalar() (*big.Int, error) { ...@@ -418,6 +418,37 @@ func (_SystemConfig *SystemConfigCallerSession) Scalar() (*big.Int, error) {
return _SystemConfig.Contract.Scalar(&_SystemConfig.CallOpts) return _SystemConfig.Contract.Scalar(&_SystemConfig.CallOpts)
} }
// UnsafeBlockSigner is a free data retrieval call binding the contract method 0x1fd19ee1.
//
// Solidity: function unsafeBlockSigner() view returns(address)
func (_SystemConfig *SystemConfigCaller) UnsafeBlockSigner(opts *bind.CallOpts) (common.Address, error) {
var out []interface{}
err := _SystemConfig.contract.Call(opts, &out, "unsafeBlockSigner")
if err != nil {
return *new(common.Address), err
}
out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address)
return out0, err
}
// UnsafeBlockSigner is a free data retrieval call binding the contract method 0x1fd19ee1.
//
// Solidity: function unsafeBlockSigner() view returns(address)
func (_SystemConfig *SystemConfigSession) UnsafeBlockSigner() (common.Address, error) {
return _SystemConfig.Contract.UnsafeBlockSigner(&_SystemConfig.CallOpts)
}
// UnsafeBlockSigner is a free data retrieval call binding the contract method 0x1fd19ee1.
//
// Solidity: function unsafeBlockSigner() view returns(address)
func (_SystemConfig *SystemConfigCallerSession) UnsafeBlockSigner() (common.Address, error) {
return _SystemConfig.Contract.UnsafeBlockSigner(&_SystemConfig.CallOpts)
}
// Version is a free data retrieval call binding the contract method 0x54fd4d50. // Version is a free data retrieval call binding the contract method 0x54fd4d50.
// //
// Solidity: function version() view returns(string) // Solidity: function version() view returns(string)
...@@ -449,25 +480,25 @@ func (_SystemConfig *SystemConfigCallerSession) Version() (string, error) { ...@@ -449,25 +480,25 @@ func (_SystemConfig *SystemConfigCallerSession) Version() (string, error) {
return _SystemConfig.Contract.Version(&_SystemConfig.CallOpts) return _SystemConfig.Contract.Version(&_SystemConfig.CallOpts)
} }
// Initialize is a paid mutator transaction binding the contract method 0x70bde19c. // Initialize is a paid mutator transaction binding the contract method 0x8f974d7f.
// //
// Solidity: function initialize(address _owner, uint256 _overhead, uint256 _scalar, bytes32 _batcherHash, uint64 _gasLimit) returns() // Solidity: function initialize(address _owner, uint256 _overhead, uint256 _scalar, bytes32 _batcherHash, uint64 _gasLimit, address _unsafeBlockSigner) returns()
func (_SystemConfig *SystemConfigTransactor) Initialize(opts *bind.TransactOpts, _owner common.Address, _overhead *big.Int, _scalar *big.Int, _batcherHash [32]byte, _gasLimit uint64) (*types.Transaction, error) { func (_SystemConfig *SystemConfigTransactor) Initialize(opts *bind.TransactOpts, _owner common.Address, _overhead *big.Int, _scalar *big.Int, _batcherHash [32]byte, _gasLimit uint64, _unsafeBlockSigner common.Address) (*types.Transaction, error) {
return _SystemConfig.contract.Transact(opts, "initialize", _owner, _overhead, _scalar, _batcherHash, _gasLimit) return _SystemConfig.contract.Transact(opts, "initialize", _owner, _overhead, _scalar, _batcherHash, _gasLimit, _unsafeBlockSigner)
} }
// Initialize is a paid mutator transaction binding the contract method 0x70bde19c. // Initialize is a paid mutator transaction binding the contract method 0x8f974d7f.
// //
// Solidity: function initialize(address _owner, uint256 _overhead, uint256 _scalar, bytes32 _batcherHash, uint64 _gasLimit) returns() // Solidity: function initialize(address _owner, uint256 _overhead, uint256 _scalar, bytes32 _batcherHash, uint64 _gasLimit, address _unsafeBlockSigner) returns()
func (_SystemConfig *SystemConfigSession) Initialize(_owner common.Address, _overhead *big.Int, _scalar *big.Int, _batcherHash [32]byte, _gasLimit uint64) (*types.Transaction, error) { func (_SystemConfig *SystemConfigSession) Initialize(_owner common.Address, _overhead *big.Int, _scalar *big.Int, _batcherHash [32]byte, _gasLimit uint64, _unsafeBlockSigner common.Address) (*types.Transaction, error) {
return _SystemConfig.Contract.Initialize(&_SystemConfig.TransactOpts, _owner, _overhead, _scalar, _batcherHash, _gasLimit) return _SystemConfig.Contract.Initialize(&_SystemConfig.TransactOpts, _owner, _overhead, _scalar, _batcherHash, _gasLimit, _unsafeBlockSigner)
} }
// Initialize is a paid mutator transaction binding the contract method 0x70bde19c. // Initialize is a paid mutator transaction binding the contract method 0x8f974d7f.
// //
// Solidity: function initialize(address _owner, uint256 _overhead, uint256 _scalar, bytes32 _batcherHash, uint64 _gasLimit) returns() // Solidity: function initialize(address _owner, uint256 _overhead, uint256 _scalar, bytes32 _batcherHash, uint64 _gasLimit, address _unsafeBlockSigner) returns()
func (_SystemConfig *SystemConfigTransactorSession) Initialize(_owner common.Address, _overhead *big.Int, _scalar *big.Int, _batcherHash [32]byte, _gasLimit uint64) (*types.Transaction, error) { func (_SystemConfig *SystemConfigTransactorSession) Initialize(_owner common.Address, _overhead *big.Int, _scalar *big.Int, _batcherHash [32]byte, _gasLimit uint64, _unsafeBlockSigner common.Address) (*types.Transaction, error) {
return _SystemConfig.Contract.Initialize(&_SystemConfig.TransactOpts, _owner, _overhead, _scalar, _batcherHash, _gasLimit) return _SystemConfig.Contract.Initialize(&_SystemConfig.TransactOpts, _owner, _overhead, _scalar, _batcherHash, _gasLimit, _unsafeBlockSigner)
} }
// RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6. // RenounceOwnership is a paid mutator transaction binding the contract method 0x715018a6.
...@@ -554,6 +585,27 @@ func (_SystemConfig *SystemConfigTransactorSession) SetGasLimit(_gasLimit uint64 ...@@ -554,6 +585,27 @@ func (_SystemConfig *SystemConfigTransactorSession) SetGasLimit(_gasLimit uint64
return _SystemConfig.Contract.SetGasLimit(&_SystemConfig.TransactOpts, _gasLimit) return _SystemConfig.Contract.SetGasLimit(&_SystemConfig.TransactOpts, _gasLimit)
} }
// SetUnsafeBlockSigner is a paid mutator transaction binding the contract method 0x18d13918.
//
// Solidity: function setUnsafeBlockSigner(address _unsafeBlockSigner) returns()
func (_SystemConfig *SystemConfigTransactor) SetUnsafeBlockSigner(opts *bind.TransactOpts, _unsafeBlockSigner common.Address) (*types.Transaction, error) {
return _SystemConfig.contract.Transact(opts, "setUnsafeBlockSigner", _unsafeBlockSigner)
}
// SetUnsafeBlockSigner is a paid mutator transaction binding the contract method 0x18d13918.
//
// Solidity: function setUnsafeBlockSigner(address _unsafeBlockSigner) returns()
func (_SystemConfig *SystemConfigSession) SetUnsafeBlockSigner(_unsafeBlockSigner common.Address) (*types.Transaction, error) {
return _SystemConfig.Contract.SetUnsafeBlockSigner(&_SystemConfig.TransactOpts, _unsafeBlockSigner)
}
// SetUnsafeBlockSigner is a paid mutator transaction binding the contract method 0x18d13918.
//
// Solidity: function setUnsafeBlockSigner(address _unsafeBlockSigner) returns()
func (_SystemConfig *SystemConfigTransactorSession) SetUnsafeBlockSigner(_unsafeBlockSigner common.Address) (*types.Transaction, error) {
return _SystemConfig.Contract.SetUnsafeBlockSigner(&_SystemConfig.TransactOpts, _unsafeBlockSigner)
}
// TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b. // TransferOwnership is a paid mutator transaction binding the contract method 0xf2fde38b.
// //
// Solidity: function transferOwnership(address newOwner) returns() // Solidity: function transferOwnership(address newOwner) returns()
......
...@@ -9,11 +9,11 @@ import ( ...@@ -9,11 +9,11 @@ import (
"github.com/ethereum-optimism/optimism/op-bindings/solc" "github.com/ethereum-optimism/optimism/op-bindings/solc"
) )
const SystemConfigStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"_initialized\",\"offset\":0,\"slot\":\"0\",\"type\":\"t_uint8\"},{\"astId\":1001,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"_initializing\",\"offset\":1,\"slot\":\"0\",\"type\":\"t_bool\"},{\"astId\":1002,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"1\",\"type\":\"t_array(t_uint256)1010_storage\"},{\"astId\":1003,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"_owner\",\"offset\":0,\"slot\":\"51\",\"type\":\"t_address\"},{\"astId\":1004,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"52\",\"type\":\"t_array(t_uint256)1009_storage\"},{\"astId\":1005,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"overhead\",\"offset\":0,\"slot\":\"101\",\"type\":\"t_uint256\"},{\"astId\":1006,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"scalar\",\"offset\":0,\"slot\":\"102\",\"type\":\"t_uint256\"},{\"astId\":1007,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"batcherHash\",\"offset\":0,\"slot\":\"103\",\"type\":\"t_bytes32\"},{\"astId\":1008,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"gasLimit\",\"offset\":0,\"slot\":\"104\",\"type\":\"t_uint64\"}],\"types\":{\"t_address\":{\"encoding\":\"inplace\",\"label\":\"address\",\"numberOfBytes\":\"20\"},\"t_array(t_uint256)1009_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[49]\",\"numberOfBytes\":\"1568\"},\"t_array(t_uint256)1010_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[50]\",\"numberOfBytes\":\"1600\"},\"t_bool\":{\"encoding\":\"inplace\",\"label\":\"bool\",\"numberOfBytes\":\"1\"},\"t_bytes32\":{\"encoding\":\"inplace\",\"label\":\"bytes32\",\"numberOfBytes\":\"32\"},\"t_uint256\":{\"encoding\":\"inplace\",\"label\":\"uint256\",\"numberOfBytes\":\"32\"},\"t_uint64\":{\"encoding\":\"inplace\",\"label\":\"uint64\",\"numberOfBytes\":\"8\"},\"t_uint8\":{\"encoding\":\"inplace\",\"label\":\"uint8\",\"numberOfBytes\":\"1\"}}}" const SystemConfigStorageLayoutJSON = "{\"storage\":[{\"astId\":1000,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"_initialized\",\"offset\":0,\"slot\":\"0\",\"type\":\"t_uint8\"},{\"astId\":1001,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"_initializing\",\"offset\":1,\"slot\":\"0\",\"type\":\"t_bool\"},{\"astId\":1002,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"1\",\"type\":\"t_array(t_uint256)1011_storage\"},{\"astId\":1003,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"_owner\",\"offset\":0,\"slot\":\"51\",\"type\":\"t_address\"},{\"astId\":1004,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"__gap\",\"offset\":0,\"slot\":\"52\",\"type\":\"t_array(t_uint256)1010_storage\"},{\"astId\":1005,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"overhead\",\"offset\":0,\"slot\":\"101\",\"type\":\"t_uint256\"},{\"astId\":1006,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"scalar\",\"offset\":0,\"slot\":\"102\",\"type\":\"t_uint256\"},{\"astId\":1007,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"unsafeBlockSigner\",\"offset\":0,\"slot\":\"103\",\"type\":\"t_address\"},{\"astId\":1008,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"batcherHash\",\"offset\":0,\"slot\":\"104\",\"type\":\"t_bytes32\"},{\"astId\":1009,\"contract\":\"contracts/L1/SystemConfig.sol:SystemConfig\",\"label\":\"gasLimit\",\"offset\":0,\"slot\":\"105\",\"type\":\"t_uint64\"}],\"types\":{\"t_address\":{\"encoding\":\"inplace\",\"label\":\"address\",\"numberOfBytes\":\"20\"},\"t_array(t_uint256)1010_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[49]\",\"numberOfBytes\":\"1568\"},\"t_array(t_uint256)1011_storage\":{\"encoding\":\"inplace\",\"label\":\"uint256[50]\",\"numberOfBytes\":\"1600\"},\"t_bool\":{\"encoding\":\"inplace\",\"label\":\"bool\",\"numberOfBytes\":\"1\"},\"t_bytes32\":{\"encoding\":\"inplace\",\"label\":\"bytes32\",\"numberOfBytes\":\"32\"},\"t_uint256\":{\"encoding\":\"inplace\",\"label\":\"uint256\",\"numberOfBytes\":\"32\"},\"t_uint64\":{\"encoding\":\"inplace\",\"label\":\"uint64\",\"numberOfBytes\":\"8\"},\"t_uint8\":{\"encoding\":\"inplace\",\"label\":\"uint8\",\"numberOfBytes\":\"1\"}}}"
var SystemConfigStorageLayout = new(solc.StorageLayout) var SystemConfigStorageLayout = new(solc.StorageLayout)
var SystemConfigDeployedBin = "0x608060405234801561001057600080fd5b50600436106100ea5760003560e01c8063b40a817c1161008c578063f2fde38b11610066578063f2fde38b146101ca578063f45e65d8146101dd578063f68016b7146101e6578063ffa1ad74146101fa57600080fd5b8063b40a817c1461019b578063c9b26f61146101ae578063e81b2c6d146101c157600080fd5b806370bde19c116100c857806370bde19c14610143578063715018a6146101585780638da5cb5b14610160578063935f029e1461018857600080fd5b80630c18c162146100ef57806329477e861461010b57806354fd4d501461012e575b600080fd5b6100f860655481565b6040519081526020015b60405180910390f35b610115627a120081565b60405167ffffffffffffffff9091168152602001610102565b610136610202565b6040516101029190610bb3565b610156610151366004610c0e565b6102a5565b005b610156610504565b60335460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610102565b610156610196366004610c5e565b610518565b6101566101a9366004610c80565b6105b1565b6101566101bc366004610c9b565b6106c2565b6100f860675481565b6101566101d8366004610cb4565b6106f2565b6100f860665481565b6068546101159067ffffffffffffffff1681565b6100f8600081565b606061022d7f00000000000000000000000000000000000000000000000000000000000000006107c5565b6102567f00000000000000000000000000000000000000000000000000000000000000006107c5565b61027f7f00000000000000000000000000000000000000000000000000000000000000006107c5565b60405160200161029193929190610ccf565b604051602081830303815290604052905090565b600054610100900460ff16158080156102c55750600054600160ff909116105b806102df5750303b1580156102df575060005460ff166001145b610370576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156103ce57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b627a120067ffffffffffffffff83161015610445576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f206c6f77006044820152606401610367565b61044d610902565b610456866106f2565b606585905560668490556067839055606880547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff841617905580156104fc57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b505050505050565b61050c6109a1565b6105166000610a22565b565b6105206109a1565b606582905560668190556040805160208101849052908101829052600090606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190529050600160007f1d2b0bda21d56b8bd12d4f94ebacffdfb35f5e226f84b461103bb8beab6353be836040516105a49190610bb3565b60405180910390a3505050565b6105b96109a1565b627a120067ffffffffffffffff82161015610630576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f206c6f77006044820152606401610367565b606880547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff831690811790915560408051602080820193909352815180820390930183528101905260025b60007f1d2b0bda21d56b8bd12d4f94ebacffdfb35f5e226f84b461103bb8beab6353be836040516106b69190610bb3565b60405180910390a35050565b6106ca6109a1565b6067819055604080516020808201849052825180830390910181529082019091526000610685565b6106fa6109a1565b73ffffffffffffffffffffffffffffffffffffffff811661079d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610367565b6107a681610a22565b50565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b60608160000361080857505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b8115610832578061081c81610d74565b915061082b9050600a83610ddb565b915061080c565b60008167ffffffffffffffff81111561084d5761084d610def565b6040519080825280601f01601f191660200182016040528015610877576020820181803683370190505b5090505b84156108fa5761088c600183610e1e565b9150610899600a86610e35565b6108a4906030610e49565b60f81b8183815181106108b9576108b9610e61565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053506108f3600a86610ddb565b945061087b565b949350505050565b600054610100900460ff16610999576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610367565b610516610a99565b60335473ffffffffffffffffffffffffffffffffffffffff163314610516576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610367565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16610b30576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610367565b61051633610a22565b60005b83811015610b54578181015183820152602001610b3c565b83811115610b63576000848401525b50505050565b60008151808452610b81816020860160208601610b39565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610bc66020830184610b69565b9392505050565b803573ffffffffffffffffffffffffffffffffffffffff81168114610bf157600080fd5b919050565b803567ffffffffffffffff81168114610bf157600080fd5b600080600080600060a08688031215610c2657600080fd5b610c2f86610bcd565b9450602086013593506040860135925060608601359150610c5260808701610bf6565b90509295509295909350565b60008060408385031215610c7157600080fd5b50508035926020909101359150565b600060208284031215610c9257600080fd5b610bc682610bf6565b600060208284031215610cad57600080fd5b5035919050565b600060208284031215610cc657600080fd5b610bc682610bcd565b60008451610ce1818460208901610b39565b80830190507f2e000000000000000000000000000000000000000000000000000000000000008082528551610d1d816001850160208a01610b39565b60019201918201528351610d38816002840160208801610b39565b0160020195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610da557610da5610d45565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082610dea57610dea610dac565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082821015610e3057610e30610d45565b500390565b600082610e4457610e44610dac565b500690565b60008219821115610e5c57610e5c610d45565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea164736f6c634300080f000a" var SystemConfigDeployedBin = "0x608060405234801561001057600080fd5b50600436106101005760003560e01c8063935f029e11610097578063f2fde38b11610066578063f2fde38b1461022e578063f45e65d814610241578063f68016b71461024a578063ffa1ad741461025e57600080fd5b8063935f029e146101ec578063b40a817c146101ff578063c9b26f6114610212578063e81b2c6d1461022557600080fd5b806354fd4d50116100d357806354fd4d501461019e578063715018a6146101b35780638da5cb5b146101bb5780638f974d7f146101d957600080fd5b80630c18c1621461010557806318d13918146101215780631fd19ee11461013657806329477e861461017b575b600080fd5b61010e60655481565b6040519081526020015b60405180910390f35b61013461012f366004610c6f565b610266565b005b6067546101569073ffffffffffffffffffffffffffffffffffffffff1681565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610118565b610185627a120081565b60405167ffffffffffffffff9091168152602001610118565b6101a661030c565b6040516101189190610d0b565b6101346103af565b60335473ffffffffffffffffffffffffffffffffffffffff16610156565b6101346101e7366004610d36565b6103c3565b6101346101fa366004610d95565b610663565b61013461020d366004610db7565b6106fc565b610134610220366004610dd2565b6107d4565b61010e60685481565b61013461023c366004610c6f565b610804565b61010e60665481565b6069546101859067ffffffffffffffff1681565b61010e600081565b61026e6108d7565b606780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff831690811790915560408051602080820193909352815180820390930183528101905260035b60007f1d2b0bda21d56b8bd12d4f94ebacffdfb35f5e226f84b461103bb8beab6353be836040516103009190610d0b565b60405180910390a35050565b60606103377f0000000000000000000000000000000000000000000000000000000000000000610958565b6103607f0000000000000000000000000000000000000000000000000000000000000000610958565b6103897f0000000000000000000000000000000000000000000000000000000000000000610958565b60405160200161039b93929190610deb565b604051602081830303815290604052905090565b6103b76108d7565b6103c16000610a95565b565b600054610100900460ff16158080156103e35750600054600160ff909116105b806103fd5750303b1580156103fd575060005460ff166001145b61048e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201527f647920696e697469616c697a656400000000000000000000000000000000000060648201526084015b60405180910390fd5b600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0016600117905580156104ec57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101001790555b627a120067ffffffffffffffff84161015610563576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f206c6f77006044820152606401610485565b61056b610b0c565b61057487610804565b606586905560668590556068849055606980547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff8516179055606780547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff8416179055801561065a57600080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050505050565b61066b6108d7565b606582905560668190556040805160208101849052908101829052600090606001604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190529050600160007f1d2b0bda21d56b8bd12d4f94ebacffdfb35f5e226f84b461103bb8beab6353be836040516106ef9190610d0b565b60405180910390a3505050565b6107046108d7565b627a120067ffffffffffffffff8216101561077b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f53797374656d436f6e6669673a20676173206c696d697420746f6f206c6f77006044820152606401610485565b606980547fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000001667ffffffffffffffff831690811790915560408051602080820193909352815180820390930183528101905260026102cf565b6107dc6108d7565b60688190556040805160208082018490528251808303909101815290820190915260006102cf565b61080c6108d7565b73ffffffffffffffffffffffffffffffffffffffff81166108af576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152608401610485565b6108b881610a95565b50565b73ffffffffffffffffffffffffffffffffffffffff163b151590565b60335473ffffffffffffffffffffffffffffffffffffffff1633146103c1576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610485565b60608160000361099b57505060408051808201909152600181527f3000000000000000000000000000000000000000000000000000000000000000602082015290565b8160005b81156109c557806109af81610e90565b91506109be9050600a83610ef7565b915061099f565b60008167ffffffffffffffff8111156109e0576109e0610f0b565b6040519080825280601f01601f191660200182016040528015610a0a576020820181803683370190505b5090505b8415610a8d57610a1f600183610f3a565b9150610a2c600a86610f51565b610a37906030610f65565b60f81b818381518110610a4c57610a4c610f7d565b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350610a86600a86610ef7565b9450610a0e565b949350505050565b6033805473ffffffffffffffffffffffffffffffffffffffff8381167fffffffffffffffffffffffff0000000000000000000000000000000000000000831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff16610ba3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610485565b6103c1600054610100900460ff16610c3d576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201527f6e697469616c697a696e670000000000000000000000000000000000000000006064820152608401610485565b6103c133610a95565b803573ffffffffffffffffffffffffffffffffffffffff81168114610c6a57600080fd5b919050565b600060208284031215610c8157600080fd5b610c8a82610c46565b9392505050565b60005b83811015610cac578181015183820152602001610c94565b83811115610cbb576000848401525b50505050565b60008151808452610cd9816020860160208601610c91565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b602081526000610c8a6020830184610cc1565b803567ffffffffffffffff81168114610c6a57600080fd5b60008060008060008060c08789031215610d4f57600080fd5b610d5887610c46565b9550602087013594506040870135935060608701359250610d7b60808801610d1e565b9150610d8960a08801610c46565b90509295509295509295565b60008060408385031215610da857600080fd5b50508035926020909101359150565b600060208284031215610dc957600080fd5b610c8a82610d1e565b600060208284031215610de457600080fd5b5035919050565b60008451610dfd818460208901610c91565b80830190507f2e000000000000000000000000000000000000000000000000000000000000008082528551610e39816001850160208a01610c91565b60019201918201528351610e54816002840160208801610c91565b0160020195945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60007fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203610ec157610ec1610e61565b5060010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b600082610f0657610f06610ec8565b500490565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600082821015610f4c57610f4c610e61565b500390565b600082610f6057610f60610ec8565b500690565b60008219821115610f7857610f78610e61565b500190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fdfea164736f6c634300080f000a"
func init() { func init() {
if err := json.Unmarshal([]byte(SystemConfigStorageLayoutJSON), SystemConfigStorageLayout); err != nil { if err := json.Unmarshal([]byte(SystemConfigStorageLayoutJSON), SystemConfigStorageLayout); err != nil {
......
...@@ -32,4 +32,4 @@ require ( ...@@ -32,4 +32,4 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468
...@@ -28,8 +28,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1 ...@@ -28,8 +28,8 @@ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw= github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 h1:QJL/gtfxGe11tApZIPCeKERQHrLZMAG0RwGV9eTgtvE= github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468 h1:7KgjBYDji5AKi42eRYI+n8Gs+ZJVilSASL3WBu82c3M=
github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY= github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
......
package predeploys package predeploys
import ( import (
"math/big"
"testing" "testing"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
...@@ -12,3 +15,26 @@ func TestGethAddresses(t *testing.T) { ...@@ -12,3 +15,26 @@ func TestGethAddresses(t *testing.T) {
// we import geth in the monorepo, and do not want to import op-bindings into geth. // we import geth in the monorepo, and do not want to import op-bindings into geth.
require.Equal(t, L1BlockAddr, types.L1BlockAddr) require.Equal(t, L1BlockAddr, types.L1BlockAddr)
} }
// TestL1BlockSlots ensures that the storage layout of the L1Block
// contract matches the hardcoded values in `op-geth`.
func TestL1BlockSlots(t *testing.T) {
layout, err := bindings.GetStorageLayout("L1Block")
require.NoError(t, err)
var l1BaseFeeSlot, overHeadSlot, scalarSlot common.Hash
for _, entry := range layout.Storage {
switch entry.Label {
case "l1FeeOverhead":
overHeadSlot = common.BigToHash(big.NewInt(int64(entry.Slot)))
case "l1FeeScalar":
scalarSlot = common.BigToHash(big.NewInt(int64(entry.Slot)))
case "basefee":
l1BaseFeeSlot = common.BigToHash(big.NewInt(int64(entry.Slot)))
}
}
require.Equal(t, types.OverheadSlot, overHeadSlot)
require.Equal(t, types.ScalarSlot, scalarSlot)
require.Equal(t, types.L1BaseFeeSlot, l1BaseFeeSlot)
}
...@@ -82,9 +82,10 @@ func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) { ...@@ -82,9 +82,10 @@ func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) {
uint642Big(config.GasPriceOracleScalar), uint642Big(config.GasPriceOracleScalar),
config.BatchSenderAddress.Hash(), config.BatchSenderAddress.Hash(),
gasLimit, gasLimit,
config.P2PSequencerAddress,
) )
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("cannot abi encode initialize for SystemConfig: %w", err)
} }
if _, err := upgradeProxy( if _, err := upgradeProxy(
backend, backend,
...@@ -106,7 +107,7 @@ func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) { ...@@ -106,7 +107,7 @@ func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) {
uint642Big(uint64(config.L1GenesisBlockTimestamp)), uint642Big(uint64(config.L1GenesisBlockTimestamp)),
) )
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("cannot abi encode initialize for L2OutputOracle: %w", err)
} }
if _, err := upgradeProxy( if _, err := upgradeProxy(
backend, backend,
...@@ -124,7 +125,7 @@ func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) { ...@@ -124,7 +125,7 @@ func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) {
} }
data, err = portalABI.Pack("initialize") data, err = portalABI.Pack("initialize")
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("cannot abi encode initialize for OptimismPortal: %w", err)
} }
if _, err := upgradeProxy( if _, err := upgradeProxy(
backend, backend,
...@@ -144,7 +145,7 @@ func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) { ...@@ -144,7 +145,7 @@ func BuildL1DeveloperGenesis(config *DeployConfig) (*core.Genesis, error) {
config.FinalSystemOwner, config.FinalSystemOwner,
) )
if err != nil { if err != nil {
return nil, err return nil, fmt.Errorf("cannot abi encode initialize for L1CrossDomainMessenger: %w", err)
} }
if _, err := upgradeProxy( if _, err := upgradeProxy(
backend, backend,
...@@ -268,6 +269,7 @@ func deployL1Contracts(config *DeployConfig, backend *backends.SimulatedBackend) ...@@ -268,6 +269,7 @@ func deployL1Contracts(config *DeployConfig, backend *backends.SimulatedBackend)
uint642Big(config.GasPriceOracleScalar), uint642Big(config.GasPriceOracleScalar),
config.BatchSenderAddress.Hash(), // left-padded 32 bytes value, version is zero anyway config.BatchSenderAddress.Hash(), // left-padded 32 bytes value, version is zero anyway
gasLimit, gasLimit,
config.P2PSequencerAddress,
}, },
}, },
{ {
...@@ -329,6 +331,7 @@ func l1Deployer(backend *backends.SimulatedBackend, opts *bind.TransactOpts, dep ...@@ -329,6 +331,7 @@ func l1Deployer(backend *backends.SimulatedBackend, opts *bind.TransactOpts, dep
deployment.Args[2].(*big.Int), deployment.Args[2].(*big.Int),
deployment.Args[3].(common.Hash), deployment.Args[3].(common.Hash),
deployment.Args[4].(uint64), deployment.Args[4].(uint64),
deployment.Args[5].(common.Address),
) )
case "L2OutputOracle": case "L2OutputOracle":
_, tx, _, err = bindings.DeployL2OutputOracle( _, tx, _, err = bindings.DeployL2OutputOracle(
...@@ -397,6 +400,10 @@ func l1Deployer(backend *backends.SimulatedBackend, opts *bind.TransactOpts, dep ...@@ -397,6 +400,10 @@ func l1Deployer(backend *backends.SimulatedBackend, opts *bind.TransactOpts, dep
} }
} }
if err != nil {
err = fmt.Errorf("cannot deploy %s: %w", deployment.Name, err)
}
return tx, err return tx, err
} }
......
...@@ -54,4 +54,4 @@ require ( ...@@ -54,4 +54,4 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468
...@@ -75,8 +75,8 @@ github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8E ...@@ -75,8 +75,8 @@ github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8E
github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g= github.com/envoyproxy/go-control-plane v0.6.9/go.mod h1:SBwIajubJHhxtWwsL9s8ss4safvEdbitLhGGK48rN6g=
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 h1:QJL/gtfxGe11tApZIPCeKERQHrLZMAG0RwGV9eTgtvE= github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468 h1:7KgjBYDji5AKi42eRYI+n8Gs+ZJVilSASL3WBu82c3M=
github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY= github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY=
github.com/ethereum-optimism/optimism/op-bindings v0.10.4 h1:CFn4+t0FUrBG5DmkKyYrLbGmzHWLdLv8QdUnlklvozc= github.com/ethereum-optimism/optimism/op-bindings v0.10.4 h1:CFn4+t0FUrBG5DmkKyYrLbGmzHWLdLv8QdUnlklvozc=
github.com/ethereum-optimism/optimism/op-bindings v0.10.4/go.mod h1:philKV8erP02ggjk2mRIdvJd2ZjMzpmqu0+zwwzKmNw= github.com/ethereum-optimism/optimism/op-bindings v0.10.4/go.mod h1:philKV8erP02ggjk2mRIdvJd2ZjMzpmqu0+zwwzKmNw=
github.com/ethereum-optimism/optimism/op-node v0.10.1 h1:kVBaOEOYLV22XEHRhB7dfdmoXepO0kx/RsZQK+Bpk1Y= github.com/ethereum-optimism/optimism/op-node v0.10.1 h1:kVBaOEOYLV22XEHRhB7dfdmoXepO0kx/RsZQK+Bpk1Y=
......
...@@ -105,6 +105,7 @@ func (m *MnemonicConfig) Secrets() (*Secrets, error) { ...@@ -105,6 +105,7 @@ func (m *MnemonicConfig) Secrets() (*Secrets, error) {
Alice: alice, Alice: alice,
Bob: bob, Bob: bob,
Mallory: mallory, Mallory: mallory,
Wallet: wallet,
}, nil }, nil
} }
...@@ -123,6 +124,9 @@ type Secrets struct { ...@@ -123,6 +124,9 @@ type Secrets struct {
Alice *ecdsa.PrivateKey Alice *ecdsa.PrivateKey
Bob *ecdsa.PrivateKey Bob *ecdsa.PrivateKey
Mallory *ecdsa.PrivateKey Mallory *ecdsa.PrivateKey
// Share the wallet to be able to generate more accounts
Wallet *hdwallet.Wallet
} }
// EncodePrivKey encodes the given private key in 32 bytes // EncodePrivKey encodes the given private key in 32 bytes
......
...@@ -8,10 +8,10 @@ import ( ...@@ -8,10 +8,10 @@ import (
"math/big" "math/big"
"time" "time"
"github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts/keystore" "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core" "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
...@@ -21,9 +21,45 @@ import ( ...@@ -21,9 +21,45 @@ import (
"github.com/ethereum/go-ethereum/eth/tracers" "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/miner"
"github.com/ethereum/go-ethereum/node" "github.com/ethereum/go-ethereum/node"
) )
func waitForL1OriginOnL2(l1BlockNum uint64, client *ethclient.Client, timeout time.Duration) (*types.Block, error) {
timeoutCh := time.After(timeout)
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
headChan := make(chan *types.Header, 100)
headSub, err := client.SubscribeNewHead(ctx, headChan)
if err != nil {
return nil, err
}
defer headSub.Unsubscribe()
for {
select {
case head := <-headChan:
block, err := client.BlockByNumber(ctx, head.Number)
if err != nil {
return nil, err
}
l1Info, err := derive.L1InfoDepositTxData(block.Transactions()[0].Data())
if err != nil {
return nil, err
}
if l1Info.Number >= l1BlockNum {
return block, nil
}
case err := <-headSub.Err():
return nil, fmt.Errorf("Error in head subscription: %w", err)
case <-timeoutCh:
return nil, errors.New("timeout")
}
}
}
func waitForTransaction(hash common.Hash, client *ethclient.Client, timeout time.Duration) (*types.Receipt, error) { func waitForTransaction(hash common.Hash, client *ethclient.Client, timeout time.Duration) (*types.Receipt, error) {
timeoutCh := time.After(timeout) timeoutCh := time.After(timeout)
ticker := time.NewTicker(100 * time.Millisecond) ticker := time.NewTicker(100 * time.Millisecond)
...@@ -72,7 +108,7 @@ func waitForBlock(number *big.Int, client *ethclient.Client, timeout time.Durati ...@@ -72,7 +108,7 @@ func waitForBlock(number *big.Int, client *ethclient.Client, timeout time.Durati
} }
} }
func initL1Geth(cfg *SystemConfig, genesis *core.Genesis) (*node.Node, *eth.Ethereum, error) { func initL1Geth(cfg *SystemConfig, genesis *core.Genesis, opts ...GethOption) (*node.Node, *eth.Ethereum, error) {
ethConfig := &ethconfig.Config{ ethConfig := &ethconfig.Config{
NetworkId: cfg.DeployConfig.L1ChainID, NetworkId: cfg.DeployConfig.L1ChainID,
Genesis: genesis, Genesis: genesis,
...@@ -87,7 +123,7 @@ func initL1Geth(cfg *SystemConfig, genesis *core.Genesis) (*node.Node, *eth.Ethe ...@@ -87,7 +123,7 @@ func initL1Geth(cfg *SystemConfig, genesis *core.Genesis) (*node.Node, *eth.Ethe
HTTPModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine"}, HTTPModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine"},
} }
l1Node, l1Eth, err := createGethNode(false, nodeConfig, ethConfig, []*ecdsa.PrivateKey{cfg.Secrets.CliqueSigner}) l1Node, l1Eth, err := createGethNode(false, nodeConfig, ethConfig, []*ecdsa.PrivateKey{cfg.Secrets.CliqueSigner}, opts...)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err
} }
...@@ -158,11 +194,25 @@ func defaultNodeConfig(name string, jwtPath string) *node.Config { ...@@ -158,11 +194,25 @@ func defaultNodeConfig(name string, jwtPath string) *node.Config {
} }
} }
type GethOption func(ethCfg *ethconfig.Config, nodeCfg *node.Config) error
// init a geth node. // init a geth node.
func initL2Geth(name string, l2ChainID *big.Int, genesis *core.Genesis, jwtPath string) (*node.Node, *eth.Ethereum, error) { func initL2Geth(name string, l2ChainID *big.Int, genesis *core.Genesis, jwtPath string, opts ...GethOption) (*node.Node, *eth.Ethereum, error) {
ethConfig := &ethconfig.Config{ ethConfig := &ethconfig.Config{
NetworkId: l2ChainID.Uint64(), NetworkId: l2ChainID.Uint64(),
Genesis: genesis, Genesis: genesis,
Miner: miner.Config{
Etherbase: common.Address{},
Notify: nil,
NotifyFull: false,
ExtraData: nil,
GasFloor: 0,
GasCeil: 0,
GasPrice: nil,
Recommit: 0,
Noverify: false,
NewPayloadTimeout: 0,
},
} }
nodeConfig := &node.Config{ nodeConfig := &node.Config{
Name: fmt.Sprintf("l2-geth-%v", name), Name: fmt.Sprintf("l2-geth-%v", name),
...@@ -176,14 +226,19 @@ func initL2Geth(name string, l2ChainID *big.Int, genesis *core.Genesis, jwtPath ...@@ -176,14 +226,19 @@ func initL2Geth(name string, l2ChainID *big.Int, genesis *core.Genesis, jwtPath
HTTPModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine"}, HTTPModules: []string{"debug", "admin", "eth", "txpool", "net", "rpc", "web3", "personal", "engine"},
JWTSecret: jwtPath, JWTSecret: jwtPath,
} }
return createGethNode(true, nodeConfig, ethConfig, nil) return createGethNode(true, nodeConfig, ethConfig, nil, opts...)
} }
// createGethNode creates an in-memory geth node based on the configuration. // createGethNode creates an in-memory geth node based on the configuration.
// The private keys are added to the keystore and are unlocked. // The private keys are added to the keystore and are unlocked.
// If the node is l2, catalyst is enabled. // If the node is l2, catalyst is enabled.
// The node should be started and then closed when done. // The node should be started and then closed when done.
func createGethNode(l2 bool, nodeCfg *node.Config, ethCfg *ethconfig.Config, privateKeys []*ecdsa.PrivateKey) (*node.Node, *eth.Ethereum, error) { func createGethNode(l2 bool, nodeCfg *node.Config, ethCfg *ethconfig.Config, privateKeys []*ecdsa.PrivateKey, opts ...GethOption) (*node.Node, *eth.Ethereum, error) {
for i, opt := range opts {
if err := opt(ethCfg, nodeCfg); err != nil {
return nil, nil, fmt.Errorf("failed to apply geth option %d: %w", i, err)
}
}
ethCfg.NoPruning = true // force everything to be an archive node ethCfg.NoPruning = true // force everything to be an archive node
n, err := node.New(nodeCfg) n, err := node.New(nodeCfg)
if err != nil { if err != nil {
......
...@@ -2,8 +2,6 @@ module github.com/ethereum-optimism/optimism/op-e2e ...@@ -2,8 +2,6 @@ module github.com/ethereum-optimism/optimism/op-e2e
go 1.18 go 1.18
replace github.com/ethereum-optimism/optimism/op-chain-ops v0.10.0 => ../op-chain-ops
require ( require (
github.com/docker/docker v20.10.21+incompatible github.com/docker/docker v20.10.21+incompatible
github.com/docker/go-connections v0.4.0 github.com/docker/go-connections v0.4.0
...@@ -15,6 +13,7 @@ require ( ...@@ -15,6 +13,7 @@ require (
github.com/ethereum-optimism/optimism/op-proposer v0.10.4 github.com/ethereum-optimism/optimism/op-proposer v0.10.4
github.com/ethereum-optimism/optimism/op-service v0.10.4 github.com/ethereum-optimism/optimism/op-service v0.10.4
github.com/ethereum/go-ethereum v1.10.26 github.com/ethereum/go-ethereum v1.10.26
github.com/google/gofuzz v1.2.1-0.20220503160820-4a35382e8fc8
github.com/libp2p/go-libp2p v0.23.3 github.com/libp2p/go-libp2p v0.23.3
github.com/stretchr/testify v1.8.1 github.com/stretchr/testify v1.8.1
) )
...@@ -169,4 +168,4 @@ require ( ...@@ -169,4 +168,4 @@ require (
lukechampine.com/blake3 v1.1.7 // indirect lukechampine.com/blake3 v1.1.7 // indirect
) )
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468
...@@ -157,8 +157,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m ...@@ -157,8 +157,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc=
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs=
github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 h1:QJL/gtfxGe11tApZIPCeKERQHrLZMAG0RwGV9eTgtvE= github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468 h1:7KgjBYDji5AKi42eRYI+n8Gs+ZJVilSASL3WBu82c3M=
github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY= github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY=
github.com/ethereum-optimism/optimism/op-batcher v0.10.4 h1:qLCdvVMgVja2AGbkKKG7xNW8nm+3C5rz4xagQ3Cg0sw= github.com/ethereum-optimism/optimism/op-batcher v0.10.4 h1:qLCdvVMgVja2AGbkKKG7xNW8nm+3C5rz4xagQ3Cg0sw=
github.com/ethereum-optimism/optimism/op-batcher v0.10.4/go.mod h1:a19oViWrL7dy1pPSIa2Dgsv8o97HOzVtKx+m2J5qQqY= github.com/ethereum-optimism/optimism/op-batcher v0.10.4/go.mod h1:a19oViWrL7dy1pPSIa2Dgsv8o97HOzVtKx+m2J5qQqY=
github.com/ethereum-optimism/optimism/op-bindings v0.10.4 h1:CFn4+t0FUrBG5DmkKyYrLbGmzHWLdLv8QdUnlklvozc= github.com/ethereum-optimism/optimism/op-bindings v0.10.4 h1:CFn4+t0FUrBG5DmkKyYrLbGmzHWLdLv8QdUnlklvozc=
...@@ -275,6 +275,7 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ ...@@ -275,6 +275,7 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.1-0.20220503160820-4a35382e8fc8 h1:Ep/joEub9YwcjRY6ND3+Y/w0ncE540RtGatVhtZL0/Q= github.com/google/gofuzz v1.2.1-0.20220503160820-4a35382e8fc8 h1:Ep/joEub9YwcjRY6ND3+Y/w0ncE540RtGatVhtZL0/Q=
github.com/google/gofuzz v1.2.1-0.20220503160820-4a35382e8fc8/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM=
github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8=
github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo=
......
...@@ -144,6 +144,7 @@ func DefaultSystemConfig(t *testing.T) SystemConfig { ...@@ -144,6 +144,7 @@ func DefaultSystemConfig(t *testing.T) SystemConfig {
"batcher": testlog.Logger(t, log.LvlInfo).New("role", "batcher"), "batcher": testlog.Logger(t, log.LvlInfo).New("role", "batcher"),
"proposer": testlog.Logger(t, log.LvlCrit).New("role", "proposer"), "proposer": testlog.Logger(t, log.LvlCrit).New("role", "proposer"),
}, },
GethOptions: map[string][]GethOption{},
P2PTopology: nil, // no P2P connectivity by default P2PTopology: nil, // no P2P connectivity by default
NonFinalizedProposals: false, NonFinalizedProposals: false,
} }
...@@ -175,6 +176,7 @@ type SystemConfig struct { ...@@ -175,6 +176,7 @@ type SystemConfig struct {
Premine map[common.Address]*big.Int Premine map[common.Address]*big.Int
Nodes map[string]*rollupNode.Config // Per node config. Don't use populate rollup.Config Nodes map[string]*rollupNode.Config // Per node config. Don't use populate rollup.Config
Loggers map[string]log.Logger Loggers map[string]log.Logger
GethOptions map[string][]GethOption
ProposerLogger log.Logger ProposerLogger log.Logger
BatcherLogger log.Logger BatcherLogger log.Logger
...@@ -265,6 +267,21 @@ func (cfg SystemConfig) Start() (*System, error) { ...@@ -265,6 +267,21 @@ func (cfg SystemConfig) Start() (*System, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
for addr, amount := range cfg.Premine {
if existing, ok := l2Genesis.Alloc[addr]; ok {
l2Genesis.Alloc[addr] = core.GenesisAccount{
Code: existing.Code,
Storage: existing.Storage,
Balance: amount,
Nonce: existing.Nonce,
}
} else {
l2Genesis.Alloc[addr] = core.GenesisAccount{
Balance: amount,
Nonce: 0,
}
}
}
makeRollupConfig := func() rollup.Config { makeRollupConfig := func() rollup.Config {
return rollup.Config{ return rollup.Config{
...@@ -296,7 +313,7 @@ func (cfg SystemConfig) Start() (*System, error) { ...@@ -296,7 +313,7 @@ func (cfg SystemConfig) Start() (*System, error) {
sys.RollupConfig = &defaultConfig sys.RollupConfig = &defaultConfig
// Initialize nodes // Initialize nodes
l1Node, l1Backend, err := initL1Geth(&cfg, l1Genesis) l1Node, l1Backend, err := initL1Geth(&cfg, l1Genesis, cfg.GethOptions["l1"]...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -304,7 +321,7 @@ func (cfg SystemConfig) Start() (*System, error) { ...@@ -304,7 +321,7 @@ func (cfg SystemConfig) Start() (*System, error) {
sys.Backends["l1"] = l1Backend sys.Backends["l1"] = l1Backend
for name := range cfg.Nodes { for name := range cfg.Nodes {
node, backend, err := initL2Geth(name, big.NewInt(int64(cfg.DeployConfig.L2ChainID)), l2Genesis, cfg.JWTFilePath) node, backend, err := initL2Geth(name, big.NewInt(int64(cfg.DeployConfig.L2ChainID)), l2Genesis, cfg.JWTFilePath, cfg.GethOptions[name]...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
......
...@@ -15,9 +15,11 @@ import ( ...@@ -15,9 +15,11 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethclient/gethclient" "github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/ethereum/go-ethereum/log" "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/libp2p/go-libp2p/core/peer" "github.com/libp2p/go-libp2p/core/peer"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
...@@ -288,6 +290,67 @@ func TestConfirmationDepth(t *testing.T) { ...@@ -288,6 +290,67 @@ func TestConfirmationDepth(t *testing.T) {
require.LessOrEqual(t, verInfo.Number+verConfDepth, l1Head.NumberU64(), "the ver L2 head block should have an origin older than the L1 head block by at least the verifier conf depth") require.LessOrEqual(t, verInfo.Number+verConfDepth, l1Head.NumberU64(), "the ver L2 head block should have an origin older than the L1 head block by at least the verifier conf depth")
} }
// TestPendingGasLimit tests the configuration of the gas limit of the pending block,
// and if it does not conflict with the regular gas limit on the verifier or sequencer.
func TestPendingGasLimit(t *testing.T) {
parallel(t)
if !verboseGethNodes {
log.Root().SetHandler(log.DiscardHandler())
}
cfg := DefaultSystemConfig(t)
// configure the L2 gas limit to be high, and the pending gas limits to be lower for resource saving.
cfg.DeployConfig.L2GenesisBlockGasLimit = 20_000_000
cfg.GethOptions["sequencer"] = []GethOption{
func(ethCfg *ethconfig.Config, nodeCfg *node.Config) error {
ethCfg.Miner.GasCeil = 10_000_000
return nil
},
}
cfg.GethOptions["verifier"] = []GethOption{
func(ethCfg *ethconfig.Config, nodeCfg *node.Config) error {
ethCfg.Miner.GasCeil = 9_000_000
return nil
},
}
sys, err := cfg.Start()
require.Nil(t, err, "Error starting up system")
defer sys.Close()
log := testlog.Logger(t, log.LvlInfo)
log.Info("genesis", "l2", sys.RollupConfig.Genesis.L2, "l1", sys.RollupConfig.Genesis.L1, "l2_time", sys.RollupConfig.Genesis.L2Time)
l2Verif := sys.Clients["verifier"]
l2Seq := sys.Clients["sequencer"]
checkGasLimit := func(client *ethclient.Client, number *big.Int, expected uint64) *types.Header {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
header, err := client.HeaderByNumber(ctx, number)
cancel()
require.NoError(t, err)
require.Equal(t, expected, header.GasLimit)
return header
}
// check if the gaslimits are matching the expected values,
// and that the verifier/sequencer can use their locally configured gas limit for the pending block.
for {
checkGasLimit(l2Seq, big.NewInt(-1), 10_000_000)
checkGasLimit(l2Verif, big.NewInt(-1), 9_000_000)
checkGasLimit(l2Seq, nil, 20_000_000)
latestVerifHeader := checkGasLimit(l2Verif, nil, 20_000_000)
// Stop once the verifier passes genesis:
// this implies we checked a new block from the sequencer, on both sequencer and verifier nodes.
if latestVerifHeader.Number.Uint64() > 0 {
break
}
time.Sleep(500 * time.Millisecond)
}
}
// TestFinalize tests if L2 finalizes after sufficient time after L1 finalizes // TestFinalize tests if L2 finalizes after sufficient time after L1 finalizes
func TestFinalize(t *testing.T) { func TestFinalize(t *testing.T) {
parallel(t) parallel(t)
......
package op_e2e
import (
"bytes"
"context"
"crypto/ecdsa"
"fmt"
"math/big"
"math/rand"
"testing"
"time"
"github.com/ethereum-optimism/optimism/op-bindings/bindings"
"github.com/ethereum-optimism/optimism/op-bindings/predeploys"
"github.com/ethereum-optimism/optimism/op-e2e/e2eutils"
"github.com/ethereum-optimism/optimism/op-node/rollup/derive"
"github.com/ethereum-optimism/optimism/op-node/testutils/fuzzerutils"
"github.com/ethereum-optimism/optimism/op-node/withdrawals"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/abi"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/ethclient/gethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
fuzz "github.com/google/gofuzz"
"github.com/stretchr/testify/require"
)
// TestGasPriceOracleFeeUpdates checks that the gas price oracle cannot be locked by mis-configuring parameters.
func TestGasPriceOracleFeeUpdates(t *testing.T) {
parallel(t)
// Define our values to set in the GasPriceOracle (we set them high to see if it can lock L2 or stop bindings
// from updating the prices once again.
overheadValue := abi.MaxUint256
scalarValue := abi.MaxUint256
var cancel context.CancelFunc
// Setup our logger handler
if !verboseGethNodes {
log.Root().SetHandler(log.DiscardHandler())
}
// Create our system configuration for L1/L2 and start it
cfg := DefaultSystemConfig(t)
sys, err := cfg.Start()
require.Nil(t, err, "Error starting up system")
defer sys.Close()
// Obtain our sequencer, verifier, and transactor keypair.
l1Client := sys.Clients["l1"]
l2Seq := sys.Clients["sequencer"]
// l2Verif := sys.Clients["verifier"]
ethPrivKey := cfg.Secrets.SysCfgOwner
// Bind to the SystemConfig & GasPriceOracle contracts
sysconfig, err := bindings.NewSystemConfig(predeploys.DevSystemConfigAddr, l1Client)
require.Nil(t, err)
gpoContract, err := bindings.NewGasPriceOracleCaller(predeploys.GasPriceOracleAddr, l2Seq)
require.Nil(t, err)
// Obtain our signer.
opts, err := bind.NewKeyedTransactorWithChainID(ethPrivKey, cfg.L1ChainIDBig())
require.Nil(t, err)
// Define our L1 transaction timeout duration.
txTimeoutDuration := 10 * time.Duration(cfg.DeployConfig.L1BlockTime) * time.Second
// Update the gas config, wait for it to show up on L2, & verify that it was set as intended
opts.Context, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
tx, err := sysconfig.SetGasConfig(opts, overheadValue, scalarValue)
cancel()
require.Nil(t, err, "sending overhead update tx")
receipt, err := waitForTransaction(tx.Hash(), l1Client, txTimeoutDuration)
require.Nil(t, err, "waiting for sysconfig set gas config update tx")
require.Equal(t, receipt.Status, types.ReceiptStatusSuccessful, "transaction failed")
_, err = waitForL1OriginOnL2(receipt.BlockNumber.Uint64(), l2Seq, txTimeoutDuration)
require.NoError(t, err, "waiting for L2 block to include the sysconfig update")
gpoOverhead, err := gpoContract.Overhead(&bind.CallOpts{})
require.Nil(t, err, "reading gpo overhead")
gpoScalar, err := gpoContract.Scalar(&bind.CallOpts{})
require.Nil(t, err, "reading gpo scalar")
if gpoOverhead.Cmp(overheadValue) != 0 {
t.Errorf("overhead that was found (%v) is not what was set (%v)", gpoOverhead, overheadValue)
}
if gpoScalar.Cmp(scalarValue) != 0 {
t.Errorf("scalar that was found (%v) is not what was set (%v)", gpoScalar, scalarValue)
}
// Now modify the scalar value & ensure that the gas params can be modified
scalarValue = big.NewInt(params.Ether)
opts.Context, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
tx, err = sysconfig.SetGasConfig(opts, overheadValue, scalarValue)
cancel()
require.Nil(t, err, "sending overhead update tx")
receipt, err = waitForTransaction(tx.Hash(), l1Client, txTimeoutDuration)
require.Nil(t, err, "waiting for sysconfig set gas config update tx")
require.Equal(t, receipt.Status, types.ReceiptStatusSuccessful, "transaction failed")
_, err = waitForL1OriginOnL2(receipt.BlockNumber.Uint64(), l2Seq, txTimeoutDuration)
require.NoError(t, err, "waiting for L2 block to include the sysconfig update")
gpoOverhead, err = gpoContract.Overhead(&bind.CallOpts{})
require.Nil(t, err, "reading gpo overhead")
gpoScalar, err = gpoContract.Scalar(&bind.CallOpts{})
require.Nil(t, err, "reading gpo scalar")
if gpoOverhead.Cmp(overheadValue) != 0 {
t.Errorf("overhead that was found (%v) is not what was set (%v)", gpoOverhead, overheadValue)
}
if gpoScalar.Cmp(scalarValue) != 0 {
t.Errorf("scalar that was found (%v) is not what was set (%v)", gpoScalar, scalarValue)
}
}
// TestL2SequencerRPCDepositTx checks that the L2 sequencer will not accept DepositTx type transactions.
// The acceptance of these transactions would allow for arbitrary minting of ETH in L2.
func TestL2SequencerRPCDepositTx(t *testing.T) {
parallel(t)
// Setup our logger handler
if !verboseGethNodes {
log.Root().SetHandler(log.DiscardHandler())
}
// Create our system configuration for L1/L2 and start it
cfg := DefaultSystemConfig(t)
sys, err := cfg.Start()
require.Nil(t, err, "Error starting up system")
defer sys.Close()
// Obtain our sequencer, verifier, and transactor keypair.
l2Seq := sys.Clients["sequencer"]
l2Verif := sys.Clients["verifier"]
txSigningKey := sys.cfg.Secrets.Alice
require.Nil(t, err)
// Create a deposit tx to send over RPC.
tx := types.NewTx(&types.DepositTx{
SourceHash: common.Hash{},
From: crypto.PubkeyToAddress(txSigningKey.PublicKey),
To: &common.Address{0xff, 0xff},
Mint: big.NewInt(1000),
Value: big.NewInt(1000),
Gas: 0,
IsSystemTransaction: false,
Data: nil,
})
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
err = l2Seq.SendTransaction(ctx, tx)
cancel()
require.Error(t, err, "a DepositTx was accepted by L2 sequencer over RPC when it should not have been.")
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
err = l2Verif.SendTransaction(ctx, tx)
cancel()
require.Error(t, err, "a DepositTx was accepted by L2 verifier over RPC when it should not have been.")
}
// TestAccount defines an account generated by startConfigWithTestAccounts
type TestAccount struct {
HDPath string
Key *ecdsa.PrivateKey
Addr common.Address
L1Opts *bind.TransactOpts
L2Opts *bind.TransactOpts
}
// startConfigWithTestAccounts takes a SystemConfig, generates additional accounts, adds them to the config, so they
// are funded on startup, starts the system, and imports the keys into the keystore, and obtains transaction opts for
// each account.
func startConfigWithTestAccounts(cfg *SystemConfig, accountsToGenerate int) (*System, []*TestAccount, error) {
// Create our test accounts and add them to the pre-mine cfg.
testAccounts := make([]*TestAccount, 0)
var err error
for i := 0; i < accountsToGenerate; i++ {
// Create our test account and add it to our list
testAccount := &TestAccount{
HDPath: fmt.Sprintf("m/44'/60'/0'/0/%d", 1000+i), // offset by 1000 to avoid collisions.
Key: nil,
L1Opts: nil,
L2Opts: nil,
}
testAccounts = append(testAccounts, testAccount)
// Obtain our generated private key
testAccount.Key, err = cfg.Secrets.Wallet.PrivateKey(accounts.Account{
URL: accounts.URL{
Path: testAccount.HDPath,
},
})
if err != nil {
return nil, nil, err
}
testAccount.Addr = crypto.PubkeyToAddress(testAccount.Key.PublicKey)
// Obtain the transaction options for contract bindings for this account.
testAccount.L1Opts, err = bind.NewKeyedTransactorWithChainID(testAccount.Key, cfg.L1ChainIDBig())
if err != nil {
return nil, nil, err
}
testAccount.L2Opts, err = bind.NewKeyedTransactorWithChainID(testAccount.Key, cfg.L2ChainIDBig())
if err != nil {
return nil, nil, err
}
// Fund the test account in our config
cfg.Premine[testAccount.Addr] = big.NewInt(params.Ether)
cfg.Premine[testAccount.Addr] = cfg.Premine[testAccount.Addr].Mul(cfg.Premine[testAccount.Addr], big.NewInt(1_000_000))
}
// Start our system
sys, err := cfg.Start()
if err != nil {
return sys, nil, err
}
// Return our results.
return sys, testAccounts, err
}
// TestMixedDepositValidity makes a number of deposit transactions, some which will succeed in transferring value,
// while others do not. It ensures that the expected nonces/balances match after several interactions.
func TestMixedDepositValidity(t *testing.T) {
parallel(t)
// Define how many deposit txs we'll make. Each deposit mints a fixed amount and transfers up to 1/3 of the user's
// balance. As such, this number cannot be too high or else the test will always fail due to lack of balance in L1.
const depositTxCount = 15
// Define how many accounts we'll use to deposit funds
const accountUsedToDeposit = 5
// Setup our logger handler
if !verboseGethNodes {
log.Root().SetHandler(log.DiscardHandler())
}
// Create our system configuration, funding all accounts we created for L1/L2, and start it
cfg := DefaultSystemConfig(t)
sys, testAccounts, err := startConfigWithTestAccounts(&cfg, accountUsedToDeposit)
require.Nil(t, err, "Error starting up system")
defer sys.Close()
// Obtain our sequencer, verifier, and transactor keypair.
l1Client := sys.Clients["l1"]
l2Seq := sys.Clients["sequencer"]
l2Verif := sys.Clients["verifier"]
require.NoError(t, err)
// Define our L1 transaction timeout duration.
txTimeoutDuration := 10 * time.Duration(cfg.DeployConfig.L1BlockTime) * time.Second
// Bind to the deposit contract
depositContract, err := bindings.NewOptimismPortal(predeploys.DevOptimismPortalAddr, l1Client)
require.NoError(t, err)
// Create a struct used to track our transactors and their transactions sent.
type TestAccountState struct {
Account *TestAccount
ExpectedL1Balance *big.Int
ExpectedL2Balance *big.Int
StartingL1Nonce uint64
ExpectedL1Nonce uint64
StartingL2Nonce uint64
ExpectedL2Nonce uint64
}
// Create the state objects for every test account we'll track changes for.
transactors := make([]*TestAccountState, 0)
for i := 0; i < len(testAccounts); i++ {
// Obtain our account
testAccount := testAccounts[i]
// Obtain the transactor's starting nonce on L1.
ctx, cancel := context.WithTimeout(context.Background(), txTimeoutDuration)
startL1Nonce, err := l1Client.NonceAt(ctx, testAccount.L1Opts.From, nil)
cancel()
require.NoError(t, err)
// Obtain the transactor's starting balance on L2.
ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
startL2Balance, err := l2Verif.BalanceAt(ctx, testAccount.L2Opts.From, nil)
cancel()
require.NoError(t, err)
// Obtain the transactor's starting nonce on L2.
ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
startL2Nonce, err := l2Verif.NonceAt(ctx, testAccount.L2Opts.From, nil)
cancel()
require.NoError(t, err)
// Add our transactor to our list
transactors = append(transactors, &TestAccountState{
Account: testAccount,
ExpectedL2Balance: startL2Balance,
ExpectedL1Nonce: startL1Nonce,
ExpectedL2Nonce: startL2Nonce,
})
}
// Create our random provider
randomProvider := rand.New(rand.NewSource(time.Now().Unix()))
// Now we create a number of deposits from each transactor
for i := 0; i < depositTxCount; i++ {
// Determine if this deposit should succeed in transferring value (not minting)
validTransfer := randomProvider.Int()%2 == 0
// Determine the transactor to use
transactorIndex := randomProvider.Int() % len(transactors)
transactor := transactors[transactorIndex]
// Determine the transactor to receive the deposit
receiverIndex := randomProvider.Int() % len(transactors)
receiver := transactors[receiverIndex]
toAddr := receiver.Account.L2Opts.From
// Create our L1 deposit transaction and send it.
mintAmount := big.NewInt(randomProvider.Int63() % 9_000_000)
transactor.Account.L1Opts.Value = mintAmount
var transferValue *big.Int
if validTransfer {
transferValue = new(big.Int).Div(transactor.ExpectedL2Balance, common.Big3) // send 1/3 our balance which should succeed.
} else {
transferValue = new(big.Int).Mul(common.Big2, transactor.ExpectedL2Balance) // trigger a revert by trying to transfer our current balance * 2
}
tx, err := depositContract.DepositTransaction(transactor.Account.L1Opts, toAddr, transferValue, 100_000, false, nil)
require.Nil(t, err, "with deposit tx")
// Wait for the deposit tx to appear in L1.
receipt, err := waitForTransaction(tx.Hash(), l1Client, txTimeoutDuration)
require.Nil(t, err, "Waiting for deposit tx on L1")
require.Equal(t, receipt.Status, types.ReceiptStatusSuccessful)
// Reconstruct the L2 tx hash to wait for the deposit in L2.
reconstructedDep, err := derive.UnmarshalDepositLogEvent(receipt.Logs[0])
require.NoError(t, err, "Could not reconstruct L2 Deposit")
tx = types.NewTx(reconstructedDep)
receipt, err = waitForTransaction(tx.Hash(), l2Verif, txTimeoutDuration)
require.NoError(t, err)
// Verify the result of the L2 tx receipt. Based on how much we transferred it should be successful/failed.
if validTransfer {
require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status, "Transaction should have succeeded")
} else {
require.Equal(t, types.ReceiptStatusFailed, receipt.Status, "Transaction should have failed")
}
// Update our expected balances.
if validTransfer && transactor != receiver {
// Transactor balances changes by minted minus transferred value.
transactor.ExpectedL2Balance = new(big.Int).Add(transactor.ExpectedL2Balance, new(big.Int).Sub(mintAmount, transferValue))
// Receiver balance changes by transferred value.
receiver.ExpectedL2Balance = new(big.Int).Add(receiver.ExpectedL2Balance, transferValue)
} else {
// If the transfer failed, minting should've still succeeded but the balance shouldn't have transferred
// to the recipient.
transactor.ExpectedL2Balance = new(big.Int).Add(transactor.ExpectedL2Balance, mintAmount)
}
transactor.ExpectedL1Nonce = transactor.ExpectedL1Nonce + 1
transactor.ExpectedL2Nonce = transactor.ExpectedL2Nonce + 1
}
// At the end, assert our account balance/nonce states.
for _, transactor := range transactors {
// Obtain the L1 account nonce
ctx, cancel := context.WithTimeout(context.Background(), txTimeoutDuration)
endL1Nonce, err := l1Client.NonceAt(ctx, transactor.Account.L1Opts.From, nil)
cancel()
require.NoError(t, err)
// Obtain the L2 sequencer account balance
ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
endL2SeqBalance, err := l2Seq.BalanceAt(ctx, transactor.Account.L2Opts.From, nil)
cancel()
require.NoError(t, err)
// Obtain the L2 sequencer account nonce
ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
endL2SeqNonce, err := l2Seq.NonceAt(ctx, transactor.Account.L2Opts.From, nil)
cancel()
require.NoError(t, err)
// Obtain the L2 verifier account balance
ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
endL2VerifBalance, err := l2Verif.BalanceAt(ctx, transactor.Account.L2Opts.From, nil)
cancel()
require.NoError(t, err)
// Obtain the L2 verifier account nonce
ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
endL2VerifNonce, err := l2Verif.NonceAt(ctx, transactor.Account.L2Opts.From, nil)
cancel()
require.NoError(t, err)
require.Equal(t, transactor.ExpectedL1Nonce, endL1Nonce, "Unexpected L1 nonce for transactor")
require.Equal(t, transactor.ExpectedL2Nonce, endL2SeqNonce, "Unexpected L2 sequencer nonce for transactor")
require.Equal(t, transactor.ExpectedL2Balance, endL2SeqBalance, "Unexpected L2 sequencer balance for transactor")
require.Equal(t, transactor.ExpectedL2Nonce, endL2VerifNonce, "Unexpected L2 verifier nonce for transactor")
require.Equal(t, transactor.ExpectedL2Balance, endL2VerifBalance, "Unexpected L2 verifier balance for transactor")
}
}
// TestMixedWithdrawalValidity makes a number of withdrawal transactions and ensures ones with modified parameters are
// rejected while unmodified ones are accepted. This runs test cases in different systems.
func TestMixedWithdrawalValidity(t *testing.T) {
parallel(t)
// Setup our logger handler
if !verboseGethNodes {
log.Root().SetHandler(log.DiscardHandler())
}
// There are 7 different fields we try modifying to cause a failure, plus one "good" test result we test.
for i := 0; i <= 8; i++ {
t.Run(fmt.Sprintf("withdrawal test#%d", i+1), func(t *testing.T) {
// Create our system configuration, funding all accounts we created for L1/L2, and start it
cfg := DefaultSystemConfig(t)
cfg.DeployConfig.FinalizationPeriodSeconds = 6
sys, err := cfg.Start()
require.NoError(t, err, "error starting up system")
defer sys.Close()
// Obtain our sequencer, verifier, and transactor keypair.
l1Client := sys.Clients["l1"]
l2Seq := sys.Clients["sequencer"]
l2Verif := sys.Clients["verifier"]
require.NoError(t, err)
// Define our L1 transaction timeout duration.
txTimeoutDuration := 10 * time.Duration(cfg.DeployConfig.L1BlockTime) * time.Second
// Bind to the deposit contract
depositContract, err := bindings.NewOptimismPortal(predeploys.DevOptimismPortalAddr, l1Client)
_ = depositContract
require.NoError(t, err)
// Create a struct used to track our transactors and their transactions sent.
type TestAccountState struct {
Account *TestAccount
ExpectedL1Balance *big.Int
ExpectedL2Balance *big.Int
ExpectedL1Nonce uint64
ExpectedL2Nonce uint64
}
// Create a test account state for our transactor.
transactorKey := cfg.Secrets.Alice
transactor := &TestAccountState{
Account: &TestAccount{
HDPath: e2eutils.DefaultMnemonicConfig.Alice,
Key: transactorKey,
L1Opts: nil,
L2Opts: nil,
},
ExpectedL1Balance: nil,
ExpectedL2Balance: nil,
ExpectedL1Nonce: 0,
ExpectedL2Nonce: 0,
}
transactor.Account.L1Opts, err = bind.NewKeyedTransactorWithChainID(transactor.Account.Key, cfg.L1ChainIDBig())
require.NoError(t, err)
transactor.Account.L2Opts, err = bind.NewKeyedTransactorWithChainID(transactor.Account.Key, cfg.L2ChainIDBig())
require.NoError(t, err)
// Obtain the transactor's starting balance on L1.
ctx, cancel := context.WithTimeout(context.Background(), txTimeoutDuration)
transactor.ExpectedL1Balance, err = l1Client.BalanceAt(ctx, transactor.Account.L1Opts.From, nil)
cancel()
require.NoError(t, err)
// Obtain the transactor's starting balance on L2.
ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
transactor.ExpectedL2Balance, err = l2Verif.BalanceAt(ctx, transactor.Account.L2Opts.From, nil)
cancel()
require.NoError(t, err)
// Bind to the L2-L1 message passer
l2l1MessagePasser, err := bindings.NewL2ToL1MessagePasser(predeploys.L2ToL1MessagePasserAddr, l2Seq)
require.NoError(t, err, "error binding to message passer on L2")
// Create our fuzzer wrapper to generate complex values (despite this not being a fuzz test, this is still a useful
// provider to fill complex data structures).
typeProvider := fuzz.NewWithSeed(time.Now().Unix()).NilChance(0).MaxDepth(10000).NumElements(0, 0x100)
fuzzerutils.AddFuzzerFunctions(typeProvider)
// Now we create a number of withdrawals from each transactor
// Determine the address our request will come from
fromAddr := crypto.PubkeyToAddress(transactor.Account.Key.PublicKey)
// Initiate Withdrawal
withdrawAmount := big.NewInt(500_000_000_000)
transactor.Account.L2Opts.Value = withdrawAmount
tx, err := l2l1MessagePasser.InitiateWithdrawal(transactor.Account.L2Opts, fromAddr, big.NewInt(21000), nil)
require.Nil(t, err, "sending initiate withdraw tx")
// Wait for the transaction to appear in L2 verifier
receipt, err := waitForTransaction(tx.Hash(), l2Verif, txTimeoutDuration)
require.Nil(t, err, "withdrawal initiated on L2 sequencer")
require.Equal(t, receipt.Status, types.ReceiptStatusSuccessful, "transaction failed")
// Obtain the header for the block containing the transaction (used to calculate gas fees)
ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
header, err := l2Verif.HeaderByNumber(ctx, receipt.BlockNumber)
cancel()
require.Nil(t, err)
// Calculate gas fees for the withdrawal in L2 to later adjust our balance.
withdrawalL2GasFee := calcGasFees(receipt.GasUsed, tx.GasTipCap(), tx.GasFeeCap(), header.BaseFee)
// Adjust our expected L2 balance (should've decreased by withdraw amount + fees)
transactor.ExpectedL2Balance = new(big.Int).Sub(transactor.ExpectedL2Balance, withdrawAmount)
transactor.ExpectedL2Balance = new(big.Int).Sub(transactor.ExpectedL2Balance, withdrawalL2GasFee)
transactor.ExpectedL2Balance = new(big.Int).Sub(transactor.ExpectedL2Balance, receipt.L1Fee)
transactor.ExpectedL2Nonce = transactor.ExpectedL2Nonce + 1
// Wait for the finalization period, then we can finalize this withdrawal.
ctx, cancel = context.WithTimeout(context.Background(), 20*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second)
blockNumber, err := withdrawals.WaitForFinalizationPeriod(ctx, l1Client, predeploys.DevOptimismPortalAddr, receipt.BlockNumber)
cancel()
require.Nil(t, err)
ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
header, err = l2Verif.HeaderByNumber(ctx, new(big.Int).SetUint64(blockNumber))
cancel()
require.Nil(t, err)
l2OutputOracle, err := bindings.NewL2OutputOracleCaller(predeploys.DevL2OutputOracleAddr, l1Client)
require.Nil(t, err)
rpcClient, err := rpc.Dial(sys.Nodes["verifier"].WSEndpoint())
require.Nil(t, err)
proofCl := gethclient.New(rpcClient)
receiptCl := ethclient.NewClient(rpcClient)
// Now create the withdrawal
params, err := withdrawals.ProveWithdrawalParameters(context.Background(), proofCl, receiptCl, tx.Hash(), header, l2OutputOracle)
require.Nil(t, err)
// Obtain our withdrawal parameters
withdrawalTransaction := &bindings.TypesWithdrawalTransaction{
Nonce: params.Nonce,
Sender: params.Sender,
Target: params.Target,
Value: params.Value,
GasLimit: params.GasLimit,
Data: params.Data,
}
l2OutputIndexParam := params.L2OutputIndex
outputRootProofParam := params.OutputRootProof
withdrawalProofParam := params.WithdrawalProof
// Determine if this will be a bad withdrawal.
badWithdrawal := i < 8
if badWithdrawal {
// Select a field to overwrite depending on which test case this is.
fieldIndex := i
// We ensure that each field changes to something different.
if fieldIndex == 0 {
originalValue := new(big.Int).Set(withdrawalTransaction.Nonce)
for originalValue.Cmp(withdrawalTransaction.Nonce) == 0 {
typeProvider.Fuzz(&withdrawalTransaction.Nonce)
}
} else if fieldIndex == 1 {
originalValue := withdrawalTransaction.Sender
for originalValue == withdrawalTransaction.Sender {
typeProvider.Fuzz(&withdrawalTransaction.Sender)
}
} else if fieldIndex == 2 {
originalValue := withdrawalTransaction.Target
for originalValue == withdrawalTransaction.Target {
typeProvider.Fuzz(&withdrawalTransaction.Target)
}
} else if fieldIndex == 3 {
originalValue := new(big.Int).Set(withdrawalTransaction.Value)
for originalValue.Cmp(withdrawalTransaction.Value) == 0 {
typeProvider.Fuzz(&withdrawalTransaction.Value)
}
} else if fieldIndex == 4 {
originalValue := new(big.Int).Set(withdrawalTransaction.GasLimit)
for originalValue.Cmp(withdrawalTransaction.GasLimit) == 0 {
typeProvider.Fuzz(&withdrawalTransaction.GasLimit)
}
} else if fieldIndex == 5 {
originalValue := new(big.Int).Set(l2OutputIndexParam)
for originalValue.Cmp(l2OutputIndexParam) == 0 {
typeProvider.Fuzz(&l2OutputIndexParam)
}
} else if fieldIndex == 6 {
// TODO: this is a large structure that is unlikely to ever produce the same value, however we should
// verify that we actually generated different values.
typeProvider.Fuzz(&outputRootProofParam)
} else if fieldIndex == 7 {
typeProvider.Fuzz(&withdrawalProofParam)
originalValue := make([][]byte, len(withdrawalProofParam))
for i := 0; i < len(withdrawalProofParam); i++ {
originalValue[i] = make([]byte, len(withdrawalProofParam[i]))
copy(originalValue[i], withdrawalProofParam[i])
for bytes.Equal(originalValue[i], withdrawalProofParam[i]) {
typeProvider.Fuzz(&withdrawalProofParam[i])
}
}
}
}
// Prove withdrawal. This checks the proof so we only finalize if this succeeds
tx, err = depositContract.ProveWithdrawalTransaction(
transactor.Account.L1Opts,
*withdrawalTransaction,
l2OutputIndexParam,
outputRootProofParam,
withdrawalProofParam,
)
// If we had a bad withdrawal, we don't update some expected value and skip to processing the next
// withdrawal. Otherwise, if it was valid, this should've succeeded so we proceed with updating our expected
// values and asserting no errors occurred.
if badWithdrawal {
require.Error(t, err)
} else {
require.NoError(t, err)
receipt, err = waitForTransaction(tx.Hash(), l1Client, txTimeoutDuration)
require.Nil(t, err, "finalize withdrawal")
require.Equal(t, types.ReceiptStatusSuccessful, receipt.Status)
// Verify balance after withdrawal
ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
header, err = l1Client.HeaderByNumber(ctx, receipt.BlockNumber)
cancel()
require.Nil(t, err)
// Ensure that withdrawal - gas fees are added to the L1 balance
// Fun fact, the fee is greater than the withdrawal amount
withdrawalL1GasFee := calcGasFees(receipt.GasUsed, tx.GasTipCap(), tx.GasFeeCap(), header.BaseFee)
transactor.ExpectedL1Balance = new(big.Int).Add(transactor.ExpectedL2Balance, withdrawAmount)
transactor.ExpectedL1Balance = new(big.Int).Sub(transactor.ExpectedL2Balance, withdrawalL1GasFee)
transactor.ExpectedL1Nonce++
// Ensure that our withdrawal was proved successfully
proveReceipt, err := waitForTransaction(tx.Hash(), l1Client, 3*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second)
require.Nil(t, err, "prove withdrawal")
require.Equal(t, types.ReceiptStatusSuccessful, proveReceipt.Status)
// Wait for finalization and then create the Finalized Withdrawal Transaction
ctx, cancel = context.WithTimeout(context.Background(), 40*time.Duration(cfg.DeployConfig.L1BlockTime)*time.Second)
defer cancel()
_, err = withdrawals.WaitForFinalizationPeriod(ctx, l1Client, predeploys.DevOptimismPortalAddr, header.Number)
require.Nil(t, err)
// Finalize withdrawal
_, err = depositContract.FinalizeWithdrawalTransaction(
transactor.Account.L1Opts,
*withdrawalTransaction,
)
require.NoError(t, err)
}
// At the end, assert our account balance/nonce states.
// Obtain the L2 sequencer account balance
ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
endL1Balance, err := l1Client.BalanceAt(ctx, transactor.Account.L1Opts.From, nil)
cancel()
require.NoError(t, err)
// Obtain the L1 account nonce
ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
endL1Nonce, err := l1Client.NonceAt(ctx, transactor.Account.L1Opts.From, nil)
cancel()
require.NoError(t, err)
// Obtain the L2 sequencer account balance
ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
endL2SeqBalance, err := l2Seq.BalanceAt(ctx, transactor.Account.L1Opts.From, nil)
cancel()
require.NoError(t, err)
// Obtain the L2 sequencer account nonce
ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
endL2SeqNonce, err := l2Seq.NonceAt(ctx, transactor.Account.L1Opts.From, nil)
cancel()
require.NoError(t, err)
// Obtain the L2 verifier account balance
ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
endL2VerifBalance, err := l2Verif.BalanceAt(ctx, transactor.Account.L1Opts.From, nil)
cancel()
require.NoError(t, err)
// Obtain the L2 verifier account nonce
ctx, cancel = context.WithTimeout(context.Background(), txTimeoutDuration)
endL2VerifNonce, err := l2Verif.NonceAt(ctx, transactor.Account.L1Opts.From, nil)
cancel()
require.NoError(t, err)
// TODO: Check L1 balance as well here. We avoided this due to time constraints as it seems L1 fees
// were off slightly.
_ = endL1Balance
//require.Equal(t, transactor.ExpectedL1Balance, endL1Balance, "Unexpected L1 balance for transactor")
require.Equal(t, transactor.ExpectedL1Nonce, endL1Nonce, "Unexpected L1 nonce for transactor")
require.Equal(t, transactor.ExpectedL2Nonce, endL2SeqNonce, "Unexpected L2 sequencer nonce for transactor")
require.Equal(t, transactor.ExpectedL2Balance, endL2SeqBalance, "Unexpected L2 sequencer balance for transactor")
require.Equal(t, transactor.ExpectedL2Nonce, endL2VerifNonce, "Unexpected L2 verifier nonce for transactor")
require.Equal(t, transactor.ExpectedL2Balance, endL2VerifBalance, "Unexpected L2 verifier balance for transactor")
})
}
}
...@@ -6,6 +6,7 @@ require ( ...@@ -6,6 +6,7 @@ require (
github.com/ethereum-optimism/optimism/op-node v0.10.1 github.com/ethereum-optimism/optimism/op-node v0.10.1
github.com/ethereum-optimism/optimism/op-service v0.10.1 github.com/ethereum-optimism/optimism/op-service v0.10.1
github.com/ethereum/go-ethereum v1.10.26 github.com/ethereum/go-ethereum v1.10.26
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
github.com/prometheus/client_golang v1.14.0 github.com/prometheus/client_golang v1.14.0
github.com/stretchr/testify v1.8.0 github.com/stretchr/testify v1.8.0
github.com/urfave/cli v1.22.10 github.com/urfave/cli v1.22.10
......
...@@ -159,6 +159,7 @@ github.com/hashicorp/go-bexpr v0.1.11 h1:6DqdA/KBjurGby9yTY0bmkathya0lfwF2SeuubC ...@@ -159,6 +159,7 @@ github.com/hashicorp/go-bexpr v0.1.11 h1:6DqdA/KBjurGby9yTY0bmkathya0lfwF2SeuubC
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs= github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao=
github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM= github.com/holiman/uint256 v1.2.0 h1:gpSYcPLWGv4sG43I2mVLiDZCNDh/EpGjSk8tmtxitHM=
github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ=
......
...@@ -160,4 +160,4 @@ require ( ...@@ -160,4 +160,4 @@ require (
lukechampine.com/blake3 v1.1.7 // indirect lukechampine.com/blake3 v1.1.7 // indirect
) )
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468
...@@ -143,8 +143,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF ...@@ -143,8 +143,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 h1:QJL/gtfxGe11tApZIPCeKERQHrLZMAG0RwGV9eTgtvE= github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468 h1:7KgjBYDji5AKi42eRYI+n8Gs+ZJVilSASL3WBu82c3M=
github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY= github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY=
github.com/ethereum-optimism/optimism/op-bindings v0.10.4 h1:CFn4+t0FUrBG5DmkKyYrLbGmzHWLdLv8QdUnlklvozc= github.com/ethereum-optimism/optimism/op-bindings v0.10.4 h1:CFn4+t0FUrBG5DmkKyYrLbGmzHWLdLv8QdUnlklvozc=
github.com/ethereum-optimism/optimism/op-bindings v0.10.4/go.mod h1:philKV8erP02ggjk2mRIdvJd2ZjMzpmqu0+zwwzKmNw= github.com/ethereum-optimism/optimism/op-bindings v0.10.4/go.mod h1:philKV8erP02ggjk2mRIdvJd2ZjMzpmqu0+zwwzKmNw=
github.com/ethereum-optimism/optimism/op-chain-ops v0.10.4 h1:10/BrNfcobBNuaIQQAUcDblzLCtNeGMhGvqHdzhENKk= github.com/ethereum-optimism/optimism/op-chain-ops v0.10.4 h1:10/BrNfcobBNuaIQQAUcDblzLCtNeGMhGvqHdzhENKk=
......
...@@ -73,6 +73,7 @@ func (ibc *IterativeBatchCall[K, V, O]) Reset() { ...@@ -73,6 +73,7 @@ func (ibc *IterativeBatchCall[K, V, O]) Reset() {
scheduled <- r scheduled <- r
} }
atomic.StoreUint32(&ibc.completed, 0)
ibc.requestsValues = requestsValues ibc.requestsValues = requestsValues
ibc.scheduled = scheduled ibc.scheduled = scheduled
if len(ibc.requestsKeys) == 0 { if len(ibc.requestsKeys) == 0 {
......
...@@ -99,4 +99,4 @@ require ( ...@@ -99,4 +99,4 @@ require (
lukechampine.com/blake3 v1.1.7 // indirect lukechampine.com/blake3 v1.1.7 // indirect
) )
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468
...@@ -105,8 +105,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m ...@@ -105,8 +105,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc=
github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs= github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs=
github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 h1:QJL/gtfxGe11tApZIPCeKERQHrLZMAG0RwGV9eTgtvE= github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468 h1:7KgjBYDji5AKi42eRYI+n8Gs+ZJVilSASL3WBu82c3M=
github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY= github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY=
github.com/ethereum-optimism/optimism/op-bindings v0.10.4 h1:CFn4+t0FUrBG5DmkKyYrLbGmzHWLdLv8QdUnlklvozc= github.com/ethereum-optimism/optimism/op-bindings v0.10.4 h1:CFn4+t0FUrBG5DmkKyYrLbGmzHWLdLv8QdUnlklvozc=
github.com/ethereum-optimism/optimism/op-bindings v0.10.4/go.mod h1:philKV8erP02ggjk2mRIdvJd2ZjMzpmqu0+zwwzKmNw= github.com/ethereum-optimism/optimism/op-bindings v0.10.4/go.mod h1:philKV8erP02ggjk2mRIdvJd2ZjMzpmqu0+zwwzKmNw=
github.com/ethereum-optimism/optimism/op-node v0.10.4 h1:ZXqfrFKgb6W4ZLbkfO9NlgaQ1djBCCPzNGbd6TgehVI= github.com/ethereum-optimism/optimism/op-node v0.10.4 h1:ZXqfrFKgb6W4ZLbkfO9NlgaQ1djBCCPzNGbd6TgehVI=
......
...@@ -67,4 +67,4 @@ require ( ...@@ -67,4 +67,4 @@ require (
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468
...@@ -110,8 +110,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF ...@@ -110,8 +110,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 h1:QJL/gtfxGe11tApZIPCeKERQHrLZMAG0RwGV9eTgtvE= github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468 h1:7KgjBYDji5AKi42eRYI+n8Gs+ZJVilSASL3WBu82c3M=
github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY= github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fjl/memsize v0.0.1 h1:+zhkb+dhUgx0/e+M8sF0QqiouvMQUiKR+QYvdxIOKcQ= github.com/fjl/memsize v0.0.1 h1:+zhkb+dhUgx0/e+M8sF0QqiouvMQUiKR+QYvdxIOKcQ=
github.com/fjl/memsize v0.0.1/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= github.com/fjl/memsize v0.0.1/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0=
......
...@@ -85,4 +85,4 @@ require ( ...@@ -85,4 +85,4 @@ require (
lukechampine.com/blake3 v1.1.7 // indirect lukechampine.com/blake3 v1.1.7 // indirect
) )
replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 replace github.com/ethereum/go-ethereum v1.10.26 => github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468
...@@ -81,8 +81,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF ...@@ -81,8 +81,8 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790 h1:QJL/gtfxGe11tApZIPCeKERQHrLZMAG0RwGV9eTgtvE= github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468 h1:7KgjBYDji5AKi42eRYI+n8Gs+ZJVilSASL3WBu82c3M=
github.com/ethereum-optimism/op-geth v0.0.0-20221205191237-0678a130d790/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY= github.com/ethereum-optimism/op-geth v0.0.0-20221216190603-60b51d600468/go.mod h1:p0Yox74PhYlq1HvijrCBCD9A3cI7rXco7hT6KrQr+rY=
github.com/ethereum-optimism/optimism/op-node v0.10.3 h1:96KbEtbfJTg5GXtNqLnrDPnXMbeynIy1G8iSc47whrA= github.com/ethereum-optimism/optimism/op-node v0.10.3 h1:96KbEtbfJTg5GXtNqLnrDPnXMbeynIy1G8iSc47whrA=
github.com/ethereum-optimism/optimism/op-node v0.10.3/go.mod h1:fsRLXH68xaLhjfr67MPEtjCocCzSXGhZIre536QccIw= github.com/ethereum-optimism/optimism/op-node v0.10.3/go.mod h1:fsRLXH68xaLhjfr67MPEtjCocCzSXGhZIre536QccIw=
github.com/ethereum-optimism/optimism/op-service v0.10.3 h1:gr+eVq6CzxMFqo0/9n6EoUkpumtYZEzO84gti6ekj/s= github.com/ethereum-optimism/optimism/op-service v0.10.3 h1:gr+eVq6CzxMFqo0/9n6EoUkpumtYZEzO84gti6ekj/s=
......
...@@ -11,7 +11,7 @@ GETH_VERSION='v1.10.26' ...@@ -11,7 +11,7 @@ GETH_VERSION='v1.10.26'
def main(): def main():
for project in ('op-service', 'op-node', 'op-proposer', 'op-batcher', 'op-bindings', 'op-chain-ops', 'op-e2e'): for project in ('op-service', 'op-node', 'op-proposer', 'op-batcher', 'op-bindings', 'op-chain-ops', 'op-e2e', 'op-wheel'):
print(f'Updating {project}...') print(f'Updating {project}...')
update_mod(project) update_mod(project)
......
...@@ -230,6 +230,11 @@ OptimismMintableERC721_Test:test_constructor_succeeds() (gas: 24162) ...@@ -230,6 +230,11 @@ OptimismMintableERC721_Test:test_constructor_succeeds() (gas: 24162)
OptimismMintableERC721_Test:test_safeMint_notBridge_reverts() (gas: 11142) OptimismMintableERC721_Test:test_safeMint_notBridge_reverts() (gas: 11142)
OptimismMintableERC721_Test:test_safeMint_succeeds() (gas: 140502) OptimismMintableERC721_Test:test_safeMint_succeeds() (gas: 140502)
OptimismMintableERC721_Test:test_tokenURI_succeeds() (gas: 163420) OptimismMintableERC721_Test:test_tokenURI_succeeds() (gas: 163420)
OptimismMintableERC721Factory_Test:test_constructor_succeeds() (gas: 9983)
OptimismMintableERC721Factory_Test:test_constructor_zeroBridge_reverts() (gas: 39114)
OptimismMintableERC721Factory_Test:test_constructor_zeroRemoteChainId_reverts() (gas: 41265)
OptimismMintableERC721Factory_Test:test_createOptimismMintableERC721_succeeds() (gas: 2276418)
OptimismMintableERC721Factory_Test:test_createOptimismMintableERC721_zeroRemoteToken_reverst() (gas: 9395)
OptimismPortalUpgradeable_Test:test_initialize_cannotInitImpl_reverts() (gas: 10791) OptimismPortalUpgradeable_Test:test_initialize_cannotInitImpl_reverts() (gas: 10791)
OptimismPortalUpgradeable_Test:test_initialize_cannotInitProxy_reverts() (gas: 15833) OptimismPortalUpgradeable_Test:test_initialize_cannotInitProxy_reverts() (gas: 15833)
OptimismPortalUpgradeable_Test:test_params_initValuesOnProxy_succeeds() (gas: 16011) OptimismPortalUpgradeable_Test:test_params_initValuesOnProxy_succeeds() (gas: 16011)
...@@ -374,7 +379,8 @@ SequencerFeeVault_Test:test_minWithdrawalAmount_succeeds() (gas: 5420) ...@@ -374,7 +379,8 @@ SequencerFeeVault_Test:test_minWithdrawalAmount_succeeds() (gas: 5420)
SequencerFeeVault_Test:test_receive_succeeds() (gas: 17336) SequencerFeeVault_Test:test_receive_succeeds() (gas: 17336)
SequencerFeeVault_Test:test_withdraw_notEnough_reverts() (gas: 9309) SequencerFeeVault_Test:test_withdraw_notEnough_reverts() (gas: 9309)
SequencerFeeVault_Test:test_withdraw_succeeds() (gas: 159816) SequencerFeeVault_Test:test_withdraw_succeeds() (gas: 159816)
SystemConfig_Initialize_TestFail:test_initialize_lowGasLimit_reverts() (gas: 61707) SystemConfig_Initialize_TestFail:test_initialize_lowGasLimit_reverts() (gas: 61952)
SystemConfig_Setters_TestFail:test_setBatcherHash_notOwner_reverts() (gas: 10501) SystemConfig_Setters_TestFail:test_setBatcherHash_notOwner_reverts() (gas: 10523)
SystemConfig_Setters_TestFail:test_setGasConfig_notOwner_reverts() (gas: 10576) SystemConfig_Setters_TestFail:test_setGasConfig_notOwner_reverts() (gas: 10510)
SystemConfig_Setters_TestFail:test_setGasLimit_notOwner_reverts() (gas: 10592) SystemConfig_Setters_TestFail:test_setGasLimit_notOwner_reverts() (gas: 10614)
SystemConfig_Setters_TestFail:test_setUnsafeBlockSigner_notOwner_reverts() (gas: 10638)
...@@ -104,27 +104,29 @@ ...@@ -104,27 +104,29 @@
➡ contracts/L1/SystemConfig.sol:SystemConfig ➡ contracts/L1/SystemConfig.sol:SystemConfig
======================= =======================
+---------------+-------------+------+--------+-------+--------------------------------------------+ +-------------------+-------------+------+--------+-------+--------------------------------------------+
| Name | Type | Slot | Offset | Bytes | Contract | | Name | Type | Slot | Offset | Bytes | Contract |
+==================================================================================================+ +======================================================================================================+
| _initialized | uint8 | 0 | 0 | 1 | contracts/L1/SystemConfig.sol:SystemConfig | | _initialized | uint8 | 0 | 0 | 1 | contracts/L1/SystemConfig.sol:SystemConfig |
|---------------+-------------+------+--------+-------+--------------------------------------------| |-------------------+-------------+------+--------+-------+--------------------------------------------|
| _initializing | bool | 0 | 1 | 1 | contracts/L1/SystemConfig.sol:SystemConfig | | _initializing | bool | 0 | 1 | 1 | contracts/L1/SystemConfig.sol:SystemConfig |
|---------------+-------------+------+--------+-------+--------------------------------------------| |-------------------+-------------+------+--------+-------+--------------------------------------------|
| __gap | uint256[50] | 1 | 0 | 1600 | contracts/L1/SystemConfig.sol:SystemConfig | | __gap | uint256[50] | 1 | 0 | 1600 | contracts/L1/SystemConfig.sol:SystemConfig |
|---------------+-------------+------+--------+-------+--------------------------------------------| |-------------------+-------------+------+--------+-------+--------------------------------------------|
| _owner | address | 51 | 0 | 20 | contracts/L1/SystemConfig.sol:SystemConfig | | _owner | address | 51 | 0 | 20 | contracts/L1/SystemConfig.sol:SystemConfig |
|---------------+-------------+------+--------+-------+--------------------------------------------| |-------------------+-------------+------+--------+-------+--------------------------------------------|
| __gap | uint256[49] | 52 | 0 | 1568 | contracts/L1/SystemConfig.sol:SystemConfig | | __gap | uint256[49] | 52 | 0 | 1568 | contracts/L1/SystemConfig.sol:SystemConfig |
|---------------+-------------+------+--------+-------+--------------------------------------------| |-------------------+-------------+------+--------+-------+--------------------------------------------|
| overhead | uint256 | 101 | 0 | 32 | contracts/L1/SystemConfig.sol:SystemConfig | | overhead | uint256 | 101 | 0 | 32 | contracts/L1/SystemConfig.sol:SystemConfig |
|---------------+-------------+------+--------+-------+--------------------------------------------| |-------------------+-------------+------+--------+-------+--------------------------------------------|
| scalar | uint256 | 102 | 0 | 32 | contracts/L1/SystemConfig.sol:SystemConfig | | scalar | uint256 | 102 | 0 | 32 | contracts/L1/SystemConfig.sol:SystemConfig |
|---------------+-------------+------+--------+-------+--------------------------------------------| |-------------------+-------------+------+--------+-------+--------------------------------------------|
| batcherHash | bytes32 | 103 | 0 | 32 | contracts/L1/SystemConfig.sol:SystemConfig | | unsafeBlockSigner | address | 103 | 0 | 20 | contracts/L1/SystemConfig.sol:SystemConfig |
|---------------+-------------+------+--------+-------+--------------------------------------------| |-------------------+-------------+------+--------+-------+--------------------------------------------|
| gasLimit | uint64 | 104 | 0 | 8 | contracts/L1/SystemConfig.sol:SystemConfig | | batcherHash | bytes32 | 104 | 0 | 32 | contracts/L1/SystemConfig.sol:SystemConfig |
+---------------+-------------+------+--------+-------+--------------------------------------------+ |-------------------+-------------+------+--------+-------+--------------------------------------------|
| gasLimit | uint64 | 105 | 0 | 8 | contracts/L1/SystemConfig.sol:SystemConfig |
+-------------------+-------------+------+--------+-------+--------------------------------------------+
======================= =======================
➡ contracts/legacy/DeployerWhitelist.sol:DeployerWhitelist ➡ contracts/legacy/DeployerWhitelist.sol:DeployerWhitelist
......
...@@ -16,14 +16,17 @@ contract SystemConfig is OwnableUpgradeable, Semver { ...@@ -16,14 +16,17 @@ contract SystemConfig is OwnableUpgradeable, Semver {
/** /**
* @notice Enum representing different types of updates. * @notice Enum representing different types of updates.
* *
* @custom:value BATCHER Represents an update to the batcher hash. * @custom:value BATCHER Represents an update to the batcher hash.
* @custom:value GAS_CONFIG Represents an update to txn fee config on L2. * @custom:value GAS_CONFIG Represents an update to txn fee config on L2.
* @custom:value GAS_LIMIT Represents an update to gas limit on L2. * @custom:value GAS_LIMIT Represents an update to gas limit on L2.
* @custom:value UNSAFE_BLOCK_SIGNER Represents an update to the signer key for unsafe
* block distrubution.
*/ */
enum UpdateType { enum UpdateType {
BATCHER, BATCHER,
GAS_CONFIG, GAS_CONFIG,
GAS_LIMIT GAS_LIMIT,
UNSAFE_BLOCK_SIGNER
} }
/** /**
...@@ -48,6 +51,13 @@ contract SystemConfig is OwnableUpgradeable, Semver { ...@@ -48,6 +51,13 @@ contract SystemConfig is OwnableUpgradeable, Semver {
*/ */
uint256 public scalar; uint256 public scalar;
/**
* @notice Address corresponding to the key that can propagate unsafe blocks
* across the p2p network. This value should not be tightly packed
* into a storage slot with another value to make state proofs more simple.
*/
address public unsafeBlockSigner;
/** /**
* @notice Identifier for the batcher. For version 1 of this configuration, this is represented * @notice Identifier for the batcher. For version 1 of this configuration, this is represented
* as an address left-padded with zeros to 32 bytes. * as an address left-padded with zeros to 32 bytes.
...@@ -82,9 +92,10 @@ contract SystemConfig is OwnableUpgradeable, Semver { ...@@ -82,9 +92,10 @@ contract SystemConfig is OwnableUpgradeable, Semver {
uint256 _overhead, uint256 _overhead,
uint256 _scalar, uint256 _scalar,
bytes32 _batcherHash, bytes32 _batcherHash,
uint64 _gasLimit uint64 _gasLimit,
address _unsafeBlockSigner
) Semver(1, 0, 0) { ) Semver(1, 0, 0) {
initialize(_owner, _overhead, _scalar, _batcherHash, _gasLimit); initialize(_owner, _overhead, _scalar, _batcherHash, _gasLimit, _unsafeBlockSigner);
} }
/** /**
...@@ -101,7 +112,8 @@ contract SystemConfig is OwnableUpgradeable, Semver { ...@@ -101,7 +112,8 @@ contract SystemConfig is OwnableUpgradeable, Semver {
uint256 _overhead, uint256 _overhead,
uint256 _scalar, uint256 _scalar,
bytes32 _batcherHash, bytes32 _batcherHash,
uint64 _gasLimit uint64 _gasLimit,
address _unsafeBlockSigner
) public initializer { ) public initializer {
require(_gasLimit >= MINIMUM_GAS_LIMIT, "SystemConfig: gas limit too low"); require(_gasLimit >= MINIMUM_GAS_LIMIT, "SystemConfig: gas limit too low");
__Ownable_init(); __Ownable_init();
...@@ -110,6 +122,7 @@ contract SystemConfig is OwnableUpgradeable, Semver { ...@@ -110,6 +122,7 @@ contract SystemConfig is OwnableUpgradeable, Semver {
scalar = _scalar; scalar = _scalar;
batcherHash = _batcherHash; batcherHash = _batcherHash;
gasLimit = _gasLimit; gasLimit = _gasLimit;
unsafeBlockSigner = _unsafeBlockSigner;
} }
/** /**
...@@ -139,6 +152,13 @@ contract SystemConfig is OwnableUpgradeable, Semver { ...@@ -139,6 +152,13 @@ contract SystemConfig is OwnableUpgradeable, Semver {
emit ConfigUpdate(VERSION, UpdateType.GAS_CONFIG, data); emit ConfigUpdate(VERSION, UpdateType.GAS_CONFIG, data);
} }
function setUnsafeBlockSigner(address _unsafeBlockSigner) external onlyOwner {
unsafeBlockSigner = _unsafeBlockSigner;
bytes memory data = abi.encode(_unsafeBlockSigner);
emit ConfigUpdate(VERSION, UpdateType.UNSAFE_BLOCK_SIGNER, data);
}
/** /**
* @notice Updates the L2 gas limit. * @notice Updates the L2 gas limit.
* *
......
...@@ -78,6 +78,7 @@ contract SystemDictator is OwnableUpgradeable { ...@@ -78,6 +78,7 @@ contract SystemDictator is OwnableUpgradeable {
uint256 scalar; uint256 scalar;
bytes32 batcherHash; bytes32 batcherHash;
uint64 gasLimit; uint64 gasLimit;
address unsafeBlockSigner;
} }
/** /**
...@@ -353,7 +354,8 @@ contract SystemDictator is OwnableUpgradeable { ...@@ -353,7 +354,8 @@ contract SystemDictator is OwnableUpgradeable {
config.systemConfigConfig.overhead, config.systemConfigConfig.overhead,
config.systemConfigConfig.scalar, config.systemConfigConfig.scalar,
config.systemConfigConfig.batcherHash, config.systemConfigConfig.batcherHash,
config.systemConfigConfig.gasLimit config.systemConfigConfig.gasLimit,
config.systemConfigConfig.unsafeBlockSigner
) )
) )
); );
......
...@@ -420,31 +420,25 @@ contract Bridge_Initializer is Messenger_Initializer { ...@@ -420,31 +420,25 @@ contract Bridge_Initializer is Messenger_Initializer {
contract ERC721Bridge_Initializer is Messenger_Initializer { contract ERC721Bridge_Initializer is Messenger_Initializer {
L1ERC721Bridge L1Bridge; L1ERC721Bridge L1Bridge;
L2ERC721Bridge L2Bridge; L2ERC721Bridge L2Bridge;
OptimismMintableERC721Factory factory;
function setUp() public virtual override { function setUp() public virtual override {
super.setUp(); super.setUp();
// Deploy the L1ERC721Bridge.
L1Bridge = new L1ERC721Bridge(address(L1Messenger), Predeploys.L2_ERC721_BRIDGE); L1Bridge = new L1ERC721Bridge(address(L1Messenger), Predeploys.L2_ERC721_BRIDGE);
L2ERC721Bridge l2b = new L2ERC721Bridge( // Deploy the implementation for the L2ERC721Bridge and etch it into the predeploy address.
vm.etch(Predeploys.L2_ERC721_BRIDGE, address(new L2ERC721Bridge(
Predeploys.L2_CROSS_DOMAIN_MESSENGER, Predeploys.L2_CROSS_DOMAIN_MESSENGER,
address(L1Bridge) address(L1Bridge)
); )).code);
vm.etch(Predeploys.L2_ERC721_BRIDGE, address(l2b).code); // Set up a reference to the L2ERC721Bridge.
L2Bridge = L2ERC721Bridge(Predeploys.L2_ERC721_BRIDGE); L2Bridge = L2ERC721Bridge(Predeploys.L2_ERC721_BRIDGE);
OptimismMintableERC721Factory f = new OptimismMintableERC721Factory( // Label the L1 and L2 bridges.
Predeploys.L2_ERC721_BRIDGE,
block.chainid
);
vm.etch(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY, address(f).code);
factory = OptimismMintableERC721Factory(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY);
vm.label(address(L1Bridge), "L1ERC721Bridge"); vm.label(address(L1Bridge), "L1ERC721Bridge");
vm.label(Predeploys.L2_ERC721_BRIDGE, "L2ERC721Bridge"); vm.label(address(L2Bridge), "L2ERC721Bridge");
vm.label(Predeploys.OPTIMISM_MINTABLE_ERC721_FACTORY, "OptimismMintableERC721Factory");
} }
} }
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import { ERC721Bridge_Initializer } from "./CommonTest.t.sol";
import { LibRLP } from "./RLP.t.sol";
import { OptimismMintableERC721 } from "../universal/OptimismMintableERC721.sol";
import { OptimismMintableERC721Factory } from "../universal/OptimismMintableERC721Factory.sol";
contract OptimismMintableERC721Factory_Test is ERC721Bridge_Initializer {
OptimismMintableERC721Factory internal factory;
event OptimismMintableERC721Created(
address indexed localToken,
address indexed remoteToken,
address deployer
);
function setUp() public override {
super.setUp();
// Set up the token pair.
factory = new OptimismMintableERC721Factory(address(L2Bridge), 1);
// Label the addresses for nice traces.
vm.label(address(factory), "OptimismMintableERC721Factory");
}
function test_constructor_succeeds() external {
assertEq(factory.bridge(), address(L2Bridge));
assertEq(factory.remoteChainId(), 1);
assertEq(factory.BRIDGE(), address(L2Bridge));
assertEq(factory.REMOTE_CHAIN_ID(), 1);
}
function test_constructor_zeroBridge_reverts() external {
vm.expectRevert("OptimismMintableERC721Factory: bridge cannot be address(0)");
new OptimismMintableERC721Factory(address(0), 1);
}
function test_constructor_zeroRemoteChainId_reverts() external {
vm.expectRevert("OptimismMintableERC721Factory: remote chain id cannot be zero");
new OptimismMintableERC721Factory(address(L2Bridge), 0);
}
function test_createOptimismMintableERC721_succeeds() external {
// Predict the address based on the factory address and nonce.
address predicted = LibRLP.computeAddress(address(factory), 1);
// Expect a token creation event.
vm.expectEmit(true, true, true, true);
emit OptimismMintableERC721Created(
predicted,
address(1234),
alice
);
// Create the token.
vm.prank(alice);
OptimismMintableERC721 created = OptimismMintableERC721(
factory.createOptimismMintableERC721(
address(1234),
"L2Token",
"L2T"
)
);
// Token address should be correct.
assertEq(address(created), predicted);
// Should be marked as created by the factory.
assertEq(factory.isOptimismMintableERC721(address(created)), true);
// Token should've been constructed correctly.
assertEq(created.name(), "L2Token");
assertEq(created.symbol(), "L2T");
assertEq(created.REMOTE_TOKEN(), address(1234));
assertEq(created.BRIDGE(), address(L2Bridge));
assertEq(created.REMOTE_CHAIN_ID(), 1);
}
function test_createOptimismMintableERC721_zeroRemoteToken_reverst() external {
// Try to create a token with a zero remote token address.
vm.expectRevert("OptimismMintableERC721Factory: L1 token address cannot be address(0)");
factory.createOptimismMintableERC721(
address(0),
"L2Token",
"L2T"
);
}
}
...@@ -13,7 +13,8 @@ contract SystemConfig_Init is CommonTest { ...@@ -13,7 +13,8 @@ contract SystemConfig_Init is CommonTest {
_overhead: 2100, _overhead: 2100,
_scalar: 1000000, _scalar: 1000000,
_batcherHash: bytes32(hex"abcd"), _batcherHash: bytes32(hex"abcd"),
_gasLimit: 9_000_000 _gasLimit: 9_000_000,
_unsafeBlockSigner: address(1)
}); });
} }
} }
...@@ -29,7 +30,8 @@ contract SystemConfig_Initialize_TestFail is CommonTest { ...@@ -29,7 +30,8 @@ contract SystemConfig_Initialize_TestFail is CommonTest {
_overhead: 0, _overhead: 0,
_scalar: 0, _scalar: 0,
_batcherHash: bytes32(hex""), _batcherHash: bytes32(hex""),
_gasLimit: MINIMUM_GAS_LIMIT - 1 _gasLimit: MINIMUM_GAS_LIMIT - 1,
_unsafeBlockSigner: address(1)
}); });
} }
} }
...@@ -49,6 +51,11 @@ contract SystemConfig_Setters_TestFail is SystemConfig_Init { ...@@ -49,6 +51,11 @@ contract SystemConfig_Setters_TestFail is SystemConfig_Init {
vm.expectRevert("Ownable: caller is not the owner"); vm.expectRevert("Ownable: caller is not the owner");
sysConf.setGasLimit(0); sysConf.setGasLimit(0);
} }
function test_setUnsafeBlockSigner_notOwner_reverts() external {
vm.expectRevert("Ownable: caller is not the owner");
sysConf.setUnsafeBlockSigner(address(0x20));
}
} }
contract SystemConfig_Setters_Test is SystemConfig_Init { contract SystemConfig_Setters_Test is SystemConfig_Init {
...@@ -94,4 +101,17 @@ contract SystemConfig_Setters_Test is SystemConfig_Init { ...@@ -94,4 +101,17 @@ contract SystemConfig_Setters_Test is SystemConfig_Init {
sysConf.setGasLimit(newGasLimit); sysConf.setGasLimit(newGasLimit);
assertEq(sysConf.gasLimit(), newGasLimit); assertEq(sysConf.gasLimit(), newGasLimit);
} }
function testFuzz_setUnsafeBlockSigner_succeeds(address newUnsafeSigner) external {
vm.expectEmit(true, true, true, true);
emit ConfigUpdate(
0,
SystemConfig.UpdateType.UNSAFE_BLOCK_SIGNER,
abi.encode(newUnsafeSigner)
);
vm.prank(sysConf.owner());
sysConf.setUnsafeBlockSigner(newUnsafeSigner);
assertEq(sysConf.unsafeBlockSigner(), newUnsafeSigner);
}
} }
...@@ -17,6 +17,7 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -17,6 +17,7 @@ const deployFn: DeployFunction = async (hre) => {
hre.deployConfig.gasPriceOracleScalar, hre.deployConfig.gasPriceOracleScalar,
batcherHash, batcherHash,
hre.deployConfig.l2GenesisBlockGasLimit, hre.deployConfig.l2GenesisBlockGasLimit,
hre.deployConfig.p2pSequencerAddress,
], ],
postDeployAction: async (contract) => { postDeployAction: async (contract) => {
await assertContractVariable( await assertContractVariable(
...@@ -35,6 +36,11 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -35,6 +36,11 @@ const deployFn: DeployFunction = async (hre) => {
hre.deployConfig.gasPriceOracleScalar hre.deployConfig.gasPriceOracleScalar
) )
await assertContractVariable(contract, 'batcherHash', batcherHash) await assertContractVariable(contract, 'batcherHash', batcherHash)
await assertContractVariable(
contract,
'unsafeBlockSigner',
hre.deployConfig.p2pSequencerAddress
)
}, },
}) })
} }
......
...@@ -93,12 +93,13 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -93,12 +93,13 @@ const deployFn: DeployFunction = async (hre) => {
systemConfigConfig: { systemConfigConfig: {
owner: hre.deployConfig.finalSystemOwner, owner: hre.deployConfig.finalSystemOwner,
overhead: hre.deployConfig.gasPriceOracleOverhead, overhead: hre.deployConfig.gasPriceOracleOverhead,
scalar: hre.deployConfig.gasPriceOracleDecimals, scalar: hre.deployConfig.gasPriceOracleScalar,
batcherHash: hre.ethers.utils.hexZeroPad( batcherHash: hre.ethers.utils.hexZeroPad(
hre.deployConfig.batchSenderAddress, hre.deployConfig.batchSenderAddress,
32 32
), ),
gasLimit: hre.deployConfig.l2GenesisBlockGasLimit, gasLimit: hre.deployConfig.l2GenesisBlockGasLimit,
unsafeBlockSigner: hre.deployConfig.p2pSequencerAddress,
}, },
} }
......
...@@ -31,6 +31,7 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -31,6 +31,7 @@ const deployFn: DeployFunction = async (hre) => {
L1ERC721BridgeProxy, L1ERC721BridgeProxy,
L1ERC721BridgeProxyWithSigner, L1ERC721BridgeProxyWithSigner,
L1ERC721Bridge, L1ERC721Bridge,
SystemConfigProxy,
] = await getContractsFromArtifacts(hre, [ ] = await getContractsFromArtifacts(hre, [
{ {
name: 'SystemDictatorProxy', name: 'SystemDictatorProxy',
...@@ -89,6 +90,11 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -89,6 +90,11 @@ const deployFn: DeployFunction = async (hre) => {
iface: 'L1ERC721Bridge', iface: 'L1ERC721Bridge',
signerOrProvider: deployer, signerOrProvider: deployer,
}, },
{
name: 'SystemConfigProxy',
iface: 'SystemConfig',
signerOrProvider: deployer,
},
]) ])
// If we have the key for the controller then we don't need to wait for external txns. // If we have the key for the controller then we don't need to wait for external txns.
...@@ -498,6 +504,40 @@ const deployFn: DeployFunction = async (hre) => { ...@@ -498,6 +504,40 @@ const deployFn: DeployFunction = async (hre) => {
'messenger', 'messenger',
L1CrossDomainMessenger.address L1CrossDomainMessenger.address
) )
// Check the SystemConfig was initialized properly.
await assertContractVariable(
SystemConfigProxy,
'owner',
hre.deployConfig.finalSystemOwner
)
await assertContractVariable(
SystemConfigProxy,
'overhead',
hre.deployConfig.gasPriceOracleOverhead
)
await assertContractVariable(
SystemConfigProxy,
'scalar',
hre.deployConfig.gasPriceOracleScalar
)
await assertContractVariable(
SystemConfigProxy,
'batcherHash',
ethers.utils.hexZeroPad(
hre.deployConfig.batchSenderAddress.toLowerCase(),
32
)
)
await assertContractVariable(
SystemConfigProxy,
'gasLimit',
hre.deployConfig.l2GenesisBlockGasLimit
)
}, },
}) })
......
...@@ -155,7 +155,6 @@ interface OptionalL2DeployConfig { ...@@ -155,7 +155,6 @@ interface OptionalL2DeployConfig {
l2GenesisBlockBaseFeePerGas: string l2GenesisBlockBaseFeePerGas: string
gasPriceOracleOverhead: number gasPriceOracleOverhead: number
gasPriceOracleScalar: number gasPriceOracleScalar: number
gasPriceOracleDecimals: number
} }
/** /**
...@@ -326,10 +325,6 @@ export const deployConfigSpec: { ...@@ -326,10 +325,6 @@ export const deployConfigSpec: {
type: 'number', type: 'number',
default: 1_000_000, default: 1_000_000,
}, },
gasPriceOracleDecimals: {
type: 'number',
default: 6,
},
governanceTokenSymbol: { governanceTokenSymbol: {
type: 'string', type: 'string',
default: 'OP', default: 'OP',
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { ERC721Bridge } from "../universal/op-erc721/ERC721Bridge.sol";
import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import { L2ERC721Bridge } from "../L2/L2ERC721Bridge.sol";
import { Semver } from "@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol";
/**
* @title L1ERC721Bridge
* @notice The L1 ERC721 bridge is a contract which works together with the L2 ERC721 bridge to
* make it possible to transfer ERC721 tokens from Ethereum to Optimism. This contract
* acts as an escrow for ERC721 tokens deposited into L2.
*/
contract L1ERC721Bridge is ERC721Bridge, Semver {
/**
* @notice Mapping of L1 token to L2 token to ID to boolean, indicating if the given L1 token
* by ID was deposited for a given L2 token.
*/
mapping(address => mapping(address => mapping(uint256 => bool))) public deposits;
/**
* @custom:semver 1.0.0
*
* @param _messenger Address of the CrossDomainMessenger on this network.
* @param _otherBridge Address of the ERC721 bridge on the other network.
*/
constructor(address _messenger, address _otherBridge)
Semver(1, 0, 0)
ERC721Bridge(_messenger, _otherBridge)
{}
/*************************
* Cross-chain Functions *
*************************/
/**
* @notice Completes an ERC721 bridge from the other domain and sends the ERC721 token to the
* recipient on this domain.
*
* @param _localToken Address of the ERC721 token on this domain.
* @param _remoteToken Address of the ERC721 token on the other domain.
* @param _from Address that triggered the bridge on the other domain.
* @param _to Address to receive the token on this domain.
* @param _tokenId ID of the token being deposited.
* @param _extraData Optional data to forward to L2. Data supplied here will not be used to
* execute any code on L2 and is only emitted as extra data for the
* convenience of off-chain tooling.
*/
function finalizeBridgeERC721(
address _localToken,
address _remoteToken,
address _from,
address _to,
uint256 _tokenId,
bytes calldata _extraData
) external onlyOtherBridge {
require(_localToken != address(this), "L1ERC721Bridge: local token cannot be self");
// Checks that the L1/L2 NFT pair has a token ID that is escrowed in the L1 Bridge.
require(
deposits[_localToken][_remoteToken][_tokenId] == true,
"L1ERC721Bridge: Token ID is not escrowed in the L1 Bridge"
);
// Mark that the token ID for this L1/L2 token pair is no longer escrowed in the L1
// Bridge.
deposits[_localToken][_remoteToken][_tokenId] = false;
// When a withdrawal is finalized on L1, the L1 Bridge transfers the NFT to the
// withdrawer.
IERC721(_localToken).safeTransferFrom(address(this), _to, _tokenId);
// slither-disable-next-line reentrancy-events
emit ERC721BridgeFinalized(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);
}
/**
* @inheritdoc ERC721Bridge
*/
function _initiateBridgeERC721(
address _localToken,
address _remoteToken,
address _from,
address _to,
uint256 _tokenId,
uint32 _minGasLimit,
bytes calldata _extraData
) internal override {
require(_remoteToken != address(0), "ERC721Bridge: remote token cannot be address(0)");
// Construct calldata for _l2Token.finalizeBridgeERC721(_to, _tokenId)
bytes memory message = abi.encodeWithSelector(
L2ERC721Bridge.finalizeBridgeERC721.selector,
_remoteToken,
_localToken,
_from,
_to,
_tokenId,
_extraData
);
// Lock token into bridge
deposits[_localToken][_remoteToken][_tokenId] = true;
IERC721(_localToken).transferFrom(_from, address(this), _tokenId);
// Send calldata into L2
messenger.sendMessage(otherBridge, message, _minGasLimit);
emit ERC721BridgeInitiated(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { ERC721Bridge } from "../universal/op-erc721/ERC721Bridge.sol";
import { ERC165Checker } from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import { L1ERC721Bridge } from "../L1/L1ERC721Bridge.sol";
import { IOptimismMintableERC721 } from "../universal/op-erc721/IOptimismMintableERC721.sol";
import { Semver } from "@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol";
/**
* @title L2ERC721Bridge
* @notice The L2 ERC721 bridge is a contract which works together with the L1 ERC721 bridge to
* make it possible to transfer ERC721 tokens from Ethereum to Optimism. This contract
* acts as a minter for new tokens when it hears about deposits into the L1 ERC721 bridge.
* This contract also acts as a burner for tokens being withdrawn.
* **WARNING**: Do not bridge an ERC721 that was originally deployed on Optimism. This
* bridge ONLY supports ERC721s originally deployed on Ethereum. Users will need to
* wait for the one-week challenge period to elapse before their Optimism-native NFT
* can be refunded on L2.
*/
contract L2ERC721Bridge is ERC721Bridge, Semver {
/**
* @custom:semver 1.0.0
*
* @param _messenger Address of the CrossDomainMessenger on this network.
* @param _otherBridge Address of the ERC721 bridge on the other network.
*/
constructor(address _messenger, address _otherBridge)
Semver(1, 0, 0)
ERC721Bridge(_messenger, _otherBridge)
{}
/**
* @notice Completes an ERC721 bridge from the other domain and sends the ERC721 token to the
* recipient on this domain.
*
* @param _localToken Address of the ERC721 token on this domain.
* @param _remoteToken Address of the ERC721 token on the other domain.
* @param _from Address that triggered the bridge on the other domain.
* @param _to Address to receive the token on this domain.
* @param _tokenId ID of the token being deposited.
* @param _extraData Optional data to forward to L1. Data supplied here will not be used to
* execute any code on L1 and is only emitted as extra data for the
* convenience of off-chain tooling.
*/
function finalizeBridgeERC721(
address _localToken,
address _remoteToken,
address _from,
address _to,
uint256 _tokenId,
bytes calldata _extraData
) external onlyOtherBridge {
require(_localToken != address(this), "L2ERC721Bridge: local token cannot be self");
// Note that supportsInterface makes a callback to the _localToken address which is user
// provided.
require(
ERC165Checker.supportsInterface(_localToken, type(IOptimismMintableERC721).interfaceId),
"L2ERC721Bridge: local token interface is not compliant"
);
require(
_remoteToken == IOptimismMintableERC721(_localToken).remoteToken(),
"L2ERC721Bridge: wrong remote token for Optimism Mintable ERC721 local token"
);
// When a deposit is finalized, we give the NFT with the same tokenId to the account
// on L2. Note that safeMint makes a callback to the _to address which is user provided.
IOptimismMintableERC721(_localToken).safeMint(_to, _tokenId);
// slither-disable-next-line reentrancy-events
emit ERC721BridgeFinalized(_localToken, _remoteToken, _from, _to, _tokenId, _extraData);
}
/**
* @inheritdoc ERC721Bridge
*/
function _initiateBridgeERC721(
address _localToken,
address _remoteToken,
address _from,
address _to,
uint256 _tokenId,
uint32 _minGasLimit,
bytes calldata _extraData
) internal override {
require(_remoteToken != address(0), "ERC721Bridge: remote token cannot be address(0)");
// Check that the withdrawal is being initiated by the NFT owner
require(
_from == IOptimismMintableERC721(_localToken).ownerOf(_tokenId),
"Withdrawal is not being initiated by NFT owner"
);
// Construct calldata for l1ERC721Bridge.finalizeBridgeERC721(_to, _tokenId)
// slither-disable-next-line reentrancy-events
address remoteToken = IOptimismMintableERC721(_localToken).remoteToken();
require(
remoteToken == _remoteToken,
"L2ERC721Bridge: remote token does not match given value"
);
// When a withdrawal is initiated, we burn the withdrawer's NFT to prevent subsequent L2
// usage
// slither-disable-next-line reentrancy-events
IOptimismMintableERC721(_localToken).burn(_from, _tokenId);
bytes memory message = abi.encodeWithSelector(
L1ERC721Bridge.finalizeBridgeERC721.selector,
remoteToken,
_localToken,
_from,
_to,
_tokenId,
_extraData
);
// Send message to L1 bridge
// slither-disable-next-line reentrancy-events
messenger.sendMessage(otherBridge, message, _minGasLimit);
// slither-disable-next-line reentrancy-events
emit ERC721BridgeInitiated(_localToken, remoteToken, _from, _to, _tokenId, _extraData);
}
}
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity ^0.8.0;
contract CallRecorder { contract CallRecorder {
struct CallInfo { struct CallInfo {
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity ^0.8.0;
import { ProxyAdmin } from "@eth-optimism/contracts-bedrock/contracts/universal/ProxyAdmin.sol"; import { ProxyAdmin } from "@eth-optimism/contracts-bedrock/contracts/universal/ProxyAdmin.sol";
import { Proxy } from "@eth-optimism/contracts-bedrock/contracts/universal/Proxy.sol"; import { Proxy } from "@eth-optimism/contracts-bedrock/contracts/universal/Proxy.sol";
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity ^0.8.0;
contract FailingReceiver { contract FailingReceiver {
receive() external payable { receive() external payable {
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity ^0.8.0;
contract Reverter { contract Reverter {
function doRevert() public pure { function doRevert() public pure {
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity ^0.8.0;
contract SimpleStorage { contract SimpleStorage {
mapping(bytes32 => bytes32) public db; mapping(bytes32 => bytes32) public db;
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity ^0.8.0;
import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol"; import { ERC20 } from "@rari-capital/solmate/src/tokens/ERC20.sol";
......
// SPDX-License-Identifier: MIT // SPDX-License-Identifier: MIT
pragma solidity 0.8.15; pragma solidity ^0.8.0;
import { ERC721 } from "@rari-capital/solmate/src/tokens/ERC721.sol"; import { ERC721 } from "@rari-capital/solmate/src/tokens/ERC721.sol";
......
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import {
CrossDomainMessenger
} from "@eth-optimism/contracts-bedrock/contracts/universal/CrossDomainMessenger.sol";
import { Address } from "@openzeppelin/contracts/utils/Address.sol";
/**
* @title ERC721Bridge
* @notice ERC721Bridge is a base contract for the L1 and L2 ERC721 bridges.
*/
abstract contract ERC721Bridge {
/**
* @notice Emitted when an ERC721 bridge to the other network is initiated.
*
* @param localToken Address of the token on this domain.
* @param remoteToken Address of the token on the remote domain.
* @param from Address that initiated bridging action.
* @param to Address to receive the token.
* @param tokenId ID of the specific token deposited.
* @param extraData Extra data for use on the client-side.
*/
event ERC721BridgeInitiated(
address indexed localToken,
address indexed remoteToken,
address indexed from,
address to,
uint256 tokenId,
bytes extraData
);
/**
* @notice Emitted when an ERC721 bridge from the other network is finalized.
*
* @param localToken Address of the token on this domain.
* @param remoteToken Address of the token on the remote domain.
* @param from Address that initiated bridging action.
* @param to Address to receive the token.
* @param tokenId ID of the specific token deposited.
* @param extraData Extra data for use on the client-side.
*/
event ERC721BridgeFinalized(
address indexed localToken,
address indexed remoteToken,
address indexed from,
address to,
uint256 tokenId,
bytes extraData
);
/**
* @notice Messenger contract on this domain.
*/
CrossDomainMessenger public immutable messenger;
/**
* @notice Address of the bridge on the other network.
*/
address public immutable otherBridge;
/**
* @notice Reserve extra slots (to a total of 50) in the storage layout for future upgrades.
*/
uint256[49] private __gap;
/**
* @notice Ensures that the caller is a cross-chain message from the other bridge.
*/
modifier onlyOtherBridge() {
require(
msg.sender == address(messenger) && messenger.xDomainMessageSender() == otherBridge,
"ERC721Bridge: function can only be called from the other bridge"
);
_;
}
/**
* @param _messenger Address of the CrossDomainMessenger on this network.
* @param _otherBridge Address of the ERC721 bridge on the other network.
*/
constructor(address _messenger, address _otherBridge) {
require(_messenger != address(0), "ERC721Bridge: messenger cannot be address(0)");
require(_otherBridge != address(0), "ERC721Bridge: other bridge cannot be address(0)");
messenger = CrossDomainMessenger(_messenger);
otherBridge = _otherBridge;
}
/**
* @notice Initiates a bridge of an NFT to the caller's account on the other chain. Note that
* this function can only be called by EOAs. Smart contract wallets should use the
* `bridgeERC721To` function after ensuring that the recipient address on the remote
* chain exists. Also note that the current owner of the token on this chain must
* approve this contract to operate the NFT before it can be bridged.
* **WARNING**: Do not bridge an ERC721 that was originally deployed on Optimism. This
* bridge only supports ERC721s originally deployed on Ethereum. Users will need to
* wait for the one-week challenge period to elapse before their Optimism-native NFT
* can be refunded on L2.
*
* @param _localToken Address of the ERC721 on this domain.
* @param _remoteToken Address of the ERC721 on the remote domain.
* @param _tokenId Token ID to bridge.
* @param _minGasLimit Minimum gas limit for the bridge message on the other domain.
* @param _extraData Optional data to forward to the other chain. Data supplied here will not
* be used to execute any code on the other chain and is only emitted as
* extra data for the convenience of off-chain tooling.
*/
function bridgeERC721(
address _localToken,
address _remoteToken,
uint256 _tokenId,
uint32 _minGasLimit,
bytes calldata _extraData
) external {
// Modifier requiring sender to be EOA. This prevents against a user error that would occur
// if the sender is a smart contract wallet that has a different address on the remote chain
// (or doesn't have an address on the remote chain at all). The user would fail to receive
// the NFT if they use this function because it sends the NFT to the same address as the
// caller. This check could be bypassed by a malicious contract via initcode, but it takes
// care of the user error we want to avoid.
require(!Address.isContract(msg.sender), "ERC721Bridge: account is not externally owned");
_initiateBridgeERC721(
_localToken,
_remoteToken,
msg.sender,
msg.sender,
_tokenId,
_minGasLimit,
_extraData
);
}
/**
* @notice Initiates a bridge of an NFT to some recipient's account on the other chain. Note
* that the current owner of the token on this chain must approve this contract to
* operate the NFT before it can be bridged.
* **WARNING**: Do not bridge an ERC721 that was originally deployed on Optimism. This
* bridge only supports ERC721s originally deployed on Ethereum. Users will need to
* wait for the one-week challenge period to elapse before their Optimism-native NFT
* can be refunded on L2.
*
* @param _localToken Address of the ERC721 on this domain.
* @param _remoteToken Address of the ERC721 on the remote domain.
* @param _to Address to receive the token on the other domain.
* @param _tokenId Token ID to bridge.
* @param _minGasLimit Minimum gas limit for the bridge message on the other domain.
* @param _extraData Optional data to forward to the other chain. Data supplied here will not
* be used to execute any code on the other chain and is only emitted as
* extra data for the convenience of off-chain tooling.
*/
function bridgeERC721To(
address _localToken,
address _remoteToken,
address _to,
uint256 _tokenId,
uint32 _minGasLimit,
bytes calldata _extraData
) external {
require(_to != address(0), "ERC721Bridge: nft recipient cannot be address(0)");
_initiateBridgeERC721(
_localToken,
_remoteToken,
msg.sender,
_to,
_tokenId,
_minGasLimit,
_extraData
);
}
/**
* @notice Internal function for initiating a token bridge to the other domain.
*
* @param _localToken Address of the ERC721 on this domain.
* @param _remoteToken Address of the ERC721 on the remote domain.
* @param _from Address of the sender on this domain.
* @param _to Address to receive the token on the other domain.
* @param _tokenId Token ID to bridge.
* @param _minGasLimit Minimum gas limit for the bridge message on the other domain.
* @param _extraData Optional data to forward to the other domain. Data supplied here will
* not be used to execute any code on the other domain and is only emitted
* as extra data for the convenience of off-chain tooling.
*/
function _initiateBridgeERC721(
address _localToken,
address _remoteToken,
address _from,
address _to,
uint256 _tokenId,
uint32 _minGasLimit,
bytes calldata _extraData
) internal virtual;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {
IERC721Enumerable
} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Enumerable.sol";
/**
* @title IOptimismMintableERC721
* @notice Interface for contracts that are compatible with the OptimismMintableERC721 standard.
* Tokens that follow this standard can be easily transferred across the ERC721 bridge.
*/
interface IOptimismMintableERC721 is IERC721Enumerable {
/**
* @notice Emitted when a token is minted.
*
* @param account Address of the account the token was minted to.
* @param tokenId Token ID of the minted token.
*/
event Mint(address indexed account, uint256 tokenId);
/**
* @notice Emitted when a token is burned.
*
* @param account Address of the account the token was burned from.
* @param tokenId Token ID of the burned token.
*/
event Burn(address indexed account, uint256 tokenId);
/**
* @notice Chain ID of the chain where the remote token is deployed.
*/
function remoteChainId() external view returns (uint256);
/**
* @notice Address of the token on the remote domain.
*/
function remoteToken() external view returns (address);
/**
* @notice Address of the ERC721 bridge on this network.
*/
function bridge() external view returns (address);
/**
* @notice Mints some token ID for a user, checking first that contract recipients
* are aware of the ERC721 protocol to prevent tokens from being forever locked.
*
* @param _to Address of the user to mint the token for.
* @param _tokenId Token ID to mint.
*/
function safeMint(address _to, uint256 _tokenId) external;
/**
* @notice Burns a token ID from a user.
*
* @param _from Address of the user to burn the token from.
* @param _tokenId Token ID to burn.
*/
function burn(address _from, uint256 _tokenId) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import {
ERC721Enumerable
} from "@openzeppelin/contracts/token/ERC721/extensions/ERC721Enumerable.sol";
import { ERC721 } from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { IOptimismMintableERC721 } from "./IOptimismMintableERC721.sol";
/**
* @title OptimismMintableERC721
* @notice This contract is the remote representation for some token that lives on another network,
* typically an Optimism representation of an Ethereum-based token. Standard reference
* implementation that can be extended or modified according to your needs.
*/
contract OptimismMintableERC721 is ERC721Enumerable, IOptimismMintableERC721 {
/**
* @inheritdoc IOptimismMintableERC721
*/
uint256 public immutable remoteChainId;
/**
* @inheritdoc IOptimismMintableERC721
*/
address public immutable remoteToken;
/**
* @inheritdoc IOptimismMintableERC721
*/
address public immutable bridge;
/**
* @notice Base token URI for this token.
*/
string public baseTokenURI;
/**
* @param _bridge Address of the bridge on this network.
* @param _remoteChainId Chain ID where the remote token is deployed.
* @param _remoteToken Address of the corresponding token on the other network.
* @param _name ERC721 name.
* @param _symbol ERC721 symbol.
*/
constructor(
address _bridge,
uint256 _remoteChainId,
address _remoteToken,
string memory _name,
string memory _symbol
) ERC721(_name, _symbol) {
require(_bridge != address(0), "OptimismMintableERC721: bridge cannot be address(0)");
require(_remoteChainId != 0, "OptimismMintableERC721: remote chain id cannot be zero");
require(
_remoteToken != address(0),
"OptimismMintableERC721: remote token cannot be address(0)"
);
remoteChainId = _remoteChainId;
remoteToken = _remoteToken;
bridge = _bridge;
// Creates a base URI in the format specified by EIP-681:
// https://eips.ethereum.org/EIPS/eip-681
baseTokenURI = string(
abi.encodePacked(
"ethereum:",
Strings.toHexString(uint160(_remoteToken), 20),
"@",
Strings.toString(_remoteChainId),
"/tokenURI?uint256="
)
);
}
/**
* @notice Modifier that prevents callers other than the bridge from calling the function.
*/
modifier onlyBridge() {
require(msg.sender == bridge, "OptimismMintableERC721: only bridge can call this function");
_;
}
/**
* @inheritdoc IOptimismMintableERC721
*/
function safeMint(address _to, uint256 _tokenId) external virtual onlyBridge {
_safeMint(_to, _tokenId);
emit Mint(_to, _tokenId);
}
/**
* @inheritdoc IOptimismMintableERC721
*/
function burn(address _from, uint256 _tokenId) external virtual onlyBridge {
_burn(_tokenId);
emit Burn(_from, _tokenId);
}
/**
* @notice Checks if a given interface ID is supported by this contract.
*
* @param _interfaceId The interface ID to check.
*
* @return True if the interface ID is supported, false otherwise.
*/
function supportsInterface(bytes4 _interfaceId)
public
view
override(ERC721Enumerable, IERC165)
returns (bool)
{
bytes4 iface1 = type(IERC165).interfaceId;
bytes4 iface2 = type(IOptimismMintableERC721).interfaceId;
return
_interfaceId == iface1 ||
_interfaceId == iface2 ||
super.supportsInterface(_interfaceId);
}
/**
* @notice Returns the base token URI.
*
* @return Base token URI.
*/
function _baseURI() internal view virtual override returns (string memory) {
return baseTokenURI;
}
}
// SPDX-License-Identifier: MIT
pragma solidity 0.8.15;
import { OptimismMintableERC721 } from "./OptimismMintableERC721.sol";
import { Semver } from "@eth-optimism/contracts-bedrock/contracts/universal/Semver.sol";
/**
* @title OptimismMintableERC721Factory
* @notice Factory contract for creating OptimismMintableERC721 contracts.
*/
contract OptimismMintableERC721Factory is Semver {
/**
* @notice Emitted whenever a new OptimismMintableERC721 contract is created.
*
* @param localToken Address of the token on the this domain.
* @param remoteToken Address of the token on the remote domain.
* @param deployer Address of the initiator of the deployment
*/
event OptimismMintableERC721Created(
address indexed localToken,
address indexed remoteToken,
address deployer
);
/**
* @notice Address of the ERC721 bridge on this network.
*/
address public immutable bridge;
/**
* @notice Chain ID for the remote network.
*/
uint256 public immutable remoteChainId;
/**
* @notice Tracks addresses created by this factory.
*/
mapping(address => bool) public isOptimismMintableERC721;
/**
* @custom:semver 1.0.0
*
* @param _bridge Address of the ERC721 bridge on this network.
*/
constructor(address _bridge, uint256 _remoteChainId) Semver(1, 0, 0) {
require(
_bridge != address(0),
"OptimismMintableERC721Factory: bridge cannot be address(0)"
);
require(
_remoteChainId != 0,
"OptimismMintableERC721Factory: remote chain id cannot be zero"
);
bridge = _bridge;
remoteChainId = _remoteChainId;
}
/**
* @notice Creates an instance of the standard ERC721.
*
* @param _remoteToken Address of the corresponding token on the other domain.
* @param _name ERC721 name.
* @param _symbol ERC721 symbol.
*/
function createOptimismMintableERC721(
address _remoteToken,
string memory _name,
string memory _symbol
) external returns (address) {
require(
_remoteToken != address(0),
"OptimismMintableERC721Factory: L1 token address cannot be address(0)"
);
address localToken = address(
new OptimismMintableERC721(bridge, remoteChainId, _remoteToken, _name, _symbol)
);
isOptimismMintableERC721[localToken] = true;
emit OptimismMintableERC721Created(localToken, _remoteToken, msg.sender);
return localToken;
}
}
/* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types'
import { HardhatRuntimeEnvironment } from 'hardhat/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
import fetch from 'node-fetch'
import {
isTargetL1Network,
predeploy,
getProxyAdmin,
validateERC721Bridge,
} from '../../src/nft-bridge-deploy-helpers'
// Handle the `ops` deployment
const getL1CrossDomainMessengerProxyDeployment = async (
hre: HardhatRuntimeEnvironment
) => {
const network = hre.network.name
if (network === 'ops-l1') {
const res = await fetch(
'http://localhost:8080/deployments/local/Proxy__OVM_L1CrossDomainMessenger.json'
)
return res.json()
} else {
return hre.deployments.get('Proxy__OVM_L1CrossDomainMessenger')
}
}
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const { deploy } = hre.deployments
const { getAddress } = hre.ethers.utils
if (!isTargetL1Network(hre.network.name)) {
console.log(`Deploying to unsupported network ${hre.network.name}`)
return
}
console.log(`Deploying L1ERC721Bridge to ${hre.network.name}`)
console.log(`Using deployer ${deployer}`)
const Deployment__L1ERC721BridgeProxy = await hre.deployments.get(
'L1ERC721BridgeProxy'
)
const L1ERC721BridgeProxy = await hre.ethers.getContractAt(
'Proxy',
Deployment__L1ERC721BridgeProxy.address
)
const admin = await L1ERC721BridgeProxy.callStatic.admin()
if (getAddress(admin) !== getAddress(deployer)) {
throw new Error('deployer is not proxy admin')
}
// Get the address of the currently deployed L1CrossDomainMessenger.
// This should be the address of the proxy
const Deployment__L1CrossDomainMessengerProxy =
await getL1CrossDomainMessengerProxyDeployment(hre)
const L1CrossDomainMessengerProxyAddress =
Deployment__L1CrossDomainMessengerProxy.address
// Deploy the L1ERC721Bridge. The arguments are
// - messenger
// - otherBridge
// Since this is the L1ERC721Bridge, the otherBridge is the
// predeploy address
await deploy('L1ERC721Bridge', {
from: deployer,
args: [L1CrossDomainMessengerProxyAddress, predeploy],
log: true,
waitConfirmations: 1,
})
const Deployment__L1ERC721Bridge = await hre.deployments.get('L1ERC721Bridge')
console.log(
`L1ERC721Bridge deployed to ${Deployment__L1ERC721Bridge.address}`
)
await validateERC721Bridge(hre, Deployment__L1ERC721Bridge.address, {
messenger: L1CrossDomainMessengerProxyAddress,
otherBridge: predeploy,
})
{
// Upgrade the Proxy to the newly deployed implementation
const tx = await L1ERC721BridgeProxy.upgradeTo(
Deployment__L1ERC721Bridge.address
)
const receipt = await tx.wait()
console.log(`L1ERC721BridgeProxy upgraded: ${receipt.transactionHash}`)
}
{
// Set the admin correctly
const newAdmin = getProxyAdmin(hre.network.name)
const tx = await L1ERC721BridgeProxy.changeAdmin(newAdmin)
const receipt = await tx.wait()
console.log(`L1ERC721BridgeProxy admin updated: ${receipt.transactionHash}`)
}
await validateERC721Bridge(hre, L1ERC721BridgeProxy.address, {
messenger: L1CrossDomainMessengerProxyAddress,
otherBridge: predeploy,
})
}
deployFn.tags = ['L1ERC721BridgeImplementation']
deployFn.dependencies = ['L1ERC721BridgeProxy']
export default deployFn
/* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
import { isTargetL1Network } from '../../src/nft-bridge-deploy-helpers'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const { deploy } = hre.deployments
if (!isTargetL1Network(hre.network.name)) {
console.log(`Deploying to unsupported network ${hre.network.name}`)
return
}
console.log(`Deploying L1ERC721BridgeProxy to ${hre.network.name}`)
console.log(`Using deployer ${deployer}`)
await deploy('L1ERC721BridgeProxy', {
contract: 'Proxy',
from: deployer,
args: [deployer],
log: true,
waitConfirmations: 1,
})
const Deployment__L1ERC721BridgeProxy = await hre.deployments.get(
'L1ERC721BridgeProxy'
)
console.log(
`L1ERC721BridgeProxy deployed to ${Deployment__L1ERC721BridgeProxy.address}`
)
}
deployFn.tags = ['L1ERC721BridgeProxy']
export default deployFn
/* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
import { predeploys } from '@eth-optimism/contracts'
import {
isTargetL2Network,
predeploy,
validateERC721Bridge,
getProxyAdmin,
} from '../../src/nft-bridge-deploy-helpers'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const { getAddress } = hre.ethers.utils
if (!isTargetL2Network(hre.network.name)) {
console.log(`Deploying to unsupported network ${hre.network.name}`)
return
}
console.log(`Deploying L2ERC721Bridge to ${hre.network.name}`)
console.log(`Using deployer ${deployer}`)
const L2ERC721BridgeProxy = await hre.ethers.getContractAt('Proxy', predeploy)
// Check to make sure that the admin of the proxy is the deployer.
// The deployer of the L2ERC721Bridge should be the same as the
// admin of the L2ERC721BridgeProxy so that it is easy to upgrade
// the implementation. The admin is then changed depending on the
// network after the L2ERC721BridgeProxy is upgraded to the implementation
const admin = await L2ERC721BridgeProxy.callStatic.admin()
if (getAddress(admin) !== getAddress(deployer)) {
throw new Error(`Unexpected admin ${admin}`)
}
const Deployment__L1ERC721Bridge = await hre.companionNetworks[
'l1'
].deployments.get('L1ERC721BridgeProxy')
const L1ERC721BridgeAddress = Deployment__L1ERC721Bridge.address
// Deploy the L2ERC721Bridge implementation
await hre.deployments.deploy('L2ERC721Bridge', {
from: deployer,
args: [predeploys.L2CrossDomainMessenger, L1ERC721BridgeAddress],
log: true,
waitConfirmations: 1,
})
const Deployment__L2ERC721Bridge = await hre.deployments.get('L2ERC721Bridge')
console.log(
`L2ERC721Bridge deployed to ${Deployment__L2ERC721Bridge.address}`
)
await validateERC721Bridge(hre, Deployment__L2ERC721Bridge.address, {
messenger: predeploys.L2CrossDomainMessenger,
otherBridge: L1ERC721BridgeAddress,
})
{
// Upgrade the implementation of the proxy to the newly deployed
// L2ERC721Bridge
const tx = await L2ERC721BridgeProxy.upgradeTo(
Deployment__L2ERC721Bridge.address
)
const receipt = await tx.wait()
console.log(
`Upgraded the implementation of the L2ERC721BridgeProxy: ${receipt.transactionhash}`
)
}
await validateERC721Bridge(hre, L2ERC721BridgeProxy.address, {
messenger: predeploys.L2CrossDomainMessenger,
otherBridge: L1ERC721BridgeAddress,
})
{
const newAdmin = getProxyAdmin(hre.network.name)
console.log(`Changing admin to ${newAdmin}`)
const tx = await L2ERC721BridgeProxy.changeAdmin(newAdmin)
const receipt = await tx.wait()
console.log(
`Changed admin of the L2ERC721BridgeProxy: ${receipt.transactionHash}`
)
}
}
deployFn.tags = ['L2ERC721BridgeImplementation']
deployFn.dependencies = ['L2ERC721BridgeProxy', 'L1ERC721BridgeProxy']
export default deployFn
/* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
const predeploy = '0x4200000000000000000000000000000000000014'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const { getAddress } = hre.ethers.utils
console.log(`Deploying L2ERC721BridgeProxy to ${hre.network.name}`)
console.log(`Using deployer ${deployer}`)
// Check to make sure that the Proxy has not been deployed yet
const pre = await hre.ethers.provider.getCode(predeploy, 'latest')
if (pre !== '0x') {
console.log(`Code already deployed to ${predeploy}`)
return
}
// A special deployer account must be used
const mainnetDeployer = getAddress(
'0x53A6eecC2dD4795Fcc68940ddc6B4d53Bd88Bd9E'
)
const goerliDeployer = getAddress(
'0x5c679a57e018f5f146838138d3e032ef4913d551'
)
const kovanDeployer = getAddress('0xa81224490b9fa4930a2e920550cd1c9106bb6d9e')
const localDeployer = getAddress('0xdfc82d475833a50de90c642770f34a9db7deb725')
// Deploy the L2ERC721BridgeProxy as a predeploy address
if (hre.network.name === 'optimism') {
if (getAddress(deployer) !== mainnetDeployer) {
throw new Error(`Incorrect deployer: ${deployer}`)
}
} else if (hre.network.name === 'optimism-goerli') {
if (getAddress(deployer) !== goerliDeployer) {
throw new Error(`Incorrect deployer: ${deployer}`)
}
} else if (hre.network.name === 'ops-l2') {
if (getAddress(deployer) !== localDeployer) {
throw new Error(`Incorrect deployer: ${deployer}`)
}
} else if (hre.network.name === 'optimism-kovan') {
if (getAddress(deployer) !== kovanDeployer) {
throw new Error(`Incorrect deployer: ${deployer}`)
}
} else {
throw new Error(`Unknown network: ${hre.network.name}`)
}
// Set the deployer as the admin of the Proxy. This is
// temporary, the admin will be updated when deploying
// the implementation
await hre.deployments.deploy('L2ERC721BridgeProxy', {
contract: 'Proxy',
from: deployer,
args: [deployer],
log: true,
waitConfirmations: 1,
})
// Check that the Proxy was deployed to the correct address
const code = await hre.ethers.provider.getCode(predeploy)
if (code === '0x') {
throw new Error('Code is not set at expected predeploy address')
}
console.log(`L2ERC721BridgeProxy deployed to ${predeploy}`)
}
deployFn.tags = ['L2ERC721BridgeProxy']
export default deployFn
/* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
import { getProxyAdmin, predeploy } from '../../src/nft-bridge-deploy-helpers'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const { getAddress } = hre.ethers.utils
console.log(`Deploying OptimismMintableERC721Factory to ${hre.network.name}`)
console.log(`Using deployer ${deployer}`)
const Deployment__OptimismMintableERC721FactoryProxy =
await hre.deployments.get('OptimismMintableERC721FactoryProxy')
const OptimismMintableERC721FactoryProxy = await hre.ethers.getContractAt(
'Proxy',
Deployment__OptimismMintableERC721FactoryProxy.address
)
// Check that the admin of the OptimismMintableERC721FactoryProxy is the
// deployer. This makes it easy to upgrade the implementation of the proxy
// and then transfer the admin privilege after deploying the implementation
const admin = await OptimismMintableERC721FactoryProxy.callStatic.admin()
if (getAddress(admin) !== getAddress(deployer)) {
throw new Error('deployer is not proxy admin')
}
let remoteChainId: number
if (hre.network.name === 'optimism') {
remoteChainId = 1
} else if (hre.network.name === 'optimism-goerli') {
remoteChainId = 5
} else if (hre.network.name === 'ops-l2') {
remoteChainId = 31337
} else if (hre.network.name === 'optimism-kovan') {
remoteChainId = 42
} else {
remoteChainId = hre.deployConfig.remoteChainId
}
if (typeof remoteChainId !== 'number') {
throw new Error('remoteChainId not defined')
}
await hre.deployments.deploy('OptimismMintableERC721Factory', {
from: deployer,
args: [predeploy, remoteChainId],
log: true,
waitConfirmations: 1,
})
const Deployment__OptimismMintableERC721Factory = await hre.deployments.get(
'OptimismMintableERC721Factory'
)
console.log(
`OptimismMintableERC721Factory deployed to ${Deployment__OptimismMintableERC721Factory.address}`
)
{
// Upgrade the Proxy to the newly deployed implementation
const tx = await OptimismMintableERC721FactoryProxy.upgradeTo(
Deployment__OptimismMintableERC721Factory.address
)
const receipt = await tx.wait()
console.log(
`OptimismMintableERC721FactoryProxy upgraded: ${receipt.transactionHash}`
)
}
{
const newAdmin = getProxyAdmin(hre.network.name)
const tx = await OptimismMintableERC721FactoryProxy.changeAdmin(newAdmin)
const receipt = await tx.wait()
console.log(
`OptimismMintableERC721FactoryProxy admin updated: ${receipt.transactionHash}`
)
}
}
deployFn.tags = ['OptimismMintableERC721FactoryImplementation']
deployFn.dependencies = ['OptimismMintableERC721FactoryProxy']
export default deployFn
/* Imports: External */
import { DeployFunction } from 'hardhat-deploy/dist/types'
import '@eth-optimism/hardhat-deploy-config'
import '@nomiclabs/hardhat-ethers'
import 'hardhat-deploy'
const deployFn: DeployFunction = async (hre) => {
const { deployer } = await hre.getNamedAccounts()
const { deploy } = hre.deployments
console.log(
`Deploying OptimismMintableERC721FactoryProxy to ${hre.network.name}`
)
console.log(`Using deployer ${deployer}`)
// Deploy the OptimismMintableERC721FactoryProxy with
// the deployer as the admin. The admin and implementation
// will be updated with the deployment of the implementation
await deploy('OptimismMintableERC721FactoryProxy', {
contract: 'Proxy',
from: deployer,
args: [deployer],
log: true,
waitConfirmations: 1,
})
const Deployment__OptimismMintableERC721FactoryProxy =
await hre.deployments.get('OptimismMintableERC721FactoryProxy')
console.log(
`OptimismMintableERC721FactoryProxy deployed to ${Deployment__OptimismMintableERC721FactoryProxy.address}`
)
}
deployFn.tags = ['OptimismMintableERC721FactoryProxy']
export default deployFn
...@@ -55,7 +55,6 @@ ...@@ -55,7 +55,6 @@
}, },
"devDependencies": { "devDependencies": {
"@defi-wonderland/smock": "^2.0.7", "@defi-wonderland/smock": "^2.0.7",
"@eth-optimism/contracts": "^0.5.39",
"@eth-optimism/contracts-bedrock": "0.10.0", "@eth-optimism/contracts-bedrock": "0.10.0",
"@eth-optimism/core-utils": "^0.12.0", "@eth-optimism/core-utils": "^0.12.0",
"@eth-optimism/hardhat-deploy-config": "^0.2.5", "@eth-optimism/hardhat-deploy-config": "^0.2.5",
......
/* Imports */
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract, constants } from 'ethers'
import { Interface } from 'ethers/lib/utils'
import {
smock,
MockContractFactory,
FakeContract,
MockContract,
} from '@defi-wonderland/smock'
import ICrossDomainMessenger from '@eth-optimism/contracts/artifacts/contracts/libraries/bridge/ICrossDomainMessenger.sol/ICrossDomainMessenger.json'
import { NON_NULL_BYTES32, NON_ZERO_ADDRESS } from '../../helpers'
import { expect } from '../../setup'
const ERR_INVALID_X_DOMAIN_MESSAGE =
'ERC721Bridge: function can only be called from the other bridge'
const DUMMY_L2_ERC721_ADDRESS = ethers.utils.getAddress(
'0x' + 'abba'.repeat(10)
)
const DUMMY_L2_BRIDGE_ADDRESS = ethers.utils.getAddress(
'0x' + 'acdc'.repeat(10)
)
const FINALIZATION_GAS = 600_000
describe('L1ERC721Bridge', () => {
// init signers
let l1MessengerImpersonator: Signer
let alice: Signer
let bob: Signer
let bobsAddress
let aliceAddress
let tokenId
let aliceInitialBalance
// we can just make up this string since it's on the "other" Layer
let Factory__L1ERC721: MockContractFactory<ContractFactory>
let IL2ERC721Bridge: Interface
before(async () => {
;[l1MessengerImpersonator, alice, bob] = await ethers.getSigners()
// deploy an ERC721 contract on L1
Factory__L1ERC721 = await smock.mock(
'@openzeppelin/contracts/token/ERC721/ERC721.sol:ERC721'
)
// get an L2ERC721Bridge Interface
IL2ERC721Bridge = (await ethers.getContractFactory('L2ERC721Bridge'))
.interface
aliceAddress = await alice.getAddress()
bobsAddress = await bob.getAddress()
aliceInitialBalance = 5
tokenId = 10
})
let L1ERC721: MockContract<Contract>
let L1ERC721Bridge: Contract
let Fake__L1CrossDomainMessenger: FakeContract
let Factory__L1ERC721Bridge: ContractFactory
beforeEach(async () => {
// Get a new mock L1 messenger
Fake__L1CrossDomainMessenger = await smock.fake<Contract>(
new ethers.utils.Interface(ICrossDomainMessenger.abi),
{ address: await l1MessengerImpersonator.getAddress() } // This allows us to use an ethers override {from: Fake__L1CrossDomainMessenger.address} to mock calls
)
// Deploy the contract under test
Factory__L1ERC721Bridge = await ethers.getContractFactory('L1ERC721Bridge')
L1ERC721Bridge = await Factory__L1ERC721Bridge.deploy(
Fake__L1CrossDomainMessenger.address,
DUMMY_L2_BRIDGE_ADDRESS
)
L1ERC721 = await Factory__L1ERC721.deploy('L1ERC721', 'ERC')
await L1ERC721.setVariable('_owners', {
[tokenId]: aliceAddress,
})
await L1ERC721.setVariable('_balances', {
[aliceAddress]: aliceInitialBalance,
})
})
describe('constructor', async () => {
it('initializes correctly', async () => {
it('reverts if cross domain messenger is address(0)', async () => {
await expect(
Factory__L1ERC721Bridge.deploy(
constants.AddressZero,
DUMMY_L2_BRIDGE_ADDRESS
)
).to.be.revertedWith('ERC721Bridge: messenger cannot be address(0)')
})
it('reverts if other bridge is address(0)', async () => {
await expect(
Factory__L1ERC721Bridge.deploy(
Fake__L1CrossDomainMessenger.address,
constants.AddressZero
)
).to.be.revertedWith('ERC721Bridge: other bridge cannot be address(0)')
})
expect(await L1ERC721Bridge.messenger()).equals(
Fake__L1CrossDomainMessenger.address
)
expect(await L1ERC721Bridge.otherBridge()).equals(DUMMY_L2_BRIDGE_ADDRESS)
})
})
describe('ERC721 deposits', () => {
beforeEach(async () => {
await L1ERC721.connect(alice).approve(L1ERC721Bridge.address, tokenId)
})
it('bridgeERC721() reverts if remote token is address(0)', async () => {
await expect(
L1ERC721Bridge.connect(alice).bridgeERC721(
L1ERC721.address,
constants.AddressZero,
tokenId,
FINALIZATION_GAS,
NON_NULL_BYTES32
)
).to.be.revertedWith('ERC721Bridge: remote token cannot be address(0)')
})
it('bridgeERC721() escrows the deposit and sends the correct deposit message', async () => {
// alice calls deposit on the bridge and the L1 bridge calls transferFrom on the token.
// emits an ERC721BridgeInitiated event with the correct arguments.
await expect(
L1ERC721Bridge.connect(alice).bridgeERC721(
L1ERC721.address,
DUMMY_L2_ERC721_ADDRESS,
tokenId,
FINALIZATION_GAS,
NON_NULL_BYTES32
)
)
.to.emit(L1ERC721Bridge, 'ERC721BridgeInitiated')
.withArgs(
L1ERC721.address,
DUMMY_L2_ERC721_ADDRESS,
aliceAddress,
aliceAddress,
tokenId,
NON_NULL_BYTES32
)
const depositCallToMessenger =
Fake__L1CrossDomainMessenger.sendMessage.getCall(0)
// alice's balance decreases by 1
const depositerBalance = await L1ERC721.balanceOf(aliceAddress)
expect(depositerBalance).to.equal(aliceInitialBalance - 1)
// bridge's balance increases by 1
const bridgeBalance = await L1ERC721.balanceOf(L1ERC721Bridge.address)
expect(bridgeBalance).to.equal(1)
// Check the correct cross-chain call was sent:
// Message should be sent to the L2 bridge
expect(depositCallToMessenger.args[0]).to.equal(DUMMY_L2_BRIDGE_ADDRESS)
// Message data should be a call telling the L2DepositedERC721 to finalize the deposit
// the L1 bridge sends the correct message to the L1 messenger
expect(depositCallToMessenger.args[1]).to.equal(
IL2ERC721Bridge.encodeFunctionData('finalizeBridgeERC721', [
DUMMY_L2_ERC721_ADDRESS,
L1ERC721.address,
aliceAddress,
aliceAddress,
tokenId,
NON_NULL_BYTES32,
])
)
expect(depositCallToMessenger.args[2]).to.equal(FINALIZATION_GAS)
// Updates the deposits mapping
expect(
await L1ERC721Bridge.deposits(
L1ERC721.address,
DUMMY_L2_ERC721_ADDRESS,
tokenId
)
).to.equal(true)
})
it('bridgeERC721To() reverts if NFT receiver is address(0)', async () => {
await expect(
L1ERC721Bridge.connect(alice).bridgeERC721To(
L1ERC721.address,
DUMMY_L2_ERC721_ADDRESS,
constants.AddressZero,
tokenId,
FINALIZATION_GAS,
NON_NULL_BYTES32
)
).to.be.revertedWith('ERC721Bridge: nft recipient cannot be address(0)')
})
it('bridgeERC721To() escrows the deposited NFT and sends the correct deposit message', async () => {
// depositor calls deposit on the bridge and the L1 bridge calls transferFrom on the token.
// emits an ERC721BridgeInitiated event with the correct arguments.
await expect(
L1ERC721Bridge.connect(alice).bridgeERC721To(
L1ERC721.address,
DUMMY_L2_ERC721_ADDRESS,
bobsAddress,
tokenId,
FINALIZATION_GAS,
NON_NULL_BYTES32
)
)
.to.emit(L1ERC721Bridge, 'ERC721BridgeInitiated')
.withArgs(
L1ERC721.address,
DUMMY_L2_ERC721_ADDRESS,
aliceAddress,
bobsAddress,
tokenId,
NON_NULL_BYTES32
)
const depositCallToMessenger =
Fake__L1CrossDomainMessenger.sendMessage.getCall(0)
// alice's balance decreases by 1
const depositerBalance = await L1ERC721.balanceOf(aliceAddress)
expect(depositerBalance).to.equal(aliceInitialBalance - 1)
// bridge's balance is increased
const bridgeBalance = await L1ERC721.balanceOf(L1ERC721Bridge.address)
expect(bridgeBalance).to.equal(1)
// bridge is owner of tokenId
const tokenIdOwner = await L1ERC721.ownerOf(tokenId)
expect(tokenIdOwner).to.equal(L1ERC721Bridge.address)
// Check the correct cross-chain call was sent:
// Message should be sent to the L2DepositedERC721 on L2
expect(depositCallToMessenger.args[0]).to.equal(DUMMY_L2_BRIDGE_ADDRESS)
// Message data should be a call telling the L2DepositedERC721 to finalize the deposit
// the L1 bridge sends the correct message to the L1 messenger
expect(depositCallToMessenger.args[1]).to.equal(
IL2ERC721Bridge.encodeFunctionData('finalizeBridgeERC721', [
DUMMY_L2_ERC721_ADDRESS,
L1ERC721.address,
aliceAddress,
bobsAddress,
tokenId,
NON_NULL_BYTES32,
])
)
expect(depositCallToMessenger.args[2]).to.equal(FINALIZATION_GAS)
// Updates the deposits mapping
expect(
await L1ERC721Bridge.deposits(
L1ERC721.address,
DUMMY_L2_ERC721_ADDRESS,
tokenId
)
).to.equal(true)
})
it('cannot bridgeERC721 from a contract account', async () => {
await expect(
L1ERC721Bridge.bridgeERC721(
L1ERC721.address,
DUMMY_L2_ERC721_ADDRESS,
tokenId,
FINALIZATION_GAS,
NON_NULL_BYTES32
)
).to.be.revertedWith('ERC721Bridge: account is not externally owned')
})
describe('Handling ERC721.transferFrom() failures that revert', () => {
it('bridgeERC721(): will revert if ERC721.transferFrom() reverts', async () => {
await expect(
L1ERC721Bridge.connect(bob).bridgeERC721To(
L1ERC721.address,
DUMMY_L2_ERC721_ADDRESS,
bobsAddress,
tokenId,
FINALIZATION_GAS,
NON_NULL_BYTES32
)
).to.be.revertedWith('ERC721: transfer from incorrect owner')
})
it('bridgeERC721To(): will revert if ERC721.transferFrom() reverts', async () => {
await expect(
L1ERC721Bridge.connect(bob).bridgeERC721To(
L1ERC721.address,
DUMMY_L2_ERC721_ADDRESS,
bobsAddress,
tokenId,
FINALIZATION_GAS,
NON_NULL_BYTES32
)
).to.be.revertedWith('ERC721: transfer from incorrect owner')
})
it('bridgeERC721To(): will revert if the L1 ERC721 is zero address', async () => {
await expect(
L1ERC721Bridge.connect(alice).bridgeERC721To(
constants.AddressZero,
DUMMY_L2_ERC721_ADDRESS,
bobsAddress,
tokenId,
FINALIZATION_GAS,
NON_NULL_BYTES32
)
).to.be.revertedWith('function call to a non-contract account')
})
it('bridgeERC721To(): will revert if the L1 ERC721 has no code', async () => {
await expect(
L1ERC721Bridge.connect(alice).bridgeERC721To(
bobsAddress,
DUMMY_L2_ERC721_ADDRESS,
bobsAddress,
tokenId,
FINALIZATION_GAS,
NON_NULL_BYTES32
)
).to.be.revertedWith('function call to a non-contract account')
})
})
})
describe('ERC721 withdrawals', () => {
it('onlyFromCrossDomainAccount: should revert on calls from a non-crossDomainMessenger L1 account', async () => {
await expect(
L1ERC721Bridge.connect(alice).finalizeBridgeERC721(
L1ERC721.address,
DUMMY_L2_ERC721_ADDRESS,
constants.AddressZero,
constants.AddressZero,
tokenId,
NON_NULL_BYTES32
)
).to.be.revertedWith(ERR_INVALID_X_DOMAIN_MESSAGE)
})
it('onlyFromCrossDomainAccount: should revert on calls from the right crossDomainMessenger, but wrong xDomainMessageSender (ie. not the L2DepositedERC721)', async () => {
await expect(
L1ERC721Bridge.finalizeBridgeERC721(
L1ERC721.address,
DUMMY_L2_ERC721_ADDRESS,
constants.AddressZero,
constants.AddressZero,
tokenId,
NON_NULL_BYTES32,
{
from: Fake__L1CrossDomainMessenger.address,
}
)
).to.be.revertedWith(ERR_INVALID_X_DOMAIN_MESSAGE)
})
describe('withdrawal attempts that pass the onlyFromCrossDomainAccount check', () => {
beforeEach(async () => {
// First Alice will send an NFT so that there's a balance to be withdrawn
await L1ERC721.connect(alice).approve(L1ERC721Bridge.address, tokenId)
await L1ERC721Bridge.connect(alice).bridgeERC721(
L1ERC721.address,
DUMMY_L2_ERC721_ADDRESS,
tokenId,
FINALIZATION_GAS,
NON_NULL_BYTES32
)
// make sure bridge owns NFT
expect(await L1ERC721.ownerOf(tokenId)).to.equal(L1ERC721Bridge.address)
Fake__L1CrossDomainMessenger.xDomainMessageSender.returns(
DUMMY_L2_BRIDGE_ADDRESS
)
})
it('should credit funds to the withdrawer to finalize withdrawal', async () => {
// finalizing the withdrawal emits an ERC721BridgeFinalized event with the correct arguments.
await expect(
L1ERC721Bridge.finalizeBridgeERC721(
L1ERC721.address,
DUMMY_L2_ERC721_ADDRESS,
NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS,
tokenId,
NON_NULL_BYTES32,
{ from: Fake__L1CrossDomainMessenger.address }
)
)
.to.emit(L1ERC721Bridge, 'ERC721BridgeFinalized')
.withArgs(
L1ERC721.address,
DUMMY_L2_ERC721_ADDRESS,
NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS,
tokenId,
NON_NULL_BYTES32
)
// NFT is transferred to new owner
expect(await L1ERC721.ownerOf(tokenId)).to.equal(NON_ZERO_ADDRESS)
// deposits state variable is updated
expect(
await L1ERC721Bridge.deposits(
L1ERC721.address,
DUMMY_L2_ERC721_ADDRESS,
tokenId
)
).to.equal(false)
})
})
})
})
/* Imports */
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract, constants } from 'ethers'
import { smock, FakeContract } from '@defi-wonderland/smock'
import ICrossDomainMessenger from '@eth-optimism/contracts/artifacts/contracts/libraries/bridge/ICrossDomainMessenger.sol/ICrossDomainMessenger.json'
import { toRpcHexString } from '@eth-optimism/core-utils'
import { NON_NULL_BYTES32, NON_ZERO_ADDRESS } from '../../helpers'
import { expect } from '../../setup'
const ERR_INVALID_X_DOMAIN_MESSAGE =
'ERC721Bridge: function can only be called from the other bridge'
const DUMMY_L1BRIDGE_ADDRESS: string =
'0x1234123412341234123412341234123412341234'
const DUMMY_L1ERC721_ADDRESS: string =
'0x2234223412342234223422342234223422342234'
const ERR_INVALID_WITHDRAWAL: string =
'Withdrawal is not being initiated by NFT owner'
const TOKEN_ID: number = 10
const FINALIZATION_GAS = 600_000
describe('L2ERC721Bridge', () => {
let alice: Signer
let aliceAddress: string
let bob: Signer
let bobsAddress: string
let l2MessengerImpersonator: Signer
let Factory__L1ERC721Bridge: ContractFactory
before(async () => {
// Create a special signer which will enable us to send messages from the L2Messenger contract
;[l2MessengerImpersonator, alice, bob] = await ethers.getSigners()
aliceAddress = await alice.getAddress()
bobsAddress = await bob.getAddress()
Factory__L1ERC721Bridge = await ethers.getContractFactory('L1ERC721Bridge')
})
let Factory__L2ERC721Bridge: ContractFactory
let L2ERC721Bridge: Contract
let L2ERC721: Contract
let Fake__L2CrossDomainMessenger: FakeContract
beforeEach(async () => {
// Get a new fake L2 messenger
Fake__L2CrossDomainMessenger = await smock.fake<Contract>(
new ethers.utils.Interface(ICrossDomainMessenger.abi),
// This allows us to use an ethers override {from: Fake__L2CrossDomainMessenger.address} to mock calls
{ address: await l2MessengerImpersonator.getAddress() }
)
// Deploy the contract under test
Factory__L2ERC721Bridge = await ethers.getContractFactory('L2ERC721Bridge')
L2ERC721Bridge = await Factory__L2ERC721Bridge.deploy(
Fake__L2CrossDomainMessenger.address,
DUMMY_L1BRIDGE_ADDRESS
)
// Deploy an L2 ERC721
L2ERC721 = await (
await ethers.getContractFactory('OptimismMintableERC721')
).deploy(
L2ERC721Bridge.address,
100,
DUMMY_L1ERC721_ADDRESS,
'L2Token',
'L2T',
{ gasLimit: 4_000_000 } // Necessary to avoid an out-of-gas error
)
})
describe('constructor', async () => {
it('reverts if cross domain messenger is address(0)', async () => {
await expect(
Factory__L2ERC721Bridge.deploy(
constants.AddressZero,
DUMMY_L1BRIDGE_ADDRESS
)
).to.be.revertedWith('ERC721Bridge: messenger cannot be address(0)')
})
it('reverts if other bridge is address(0)', async () => {
await expect(
Factory__L2ERC721Bridge.deploy(
Fake__L2CrossDomainMessenger.address,
constants.AddressZero
)
).to.be.revertedWith('ERC721Bridge: other bridge cannot be address(0)')
})
it('initializes correctly', async () => {
expect(await L2ERC721Bridge.messenger()).equals(
Fake__L2CrossDomainMessenger.address
)
expect(await L2ERC721Bridge.otherBridge()).equals(DUMMY_L1BRIDGE_ADDRESS)
})
})
// test the transfer flow of moving a token from L1 to L2
describe('finalizeBridgeERC721', () => {
it('onlyFromCrossDomainAccount: should revert on calls from a non-crossDomainMessenger L2 account', async () => {
await expect(
L2ERC721Bridge.connect(alice).finalizeBridgeERC721(
DUMMY_L1ERC721_ADDRESS,
NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS,
TOKEN_ID,
NON_NULL_BYTES32
)
).to.be.revertedWith(ERR_INVALID_X_DOMAIN_MESSAGE)
})
it('onlyFromCrossDomainAccount: should revert on calls from the right crossDomainMessenger, but wrong xDomainMessageSender (ie. not the L1ERC721Bridge)', async () => {
Fake__L2CrossDomainMessenger.xDomainMessageSender.returns(
NON_ZERO_ADDRESS
)
await expect(
L2ERC721Bridge.connect(l2MessengerImpersonator).finalizeBridgeERC721(
DUMMY_L1ERC721_ADDRESS,
NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS,
NON_ZERO_ADDRESS,
TOKEN_ID,
NON_NULL_BYTES32,
{
from: Fake__L2CrossDomainMessenger.address,
}
)
).to.be.revertedWith(ERR_INVALID_X_DOMAIN_MESSAGE)
})
it('should credit funds to the depositor', async () => {
Fake__L2CrossDomainMessenger.xDomainMessageSender.returns(
DUMMY_L1BRIDGE_ADDRESS
)
// Assert that nobody owns the L2 token initially
await expect(L2ERC721.ownerOf(TOKEN_ID)).to.be.revertedWith(
'ERC721: invalid token ID'
)
// Successfully finalizes the deposit.
const expectedResult = expect(
L2ERC721Bridge.connect(l2MessengerImpersonator).finalizeBridgeERC721(
L2ERC721.address,
DUMMY_L1ERC721_ADDRESS,
aliceAddress,
bobsAddress,
TOKEN_ID,
NON_NULL_BYTES32,
{
from: Fake__L2CrossDomainMessenger.address,
}
)
)
// Depositing causes an ERC721BridgeFinalized event to be emitted.
await expectedResult.to
.emit(L2ERC721Bridge, 'ERC721BridgeFinalized')
.withArgs(
L2ERC721.address,
DUMMY_L1ERC721_ADDRESS,
aliceAddress,
bobsAddress,
TOKEN_ID,
NON_NULL_BYTES32
)
// Causes a Transfer event to be emitted from the L2 ERC721.
await expectedResult.to
.emit(L2ERC721, 'Transfer')
.withArgs(constants.AddressZero, bobsAddress, TOKEN_ID)
// Bob is now the owner of the L2 ERC721
const tokenIdOwner = await L2ERC721.ownerOf(TOKEN_ID)
tokenIdOwner.should.equal(bobsAddress)
})
})
describe('withdrawals', () => {
let L2Token: Contract
beforeEach(async () => {
L2Token = await (
await ethers.getContractFactory('OptimismMintableERC721')
).deploy(
L2ERC721Bridge.address,
100,
DUMMY_L1ERC721_ADDRESS,
'L2Token',
'L2T'
)
await ethers.provider.send('hardhat_impersonateAccount', [
L2ERC721Bridge.address,
])
await ethers.provider.send('hardhat_setBalance', [
L2ERC721Bridge.address,
toRpcHexString(ethers.utils.parseEther('1')),
])
const signer = await ethers.getSigner(L2ERC721Bridge.address)
await L2Token.connect(signer).safeMint(aliceAddress, TOKEN_ID)
})
it('bridgeERC721() reverts if remote token is address(0)', async () => {
await expect(
L2ERC721Bridge.connect(alice).bridgeERC721(
L2Token.address,
constants.AddressZero,
TOKEN_ID,
FINALIZATION_GAS,
NON_NULL_BYTES32
)
).to.be.revertedWith('ERC721Bridge: remote token cannot be address(0)')
})
it('bridgeERC721() reverts when called by non-owner of nft', async () => {
await expect(
L2ERC721Bridge.connect(bob).bridgeERC721(
L2Token.address,
DUMMY_L1ERC721_ADDRESS,
TOKEN_ID,
0,
NON_NULL_BYTES32
)
).to.be.revertedWith(ERR_INVALID_WITHDRAWAL)
})
it('bridgeERC721() reverts if called by a contract', async () => {
await expect(
L2ERC721Bridge.connect(l2MessengerImpersonator).bridgeERC721(
L2Token.address,
DUMMY_L1ERC721_ADDRESS,
TOKEN_ID,
0,
NON_NULL_BYTES32
)
).to.be.revertedWith('ERC721Bridge: account is not externally owned')
})
it('bridgeERC721() burns and sends the correct withdrawal message', async () => {
// Make sure that alice begins as the NFT owner
expect(await L2Token.ownerOf(TOKEN_ID)).to.equal(aliceAddress)
// Initiates a successful withdrawal.
const expectedResult = expect(
L2ERC721Bridge.connect(alice).bridgeERC721(
L2Token.address,
DUMMY_L1ERC721_ADDRESS,
TOKEN_ID,
0,
NON_NULL_BYTES32
)
)
// A successful withdrawal causes an ERC721BridgeInitiated event to be emitted from the L2 ERC721 Bridge.
await expectedResult.to
.emit(L2ERC721Bridge, 'ERC721BridgeInitiated')
.withArgs(
L2Token.address,
DUMMY_L1ERC721_ADDRESS,
aliceAddress,
aliceAddress,
TOKEN_ID,
NON_NULL_BYTES32
)
// A withdrawal also causes a Transfer event to be emitted the L2 ERC721, signifying that the L2 token
// has been burnt.
await expectedResult.to
.emit(L2Token, 'Transfer')
.withArgs(aliceAddress, constants.AddressZero, TOKEN_ID)
// Assert Alice's balance went down
const aliceBalance = await L2Token.balanceOf(aliceAddress)
expect(aliceBalance).to.equal(0)
// Assert that the token isn't owned by anyone
await expect(L2Token.ownerOf(TOKEN_ID)).to.be.revertedWith(
'ERC721: invalid token ID'
)
const withdrawalCallToMessenger =
Fake__L2CrossDomainMessenger.sendMessage.getCall(0)
// Assert the correct cross-chain call was sent:
// Message should be sent to the L1ERC721Bridge on L1
expect(withdrawalCallToMessenger.args[0]).to.equal(DUMMY_L1BRIDGE_ADDRESS)
// Message data should be a call telling the L1ERC721Bridge to finalize the withdrawal
expect(withdrawalCallToMessenger.args[1]).to.equal(
Factory__L1ERC721Bridge.interface.encodeFunctionData(
'finalizeBridgeERC721',
[
DUMMY_L1ERC721_ADDRESS,
L2Token.address,
aliceAddress,
aliceAddress,
TOKEN_ID,
NON_NULL_BYTES32,
]
)
)
// gaslimit should be correct
expect(withdrawalCallToMessenger.args[2]).to.equal(0)
})
it('bridgeERC721To() reverts if NFT receiver is address(0)', async () => {
await expect(
L2ERC721Bridge.connect(alice).bridgeERC721To(
L2Token.address,
DUMMY_L1ERC721_ADDRESS,
constants.AddressZero,
TOKEN_ID,
0,
NON_NULL_BYTES32
)
).to.be.revertedWith('ERC721Bridge: nft recipient cannot be address(0)')
})
it('bridgeERC721To() reverts when called by non-owner of nft', async () => {
await expect(
L2ERC721Bridge.connect(bob).bridgeERC721To(
L2Token.address,
DUMMY_L1ERC721_ADDRESS,
bobsAddress,
TOKEN_ID,
0,
NON_NULL_BYTES32
)
).to.be.revertedWith(ERR_INVALID_WITHDRAWAL)
})
it('bridgeERC721To() burns and sends the correct withdrawal message', async () => {
// Make sure that alice begins as the NFT owner
expect(await L2Token.ownerOf(TOKEN_ID)).to.equal(aliceAddress)
// Initiates a successful withdrawal.
const expectedResult = expect(
L2ERC721Bridge.connect(alice).bridgeERC721To(
L2Token.address,
DUMMY_L1ERC721_ADDRESS,
bobsAddress,
TOKEN_ID,
0,
NON_NULL_BYTES32
)
)
// A successful withdrawal causes an ERC721BridgeInitiated event to be emitted from the L2 ERC721 Bridge.
await expectedResult.to
.emit(L2ERC721Bridge, 'ERC721BridgeInitiated')
.withArgs(
L2Token.address,
DUMMY_L1ERC721_ADDRESS,
aliceAddress,
bobsAddress,
TOKEN_ID,
NON_NULL_BYTES32
)
// A withdrawal also causes a Transfer event to be emitted the L2 ERC721, signifying that the L2 token
// has been burnt.
await expectedResult.to
.emit(L2Token, 'Transfer')
.withArgs(aliceAddress, constants.AddressZero, TOKEN_ID)
// Assert Alice's balance went down
const aliceBalance = await L2Token.balanceOf(aliceAddress)
expect(aliceBalance).to.equal(0)
// Assert that the token isn't owned by anyone
await expect(L2Token.ownerOf(TOKEN_ID)).to.be.revertedWith(
'ERC721: invalid token ID'
)
const withdrawalCallToMessenger =
Fake__L2CrossDomainMessenger.sendMessage.getCall(0)
// Assert the correct cross-chain call was sent.
// Message should be sent to the L1ERC721Bridge on L1
expect(withdrawalCallToMessenger.args[0]).to.equal(DUMMY_L1BRIDGE_ADDRESS)
// The message data should be a call telling the L1ERC721Bridge to finalize the withdrawal
expect(withdrawalCallToMessenger.args[1]).to.equal(
Factory__L1ERC721Bridge.interface.encodeFunctionData(
'finalizeBridgeERC721',
[
DUMMY_L1ERC721_ADDRESS,
L2Token.address,
aliceAddress,
bobsAddress,
TOKEN_ID,
NON_NULL_BYTES32,
]
)
)
// gas value is ignored and set to 0.
expect(withdrawalCallToMessenger.args[2]).to.equal(0)
})
})
})
/* External Imports */
import { ethers } from 'hardhat'
import { Signer, Contract } from 'ethers'
import { smock, FakeContract } from '@defi-wonderland/smock'
/* Internal Imports */
import { expect } from '../../setup'
const TOKEN_ID = 10
const DUMMY_L1ERC721_ADDRESS: string =
'0x0034223412342234223422342234223422342234'
describe('OptimismMintableERC721', () => {
let l2BridgeImpersonator: Signer
let alice: Signer
let Fake__L2ERC721Bridge: FakeContract
let OptimismMintableERC721: Contract
let l2BridgeImpersonatorAddress: string
let aliceAddress: string
let baseUri: string
const remoteChainId = 100
let Factory__OptimismMintableERC721
before(async () => {
;[l2BridgeImpersonator, alice] = await ethers.getSigners()
l2BridgeImpersonatorAddress = await l2BridgeImpersonator.getAddress()
aliceAddress = await alice.getAddress()
baseUri = ''.concat(
'ethereum:',
DUMMY_L1ERC721_ADDRESS,
'@',
remoteChainId.toString(),
'/tokenURI?uint256='
)
Factory__OptimismMintableERC721 = await ethers.getContractFactory(
'OptimismMintableERC721'
)
OptimismMintableERC721 = await Factory__OptimismMintableERC721.deploy(
l2BridgeImpersonatorAddress,
remoteChainId,
DUMMY_L1ERC721_ADDRESS,
'L2ERC721',
'ERC'
)
// Get a new fake L2 bridge
Fake__L2ERC721Bridge = await smock.fake<Contract>(
'L2ERC721Bridge',
// This allows us to use an ethers override {from: Fake__L2ERC721Bridge.address} to mock calls
{ address: await l2BridgeImpersonator.getAddress() }
)
// mint an nft to alice
await OptimismMintableERC721.connect(l2BridgeImpersonator).safeMint(
aliceAddress,
TOKEN_ID,
{
from: Fake__L2ERC721Bridge.address,
}
)
})
describe('constructor', () => {
it('should revert if bridge is address(0)', async () => {
await expect(
Factory__OptimismMintableERC721.deploy(
ethers.constants.AddressZero,
remoteChainId,
DUMMY_L1ERC721_ADDRESS,
'L2ERC721',
'ERC'
)
).to.be.revertedWith(
'OptimismMintableERC721: bridge cannot be address(0)'
)
})
it('should revert if remote chain id is address(0)', async () => {
await expect(
Factory__OptimismMintableERC721.deploy(
l2BridgeImpersonatorAddress,
0,
DUMMY_L1ERC721_ADDRESS,
'L2ERC721',
'ERC'
)
).to.be.revertedWith(
'OptimismMintableERC721: remote chain id cannot be zero'
)
})
it('should revert if remote token is address(0)', async () => {
await expect(
Factory__OptimismMintableERC721.deploy(
l2BridgeImpersonatorAddress,
remoteChainId,
ethers.constants.AddressZero,
'L2ERC721',
'ERC'
)
).to.be.revertedWith(
'OptimismMintableERC721: remote token cannot be address(0)'
)
})
it('should be able to create a standard ERC721 contract with the correct parameters', async () => {
expect(await OptimismMintableERC721.bridge()).to.equal(
l2BridgeImpersonatorAddress
)
expect(await OptimismMintableERC721.remoteToken()).to.equal(
DUMMY_L1ERC721_ADDRESS
)
expect(await OptimismMintableERC721.name()).to.equal('L2ERC721')
expect(await OptimismMintableERC721.symbol()).to.equal('ERC')
expect(await OptimismMintableERC721.baseTokenURI()).to.equal(baseUri)
// alice has been minted an nft
expect(await OptimismMintableERC721.ownerOf(TOKEN_ID)).to.equal(
aliceAddress
)
})
})
describe('mint and burn', () => {
it('should not allow anyone but the L2 bridge to mint and burn', async () => {
await expect(
OptimismMintableERC721.connect(alice).safeMint(aliceAddress, 100)
).to.be.revertedWith(
'OptimismMintableERC721: only bridge can call this function'
)
await expect(
OptimismMintableERC721.connect(alice).burn(aliceAddress, 100)
).to.be.revertedWith(
'OptimismMintableERC721: only bridge can call this function'
)
})
})
describe('supportsInterface', () => {
it('should return the correct interface support', async () => {
// ERC165
expect(await OptimismMintableERC721.supportsInterface(0x01ffc9a7)).to.be
.true
// OptimismMintableERC721
expect(await OptimismMintableERC721.supportsInterface(0xe49bc7f8)).to.be
.true
// ERC721
expect(await OptimismMintableERC721.supportsInterface(0x80ac58cd)).to.be
.true
// Some bad interface
expect(await OptimismMintableERC721.supportsInterface(0xffffffff)).to.be
.false
})
})
describe('tokenURI', () => {
it('should return the correct token uri', async () => {
const tokenUri = baseUri.concat(TOKEN_ID.toString())
expect(await OptimismMintableERC721.tokenURI(TOKEN_ID)).to.equal(tokenUri)
})
})
})
/* External Imports */
import { ethers } from 'hardhat'
import { Signer, ContractFactory, Contract } from 'ethers'
import {
smock,
MockContractFactory,
MockContract,
} from '@defi-wonderland/smock'
/* Internal Imports */
import { expect } from '../../setup'
const DUMMY_L2_BRIDGE_ADDRESS: string = ethers.utils.getAddress(
'0x' + 'acdc'.repeat(10)
)
describe('OptimismMintableERC721Factory', () => {
let signer: Signer
let Factory__L1ERC721: MockContractFactory<ContractFactory>
let L1ERC721: MockContract<Contract>
let OptimismMintableERC721Factory: Contract
let baseURI: string
const remoteChainId = 100
let Factory__OptimismMintableERC721Factory: ContractFactory
beforeEach(async () => {
;[signer] = await ethers.getSigners()
// deploy an ERC721 contract on L1
Factory__L1ERC721 = await smock.mock(
'@openzeppelin/contracts/token/ERC721/ERC721.sol:ERC721'
)
L1ERC721 = await Factory__L1ERC721.deploy('L1ERC721', 'ERC')
Factory__OptimismMintableERC721Factory = await ethers.getContractFactory(
'OptimismMintableERC721Factory'
)
OptimismMintableERC721Factory =
await Factory__OptimismMintableERC721Factory.deploy(
DUMMY_L2_BRIDGE_ADDRESS,
remoteChainId
)
baseURI = ''.concat(
'ethereum:',
L1ERC721.address.toLowerCase(),
'@',
remoteChainId.toString(),
'/tokenURI?uint256='
)
})
it('should revert if bridge is initialized as address(0)', async () => {
await expect(
Factory__OptimismMintableERC721Factory.deploy(
ethers.constants.AddressZero,
remoteChainId
)
).to.be.revertedWith(
'OptimismMintableERC721Factory: bridge cannot be address(0)'
)
})
it('should revert if remote chain id is initialized as zero', async () => {
await expect(
Factory__OptimismMintableERC721Factory.deploy(DUMMY_L2_BRIDGE_ADDRESS, 0)
).to.be.revertedWith(
'OptimismMintableERC721Factory: remote chain id cannot be zero'
)
})
it('should be deployed with the correct constructor argument', async () => {
expect(await OptimismMintableERC721Factory.bridge()).to.equal(
DUMMY_L2_BRIDGE_ADDRESS
)
})
it('should be able to create a standard ERC721 contract', async () => {
const tx = await OptimismMintableERC721Factory.createOptimismMintableERC721(
L1ERC721.address,
'L2ERC721',
'ERC'
)
const receipt = await tx.wait()
// Get the OptimismMintableERC721Created event
const erc721CreatedEvent = receipt.events[0]
// Expect there to be an event emitted for the standard token creation
expect(erc721CreatedEvent.event).to.be.eq('OptimismMintableERC721Created')
// Get the L2 ERC721 address from the emitted event and check it was created correctly
const l2ERC721Address = erc721CreatedEvent.args.localToken
const OptimismMintableERC721 = new Contract(
l2ERC721Address,
(await ethers.getContractFactory('OptimismMintableERC721')).interface,
signer
)
expect(await OptimismMintableERC721.bridge()).to.equal(
DUMMY_L2_BRIDGE_ADDRESS
)
expect(await OptimismMintableERC721.remoteToken()).to.equal(
L1ERC721.address
)
expect(await OptimismMintableERC721.name()).to.equal('L2ERC721')
expect(await OptimismMintableERC721.symbol()).to.equal('ERC')
expect(await OptimismMintableERC721.baseTokenURI()).to.equal(baseURI)
expect(
await OptimismMintableERC721Factory.isOptimismMintableERC721(
OptimismMintableERC721.address
)
).to.equal(true)
})
it('should not be able to create a standard token with a 0 address for l1 token', async () => {
await expect(
OptimismMintableERC721Factory.createOptimismMintableERC721(
ethers.constants.AddressZero,
'L2ERC721',
'ERC'
)
).to.be.revertedWith(
'OptimismMintableERC721Factory: L1 token address cannot be address(0)'
)
})
})
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment