├── .gitignore ├── LICENSE ├── README.md ├── go.mod ├── go.sum ├── main.go ├── signer ├── ecdsa_signer.go ├── signer.go └── wallet_signer.go └── withdraw ├── fpwithdraw.go ├── utils.go └── withdraw.go /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Base 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # withdrawer 2 | 3 | Golang utility for proving and finalizing ETH withdrawals from op-stack chains. 4 | 5 | 6 | 7 | [![GitHub contributors](https://img.shields.io/github/contributors/base/withdrawer)](https://github.com/base/withdrawer/graphs/contributors) 8 | [![GitHub commit activity](https://img.shields.io/github/commit-activity/w/base/withdrawer)](https://github.com/base/withdrawer/graphs/commit-activity) 9 | [![GitHub Stars](https://img.shields.io/github/stars/base/withdrawer.svg)](https://github.com/base/withdrawer/stargazers) 10 | ![GitHub repo size](https://img.shields.io/github/repo-size/base/withdrawer) 11 | [![GitHub](https://img.shields.io/github/license/base/withdrawer?color=blue)](https://github.com/base/withdrawer/blob/main/LICENSE) 12 | 13 | 14 | 15 | [![Website base.org](https://img.shields.io/website-up-down-green-red/https/base.org.svg)](https://base.org) 16 | [![Blog](https://img.shields.io/badge/blog-up-green)](https://base.mirror.xyz/) 17 | [![Docs](https://img.shields.io/badge/docs-up-green)](https://docs.base.org/) 18 | [![Discord](https://img.shields.io/discord/1067165013397213286?label=discord)](https://base.org/discord) 19 | [![Twitter BuildOnBase](https://img.shields.io/twitter/follow/BuildOnBase?style=social)](https://x.com/BuildOnBase) 20 | 21 | 22 | 23 | [![GitHub pull requests by-label](https://img.shields.io/github/issues-pr-raw/base/withdrawer)](https://github.com/base/withdrawer/pulls) 24 | [![GitHub Issues](https://img.shields.io/github/issues-raw/base/withdrawer.svg)](https://github.com/base/withdrawer/issues) 25 | 26 | ## Installation 27 | 28 | ``` 29 | git clone https://github.com/base/withdrawer.git 30 | cd withdrawer 31 | go install . 32 | ``` 33 | 34 | ## Usage 35 | 36 | > [!CAUTION] 37 | > Do not send ERC-20 or other tokens to the L2StandardBridge, only native ETH is supported. 38 | 39 | ### Without Fault Proofs 40 | 41 | #### Step 1 42 | 43 | Initiate a withdrawal on L2 by sending ETH to the `L2StandardBridge` contract at `0x4200000000000000000000000000000000000010`, and note the tx hash. 44 | Example on Base Sepolia: [0x5e47346867cf87d8e8c82cae1d30a94b8d5587dc9d354aef5c5a7b4c84ad9463](https://sepolia.basescan.org/tx/0x5e47346867cf87d8e8c82cae1d30a94b8d5587dc9d354aef5c5a7b4c84ad9463). 45 | 46 | > [!NOTE] 47 | > Users are required to wait for a period of seven days when moving assets out of Base mainnet into the Ethereum mainnet. This period of time is called the Challenge Period and serves to help secure the assets stored on Base mainnet. 48 | 49 | #### Step 2 50 | 51 | Prove your withdrawal: 52 | 53 | ``` 54 | withdrawer --network base-mainnet --withdrawal --rpc --private-key 55 | ``` 56 | 57 | or use a ledger: 58 | 59 | ``` 60 | withdrawer --network base-mainnet --withdrawal --rpc --ledger 61 | ``` 62 | 63 | Example output: 64 | 65 | ``` 66 | Proved withdrawal for 0xc4055dcb2e4647c37166caba8c7392625c2b62f9117a8bc4d96270da24b38f13: 0x6b6d1cc45b6601a30646847f638847feb629221ee71bbe6a3de7e6d0fbfe8fad 67 | waiting for tx confirmation 68 | 0x6b6d1cc45b6601a30646847f638847feb629221ee71bbe6a3de7e6d0fbfe8fad confirmed 69 | ``` 70 | 71 | _Note: this can be called from any L1 address, it does not have to be the same address that initiated the withdrawal on the L2._ 72 | 73 | #### Step 3 74 | 75 | After the finalization period, finalize your withdrawal (same command as above): 76 | 77 | ``` 78 | withdrawer --network base-mainnet --withdrawal --rpc --private-key 79 | ``` 80 | 81 | Example output: 82 | 83 | ``` 84 | Completed withdrawal for 0xc4055dcb2e4647c37166caba8c7392625c2b62f9117a8bc4d96270da24b38f13: 0x1c457f1992f48f1f959ceaee5b3c7e699a26f6f05d93997d49dafe703fd66dea 85 | waiting for tx confirmation 86 | 0x1c457f1992f48f1f959ceaee5b3c7e699a26f6f05d93997d49dafe703fd66dea confirmed 87 | ``` 88 | 89 | _Note: this can be called from any L1 address, it does not have to be the same address that initiated the withdrawal on the L2._ 90 | 91 | ### With Fault Proofs 92 | 93 | > [!NOTE] 94 | > With the recent fault proofs upgrade for Base on Sepolia testnet, withdrawals are required to wait for a period of seven days. This mirrors the Challenge Period that exists for Base mainnet. Additionally, withdrawals are required to be finalized against dispute games that resolve in favor of the output root claim. If the dispute game is blacklisted, resolves against the output root claim (challenger wins), or the respected game type is changed, then the withdrawal will need to be re-proven. 95 | 96 | #### Step 1 97 | 98 | Initiate a withdrawal on L2 by sending ETH to the `L2StandardBridge` contract at `0x4200000000000000000000000000000000000010`, and note the tx hash. 99 | Example on Base Sepolia: [0x5e47346867cf87d8e8c82cae1d30a94b8d5587dc9d354aef5c5a7b4c84ad9463](https://sepolia.basescan.org/tx/0x5e47346867cf87d8e8c82cae1d30a94b8d5587dc9d354aef5c5a7b4c84ad9463). 100 | 101 | #### Step 2 102 | 103 | Prove your withdrawal: 104 | 105 | ``` 106 | withdrawer --network base-mainnet --withdrawal --rpc --private-key --fault-proofs 107 | ``` 108 | 109 | or use a ledger: 110 | 111 | ``` 112 | withdrawer --network base-mainnet --withdrawal --rpc --ledger --fault-proofs 113 | ``` 114 | 115 | Example output: 116 | 117 | ``` 118 | Proved withdrawal for 0xc4055dcb2e4647c37166caba8c7392625c2b62f9117a8bc4d96270da24b38f13: 0x6b6d1cc45b6601a30646847f638847feb629221ee71bbe6a3de7e6d0fbfe8fad 119 | waiting for tx confirmation 120 | 0x6b6d1cc45b6601a30646847f638847feb629221ee71bbe6a3de7e6d0fbfe8fad confirmed 121 | ``` 122 | 123 | _Note: this can be called from any L1 address, it does not have to be the same address that initiated the withdrawal on the L2._ 124 | 125 | #### Step 3 126 | 127 | > [!IMPORTANT] 128 | > Unlike the non fault proof withdrawal flow, you MUST use the same address that proved the withdrawal to finalize the withdrawal. 129 | 130 | After the dispute game has resolved in favor of the root claim AND the finalization period has elapsed, finalize your withdrawal (same command as above): 131 | 132 | ``` 133 | withdrawer --network base-mainnet --withdrawal --rpc --private-key --fault-proofs 134 | ``` 135 | 136 | Example output: 137 | 138 | ``` 139 | Completed withdrawal for 0xc4055dcb2e4647c37166caba8c7392625c2b62f9117a8bc4d96270da24b38f13: 0x1c457f1992f48f1f959ceaee5b3c7e699a26f6f05d93997d49dafe703fd66dea 140 | waiting for tx confirmation 141 | 0x1c457f1992f48f1f959ceaee5b3c7e699a26f6f05d93997d49dafe703fd66dea confirmed 142 | ``` 143 | 144 | ## Flags 145 | 146 | ``` 147 | Usage of withdrawer: 148 | -rpc string 149 | Ethereum L1 RPC url 150 | -network string 151 | op-stack network to withdraw.go from (one of: base-mainnet, base-sepolia, op-mainnet, op-sepolia) (default "base-mainnet") 152 | -withdrawal string 153 | TX hash of the L2 withdrawal transaction 154 | -fault-proofs 155 | Use fault proofs withdrawal flow (only for networks that support fault proofs) 156 | -private-key string 157 | Private key to use for signing transactions 158 | -mnemonic string 159 | Mnemonic to use for signing transactions 160 | -ledger 161 | Use ledger device for signing transactions 162 | -hd-path string 163 | Hierarchical deterministic derivation path for mnemonic or ledger (default "m/44'/60'/0'/0/0") 164 | -l2-rpc string 165 | Custom network L2 RPC url 166 | -l2oo-address string 167 | Custom network L2OutputOracle address 168 | -portal-address string 169 | Custom network OptimismPortal address 170 | -dfg-address string 171 | Custom network DisputeGameFactory address (only for networks that support fault proofs) 172 | ``` 173 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/base/withdrawer 2 | 3 | go 1.21.1 4 | 5 | require ( 6 | github.com/decred/dcrd/hdkeychain/v3 v3.1.2 7 | github.com/ethereum-optimism/optimism v1.8.0 8 | github.com/ethereum/go-ethereum v1.13.15 9 | github.com/tyler-smith/go-bip39 v1.1.0 10 | ) 11 | 12 | require ( 13 | github.com/BurntSushi/toml v1.4.0 // indirect 14 | github.com/DataDog/zstd v1.5.5 // indirect 15 | github.com/Microsoft/go-winio v0.6.1 // indirect 16 | github.com/beorn7/perks v1.0.1 // indirect 17 | github.com/bits-and-blooms/bitset v1.10.0 // indirect 18 | github.com/btcsuite/btcd v0.24.2 // indirect 19 | github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect 20 | github.com/btcsuite/btcd/btcutil v1.1.5 // indirect 21 | github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 // indirect 22 | github.com/cespare/xxhash/v2 v2.2.0 // indirect 23 | github.com/cockroachdb/errors v1.11.1 // indirect 24 | github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect 25 | github.com/cockroachdb/pebble v0.0.0-20231018212520-f6cde3fc2fa4 // indirect 26 | github.com/cockroachdb/redact v1.1.5 // indirect 27 | github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 // indirect 28 | github.com/consensys/bavard v0.1.13 // indirect 29 | github.com/consensys/gnark-crypto v0.12.1 // indirect 30 | github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect 31 | github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 // indirect 32 | github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect 33 | github.com/deckarep/golang-set/v2 v2.1.0 // indirect 34 | github.com/decred/base58 v1.0.5 // indirect 35 | github.com/decred/dcrd/crypto/blake256 v1.0.1 // indirect 36 | github.com/decred/dcrd/crypto/ripemd160 v1.0.2 // indirect 37 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect 38 | github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 // indirect 39 | github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240803025447-c92ef420eec2 // indirect 40 | github.com/ethereum/c-kzg-4844 v0.4.0 // indirect 41 | github.com/fsnotify/fsnotify v1.7.0 // indirect 42 | github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 // indirect 43 | github.com/getsentry/sentry-go v0.18.0 // indirect 44 | github.com/go-ole/go-ole v1.3.0 // indirect 45 | github.com/gofrs/flock v0.8.1 // indirect 46 | github.com/gogo/protobuf v1.3.2 // indirect 47 | github.com/golang/protobuf v1.5.4 // indirect 48 | github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect 49 | github.com/google/uuid v1.6.0 // indirect 50 | github.com/gorilla/websocket v1.5.1 // indirect 51 | github.com/holiman/uint256 v1.3.0 // indirect 52 | github.com/huin/goupnp v1.3.0 // indirect 53 | github.com/jackpal/go-nat-pmp v1.0.2 // indirect 54 | github.com/karalabe/usb v0.0.3-0.20230711191512-61db3e06439c // indirect 55 | github.com/klauspost/compress v1.17.9 // indirect 56 | github.com/kr/pretty v0.3.1 // indirect 57 | github.com/kr/text v0.2.0 // indirect 58 | github.com/mattn/go-runewidth v0.0.14 // indirect 59 | github.com/mmcloughlin/addchain v0.4.0 // indirect 60 | github.com/nxadm/tail v1.4.11 // indirect 61 | github.com/olekukonko/tablewriter v0.0.5 // indirect 62 | github.com/pkg/errors v0.9.1 // indirect 63 | github.com/prometheus/client_golang v1.19.1 // indirect 64 | github.com/prometheus/client_model v0.6.1 // indirect 65 | github.com/prometheus/common v0.48.0 // indirect 66 | github.com/prometheus/procfs v0.12.0 // indirect 67 | github.com/rivo/uniseg v0.4.3 // indirect 68 | github.com/rogpeppe/go-internal v1.10.0 // indirect 69 | github.com/russross/blackfriday/v2 v2.1.0 // indirect 70 | github.com/shirou/gopsutil v3.21.11+incompatible // indirect 71 | github.com/supranational/blst v0.3.11 // indirect 72 | github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a // indirect 73 | github.com/tklauser/go-sysconf v0.3.12 // indirect 74 | github.com/tklauser/numcpus v0.6.1 // indirect 75 | github.com/urfave/cli/v2 v2.27.1 // indirect 76 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect 77 | github.com/yusufpapurcu/wmi v1.2.3 // indirect 78 | golang.org/x/crypto v0.25.0 // indirect 79 | golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect 80 | golang.org/x/mod v0.19.0 // indirect 81 | golang.org/x/net v0.26.0 // indirect 82 | golang.org/x/sync v0.7.0 // indirect 83 | golang.org/x/sys v0.22.0 // indirect 84 | golang.org/x/term v0.22.0 // indirect 85 | golang.org/x/text v0.16.0 // indirect 86 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect 87 | google.golang.org/protobuf v1.34.1 // indirect 88 | rsc.io/tmplfunc v0.0.3 // indirect 89 | ) 90 | 91 | replace github.com/ethereum/go-ethereum => github.com/ethereum-optimism/op-geth v1.101315.3-rc.2 92 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= 2 | github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= 3 | github.com/DataDog/zstd v1.5.5 h1:oWf5W7GtOLgp6bciQYDmhHHjdhYkALu6S/5Ni9ZgSvQ= 4 | github.com/DataDog/zstd v1.5.5/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw= 5 | github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= 6 | github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= 7 | github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= 8 | github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= 9 | github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= 10 | github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412 h1:w1UutsfOrms1J05zt7ISrnJIXKzwaspym5BTKGx93EI= 11 | github.com/agl/ed25519 v0.0.0-20170116200512-5312a6153412/go.mod h1:WPjqKcmVOxf0XSf3YxCJs6N6AOSrOx3obionmG7T0y0= 12 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 13 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 14 | github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= 15 | github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= 16 | github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= 17 | github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= 18 | github.com/btcsuite/btcd v0.23.5-0.20231215221805-96c9fd8078fd/go.mod h1:nm3Bko6zh6bWP60UxwoT5LzdGJsQJaPo6HjduXq9p6A= 19 | github.com/btcsuite/btcd v0.24.2 h1:aLmxPguqxza+4ag8R1I2nnJjSu2iFn/kqtHTIImswcY= 20 | github.com/btcsuite/btcd v0.24.2/go.mod h1:5C8ChTkl5ejr3WHj8tkQSCmydiMEPB0ZhQhehpq7Dgg= 21 | github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= 22 | github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= 23 | github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= 24 | github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= 25 | github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= 26 | github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= 27 | github.com/btcsuite/btcd/btcutil v1.1.5 h1:+wER79R5670vs/ZusMTF1yTcRYE5GUsFbdjdisflzM8= 28 | github.com/btcsuite/btcd/btcutil v1.1.5/go.mod h1:PSZZ4UitpLBWzxGd5VGOrLnmOjtPP/a6HaFo12zMs00= 29 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= 30 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= 31 | github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0 h1:59Kx4K6lzOW5w6nFlA0v5+lk/6sjybR934QNHSJZPTQ= 32 | github.com/btcsuite/btcd/chaincfg/chainhash v1.1.0/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= 33 | github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= 34 | github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= 35 | github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= 36 | github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= 37 | github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= 38 | github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= 39 | github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= 40 | github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= 41 | github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= 42 | github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= 43 | github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= 44 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 45 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 46 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 47 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 48 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 49 | github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f h1:otljaYPt5hWxV3MUfO5dFPFiOXg9CyG5/kCfayTqsJ4= 50 | github.com/cockroachdb/datadriven v1.0.3-0.20230413201302-be42291fc80f/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU= 51 | github.com/cockroachdb/errors v1.11.1 h1:xSEW75zKaKCWzR3OfxXUxgrk/NtT4G1MiOv5lWZazG8= 52 | github.com/cockroachdb/errors v1.11.1/go.mod h1:8MUxA3Gi6b25tYlFEBGLf+D8aISL+M4MIpiWMSNRfxw= 53 | github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= 54 | github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b/go.mod h1:Vz9DsVWQQhf3vs21MhPMZpMGSht7O/2vFW2xusFUVOs= 55 | github.com/cockroachdb/pebble v0.0.0-20231018212520-f6cde3fc2fa4 h1:PuHFhOUMnD62r80dN+Ik5qco2drekgsUSVdcHsvllec= 56 | github.com/cockroachdb/pebble v0.0.0-20231018212520-f6cde3fc2fa4/go.mod h1:sEHm5NOXxyiAoKWhoFxT8xMgd/f3RA6qUqQ1BXKrh2E= 57 | github.com/cockroachdb/redact v1.1.5 h1:u1PMllDkdFfPWaNGMyLD1+so+aq3uUItthCFqzwPJ30= 58 | github.com/cockroachdb/redact v1.1.5/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= 59 | github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= 60 | github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= 61 | github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= 62 | github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= 63 | github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= 64 | github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= 65 | github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= 66 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 67 | github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= 68 | github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= 69 | github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= 70 | github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= 71 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 72 | github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 73 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 74 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 75 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 76 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 77 | github.com/dchest/siphash v1.2.3 h1:QXwFc8cFOR2dSa/gE6o/HokBMWtLUaNDVd+22aKHeEA= 78 | github.com/dchest/siphash v1.2.3/go.mod h1:0NvQU092bT0ipiFN++/rXm69QG9tVxLAlQHIXMPAkHc= 79 | github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= 80 | github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= 81 | github.com/decred/base58 v1.0.5 h1:hwcieUM3pfPnE/6p3J100zoRfGkQxBulZHo7GZfOqic= 82 | github.com/decred/base58 v1.0.5/go.mod h1:s/8lukEHFA6bUQQb/v3rjUySJ2hu+RioCzLukAVkrfw= 83 | github.com/decred/dcrd/chaincfg/chainhash v1.0.4 h1:zRCv6tdncLfLTKYqu7hrXvs7hW+8FO/NvwoFvGsrluU= 84 | github.com/decred/dcrd/chaincfg/chainhash v1.0.4/go.mod h1:hA86XxlBWwHivMvxzXTSD0ZCG/LoYsFdWnCekkTMCqY= 85 | github.com/decred/dcrd/chaincfg/v3 v3.2.1 h1:x9zKJaU24WAKbxAR1UyFKHlM3oJgP0H9LodokM4X5lM= 86 | github.com/decred/dcrd/chaincfg/v3 v3.2.1/go.mod h1:SDCWDtY7BLj0leXc9FuoA1YjSVKyCIBVAyxwZn6+sXc= 87 | github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= 88 | github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= 89 | github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= 90 | github.com/decred/dcrd/crypto/ripemd160 v1.0.2 h1:TvGTmUBHDU75OHro9ojPLK+Yv7gDl2hnUvRocRCjsys= 91 | github.com/decred/dcrd/crypto/ripemd160 v1.0.2/go.mod h1:uGfjDyePSpa75cSQLzNdVmWlbQMBuiJkvXw/MNKRY4M= 92 | github.com/decred/dcrd/dcrec v1.0.1 h1:gDzlndw0zYxM5BlaV17d7ZJV6vhRe9njPBFeg4Db2UY= 93 | github.com/decred/dcrd/dcrec v1.0.1/go.mod h1:CO+EJd8eHFb8WHa84C7ZBkXsNUIywaTHb+UAuI5uo6o= 94 | github.com/decred/dcrd/dcrec/edwards/v2 v2.0.3 h1:l/lhv2aJCUignzls81+wvga0TFlyoZx8QxRMQgXpZik= 95 | github.com/decred/dcrd/dcrec/edwards/v2 v2.0.3/go.mod h1:AKpV6+wZ2MfPRJnTbQ6NPgWrKzbe9RCIlCF/FKzMtM8= 96 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= 97 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= 98 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= 99 | github.com/decred/dcrd/hdkeychain/v3 v3.1.2 h1:x25WuuE7zM/20EynuVMyOhL0K8BwGBBsexGq8xTiHFA= 100 | github.com/decred/dcrd/hdkeychain/v3 v3.1.2/go.mod h1:FnNJmZ7jqUDeAo6/c/xkQi5cuxh3EWtJeMmW6/Z8lcc= 101 | github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= 102 | github.com/decred/dcrd/txscript/v4 v4.1.1 h1:R4M2+jMujgQA91899SkL0cW66d6DC76Gx+1W1oEHjc0= 103 | github.com/decred/dcrd/txscript/v4 v4.1.1/go.mod h1:7ybmJoI+b6dxvQ+0aXdZpkyrj0PbnylJCzFxD1g8+/A= 104 | github.com/decred/dcrd/wire v1.7.0 h1:5JHiDjEQeS4XUl4PfnTZYLwAD/E/+LwBmPRec/fP76o= 105 | github.com/decred/dcrd/wire v1.7.0/go.mod h1:lAqrzV0SU4kyV6INLEJgDtUjJaTaVKrbF4LHtaYl+zU= 106 | github.com/decred/slog v1.2.0 h1:soHAxV52B54Di3WtKLfPum9OFfWqwtf/ygf9njdfnPM= 107 | github.com/decred/slog v1.2.0/go.mod h1:kVXlGnt6DHy2fV5OjSeuvCJ0OmlmTF6LFpEPMu/fOY0= 108 | github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3 h1:RWHKLhCrQThMfch+QJ1Z8veEq5ZO3DfIhZ7xgRP9WTc= 109 | github.com/ethereum-optimism/go-ethereum-hdwallet v0.1.3/go.mod h1:QziizLAiF0KqyLdNJYD7O5cpDlaFMNZzlxYNcWsJUxs= 110 | github.com/ethereum-optimism/op-geth v1.101315.3-rc.2 h1:4Ne3RUZ09uqY5QnbVuDVD2Xt8JbxegCv3mkICt3aT6c= 111 | github.com/ethereum-optimism/op-geth v1.101315.3-rc.2/go.mod h1:nZ3TvP4mhOsfKkrgaT3GrDO4oCn5awPXFHKpVHuO63s= 112 | github.com/ethereum-optimism/optimism v1.8.0 h1:ePZAEFBnVJVEFZ9w5eO5/x71WbXOCfg1sEGKNjxl8mw= 113 | github.com/ethereum-optimism/optimism v1.8.0/go.mod h1:DFML0CU9dS/9VL6RXG9wHdoLbMvdwGG2njmgC+ipyoM= 114 | github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240803025447-c92ef420eec2 h1:ySJykDUyb8RbcfLL3pz0Cs5Ji6NMVT7kmAY634DOCoE= 115 | github.com/ethereum-optimism/superchain-registry/superchain v0.0.0-20240803025447-c92ef420eec2/go.mod h1:zy9f3TNPS7pwW4msMitF83fp0Wf452tZ6+Fg6d4JyXM= 116 | github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= 117 | github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= 118 | github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= 119 | github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= 120 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= 121 | github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 122 | github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= 123 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 124 | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= 125 | github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 126 | github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08 h1:f6D9Hr8xV8uYKlyuj8XIruxlh9WjVjdh1gIicAS7ays= 127 | github.com/gballet/go-libpcsclite v0.0.0-20191108122812-4678299bea08/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= 128 | github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= 129 | github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= 130 | github.com/getsentry/sentry-go v0.18.0 h1:MtBW5H9QgdcJabtZcuJG80BMOwaBpkRDZkxRkNC1sN0= 131 | github.com/getsentry/sentry-go v0.18.0/go.mod h1:Kgon4Mby+FJ7ZWHFUAZgVaIa8sxHtnRJRLTXZr51aKQ= 132 | github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= 133 | github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= 134 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 135 | github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= 136 | github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= 137 | github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= 138 | github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= 139 | github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= 140 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 141 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 142 | github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= 143 | github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 144 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 145 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 146 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 147 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 148 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 149 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 150 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 151 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 152 | github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 153 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 154 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 155 | github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 156 | github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= 157 | github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 158 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 159 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 160 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 161 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 162 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 163 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 164 | github.com/google/gofuzz v1.2.1-0.20220503160820-4a35382e8fc8 h1:Ep/joEub9YwcjRY6ND3+Y/w0ncE540RtGatVhtZL0/Q= 165 | github.com/google/gofuzz v1.2.1-0.20220503160820-4a35382e8fc8/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 166 | github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 167 | github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= 168 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 169 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 170 | github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 171 | github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= 172 | github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= 173 | github.com/hashicorp/go-bexpr v0.1.11 h1:6DqdA/KBjurGby9yTY0bmkathya0lfwF2SeuubCI7dY= 174 | github.com/hashicorp/go-bexpr v0.1.11/go.mod h1:f03lAo0duBlDIUMGCuad8oLcgejw4m7U+N8T+6Kz1AE= 175 | github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= 176 | github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= 177 | github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= 178 | github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= 179 | github.com/holiman/uint256 v1.3.0 h1:4wdcm/tnd0xXdu7iS3ruNvxkWwrb4aeBQv19ayYn8F4= 180 | github.com/holiman/uint256 v1.3.0/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= 181 | github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= 182 | github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= 183 | github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= 184 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 185 | github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= 186 | github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= 187 | github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 188 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 189 | github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= 190 | github.com/karalabe/usb v0.0.3-0.20230711191512-61db3e06439c h1:AqsttAyEyIEsNz5WLRwuRwjiT5CMDUfLk6cFJDVPebs= 191 | github.com/karalabe/usb v0.0.3-0.20230711191512-61db3e06439c/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU= 192 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 193 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 194 | github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= 195 | github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= 196 | github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 197 | github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= 198 | github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 199 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 200 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 201 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 202 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 203 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 204 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 205 | github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= 206 | github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= 207 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 208 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 209 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 210 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 211 | github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= 212 | github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU= 213 | github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 214 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 215 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 216 | github.com/mitchellh/pointerstructure v1.2.1 h1:ZhBBeX8tSlRpu/FFhXH4RC4OJzFlqsQhoHZAz4x7TIw= 217 | github.com/mitchellh/pointerstructure v1.2.1/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= 218 | github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= 219 | github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= 220 | github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= 221 | github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= 222 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 223 | github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY= 224 | github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc= 225 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 226 | github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 227 | github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 228 | github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= 229 | github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= 230 | github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= 231 | github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= 232 | github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= 233 | github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= 234 | github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= 235 | github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= 236 | github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= 237 | github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= 238 | github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 239 | github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= 240 | github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= 241 | github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo= 242 | github.com/onsi/gomega v1.31.1/go.mod h1:y40C95dwAD1Nz36SsEnxvfFe8FFfNxzI5eJ0EYGyAy0= 243 | github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= 244 | github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= 245 | github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= 246 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 247 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 248 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 249 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 250 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 251 | github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= 252 | github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= 253 | github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= 254 | github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= 255 | github.com/prometheus/common v0.48.0 h1:QO8U2CdOzSn1BBsmXJXduaaW+dY/5QLjfB8svtSzKKE= 256 | github.com/prometheus/common v0.48.0/go.mod h1:0/KsvlIEfPQCQ5I2iNSAWKPZziNCvRs5EC6ILDTlAPc= 257 | github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= 258 | github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= 259 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 260 | github.com/rivo/uniseg v0.4.3 h1:utMvzDsuh3suAEnhH0RdHmoPbU648o6CvXxTx4SBMOw= 261 | github.com/rivo/uniseg v0.4.3/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= 262 | github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= 263 | github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= 264 | github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= 265 | github.com/rs/cors v1.11.0 h1:0B9GE/r9Bc2UxRMMtymBkHTenPkHDv0CW4Y98GBY+po= 266 | github.com/rs/cors v1.11.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= 267 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 268 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 269 | github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= 270 | github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 271 | github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= 272 | github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= 273 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 274 | github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= 275 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 276 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 277 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 278 | github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= 279 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 280 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 281 | github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= 282 | github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= 283 | github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= 284 | github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a h1:1ur3QoCqvE5fl+nylMaIr9PVV1w343YRDtsy+Rwu7XI= 285 | github.com/syndtr/goleveldb v1.0.1-0.20220614013038-64ee5596c38a/go.mod h1:RRCYJbIwD5jmqPI9XoAFR0OcDxqUctll6zUj/+B4S48= 286 | github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= 287 | github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= 288 | github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= 289 | github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= 290 | github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= 291 | github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= 292 | github.com/urfave/cli/v2 v2.27.1 h1:8xSQ6szndafKVRmfyeUMxkNUJQMjL1F2zmsZ+qHpfho= 293 | github.com/urfave/cli/v2 v2.27.1/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= 294 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= 295 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= 296 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 297 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 298 | github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= 299 | github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 300 | golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= 301 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 302 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 303 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 304 | golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= 305 | golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= 306 | golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM= 307 | golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc= 308 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 309 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 310 | golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= 311 | golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 312 | golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 313 | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 314 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 315 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 316 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 317 | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 318 | golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 319 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 320 | golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= 321 | golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= 322 | golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= 323 | golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= 324 | golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= 325 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 326 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 327 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 328 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 329 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 330 | golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= 331 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 332 | golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 333 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 334 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 335 | golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 336 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 337 | golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 338 | golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 339 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 340 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 341 | golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 342 | golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 343 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 344 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 345 | golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 346 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 347 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 348 | golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 349 | golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 350 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 351 | golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 352 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 353 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 354 | golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 355 | golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= 356 | golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 357 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 358 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= 359 | golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= 360 | golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= 361 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 362 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 363 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 364 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 365 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 366 | golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= 367 | golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= 368 | golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= 369 | golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 370 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 371 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 372 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 373 | golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 374 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 375 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= 376 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= 377 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 378 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 379 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 380 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 381 | golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= 382 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 383 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 384 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 385 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 386 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 387 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 388 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 389 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 390 | google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= 391 | google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 392 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 393 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 394 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 395 | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 396 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= 397 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 398 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 399 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 400 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 401 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 402 | gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 403 | gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 404 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 405 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 406 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 407 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 408 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 409 | lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= 410 | lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= 411 | rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= 412 | rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= 413 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "flag" 6 | "fmt" 7 | "math/big" 8 | "os" 9 | "strings" 10 | 11 | "github.com/ethereum/go-ethereum/log" 12 | 13 | "github.com/ethereum-optimism/optimism/op-node/bindings" 14 | bindingspreview "github.com/ethereum-optimism/optimism/op-node/bindings/preview" 15 | oplog "github.com/ethereum-optimism/optimism/op-service/log" 16 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 17 | "github.com/ethereum/go-ethereum/common" 18 | "github.com/ethereum/go-ethereum/ethclient" 19 | "github.com/ethereum/go-ethereum/rpc" 20 | 21 | "github.com/base/withdrawer/signer" 22 | "github.com/base/withdrawer/withdraw" 23 | ) 24 | 25 | type network struct { 26 | l2RPC string 27 | portalAddress string 28 | l2OOAddress string 29 | disputeGameFactory string 30 | faultProofs bool 31 | } 32 | 33 | var networks = map[string]network{ 34 | "base-mainnet": { 35 | l2RPC: "https://mainnet.base.org", 36 | portalAddress: "0x49048044D57e1C92A77f79988d21Fa8fAF74E97e", 37 | l2OOAddress: "0x0000000000000000000000000000000000000000", 38 | disputeGameFactory: "0x43edB88C4B80fDD2AdFF2412A7BebF9dF42cB40e", 39 | faultProofs: true, 40 | }, 41 | "base-sepolia": { 42 | l2RPC: "https://sepolia.base.org", 43 | portalAddress: "0x49f53e41452C74589E85cA1677426Ba426459e85", 44 | l2OOAddress: "0x0000000000000000000000000000000000000000", 45 | disputeGameFactory: "0xd6E6dBf4F7EA0ac412fD8b65ED297e64BB7a06E1", 46 | faultProofs: true, 47 | }, 48 | "op-mainnet": { 49 | l2RPC: "https://mainnet.optimism.io", 50 | portalAddress: "0xbEb5Fc579115071764c7423A4f12eDde41f106Ed", 51 | l2OOAddress: "0x0000000000000000000000000000000000000000", 52 | disputeGameFactory: "0xe5965Ab5962eDc7477C8520243A95517CD252fA9", 53 | faultProofs: true, 54 | }, 55 | "op-sepolia": { 56 | l2RPC: "https://sepolia.optimism.io", 57 | portalAddress: "0x16Fc5058F25648194471939df75CF27A2fdC48BC", 58 | l2OOAddress: "0x0000000000000000000000000000000000000000", 59 | disputeGameFactory: "0x05F9613aDB30026FFd634f38e5C4dFd30a197Fa1", 60 | faultProofs: true, 61 | }, 62 | } 63 | 64 | func main() { 65 | var networkKeys []string 66 | for n := range networks { 67 | networkKeys = append(networkKeys, n) 68 | } 69 | 70 | var rpcFlag string 71 | var networkFlag string 72 | var l2RpcFlag string 73 | var faultProofs bool 74 | var portalAddress string 75 | var l2OOAddress string 76 | var dgfAddress string 77 | var withdrawalFlag string 78 | var privateKey string 79 | var ledger bool 80 | var mnemonic string 81 | var hdPath string 82 | 83 | flag.StringVar(&rpcFlag, "rpc", "", "Ethereum L1 RPC url") 84 | flag.StringVar(&networkFlag, "network", "base-mainnet", fmt.Sprintf("op-stack network to withdraw.go from (one of: %s)", strings.Join(networkKeys, ", "))) 85 | flag.StringVar(&l2RpcFlag, "l2-rpc", "", "Custom network L2 RPC url") 86 | flag.BoolVar(&faultProofs, "fault-proofs", false, "Use fault proofs") 87 | flag.StringVar(&portalAddress, "portal-address", "", "Custom network OptimismPortal address") 88 | flag.StringVar(&l2OOAddress, "l2oo-address", "", "Custom network L2OutputOracle address") 89 | flag.StringVar(&dgfAddress, "dgf-address", "", "Custom network DisputeGameFactory address") 90 | flag.StringVar(&withdrawalFlag, "withdrawal", "", "TX hash of the L2 withdrawal transaction") 91 | flag.StringVar(&privateKey, "private-key", "", "Private key to use for signing transactions") 92 | flag.BoolVar(&ledger, "ledger", false, "Use ledger device for signing transactions") 93 | flag.StringVar(&mnemonic, "mnemonic", "", "Mnemonic to use for signing transactions") 94 | flag.StringVar(&hdPath, "hd-path", "m/44'/60'/0'/0/0", "Hierarchical deterministic derivation path for mnemonic or ledger") 95 | flag.Parse() 96 | 97 | log.SetDefault(oplog.NewLogger(os.Stderr, oplog.DefaultCLIConfig())) 98 | 99 | n, ok := networks[networkFlag] 100 | if !ok { 101 | log.Crit("Unknown network", "network", networkFlag) 102 | } 103 | 104 | // check for non-compatible networks with given flags 105 | if faultProofs { 106 | if n.faultProofs == false { 107 | log.Crit("Fault proofs are not supported on this network") 108 | } 109 | } else { 110 | if n.faultProofs == true { 111 | log.Crit("Fault proofs are required on this network, please provide the --fault-proofs flag") 112 | } 113 | } 114 | 115 | // check for non-empty flags for non-fault proof networks 116 | if !faultProofs && (l2RpcFlag != "" || portalAddress != "" || l2OOAddress != "") { 117 | if l2RpcFlag == "" { 118 | log.Crit("Missing --l2-rpc flag") 119 | } 120 | if portalAddress == "" { 121 | log.Crit("Missing --portal-address flag") 122 | } 123 | if l2OOAddress == "" { 124 | log.Crit("Missing --l2oo-address flag") 125 | } 126 | n = network{ 127 | l2RPC: l2RpcFlag, 128 | portalAddress: portalAddress, 129 | l2OOAddress: l2OOAddress, 130 | faultProofs: faultProofs, 131 | } 132 | } 133 | 134 | // check for non-empty flags for fault proof networks 135 | if faultProofs && (l2RpcFlag != "" || dgfAddress != "" || portalAddress != "") { 136 | if l2RpcFlag == "" { 137 | log.Crit("Missing --l2-rpc flag") 138 | } 139 | if dgfAddress == "" { 140 | log.Crit("Missing --dfg-address flag") 141 | } 142 | if portalAddress == "" { 143 | log.Crit("Missing --portal-address flag") 144 | } 145 | n = network{ 146 | l2RPC: l2RpcFlag, 147 | portalAddress: portalAddress, 148 | disputeGameFactory: dgfAddress, 149 | faultProofs: faultProofs, 150 | } 151 | } 152 | 153 | if rpcFlag == "" { 154 | log.Crit("Missing --rpc flag") 155 | } 156 | 157 | if withdrawalFlag == "" { 158 | log.Crit("Missing --withdrawal flag") 159 | } 160 | withdrawal := common.HexToHash(withdrawalFlag) 161 | 162 | options := 0 163 | if privateKey != "" { 164 | options++ 165 | } 166 | if ledger { 167 | options++ 168 | } 169 | if mnemonic != "" { 170 | options++ 171 | } 172 | if options != 1 { 173 | log.Crit("One (and only one) of --private-key, --ledger, --mnemonic must be set") 174 | } 175 | 176 | // instantiate shared variables 177 | s, err := signer.CreateSigner(privateKey, mnemonic, hdPath) 178 | if err != nil { 179 | log.Crit("Error creating signer", "error", err) 180 | } 181 | 182 | withdrawer, err := CreateWithdrawHelper(rpcFlag, withdrawal, n, s) 183 | if err != nil { 184 | log.Crit("Error creating withdrawer", "error", err) 185 | } 186 | 187 | // handle withdrawals with or without the fault proofs withdrawer 188 | isFinalized, err := withdrawer.IsProofFinalized() 189 | if err != nil { 190 | log.Crit("Error querying withdrawal finalization status", "error", err) 191 | } 192 | if isFinalized { 193 | fmt.Println("Withdrawal already finalized") 194 | return 195 | } 196 | 197 | // TODO: Add functionality to generate output root proposal and prove to that proposal for FPs 198 | err = withdrawer.CheckIfProvable() 199 | if err != nil { 200 | log.Crit("Withdrawal is not provable", "error", err) 201 | } 202 | 203 | proofTime, err := withdrawer.GetProvenWithdrawalTime() 204 | if err != nil { 205 | log.Crit("Error querying withdrawal proof", "error", err) 206 | } 207 | 208 | if proofTime == 0 { 209 | err = withdrawer.ProveWithdrawal() 210 | if err != nil { 211 | log.Crit("Error proving withdrawal", "error", err) 212 | } 213 | 214 | if faultProofs { 215 | fmt.Println("The withdrawal has been successfully proven, finalization of the withdrawal can be done once the dispute game has finished and the finalization period has elapsed") 216 | } else { 217 | fmt.Println("The withdrawal has been successfully proven, finalization of the withdrawal can be done once the finalization period has elapsed") 218 | } 219 | return 220 | } 221 | 222 | // TODO: Add edge-case handling for FPs if a withdrawal needs to be re-proven due to blacklisted / failed dispute game resolution 223 | err = withdrawer.FinalizeWithdrawal() 224 | if err != nil { 225 | log.Crit("Error completing withdrawal", "error", err) 226 | } 227 | } 228 | 229 | func CreateWithdrawHelper(l1Rpc string, withdrawal common.Hash, n network, s signer.Signer) (withdraw.WithdrawHelper, error) { 230 | ctx := context.Background() 231 | 232 | l1Client, err := ethclient.DialContext(ctx, l1Rpc) 233 | if err != nil { 234 | return nil, fmt.Errorf("Error dialing L1 client: %w", err) 235 | } 236 | 237 | l1ChainID, err := l1Client.ChainID(ctx) 238 | if err != nil { 239 | return nil, fmt.Errorf("Error querying chain ID: %w", err) 240 | } 241 | 242 | l1Nonce, err := l1Client.PendingNonceAt(ctx, s.Address()) 243 | if err != nil { 244 | return nil, fmt.Errorf("Error querying nonce: %w", err) 245 | } 246 | 247 | l1opts := &bind.TransactOpts{ 248 | From: s.Address(), 249 | Signer: s.SignerFn(l1ChainID), 250 | Context: ctx, 251 | Nonce: big.NewInt(int64(l1Nonce)), 252 | } 253 | 254 | l2Client, err := rpc.DialContext(ctx, n.l2RPC) 255 | if err != nil { 256 | return nil, fmt.Errorf("Error dialing L2 client: %w", err) 257 | } 258 | 259 | if n.faultProofs { 260 | portal, err := bindingspreview.NewOptimismPortal2(common.HexToAddress(n.portalAddress), l1Client) 261 | if err != nil { 262 | return nil, fmt.Errorf("Error binding OptimismPortal2 contract: %w", err) 263 | } 264 | 265 | dgf, err := bindings.NewDisputeGameFactory(common.HexToAddress(n.disputeGameFactory), l1Client) 266 | if err != nil { 267 | return nil, fmt.Errorf("Error binding DisputeGameFactory contract: %w", err) 268 | } 269 | 270 | return &withdraw.FPWithdrawer{ 271 | Ctx: ctx, 272 | L1Client: l1Client, 273 | L2Client: l2Client, 274 | L2TxHash: withdrawal, 275 | Portal: portal, 276 | Factory: dgf, 277 | Opts: l1opts, 278 | }, nil 279 | } else { 280 | portal, err := bindings.NewOptimismPortal(common.HexToAddress(n.portalAddress), l1Client) 281 | if err != nil { 282 | return nil, fmt.Errorf("Error binding OptimismPortal contract: %w", err) 283 | } 284 | 285 | l2oo, err := bindings.NewL2OutputOracle(common.HexToAddress(n.l2OOAddress), l1Client) 286 | if err != nil { 287 | return nil, fmt.Errorf("Error binding L2OutputOracle contract: %w", err) 288 | } 289 | 290 | return &withdraw.Withdrawer{ 291 | Ctx: ctx, 292 | L1Client: l1Client, 293 | L2Client: l2Client, 294 | L2TxHash: withdrawal, 295 | Portal: portal, 296 | Oracle: l2oo, 297 | Opts: l1opts, 298 | }, nil 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /signer/ecdsa_signer.go: -------------------------------------------------------------------------------- 1 | package signer 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "math/big" 6 | 7 | opcrypto "github.com/ethereum-optimism/optimism/op-service/crypto" 8 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 9 | "github.com/ethereum/go-ethereum/common" 10 | "github.com/ethereum/go-ethereum/crypto" 11 | ) 12 | 13 | // ecdsaSigner represents an ECDSA signer. 14 | type ecdsaSigner struct { 15 | *ecdsa.PrivateKey 16 | } 17 | 18 | // Address returns the address associated with the ECDSA signer. 19 | func (s *ecdsaSigner) Address() common.Address { 20 | return crypto.PubkeyToAddress(s.PublicKey) 21 | } 22 | 23 | // SignerFn returns a signer function using the ECDSA private key and chain ID. 24 | func (s *ecdsaSigner) SignerFn(chainID *big.Int) bind.SignerFn { 25 | return opcrypto.PrivateKeySignerFn(s.PrivateKey, chainID) 26 | } 27 | 28 | // SignData signs the given data using the ECDSA private key. 29 | func (s *ecdsaSigner) SignData(data []byte) ([]byte, error) { 30 | sig, err := crypto.Sign(crypto.Keccak256(data), s.PrivateKey) 31 | if err != nil { 32 | return nil, err 33 | } 34 | // Adjust the recovery ID for Ethereum compatibility 35 | sig[crypto.RecoveryIDOffset] += 27 36 | return sig, nil 37 | } 38 | 39 | -------------------------------------------------------------------------------- /signer/signer.go: -------------------------------------------------------------------------------- 1 | package signer 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | 7 | "github.com/ethereum/go-ethereum/accounts" 8 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 9 | "github.com/ethereum/go-ethereum/accounts/usbwallet" 10 | "github.com/ethereum/go-ethereum/common" 11 | "github.com/ethereum/go-ethereum/crypto" 12 | ) 13 | 14 | // Signer defines the interface for interacting with different types of signers. 15 | type Signer interface { 16 | Address() common.Address // Address returns the Ethereum address associated with the signer. 17 | SignerFn(chainID *big.Int) bind.SignerFn // SignerFn returns a signer function used for transaction signing. 18 | SignData([]byte) ([]byte, error) // SignData signs the given data using the signer's private key. 19 | } 20 | 21 | // CreateSigner creates a signer based on the provided private key, mnemonic, or hardware wallet. 22 | func CreateSigner(privateKey, mnemonic, hdPath string) (Signer, error) { 23 | if privateKey != "" { 24 | key, err := crypto.HexToECDSA(privateKey) 25 | if err != nil { 26 | return nil, fmt.Errorf("error parsing private key: %w", err) 27 | } 28 | return &ecdsaSigner{key}, nil 29 | } 30 | 31 | path, err := accounts.ParseDerivationPath(hdPath) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | if mnemonic != "" { 37 | key, err := derivePrivateKeyFromMnemonic(mnemonic, path) 38 | if err != nil { 39 | return nil, fmt.Errorf("error deriving key from mnemonic: %w", err) 40 | } 41 | return &ecdsaSigner{key}, nil 42 | } 43 | 44 | // Assume using a hardware wallet (e.g., Ledger) 45 | ledgerHub, err := usbwallet.NewLedgerHub() 46 | if err != nil { 47 | return nil, fmt.Errorf("error starting Ledger: %w", err) 48 | } 49 | wallets := ledgerHub.Wallets() 50 | if len(wallets) == 0 { 51 | return nil, fmt.Errorf("no Ledger device found, please connect your Ledger") 52 | } else if len(wallets) > 1 { 53 | return nil, fmt.Errorf("multiple Ledger devices found, please use only one at a time") 54 | } 55 | wallet := wallets[0] 56 | if err := wallet.Open(""); err != nil { 57 | return nil, fmt.Errorf("error opening Ledger: %w", err) 58 | } 59 | account, err := wallet.Derive(path, true) 60 | if err != nil { 61 | return nil, fmt.Errorf("error deriving Ledger account (have you unlocked?): %w", err) 62 | } 63 | return &walletSigner{ 64 | wallet: wallet, 65 | account: account, 66 | }, nil 67 | } 68 | -------------------------------------------------------------------------------- /signer/wallet_signer.go: -------------------------------------------------------------------------------- 1 | package signer 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "math/big" 6 | 7 | "github.com/decred/dcrd/hdkeychain/v3" 8 | "github.com/ethereum/go-ethereum/accounts" 9 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 10 | "github.com/ethereum/go-ethereum/common" 11 | "github.com/ethereum/go-ethereum/core/types" 12 | "github.com/ethereum/go-ethereum/crypto" 13 | "github.com/tyler-smith/go-bip39" 14 | ) 15 | 16 | // walletSigner represents a signer based on a wallet account. 17 | type walletSigner struct { 18 | wallet accounts.Wallet 19 | account accounts.Account 20 | } 21 | 22 | // Address returns the Ethereum address associated with the signer. 23 | func (s *walletSigner) Address() common.Address { 24 | return s.account.Address 25 | } 26 | 27 | // SignerFn returns a signer function used for transaction signing. 28 | func (s *walletSigner) SignerFn(chainID *big.Int) bind.SignerFn { 29 | return func(address common.Address, tx *types.Transaction) (*types.Transaction, error) { 30 | return s.wallet.SignTx(s.account, tx, chainID) 31 | } 32 | } 33 | 34 | // SignData signs the given data using the signer's private key. 35 | func (s *walletSigner) SignData(data []byte) ([]byte, error) { 36 | return s.wallet.SignData(s.account, accounts.MimetypeTypedData, data) 37 | } 38 | 39 | // derivePrivateKeyFromMnemonic derives an ECDSA private key from a mnemonic phrase and derivation path. 40 | func derivePrivateKeyFromMnemonic(mnemonic string, path accounts.DerivationPath) (*ecdsa.PrivateKey, error) { 41 | // Parse the seed string into the master BIP32 key. 42 | seed, err := bip39.NewSeedWithErrorChecking(mnemonic, "") 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | // Derive the private key based on the derivation path. 48 | privKey, err := hdkeychain.NewMaster(seed, fakeNetworkParams{}) 49 | if err != nil { 50 | return nil, err 51 | } 52 | 53 | for _, child := range path { 54 | privKey, err = privKey.Child(child) 55 | if err != nil { 56 | return nil, err 57 | } 58 | } 59 | 60 | // Serialize the private key and convert it to an ECDSA private key. 61 | rawPrivKey, err := privKey.SerializedPrivKey() 62 | if err != nil { 63 | return nil, err 64 | } 65 | 66 | return crypto.ToECDSA(rawPrivKey) 67 | } 68 | 69 | // fakeNetworkParams is used for HD key derivation and provides fake network parameters. 70 | type fakeNetworkParams struct{} 71 | 72 | func (f fakeNetworkParams) HDPrivKeyVersion() [4]byte { 73 | return [4]byte{} 74 | } 75 | 76 | func (f fakeNetworkParams) HDPubKeyVersion() [4]byte { 77 | return [4]byte{} 78 | } 79 | -------------------------------------------------------------------------------- /withdraw/fpwithdraw.go: -------------------------------------------------------------------------------- 1 | package withdraw 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "math/big" 8 | "time" 9 | 10 | "github.com/ethereum-optimism/optimism/op-node/bindings" 11 | bindingspreview "github.com/ethereum-optimism/optimism/op-node/bindings/preview" 12 | "github.com/ethereum-optimism/optimism/op-node/withdrawals" 13 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 14 | "github.com/ethereum/go-ethereum/common" 15 | "github.com/ethereum/go-ethereum/ethclient" 16 | "github.com/ethereum/go-ethereum/ethclient/gethclient" 17 | "github.com/ethereum/go-ethereum/rpc" 18 | ) 19 | 20 | type FPWithdrawer struct { 21 | Ctx context.Context 22 | L1Client *ethclient.Client 23 | L2Client *rpc.Client 24 | L2TxHash common.Hash 25 | Portal *bindingspreview.OptimismPortal2 26 | Factory *bindings.DisputeGameFactory 27 | Opts *bind.TransactOpts 28 | } 29 | 30 | func (w *FPWithdrawer) CheckIfProvable() error { 31 | l2WithdrawalBlock, err := txBlock(w.Ctx, w.L2Client, w.L2TxHash) 32 | if err != nil { 33 | return fmt.Errorf("error querying withdrawal tx block: %w", err) 34 | } 35 | 36 | latestGame, err := withdrawals.FindLatestGame(w.Ctx, &w.Factory.DisputeGameFactoryCaller, &w.Portal.OptimismPortal2Caller) 37 | if err != nil { 38 | return fmt.Errorf("failed to find latest game: %w", err) 39 | } 40 | l2BlockNumber := new(big.Int).SetBytes(latestGame.ExtraData[0:32]) 41 | 42 | if l2BlockNumber.Uint64() < l2WithdrawalBlock.Uint64() { 43 | return fmt.Errorf("the latest L2 block proposed in the DisputeGameFactory is %d and is not past L2 block %d that includes the withdrawal - the withdrawal cannot be proven yet", 44 | l2BlockNumber.Uint64(), l2WithdrawalBlock.Uint64()) 45 | } 46 | return nil 47 | } 48 | 49 | func (w *FPWithdrawer) getWithdrawalHash() (common.Hash, error) { 50 | l2 := ethclient.NewClient(w.L2Client) 51 | receipt, err := l2.TransactionReceipt(w.Ctx, w.L2TxHash) 52 | if err != nil { 53 | return common.HexToHash(""), err 54 | } 55 | 56 | ev, err := withdrawals.ParseMessagePassed(receipt) 57 | if err != nil { 58 | return common.HexToHash(""), err 59 | } 60 | 61 | hash, err := withdrawals.WithdrawalHash(ev) 62 | if err != nil { 63 | return common.HexToHash(""), err 64 | } 65 | 66 | return hash, nil 67 | } 68 | 69 | func (w *FPWithdrawer) GetProvenWithdrawalTime() (uint64, error) { 70 | hash, err := w.getWithdrawalHash() 71 | if err != nil { 72 | return 0, err 73 | } 74 | 75 | // the proven withdrawal structure now contains an additional mapping, as withdrawal proofs are now stored per submitter address 76 | provenWithdrawal, err := w.Portal.ProvenWithdrawals(&bind.CallOpts{}, hash, w.Opts.From) 77 | if err != nil { 78 | return 0, err 79 | } 80 | 81 | return provenWithdrawal.Timestamp, nil 82 | } 83 | 84 | func (w *FPWithdrawer) ProveWithdrawal() error { 85 | l2 := ethclient.NewClient(w.L2Client) 86 | l2g := gethclient.New(w.L2Client) 87 | 88 | params, err := ProveWithdrawalParametersFaultProofs(w.Ctx, l2g, l2, l2, w.L2TxHash, &w.Factory.DisputeGameFactoryCaller, &w.Portal.OptimismPortal2Caller) 89 | if err != nil { 90 | return err 91 | } 92 | 93 | // create the proof 94 | tx, err := w.Portal.ProveWithdrawalTransaction( 95 | w.Opts, 96 | bindingspreview.TypesWithdrawalTransaction{ 97 | Nonce: params.Nonce, 98 | Sender: params.Sender, 99 | Target: params.Target, 100 | Value: params.Value, 101 | GasLimit: params.GasLimit, 102 | Data: params.Data, 103 | }, 104 | params.L2OutputIndex, // this is overloaded and is the DisputeGame index in this context 105 | bindingspreview.TypesOutputRootProof{ 106 | Version: params.OutputRootProof.Version, 107 | StateRoot: params.OutputRootProof.StateRoot, 108 | MessagePasserStorageRoot: params.OutputRootProof.MessagePasserStorageRoot, 109 | LatestBlockhash: params.OutputRootProof.LatestBlockhash, 110 | }, 111 | params.WithdrawalProof, 112 | ) 113 | if err != nil { 114 | return err 115 | } 116 | 117 | fmt.Printf("Proved withdrawal for %s: %s\n", w.L2TxHash.String(), tx.Hash().String()) 118 | 119 | // Wait 5 mins max for confirmation 120 | ctxWithTimeout, cancel := context.WithTimeout(w.Ctx, 5*time.Minute) 121 | defer cancel() 122 | return waitForConfirmation(ctxWithTimeout, w.L1Client, tx.Hash()) 123 | } 124 | 125 | func (w *FPWithdrawer) IsProofFinalized() (bool, error) { 126 | return w.Portal.FinalizedWithdrawals(&bind.CallOpts{}, w.L2TxHash) 127 | } 128 | 129 | func (w *FPWithdrawer) FinalizeWithdrawal() error { 130 | // get the withdrawal hash 131 | hash, err := w.getWithdrawalHash() 132 | if err != nil { 133 | return err 134 | } 135 | 136 | // check if the withdrawal can be finalized using the calculated withdrawal hash 137 | err = w.Portal.CheckWithdrawal(&bind.CallOpts{}, hash, w.Opts.From) 138 | if err != nil { 139 | return err 140 | } 141 | 142 | // get the WithdrawalTransaction info needed to finalize the withdrawal 143 | l2 := ethclient.NewClient(w.L2Client) 144 | 145 | // Transaction receipt 146 | receipt, err := l2.TransactionReceipt(w.Ctx, w.L2TxHash) 147 | if err != nil { 148 | return err 149 | } 150 | // Parse the receipt 151 | ev, err := withdrawals.ParseMessagePassed(receipt) 152 | if err != nil { 153 | return err 154 | } 155 | 156 | // finalize the withdrawal 157 | tx, err := w.Portal.FinalizeWithdrawalTransaction( 158 | w.Opts, 159 | bindingspreview.TypesWithdrawalTransaction{ 160 | Nonce: ev.Nonce, 161 | Sender: ev.Sender, 162 | Target: ev.Target, 163 | Value: ev.Value, 164 | GasLimit: ev.GasLimit, 165 | Data: ev.Data, 166 | }, 167 | ) 168 | if err != nil { 169 | return err 170 | } 171 | 172 | fmt.Printf("Completed withdrawal for %s: %s\n", w.L2TxHash.String(), tx.Hash().String()) 173 | 174 | // Wait 5 mins max for confirmation 175 | ctxWithTimeout, cancel := context.WithTimeout(w.Ctx, 5*time.Minute) 176 | defer cancel() 177 | return waitForConfirmation(ctxWithTimeout, w.L1Client, tx.Hash()) 178 | } 179 | 180 | // ProveWithdrawalParametersFaultProofs calls ProveWithdrawalParametersForBlock with the most recent L2 output after the latest game. 181 | func ProveWithdrawalParametersFaultProofs(ctx context.Context, proofCl withdrawals.ProofClient, l2ReceiptCl withdrawals.ReceiptClient, l2BlockCl withdrawals.BlockClient, txHash common.Hash, disputeGameFactoryContract *bindings.DisputeGameFactoryCaller, optimismPortal2Contract *bindingspreview.OptimismPortal2Caller) (withdrawals.ProvenWithdrawalParameters, error) { 182 | latestGame, err := FindEarliestGame(ctx, l2ReceiptCl, txHash, disputeGameFactoryContract, optimismPortal2Contract) 183 | if err != nil { 184 | return withdrawals.ProvenWithdrawalParameters{}, fmt.Errorf("failed to find game: %w", err) 185 | } 186 | 187 | l2BlockNumber := new(big.Int).SetBytes(latestGame.ExtraData[0:32]) 188 | l2OutputIndex := latestGame.Index 189 | return withdrawals.ProveWithdrawalParametersForBlock(ctx, proofCl, l2ReceiptCl, l2BlockCl, txHash, l2BlockNumber, l2OutputIndex) 190 | } 191 | 192 | // FindEarliestGame finds the earliest game in the DisputeGameFactory contract that is after the given transaction receipt. 193 | // Note that this does not support checking for invalid games (e.g. games that were successfully challenged). 194 | func FindEarliestGame(ctx context.Context, l2ReceiptCl withdrawals.ReceiptClient, txHash common.Hash, disputeGameFactoryContract *bindings.DisputeGameFactoryCaller, optimismPortal2Contract *bindingspreview.OptimismPortal2Caller) (*bindings.IDisputeGameFactoryGameSearchResult, error) { 195 | receipt, err := l2ReceiptCl.TransactionReceipt(ctx, txHash) 196 | if err != nil { 197 | return nil, err 198 | } 199 | 200 | respectedGameType, err := optimismPortal2Contract.RespectedGameType(&bind.CallOpts{}) 201 | if err != nil { 202 | return nil, fmt.Errorf("failed to get respected game type: %w", err) 203 | } 204 | 205 | gameCount, err := disputeGameFactoryContract.GameCount(&bind.CallOpts{}) 206 | if err != nil { 207 | return nil, fmt.Errorf("failed to get game count: %w", err) 208 | } 209 | if gameCount.Cmp(common.Big0) == 0 { 210 | return nil, errors.New("no games") 211 | } 212 | 213 | lo := new(big.Int) 214 | hi := new(big.Int).Sub(gameCount, common.Big1) 215 | 216 | for lo.Cmp(hi) < 0 { 217 | mid := new(big.Int).Add(lo, hi) 218 | mid.Div(mid, common.Big2) 219 | latestGames, err := disputeGameFactoryContract.FindLatestGames(&bind.CallOpts{}, respectedGameType, mid, common.Big1) 220 | if err != nil { 221 | return nil, err 222 | } 223 | l2BlockNumber := new(big.Int) 224 | if len(latestGames) > 0 { 225 | l2BlockNumber = new(big.Int).SetBytes(latestGames[0].ExtraData[0:32]) 226 | } 227 | if l2BlockNumber.Cmp(receipt.BlockNumber) < 0 { 228 | lo = mid.Add(mid, common.Big1) 229 | } else { 230 | hi = mid 231 | } 232 | } 233 | 234 | latestGames, err := disputeGameFactoryContract.FindLatestGames(&bind.CallOpts{}, respectedGameType, lo, common.Big1) 235 | if err != nil { 236 | return nil, err 237 | } 238 | if len(latestGames) == 0 { 239 | return nil, errors.New("no games found") 240 | } 241 | latestGame := latestGames[0] 242 | 243 | return &latestGame, nil 244 | } 245 | -------------------------------------------------------------------------------- /withdraw/utils.go: -------------------------------------------------------------------------------- 1 | package withdraw 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "math/big" 8 | "time" 9 | 10 | "github.com/ethereum/go-ethereum" 11 | "github.com/ethereum/go-ethereum/common" 12 | "github.com/ethereum/go-ethereum/core/types" 13 | "github.com/ethereum/go-ethereum/ethclient" 14 | "github.com/ethereum/go-ethereum/rpc" 15 | ) 16 | 17 | type WithdrawHelper interface { 18 | CheckIfProvable() error 19 | GetProvenWithdrawalTime() (uint64, error) 20 | ProveWithdrawal() error 21 | IsProofFinalized() (bool, error) 22 | FinalizeWithdrawal() error 23 | } 24 | 25 | func txBlock(ctx context.Context, l2c *rpc.Client, l2TxHash common.Hash) (*big.Int, error) { 26 | l2 := ethclient.NewClient(l2c) 27 | // Figure out when our withdrawal was included 28 | receipt, err := l2.TransactionReceipt(ctx, l2TxHash) 29 | if err != nil { 30 | return nil, err 31 | } 32 | if receipt.Status != types.ReceiptStatusSuccessful { 33 | return nil, errors.New("unsuccessful withdrawal receipt status") 34 | } 35 | return receipt.BlockNumber, nil 36 | } 37 | 38 | func waitForConfirmation(ctx context.Context, client *ethclient.Client, tx common.Hash) error { 39 | for { 40 | receipt, err := client.TransactionReceipt(ctx, tx) 41 | if err == ethereum.NotFound { 42 | fmt.Printf("waiting for tx confirmation\n") 43 | select { 44 | case <-ctx.Done(): 45 | return ctx.Err() 46 | case <-time.After(5 * time.Second): 47 | } 48 | } else if err != nil { 49 | return err 50 | } else if receipt.Status != types.ReceiptStatusSuccessful { 51 | return errors.New("unsuccessful withdrawal receipt status") 52 | } else { 53 | break 54 | } 55 | } 56 | fmt.Printf("%s confirmed\n", tx.String()) 57 | return nil 58 | } 59 | -------------------------------------------------------------------------------- /withdraw/withdraw.go: -------------------------------------------------------------------------------- 1 | package withdraw 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "fmt" 7 | "time" 8 | 9 | "github.com/ethereum-optimism/optimism/op-node/bindings" 10 | "github.com/ethereum-optimism/optimism/op-node/withdrawals" 11 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 12 | "github.com/ethereum/go-ethereum/common" 13 | "github.com/ethereum/go-ethereum/core/types" 14 | "github.com/ethereum/go-ethereum/ethclient" 15 | "github.com/ethereum/go-ethereum/ethclient/gethclient" 16 | "github.com/ethereum/go-ethereum/rpc" 17 | ) 18 | 19 | type Withdrawer struct { 20 | Ctx context.Context 21 | L1Client *ethclient.Client 22 | L2Client *rpc.Client 23 | L2TxHash common.Hash 24 | Portal *bindings.OptimismPortal 25 | Oracle *bindings.L2OutputOracle 26 | Opts *bind.TransactOpts 27 | } 28 | 29 | func (w *Withdrawer) CheckIfProvable() error { 30 | // check to make sure it is possible to prove the provided withdrawal 31 | submissionInterval, err := w.Oracle.SUBMISSIONINTERVAL(&bind.CallOpts{}) 32 | if err != nil { 33 | return fmt.Errorf("error querying output proposal submission interval: %w", err) 34 | } 35 | 36 | l2BlockTime, err := w.Oracle.L2BLOCKTIME(&bind.CallOpts{}) 37 | if err != nil { 38 | return fmt.Errorf("error querying output proposal L2 block time: %w", err) 39 | } 40 | 41 | l2OutputBlock, err := w.Oracle.LatestBlockNumber(&bind.CallOpts{}) 42 | if err != nil { 43 | return fmt.Errorf("error querying latest proposed block: %w", err) 44 | } 45 | 46 | l2WithdrawalBlock, err := txBlock(w.Ctx, w.L2Client, w.L2TxHash) 47 | if err != nil { 48 | return fmt.Errorf("error querying withdrawal tx block: %w", err) 49 | } 50 | 51 | if l2OutputBlock.Uint64() < l2WithdrawalBlock.Uint64() { 52 | return fmt.Errorf("the latest L2 output is %d and is not past L2 block %d that includes the withdrawal, no withdrawal can be proved yet - please wait for the next proposal submission, which happens every %v", 53 | l2OutputBlock.Uint64(), l2WithdrawalBlock.Uint64(), time.Duration(submissionInterval.Int64()*l2BlockTime.Int64())*time.Second) 54 | } 55 | return nil 56 | } 57 | 58 | func (w *Withdrawer) GetProvenWithdrawalTime() (uint64, error) { 59 | l2 := ethclient.NewClient(w.L2Client) 60 | receipt, err := l2.TransactionReceipt(w.Ctx, w.L2TxHash) 61 | if err != nil { 62 | return 0, err 63 | } 64 | 65 | ev, err := withdrawals.ParseMessagePassed(receipt) 66 | if err != nil { 67 | return 0, err 68 | } 69 | 70 | hash, err := withdrawals.WithdrawalHash(ev) 71 | if err != nil { 72 | return 0, err 73 | } 74 | 75 | provenWithdrawal, err := w.Portal.ProvenWithdrawals(&bind.CallOpts{}, hash) 76 | if err != nil { 77 | return 0, err 78 | } 79 | 80 | return provenWithdrawal.Timestamp.Uint64(), nil 81 | } 82 | 83 | func (w *Withdrawer) ProveWithdrawal() error { 84 | l2 := ethclient.NewClient(w.L2Client) 85 | l2g := gethclient.New(w.L2Client) 86 | 87 | l2OutputBlock, err := w.Oracle.LatestBlockNumber(&bind.CallOpts{}) 88 | if err != nil { 89 | return err 90 | } 91 | 92 | // We generate a proof for the latest L2 output, which shouldn't require archive-node data if it's recent enough. 93 | header, err := l2.HeaderByNumber(w.Ctx, l2OutputBlock) 94 | if err != nil { 95 | return err 96 | } 97 | params, err := withdrawals.ProveWithdrawalParameters(w.Ctx, l2g, l2, l2, w.L2TxHash, header, &w.Oracle.L2OutputOracleCaller) 98 | if err != nil { 99 | return err 100 | } 101 | 102 | // Create the prove tx 103 | tx, err := w.Portal.ProveWithdrawalTransaction( 104 | w.Opts, 105 | bindings.TypesWithdrawalTransaction{ 106 | Nonce: params.Nonce, 107 | Sender: params.Sender, 108 | Target: params.Target, 109 | Value: params.Value, 110 | GasLimit: params.GasLimit, 111 | Data: params.Data, 112 | }, 113 | params.L2OutputIndex, 114 | params.OutputRootProof, 115 | params.WithdrawalProof, 116 | ) 117 | if err != nil { 118 | return err 119 | } 120 | 121 | fmt.Printf("Proved withdrawal for %s: %s\n", w.L2TxHash.String(), tx.Hash().String()) 122 | 123 | // Wait 5 mins max for confirmation 124 | ctxWithTimeout, cancel := context.WithTimeout(w.Ctx, 5*time.Minute) 125 | defer cancel() 126 | return waitForConfirmation(ctxWithTimeout, w.L1Client, tx.Hash()) 127 | } 128 | 129 | func (w *Withdrawer) IsProofFinalized() (bool, error) { 130 | return w.Portal.FinalizedWithdrawals(&bind.CallOpts{}, w.L2TxHash) 131 | } 132 | 133 | func (w *Withdrawer) FinalizeWithdrawal() error { 134 | l2 := ethclient.NewClient(w.L2Client) 135 | l2g := gethclient.New(w.L2Client) 136 | 137 | // Figure out when our withdrawal was included 138 | receipt, err := l2.TransactionReceipt(w.Ctx, w.L2TxHash) 139 | if err != nil { 140 | return fmt.Errorf("cannot get receipt for withdrawal tx %s: %v", w.L2TxHash, err) 141 | } 142 | if receipt.Status != types.ReceiptStatusSuccessful { 143 | return errors.New("unsuccessful withdrawal receipt status") 144 | } 145 | 146 | l2WithdrawalBlock, err := l2.HeaderByNumber(w.Ctx, receipt.BlockNumber) 147 | if err != nil { 148 | return fmt.Errorf("error getting header by number for block %s: %v", receipt.BlockNumber, err) 149 | } 150 | 151 | // Figure out what the Output oracle on L1 has seen so far 152 | l2OutputBlockNr, err := w.Oracle.LatestBlockNumber(&bind.CallOpts{}) 153 | if err != nil { 154 | return err 155 | } 156 | 157 | l2OutputBlock, err := l2.HeaderByNumber(w.Ctx, l2OutputBlockNr) 158 | if err != nil { 159 | return fmt.Errorf("error getting header by number for latest block %s: %v", l2OutputBlockNr, err) 160 | } 161 | 162 | // Check if the L2 output is even old enough to include the withdrawal 163 | if l2OutputBlock.Number.Uint64() < l2WithdrawalBlock.Number.Uint64() { 164 | return fmt.Errorf("the latest L2 output is %d and is not past L2 block %d that includes the withdrawal yet, no withdrawal can be completed yet", l2OutputBlock.Number.Uint64(), l2WithdrawalBlock.Number.Uint64()) 165 | } 166 | 167 | l1Head, err := w.L1Client.HeaderByNumber(w.Ctx, nil) 168 | if err != nil { 169 | return err 170 | } 171 | 172 | // Check if the withdrawal may be completed yet 173 | finalizationPeriod, err := w.Oracle.FINALIZATIONPERIODSECONDS(&bind.CallOpts{}) 174 | if err != nil { 175 | return err 176 | } 177 | 178 | if l2WithdrawalBlock.Time+finalizationPeriod.Uint64() >= l1Head.Time { 179 | return fmt.Errorf("withdrawal tx %s was included in L2 block %d (time %d) but L1 only knows of L2 proposal %d (time %d) at head %d (time %d) which has not reached output confirmation yet (period is %d)", 180 | w.L2TxHash, l2WithdrawalBlock.Number.Uint64(), l2WithdrawalBlock.Time, l2OutputBlock.Number.Uint64(), l2OutputBlock.Time, l1Head.Number.Uint64(), l1Head.Time, finalizationPeriod.Uint64()) 181 | } 182 | 183 | // We generate a proof for the latest L2 output, which shouldn't require archive-node data if it's recent enough. 184 | // Note that for the `FinalizeWithdrawalTransaction` function, this proof isn't needed. We simply use some of the 185 | // params for the `WithdrawalTransaction` type generated in the bindings. 186 | header, err := l2.HeaderByNumber(w.Ctx, l2OutputBlockNr) 187 | if err != nil { 188 | return err 189 | } 190 | 191 | params, err := withdrawals.ProveWithdrawalParameters(w.Ctx, l2g, l2, l2, w.L2TxHash, header, &w.Oracle.L2OutputOracleCaller) 192 | if err != nil { 193 | return err 194 | } 195 | 196 | // Create the withdrawal tx 197 | tx, err := w.Portal.FinalizeWithdrawalTransaction( 198 | w.Opts, 199 | bindings.TypesWithdrawalTransaction{ 200 | Nonce: params.Nonce, 201 | Sender: params.Sender, 202 | Target: params.Target, 203 | Value: params.Value, 204 | GasLimit: params.GasLimit, 205 | Data: params.Data, 206 | }, 207 | ) 208 | if err != nil { 209 | return err 210 | } 211 | 212 | fmt.Printf("Completed withdrawal for %s: %s\n", w.L2TxHash.String(), tx.Hash().String()) 213 | 214 | // Wait 5 mins max for confirmation 215 | ctxWithTimeout, cancel := context.WithTimeout(w.Ctx, 5*time.Minute) 216 | defer cancel() 217 | return waitForConfirmation(ctxWithTimeout, w.L1Client, tx.Hash()) 218 | } 219 | --------------------------------------------------------------------------------