From 4a0c9a06a2e1b90d6218565d35d0598b0bba237c Mon Sep 17 00:00:00 2001 From: Sergey Kudasov Date: Fri, 23 Aug 2024 14:47:18 +0200 Subject: [PATCH 1/8] add TLS ignore in order to connect to CRIB, add debug (#1088) --- client/miner.go | 5 +++-- client/rpc.go | 20 ++++++++++++++++++-- client/rpc_suite_test.go | 2 +- client/rpc_test.go | 12 ++++++------ 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/client/miner.go b/client/miner.go index cc872c07d..2ed6f2eaf 100644 --- a/client/miner.go +++ b/client/miner.go @@ -1,6 +1,7 @@ package client import ( + "net/http" "strconv" "time" @@ -18,9 +19,9 @@ type RemoteAnvilMiner struct { } // NewRemoteAnvilMiner creates a new remote miner client -func NewRemoteAnvilMiner(url string) *RemoteAnvilMiner { +func NewRemoteAnvilMiner(url string, headers http.Header) *RemoteAnvilMiner { return &RemoteAnvilMiner{ - Client: NewRPCClient(url), + Client: NewRPCClient(url, headers), stop: make(chan struct{}), } } diff --git a/client/rpc.go b/client/rpc.go index 06cafdd84..232021f1a 100644 --- a/client/rpc.go +++ b/client/rpc.go @@ -2,8 +2,11 @@ package client import ( "context" + "crypto/tls" "encoding/json" "fmt" + "net/http" + "os" "strconv" "time" @@ -24,8 +27,21 @@ type RPCClient struct { } // NewRPCClient creates Anvil client -func NewRPCClient(url string) *RPCClient { - return &RPCClient{URL: url, client: resty.New()} +func NewRPCClient(url string, headers http.Header) *RPCClient { + isDebug := os.Getenv("RESTY_DEBUG") == "true" + h := make(map[string]string) + for k, v := range headers { + h[k] = v[0] + } + // TODO: use proper certificated in CRIB + //nolint + return &RPCClient{ + URL: url, + client: resty.New(). + SetDebug(isDebug). + SetHeaders(h). + SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true}), + } } // AnvilMine calls "evm_mine", mines one or more blocks, see the reference on RPCClient diff --git a/client/rpc_suite_test.go b/client/rpc_suite_test.go index 10f43157e..dfede622a 100644 --- a/client/rpc_suite_test.go +++ b/client/rpc_suite_test.go @@ -20,7 +20,7 @@ func TestRPCSuite(t *testing.T) { require.NoError(t, err) printGasPrices(t, client) // set a base fee - anvilClient := NewRPCClient(ac.URL) + anvilClient := NewRPCClient(ac.URL, nil) // set fee for the next block err = anvilClient.AnvilSetNextBlockBaseFeePerGas([]interface{}{"2000000000"}) require.NoError(t, err) diff --git a/client/rpc_test.go b/client/rpc_test.go index ab767e19a..0f4b739bc 100644 --- a/client/rpc_test.go +++ b/client/rpc_test.go @@ -104,7 +104,7 @@ func TestRPCAPI(t *testing.T) { bnBefore, err := client.BlockNumber(context.Background()) require.NoError(t, err) - ac := NewRPCClient(url) + ac := NewRPCClient(url, nil) err = ac.GethSetHead(10) require.NoError(t, err) bnAfter, err := client.BlockNumber(context.Background()) @@ -121,7 +121,7 @@ func TestRPCAPI(t *testing.T) { tx, err := sendTestTransaction(t, client, big.NewInt(1e9), big.NewInt(1e9), false) require.NoError(t, err) - anvilClient := NewRPCClient(ac.URL) + anvilClient := NewRPCClient(ac.URL, nil) err = anvilClient.AnvilDropTransaction([]interface{}{tx.Hash().String()}) require.NoError(t, err) status, err := anvilClient.AnvilTxPoolStatus(nil) @@ -136,7 +136,7 @@ func TestRPCAPI(t *testing.T) { client, err := ethclient.Dial(ac.URL) require.NoError(t, err) - anvilClient := NewRPCClient(ac.URL) + anvilClient := NewRPCClient(ac.URL, nil) err = anvilClient.AnvilSetBlockGasLimit([]interface{}{"1"}) require.NoError(t, err) @@ -157,7 +157,7 @@ func TestRPCAPI(t *testing.T) { require.NoError(t, err) printGasPrices(t, client) - anvilClient := NewRPCClient(ac.URL) + anvilClient := NewRPCClient(ac.URL, nil) err = anvilClient.AnvilSetNextBlockBaseFeePerGas([]interface{}{"10000000000"}) require.NoError(t, err) printGasPrices(t, client) @@ -182,7 +182,7 @@ func TestRPCAPI(t *testing.T) { require.NoError(t, err) client, err := ethclient.Dial(ac.URL) require.NoError(t, err) - pm := NewRemoteAnvilMiner(ac.URL) + pm := NewRemoteAnvilMiner(ac.URL, nil) pm.MinePeriodically(500 * time.Millisecond) time.Sleep(period * time.Duration(iterations)) pm.Stop() @@ -201,7 +201,7 @@ func TestRPCAPI(t *testing.T) { client, err := ethclient.Dial(ac.URL) require.NoError(t, err) stopTxns, errCh := sendTestTransactions(t, client, sendTransactionEvery, big.NewInt(1e9), big.NewInt(1e9), false) - pm := NewRemoteAnvilMiner(ac.URL) + pm := NewRemoteAnvilMiner(ac.URL, nil) pm.MineBatch(txnInBlock, 1*time.Second, 1*time.Minute) time.Sleep(sendTransactionEvery * time.Duration(iterations) * 2) pm.Stop() From f30d0d0476f45162d8ec57f4018838b8611eb12f Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Mon, 26 Aug 2024 10:39:21 +0200 Subject: [PATCH 2/8] Merge Seth to CTF (#1087) --- .github/workflows/seth-lint.yml | 29 + .github/workflows/seth-test-api.yml | 36 + .github/workflows/seth-test-bumping.yml | 36 + .github/workflows/seth-test-cli.yml | 36 + .../workflows/seth-test-decode-testnet.yml | 39 + .github/workflows/seth-test-decode.yml | 36 + .github/workflows/seth-test-others.yml | 36 + .github/workflows/seth-test-trace.yml | 36 + .pre-commit-config.yaml | 2 + .prettierignore | 1 + README.md | 1 + _typos.toml | 4 +- seth/.gitignore | 60 + seth/LICENSE.txt | 21 + seth/Makefile | 67 + seth/README.md | 782 ++++ seth/abi_finder.go | 146 + seth/block_stats.go | 299 ++ seth/client.go | 1386 +++++++ seth/client_api_test.go | 359 ++ seth/client_builder.go | 246 ++ seth/client_contract_map_test.go | 167 + seth/client_decode_test.go | 236 ++ seth/client_helpers.go | 44 + seth/client_main_test.go | 217 ++ seth/client_test.go | 72 + seth/client_trace_test.go | 1796 +++++++++ seth/cmd/seth.go | 307 ++ seth/cmd/seth/seth.go | 13 + seth/config.go | 303 ++ seth/config_test.go | 216 ++ seth/contract_map.go | 119 + seth/contract_store.go | 130 + seth/contract_store_test.go | 104 + seth/contracts/NetworkDebugContract.sol | 358 ++ seth/contracts/NetworkDebugSubContract.sol | 61 + seth/contracts/abi/DebugContractCallback.abi | 1 + seth/contracts/abi/LinkTokenLegacy.abi | 1 + seth/contracts/abi/LinkTokenModern.abi | 1 + seth/contracts/abi/NetworkDebugContract.abi | 1 + .../contracts/abi/NetworkDebugSubContract.abi | 1 + seth/contracts/bin/DebugContractCallback.bin | 0 seth/contracts/bin/NetworkDebugContract.bin | 1 + .../contracts/bin/NetworkDebugSubContract.bin | 1 + .../bind/debug/NetworkDebugContract.go | 3286 +++++++++++++++++ seth/contracts/bind/link/link_token.go | 2038 ++++++++++ .../link_token_interface.go | 701 ++++ .../bind/sub/NetworkDebugSubContract.go | 1168 ++++++ seth/contracts/emptyContractDir/.gitkeep | 0 .../NetworkDebugContract.abi | 1 + seth/decode.go | 459 +++ seth/docs/abi_finder_contract_map.md | 33 + seth/docs/contract_store.md | 7 + seth/docs/tracing_example.png | Bin 0 -> 259998 bytes seth/dot_graph.go | 395 ++ seth/examples/example_deployment_test.go | 84 + seth/examples/example_test.go | 220 ++ seth/examples/example_tracing_test.go | 23 + seth/examples_wasp/README.md | 109 + seth/examples_wasp/client_main_test.go | 130 + seth/examples_wasp/client_wasp_test.go | 64 + seth/examples_wasp/go.mod | 228 ++ seth/examples_wasp/go.sum | 1349 +++++++ seth/examples_wasp/seth.toml | 23 + seth/flake.lock | 111 + seth/flake.nix | 16 + seth/gas.go | 125 + seth/gas_adjuster.go | 542 +++ seth/gas_bump_test.go | 624 ++++ seth/gas_test.go | 38 + seth/geth_data/clique_genesis.json | 33 + seth/geth_data/keystore/key1 | 1 + seth/geth_data/password.txt | 0 seth/go.mod | 60 + seth/go.sum | 232 ++ seth/header_cache.go | 85 + seth/http_logging_transport.go | 66 + seth/keyfile.go | 106 + seth/log.go | 32 + seth/nonce.go | 152 + seth/retry.go | 249 ++ seth/seth.toml | 228 ++ seth/shell.nix | 21 + seth/test_utils/client.go | 170 + seth/test_utils/config.go | 23 + seth/tracing.go | 718 ++++ seth/tracing_cli_test.go | 33 + seth/util.go | 455 +++ seth/util_test.go | 231 ++ sonar-project.properties | 2 +- 90 files changed, 22477 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/seth-lint.yml create mode 100644 .github/workflows/seth-test-api.yml create mode 100644 .github/workflows/seth-test-bumping.yml create mode 100644 .github/workflows/seth-test-cli.yml create mode 100644 .github/workflows/seth-test-decode-testnet.yml create mode 100644 .github/workflows/seth-test-decode.yml create mode 100644 .github/workflows/seth-test-others.yml create mode 100644 .github/workflows/seth-test-trace.yml create mode 100644 seth/.gitignore create mode 100644 seth/LICENSE.txt create mode 100644 seth/Makefile create mode 100644 seth/README.md create mode 100644 seth/abi_finder.go create mode 100644 seth/block_stats.go create mode 100644 seth/client.go create mode 100644 seth/client_api_test.go create mode 100644 seth/client_builder.go create mode 100644 seth/client_contract_map_test.go create mode 100644 seth/client_decode_test.go create mode 100644 seth/client_helpers.go create mode 100644 seth/client_main_test.go create mode 100644 seth/client_test.go create mode 100644 seth/client_trace_test.go create mode 100644 seth/cmd/seth.go create mode 100644 seth/cmd/seth/seth.go create mode 100644 seth/config.go create mode 100644 seth/config_test.go create mode 100644 seth/contract_map.go create mode 100644 seth/contract_store.go create mode 100644 seth/contract_store_test.go create mode 100644 seth/contracts/NetworkDebugContract.sol create mode 100644 seth/contracts/NetworkDebugSubContract.sol create mode 100644 seth/contracts/abi/DebugContractCallback.abi create mode 100644 seth/contracts/abi/LinkTokenLegacy.abi create mode 100644 seth/contracts/abi/LinkTokenModern.abi create mode 100644 seth/contracts/abi/NetworkDebugContract.abi create mode 100644 seth/contracts/abi/NetworkDebugSubContract.abi create mode 100644 seth/contracts/bin/DebugContractCallback.bin create mode 100644 seth/contracts/bin/NetworkDebugContract.bin create mode 100644 seth/contracts/bin/NetworkDebugSubContract.bin create mode 100644 seth/contracts/bind/debug/NetworkDebugContract.go create mode 100644 seth/contracts/bind/link/link_token.go create mode 100644 seth/contracts/bind/link_token_interface/link_token_interface.go create mode 100644 seth/contracts/bind/sub/NetworkDebugSubContract.go create mode 100644 seth/contracts/emptyContractDir/.gitkeep create mode 100644 seth/contracts/invalidContractDir/NetworkDebugContract.abi create mode 100644 seth/decode.go create mode 100644 seth/docs/abi_finder_contract_map.md create mode 100644 seth/docs/contract_store.md create mode 100644 seth/docs/tracing_example.png create mode 100644 seth/dot_graph.go create mode 100644 seth/examples/example_deployment_test.go create mode 100644 seth/examples/example_test.go create mode 100644 seth/examples/example_tracing_test.go create mode 100644 seth/examples_wasp/README.md create mode 100644 seth/examples_wasp/client_main_test.go create mode 100644 seth/examples_wasp/client_wasp_test.go create mode 100644 seth/examples_wasp/go.mod create mode 100644 seth/examples_wasp/go.sum create mode 100644 seth/examples_wasp/seth.toml create mode 100644 seth/flake.lock create mode 100644 seth/flake.nix create mode 100644 seth/gas.go create mode 100644 seth/gas_adjuster.go create mode 100644 seth/gas_bump_test.go create mode 100644 seth/gas_test.go create mode 100644 seth/geth_data/clique_genesis.json create mode 100644 seth/geth_data/keystore/key1 create mode 100644 seth/geth_data/password.txt create mode 100644 seth/go.mod create mode 100644 seth/go.sum create mode 100644 seth/header_cache.go create mode 100644 seth/http_logging_transport.go create mode 100644 seth/keyfile.go create mode 100644 seth/log.go create mode 100644 seth/nonce.go create mode 100644 seth/retry.go create mode 100644 seth/seth.toml create mode 100644 seth/shell.nix create mode 100644 seth/test_utils/client.go create mode 100644 seth/test_utils/config.go create mode 100644 seth/tracing.go create mode 100644 seth/tracing_cli_test.go create mode 100644 seth/util.go create mode 100644 seth/util_test.go diff --git a/.github/workflows/seth-lint.yml b/.github/workflows/seth-lint.yml new file mode 100644 index 000000000..158432384 --- /dev/null +++ b/.github/workflows/seth-lint.yml @@ -0,0 +1,29 @@ +name: SETH Lint +on: + push: +permissions: + contents: read +jobs: + golangci: + defaults: + run: + working-directory: seth + name: lint + runs-on: ubuntu-latest + steps: + - name: Checkout repo + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - name: Check for changes in Seth project + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + filters: | + src: + - 'seth/**' + - uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26 + if: steps.changes.outputs.src == 'true' + with: + nix_path: nixpkgs=channel:nixos-unstable + - name: Run tests + run: | + nix develop -c make lint diff --git a/.github/workflows/seth-test-api.yml b/.github/workflows/seth-test-api.yml new file mode 100644 index 000000000..4a3fce74e --- /dev/null +++ b/.github/workflows/seth-test-api.yml @@ -0,0 +1,36 @@ +name: SETH API tests +on: + push: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + test: + defaults: + run: + working-directory: seth + runs-on: ubuntu-latest + strategy: + matrix: + network: [Anvil, Geth] + env: + SETH_ROOT_PRIVATE_KEY: ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + steps: + - name: Checkout repo + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - name: Check for changes in Seth project + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + filters: | + src: + - 'seth/**' + - uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26 + if: steps.changes.outputs.src == 'true' + with: + nix_path: nixpkgs=channel:nixos-unstable + - name: Run tests + env: + SETH_NETWORK: ${{ matrix.network }} + run: | + nix develop -c make "$SETH_NETWORK" && make SETH_LOG_LEVEL=debug root_private_key="$SETH_ROOT_PRIVATE_KEY" network="$SETH_NETWORK" test_api diff --git a/.github/workflows/seth-test-bumping.yml b/.github/workflows/seth-test-bumping.yml new file mode 100644 index 000000000..6d01bc35e --- /dev/null +++ b/.github/workflows/seth-test-bumping.yml @@ -0,0 +1,36 @@ +name: SETH Gas bumping tests +on: + push: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + test: + defaults: + run: + working-directory: seth + runs-on: ubuntu-latest + strategy: + matrix: + network: [Geth] + env: + SETH_ROOT_PRIVATE_KEY: ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + steps: + - name: Checkout repo + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - name: Check for changes in Seth project + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + filters: | + src: + - 'seth/**' + - uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26 + if: steps.changes.outputs.src == 'true' + with: + nix_path: nixpkgs=channel:nixos-unstable + - name: Run tests + env: + SETH_NETWORK: ${{ matrix.network }} + run: | + nix develop -c make "$SETH_NETWORK" && make SETH_LOG_LEVEL=debug root_private_key="$SETH_ROOT_PRIVATE_KEY" network="$SETH_NETWORK" test_gas_bumping diff --git a/.github/workflows/seth-test-cli.yml b/.github/workflows/seth-test-cli.yml new file mode 100644 index 000000000..9ab8094b5 --- /dev/null +++ b/.github/workflows/seth-test-cli.yml @@ -0,0 +1,36 @@ +name: SETH CLI tests +on: + push: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + test: + defaults: + run: + working-directory: seth + runs-on: ubuntu-latest + strategy: + matrix: + network: [Anvil, Geth] + env: + SETH_ROOT_PRIVATE_KEY: ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + steps: + - name: Checkout repo + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - name: Check for changes in Seth project + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + filters: | + src: + - 'seth/**' + - uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26 + if: steps.changes.outputs.src == 'true' + with: + nix_path: nixpkgs=channel:nixos-unstable + - name: Run tests + env: + SETH_NETWORK: ${{ matrix.network }} + run: | + nix develop -c make "$SETH_NETWORK" && make SETH_LOG_LEVEL=debug root_private_key="$SETH_ROOT_PRIVATE_KEY" network="$SETH_NETWORK" test_cli diff --git a/.github/workflows/seth-test-decode-testnet.yml b/.github/workflows/seth-test-decode-testnet.yml new file mode 100644 index 000000000..13981695d --- /dev/null +++ b/.github/workflows/seth-test-decode-testnet.yml @@ -0,0 +1,39 @@ +name: SETH Decoding tests (testnets) +on: + push: + tags: + - v.** +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + test: + defaults: + run: + working-directory: seth + runs-on: ubuntu-latest + strategy: + matrix: + # TODO: Sepolia is usually down but it worked, turn it on when possible + # TODO: Mumbai is not working right now + network: [Fuji] + steps: + - name: Checkout repo + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - name: Check for changes in Seth project + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + filters: | + src: + - 'seth/**' + - uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26 + if: steps.changes.outputs.src == 'true' + with: + nix_path: nixpkgs=channel:nixos-unstable + - name: Run tests + env: + SETH_NETWORK: ${{ matrix.network }} + SETH_ROOT_PRIVATE_KEY: ${{ secrets.TESTNET_COMMON_KEYS }} + run: | + nix develop -c make SETH_LOG_LEVEL=debug root_private_key="$SETH_ROOT_PRIVATE_KEY" network="$SETH_NETWORK" test diff --git a/.github/workflows/seth-test-decode.yml b/.github/workflows/seth-test-decode.yml new file mode 100644 index 000000000..dfc78c514 --- /dev/null +++ b/.github/workflows/seth-test-decode.yml @@ -0,0 +1,36 @@ +name: SETH Decoding tests +on: + push: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + test: + defaults: + run: + working-directory: seth + runs-on: ubuntu-latest + strategy: + matrix: + network: [Anvil, Geth] + env: + SETH_ROOT_PRIVATE_KEY: ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + steps: + - name: Checkout repo + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - name: Check for changes in Seth project + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + filters: | + src: + - 'seth/**' + - uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26 + if: steps.changes.outputs.src == 'true' + with: + nix_path: nixpkgs=channel:nixos-unstable + - name: Run tests + env: + SETH_NETWORK: ${{ matrix.network }} + run: | + nix develop -c make "$SETH_NETWORK" && make SETH_LOG_LEVEL=debug root_private_key="$SETH_ROOT_PRIVATE_KEY" network="$SETH_NETWORK" test diff --git a/.github/workflows/seth-test-others.yml b/.github/workflows/seth-test-others.yml new file mode 100644 index 000000000..e6df81892 --- /dev/null +++ b/.github/workflows/seth-test-others.yml @@ -0,0 +1,36 @@ +name: SETH Other tests +on: + push: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + test: + defaults: + run: + working-directory: seth + runs-on: ubuntu-latest + strategy: + matrix: + network: [Geth] + env: + SETH_ROOT_PRIVATE_KEY: ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + steps: + - name: Checkout repo + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - name: Check for changes in Seth project + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + filters: | + src: + - 'seth/**' + - uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26 + if: steps.changes.outputs.src == 'true' + with: + nix_path: nixpkgs=channel:nixos-unstable + - name: Run tests + env: + SETH_NETWORK: ${{ matrix.network }} + run: | + nix develop -c make "$SETH_NETWORK" && make SETH_LOG_LEVEL=debug root_private_key="$SETH_ROOT_PRIVATE_KEY" network="$SETH_NETWORK" test_others diff --git a/.github/workflows/seth-test-trace.yml b/.github/workflows/seth-test-trace.yml new file mode 100644 index 000000000..89e3a4b99 --- /dev/null +++ b/.github/workflows/seth-test-trace.yml @@ -0,0 +1,36 @@ +name: SETH Tracing tests +on: + push: +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true +jobs: + test: + defaults: + run: + working-directory: seth + runs-on: ubuntu-latest + strategy: + matrix: + network: [Anvil, Geth] + env: + SETH_ROOT_PRIVATE_KEY: ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 + steps: + - name: Checkout repo + uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 + - name: Check for changes in Seth project + uses: dorny/paths-filter@de90cc6fb38fc0963ad72b210f1f284cd68cea36 # v3.0.2 + id: changes + with: + filters: | + src: + - 'seth/**' + - uses: cachix/install-nix-action@8887e596b4ee1134dae06b98d573bd674693f47c # v26 + if: steps.changes.outputs.src == 'true' + with: + nix_path: nixpkgs=channel:nixos-unstable + - name: Run tests + env: + SETH_NETWORK: ${{ matrix.network }} + run: | + nix develop -c make "$SETH_NETWORK" && make SETH_LOG_LEVEL=debug root_private_key="$SETH_ROOT_PRIVATE_KEY" network="$SETH_NETWORK" test_trace diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4cec0df13..1ab5c757a 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,3 +1,5 @@ +exclude: '(\.bin|\.abi)' + repos: - repo: local hooks: diff --git a/.prettierignore b/.prettierignore index 5c8b62609..f17e00f4f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -8,3 +8,4 @@ node_modules/ index.yaml wasp/** havoc/** +seth/** diff --git a/README.md b/README.md index 4ab7f4af5..1d370e294 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ CTF contains a set of useful libraries: - [WASP](wasp/README.md) - Scalable protocol-agnostic load testing library for `Go` - [Havoc](havoc/README.md) - Chaos testing library +- [Seth](seth/README.md) - Ethereum client library with transaction tracing and gas bumping ## k8s package diff --git a/_typos.toml b/_typos.toml index 1d04c9b9b..baa76d175 100644 --- a/_typos.toml +++ b/_typos.toml @@ -6,5 +6,7 @@ extend-exclude = [ "**/*.tgz", "**/*.png", "wasp/HOW_IT_WORKS.md", - "wasp/dashboard/**" + "wasp/dashboard/**", + "seth/*_test.go", + "seth/dot_graph.go" ] diff --git a/seth/.gitignore b/seth/.gitignore new file mode 100644 index 000000000..33e28a0fc --- /dev/null +++ b/seth/.gitignore @@ -0,0 +1,60 @@ +# IDE and environment +.idea/ +.vscode/ +.DS_STORE + +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories +dist/ +vendor/ +node_modules/ +.yarn/ + +# Mercuy server configuration file +config.toml +config.*.toml + +# Personal/secret env vars +.envrc-personal + +# Other +tmp/ +*.log +*.swp +.air.toml +.DS_Store +output.txt + +# Default binary name with go build +main +test.sh + +# Ignore DevSpace cache and log folder +.devspace/ + +# env files +.env +.envrc + +# Geth data +geth_data/geth/** + +/traces +reverted_transactions*.json +/dot_graphs +artifacts/ + +#map of address -> ABI name of deployed contracts +deployed_contracts*.toml diff --git a/seth/LICENSE.txt b/seth/LICENSE.txt new file mode 100644 index 000000000..3682fb80f --- /dev/null +++ b/seth/LICENSE.txt @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2023 SmartContract ChainLink, Ltd. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/seth/Makefile b/seth/Makefile new file mode 100644 index 000000000..f41ce5c5e --- /dev/null +++ b/seth/Makefile @@ -0,0 +1,67 @@ +.PHONY: build +build: + solc --abi --overwrite -o contracts/abi contracts/NetworkDebugContract.sol + solc --bin --overwrite -o contracts/bin contracts/NetworkDebugContract.sol + abigen --bin=contracts/bin/NetworkDebugContract.bin --abi=contracts/abi/NetworkDebugContract.abi --pkg=network_debug_contract --out=contracts/bind/debug/NetworkDebugContract.go + solc --abi --overwrite -o contracts/abi contracts/NetworkDebugSubContract.sol + solc --bin --overwrite -o contracts/bin contracts/NetworkDebugSubContract.sol + abigen --bin=contracts/bin/NetworkDebugSubContract.bin --abi=contracts/abi/NetworkDebugSubContract.abi --pkg=network_debug_sub_contract --out=contracts/bind/sub/NetworkDebugSubContract.go + +.PHONY: AnvilSync +AnvilSync: + anvil + +.PHONY: Anvil +Anvil: + anvil > /dev/null 2>&1 & + +.PHONY: Geth +Geth: + rm -rf geth_data/geth + geth init --datadir geth_data/ geth_data/clique_genesis.json + geth --graphql --http --http.api admin,debug,web3,eth,txpool,personal,miner,net --http.corsdomain "*" --ws --ws.api admin,debug,web3,eth,txpool,personal,miner,net --ws.origins "*" --mine --miner.etherbase 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --unlock f39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --allow-insecure-unlock --datadir ./geth_data --password geth_data/password.txt --nodiscover --vmdebug --networkid 1337 > /dev/null 2>&1 & + +.PHONY: GethSync +GethSync: + rm -rf geth_data/geth + geth init --datadir geth_data/ geth_data/clique_genesis.json + geth --graphql --http --http.api admin,debug,web3,eth,txpool,personal,miner,net --http.corsdomain "*" --ws --ws.api admin,debug,web3,eth,txpool,personal,miner,net --ws.origins "*" --mine --miner.etherbase 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --unlock f39Fd6e51aad88F6F4ce6aB8827279cffFb92266 --allow-insecure-unlock --datadir ./geth_data --password geth_data/password.txt --nodiscover --vmdebug --networkid 1337 + +.PHONY: test +test: + SETH_NETWORK=$(network) SETH_ROOT_PRIVATE_KEY=$(root_private_key) go test -v -count 1 -race `go list ./... | grep -v examples` -run TestSmoke + +.PHONY: kill_node +kill_node: + pkill -f geth || pkill -f anvil || true + +# this one is without -race flag, because zerolog is not thread safe and fails the run +.PHONY: test_api +test_api: + SETH_NETWORK=$(network) SETH_ROOT_PRIVATE_KEY=$(root_private_key) go test -v -count 1 `go list ./... | grep -v examples` -run TestAPI + +.PHONY: test_trace +test_trace: + SETH_NETWORK=$(network) SETH_ROOT_PRIVATE_KEY=$(root_private_key) go test -v -count 1 -race `go list ./... | grep -v examples` -run TestTrace + +.PHONY: test_cli +test_cli: + SETH_NETWORK=$(network) SETH_ROOT_PRIVATE_KEY=$(root_private_key) go test -v -count 1 -race `go list ./... | grep -v examples` -run TestCLI + +.PHONY: test_others +test_others: + SETH_NETWORK=$(network) SETH_ROOT_PRIVATE_KEY=$(root_private_key) go test -v -count 1 -race `go list ./... | grep -v examples` -run "TestContractMap|TestGasEstimator|TestRPCHealthCheck|TestUtil|TestContract|TestConfig" + +# this one is without -race flag, because zerolog is not thread safe and fails the run +.PHONY: test_gas_bumping +test_gas_bumping: + SETH_NETWORK=$(network) SETH_ROOT_PRIVATE_KEY=$(root_private_key) go test -v -count 1 `go list ./... | grep -v examples` -run "TestGasBumping" + +.PHONY: test+cover +test_cover: + SETH_NETWORK=$(network) SETH_ROOT_PRIVATE_KEY=$(root_private_key) go test -v -coverprofile cover.out -count 1 `go list ./... | grep -v examples` -run "TestAPI|TestSmoke|TestContract|TestGasEstimator" + go tool cover -html cover.out + +.PHONY: lint +lint: + golangci-lint --color=always run -v diff --git a/seth/README.md b/seth/README.md new file mode 100644 index 000000000..91a80e42e --- /dev/null +++ b/seth/README.md @@ -0,0 +1,782 @@ +# Seth + +Reliable and debug-friendly Ethereum client + +[![Decoding tests](https://github.com/smartcontractkit/chainlink-testing-framework/seth/actions/workflows/seth-test-decode.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_decode.yml) +[![Tracing tests](https://github.com/smartcontractkit/chainlink-testing-framework/seth/actions/workflows/seth-test-trace.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_trace.yml) +[![Gas bumping tests](https://github.com/smartcontractkit/chainlink-testing-framework/seth/actions/workflows/seth-test-bumping.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_cli.yml) +[![API tests](https://github.com/smartcontractkit/chainlink-testing-framework/seth/actions/workflows/seth-test-api.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_api.yml) +[![CLI tests](https://github.com/smartcontractkit/chainlink-testing-framework/seth/actions/workflows/seth-test-cli.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_cli.yml) +[![Integration tests (testnets)](https://github.com/smartcontractkit/chainlink-testing-framework/seth/actions/workflows/seth-test-decode-testnet.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_decode_testnet.yml) +
+ +# Content + +1. [Goals](#goals) +2. [Features](#features) +3. [Examples](#examples) +4. [Setup](#setup) + 1. [Building test contracts](#building-test-contracts) + 2. [Testing](#testing) +5. [Configuration](#config) + 1. [Simplified configuration](#simplified-configuration) + 2. [ClientBuilder](#clientbuilder) + 3. [Supported env vars](#supported-env-vars) + 4. [TOML configuration](#toml-configuration) +6. [Automated gas price estimation](#automatic-gas-estimator) +7. [DOT Graphs of transactions](#dot-graphs) +8. [Using multiple private keys](#using-multiple-keys) +9. [Experimental features](#experimental-features) +10. [Gas bumping for slow transactions](#gas-bumping-for-slow-transactions) +11. [CLI](#cli) +12. [Manual gas price estimation](#manual-gas-price-estimation) +13. [Block Stats](#block-stats) +14. [Single transaction tracing](#single-transaction-tracing) +15. [Bulk transaction tracing](#bulk-transaction-tracing) + +## Goals + +- Be a thin, debuggable and battle tested wrapper on top of `go-ethereum` +- Decode all transaction inputs/outputs/logs for all ABIs you are working with, automatically +- Simple synchronous API +- Do not handle `nonces` on the client side, trust the server +- Do not wrap `bind` generated contracts, small set of additional debug API +- Resilient: should execute transactions even if there is a gas spike or an RPC outage (failover) +- Well tested: should provide a suite of e2e tests that can be run on testnets to check integration + +## Features + +- [x] Decode named inputs +- [x] Decode named outputs +- [x] Decode anonymous outputs +- [x] Decode logs +- [x] Decode indexed logs +- [x] Decode old string reverts +- [x] Decode new typed reverts +- [x] EIP-1559 support +- [x] Multi-keys client support +- [x] CLI to manipulate test keys +- [x] Simple manual gas price estimation +- [ ] Fail over client logic +- [ ] Decode collided event hashes +- [x] Tracing support (4byte) +- [x] Tracing support (callTracer) +- [ ] Tracing support (prestate) +- [x] Tracing decoding +- [x] Tracing tests +- [ ] More tests for corner cases of decoding/tracing +- [x] Saving of deployed contracts mapping (`address -> ABI_name`) for live networks +- [x] Reading of deployed contracts mappings for live networks +- [x] Automatic gas estimator (experimental) +- [x] Block stats CLI +- [x] Check if address has a pending nonce (transaction) and panic if it does +- [x] DOT graph output for tracing +- [x] Gas bumping for slow transactions + +You can read more about how ABI finding and contract map works [here](./docs/abi_finder_contract_map.md) and about contract store here [here](./docs/contract_store.md). + +## Examples + +Check [examples](./examples) folder + +Lib provides a small amount of helpers for decoding handling that you can use with vanilla `go-ethereum` generated wrappers + +```go +// Decode waits for transaction and decode all the data/errors +Decode(tx *types.Transaction, txErr error) (*DecodedTransaction, error) + +// NewTXOpts returns a new sequential transaction options wrapper, +// sets opts.GasPrice and opts.GasLimit from seth.toml or override with options +NewTXOpts(o ...TransactOpt) *bind.TransactOpts + +// NewCallOpts returns a new call options wrapper +NewCallOpts(o ...CallOpt) *bind.CallOpts +``` + +By default, we are using the `root` key `0`, but you can also use any of the private keys passed as part of `Network` configuration in `seth.toml` or ephemeral keys. + +```go +// NewCallKeyOpts returns a new sequential call options wrapper from the key N +NewCallKeyOpts(keyNum int, o ...CallOpt) *bind.CallOpts + +// NewTXKeyOpts returns a new transaction options wrapper called from the key N +NewTXKeyOpts(keyNum int, o ...TransactOpt) *bind.TransactOpts +``` + +Start `Geth` in a separate terminal, then run the examples + +```sh +make GethSync +cd examples +go test -v +``` + +## Setup + +We are using [nix](https://nixos.org/) + +Enter the shell + +```sh +nix develop +``` + +## Building test contracts + +We have `go-ethereum` and [foundry](https://github.com/foundry-rs/foundry) tools inside `nix` shell + +```sh +make build +``` + +## Testing + +To run tests on a local network, first start it + +```sh +make AnvilSync +``` + +Or use latest `Geth` + +```sh +make GethSync +``` + +You can use default `hardhat` key `ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80` to run tests + +Run the [decode](./client_decode_test.go) tests + +```sh +make network=Anvil root_private_key=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 test +make network=Geth root_private_key=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 test +``` + +Check other params in [seth.toml](./seth.toml), select any network and use your key for testnets + +User facing API tests are [here](./client_api_test.go) + +```sh +make network=Anvil root_private_key=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 test_api +make network=Geth root_private_key=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 test_api +``` + +CLI tests + +```sh +make network=Anvil root_private_key=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 test_cli +make network=Geth root_private_key=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 test_cli +``` + +Tracing tests + +```sh +make network=Anvil root_private_key=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 test_trace +make network=Geth root_private_key=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 test_trace +``` + +# Config + +### Simplified configuration + +If you do not want to set all the parameters, you can use a simplified progammatical configuration. Here's an example: + +```go +cfg := seth.DefaultConfig("ws://localhost:8546", []string{"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"}) +client, err := seth.NewClientWithConfig(cfg) +if err != nil { + log.Fatal(err) +} +``` + +This config uses what we consider reasonable defaults, such as: + +- 5 minute transaction confirmation timeout +- 1 minute RPC node dial timeout +- enabled EIP-1559 dynamic fees and automatic gas prices estimation (with 200 blocks history; will auto-disable itself if RPC doesn't support EIP-1559) +- tracing only of reverted transaction to console and DOT graphs +- checking of RPC node health on client creation +- no ephemeral keys + +### ClientBuilder + +You can also use a `ClientBuilder` to build a config programmatically. Here's an extensive example: + +```go +client, err := builder. + // network + WithNetworkName("my network"). + WithRpcUrl("ws://localhost:8546"). + WithPrivateKeys([]string{"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"}). + WithRpcDialTimeout(10*time.Second). + WithTransactionTimeouts(1*time.Minute). + // addresses + WithEphemeralAddresses(10, 10). + // tracing + WithTracing(seth.TracingLevel_All, []string{seth.TraceOutput_Console}). + // protections + WithProtections(true, true). + // artifacts folder + WithArtifactsFolder("some_folder"). + // nonce manager + WithNonceManager(10, 3, 60, 5). + // EIP-1559 and gas estimations + WithEIP1559DynamicFees(true). + WithDynamicGasPrices(120_000_000_000, 44_000_000_000). + WithGasPriceEstimations(false, 10, seth.Priority_Fast). + // gas bumping: retries, max gas price, bumping strategy function + WithGasBumping(5, 100_000_000_000, PriorityBasedGasBumpingStrategyFn). + Build() + +if err != nil { + log.Fatal(err) +} +``` + +By default, it uses the same values as simplified configuration, but you can override them by calling the appropriate methods. Builder includes only options +that we thought to be most useful, it's not a 1:1 mapping of all fields in the `Config` struct. Therefore, if you need to set some more advanced options, you should create the `Config` struct directly, +use TOML config or manually set the fields on the `Config` struct returned by the builder. + +### Supported env vars + +Some crucial data is stored in env vars, create `.envrc` and use `source .envrc`, or use `direnv` + +```sh +export SETH_LOG_LEVEL=info # global logger level +export SETH_CONFIG_PATH=seth.toml # path to the toml config +export SETH_NETWORK=Geth # selected network +export SETH_ROOT_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 # root private key + +alias seth="SETH_CONFIG_PATH=seth.toml go run cmd/seth/seth.go" # useful alias for CLI +``` + +Alternatively if you don't have a network defined in the TOML you can still use the CLI by providing these 2 key env vars: + +```sh +export SETH_URL=https://rpc.fuji.testnet.anyswap.exchange +export SETH_CHAIN_ID=43113 + +go run cmd/seth/seth.go ... # your command +``` + +In that case you should still pass network name with `-n` flag. + +### TOML configuration + +Set up your ABI directory (relative to `seth.toml`) + +```toml +abi_dir = "contracts/abi" +``` + +Setup your BIN directory (relative to `seth.toml`) + +```toml +bin_dir = "contracts/bin" +``` + +Decide whether you want to generate any `ephemeral` keys: + +```toml +# Set number of ephemeral keys to be generated (0 for no ephemeral keys). Each key will receive a proportion of native tokens from root private key's balance with the value equal to `(root_balance / ephemeral_keys_number) - transfer_fee * ephemeral_keys_number`. +ephemeral_addresses_number = 10 +``` + +You can enable auto-tracing for all transactions meeting configured level, which means that every time you use `Decode()` we will decode the transaction and also trace all calls made within the transaction, together with all inputs, outputs, logs and events. Three tracing levels are available: + +- `all` - trace all transactions +- `reverted` - trace only reverted transactions (that's default setting used if you don't set `tracing_level`) +- `none` - don't trace any transactions + +Example: + +```toml +tracing_level = "reverted" +``` + +Additionally, you can decide where tracing/decoding data goes to. There are three options: + +- `console` - we will print all tracing data to the console +- `json` - we will save tracing data for each transaction to a JSON file +- `dot` - we will save tracing data for each transaction to a DOT file (graph) + +```toml +trace_outputs = ["console", "json", "dot"] +``` + +For info on viewing DOT files please check the [DOT graphs](#dot-graphs) section below. + +Example: +![image](./docs/tracing_example.png) +These two options should be used with care, when `tracing_level` is set to `all` as they might generate a lot of data. + +If you want to check if the RPC is healthy on start, you can enable it with: + +```toml +check_rpc_health_on_start = false +``` + +It will execute a simple check of transferring 10k wei from root key to root key and check if the transaction was successful. + +You can add more networks like this: + +```toml +[[Networks]] +name = "Fuji" +transaction_timeout = "30s" +# gas limit should be explicitly set only if you are connecting to a node that's incapable of estimating gas limit itself (should only happen for very old versions) +# gas_limit = 9_000_000 +# hardcoded gas limit for sending funds that will be used if estimation of gas limit fails +transfer_gas_fee = 21_000 +# legacy transactions +gas_price = 1_000_000_000 +# EIP-1559 transactions +eip_1559_dynamic_fees = true +gas_fee_cap = 25_000_000_000 +gas_tip_cap = 1_800_000_000 +urls_secret = ["..."] +# if set to true we will dynamically estimate gas for every transaction (explained in more detail below) +gas_price_estimation_enabled = true +# how many last blocks to use, when estimating gas for a transaction +gas_price_estimation_blocks = 1000 +# priority of the transaction, can be "fast", "standard" or "slow" (the higher the priority, the higher adjustment factor and buffer will be used for gas estimation) [default: "standard"] +gas_price_estimation_tx_priority = "slow" +``` + +If you don't we will use the default settings for `Default` network. + +ChainID is not needed, as it's fetched from the node. + +If you want to save addresses of deployed contracts, you can enable it with: + +```toml +save_deployed_contracts_map = true +``` + +If you want to re-use previously deployed contracts you can indicate file name in `seth.toml`: + +```toml +contract_map_file = "deployed_contracts_mumbai.toml" +``` + +Both features only work for live networks. Otherwise, they are ignored, and nothing is saved/read from for simulated networks. + +### Automatic Gas Estimator + +This section explains how to configure and understand the automatic gas estimator, which is crucial for executing transactions on Ethereum-based networks. Here’s what you need to know: + +#### Configuration Requirements + +Before using the automatic gas estimator, it's essential to set the default gas-related parameters for your network: + +- **Non-EIP-1559 Networks**: Set the `gas_price` to define the cost per unit of gas if your network doesn't support EIP-1559. +- **EIP-1559 Networks**: If your network supports EIP-1559, set the following: + - `eip_1559_dynamic_fees`: Enables dynamic fee structure. + - `gas_fee_cap`: The maximum fee you're willing to pay per gas. + - `gas_tip_cap`: An optional tip to prioritize your transaction within a block (although if it's set to `0` there's a high chance your transaction will take longer to execute as it will be less attractive to miners, so do set it). + +These settings act as a fallback if the gas estimation fails. Additionally, always specify `transfer_gas_fee` for the fee associated with token transfers. + +If you do not know if your network supports EIP-1559, but you want to give it a try it's recommended that you also set `gas_price` as a fallback. When we try to use EIP-1559 during gas price estimation, but it fails, we will fallback to using non-EIP-1559 logic. If that one fails as well, we will use hardcoded `gas_price` value. + +#### How Gas Estimation Works + +Gas estimation varies based on whether the network is a private Ethereum Network or a live network. + +- **Private Ethereum Networks**: no estimation is needed. We always use hardcoded values. + +For real networks, the estimation process differs for legacy transactions and those compliant with EIP-1559: + +##### Legacy Transactions + +1. **Initial Price**: Query the network node for the current suggested gas price. +2. **Priority Adjustment**: Modify the initial price based on `gas_price_estimation_tx_priority`. Higher priority increases the price to ensure faster inclusion in a block. +3. **Congestion Analysis**: Examine the last X blocks (as specified by `gas_price_estimation_blocks`) to determine network congestion, calculating the usage rate of gas in each block and giving recent blocks more weight. +4. **Buffering**: Add a buffer to the adjusted gas price to increase transaction reliability during high congestion. + +##### EIP-1559 Transactions + +1. **Tip Fee Query**: Ask the node for the current recommended tip fee. +2. **Fee History Analysis**: Gather the base fee and tip history from recent blocks to establish a fee baseline. +3. **Fee Selection**: Use the greater of the node's suggested tip or the historical average tip for upcoming calculations. +4. **Priority and Adjustment**: Increase the base and tip fees based on transaction priority (`gas_price_estimation_tx_priority`), which influences how much you are willing to spend to expedite your transaction. +5. **Final Fee Calculation**: Sum the base fee and adjusted tip to set the `gas_fee_cap`. +6. **Congestion Buffer**: Similar to legacy transactions, analyze congestion and apply a buffer to both the fee cap and the tip to secure transaction inclusion. + +Understanding and setting these parameters correctly ensures that your transactions are processed efficiently and cost-effectively on the network. + +Finally, `gas_price_estimation_tx_priority` is also used, when deciding, which percentile to use for base fee and tip for historical fee data. Here's how that looks: + +```go +case Priority_Fast: + baseFee = stats.GasPrice.Perc99 + historicalGasTipCap = stats.TipCap.Perc99 +case Priority_Standard: + baseFee = stats.GasPrice.Perc50 + historicalGasTipCap = stats.TipCap.Perc50 +case Priority_Slow: + baseFee = stats.GasPrice.Perc25 + historicalGasTipCap = stats.TipCap.Perc25 +``` + +##### Adjustment factor + +All values are multiplied by the adjustment factor, which is calculated based on `gas_price_estimation_tx_priority`: + +```go +case Priority_Fast: + return 1.2 +case Priority_Standard: + return 1.0 +case Priority_Slow: + return 0.8 +``` + +For fast transactions we will increase gas price by 20%, for standard we will use the value as is and for slow we will decrease it by 20%. + +##### Buffer percents + +We further adjust the gas price by adding a buffer to it, based on congestion rate: + +```go +case Congestion_Low: + return 1.10, nil +case Congestion_Medium: + return 1.20, nil +case Congestion_High: + return 1.30, nil +case Congestion_VeryHigh: + return 1.40, nil +``` + +For low congestion rate we will increase gas price by 10%, for medium by 20%, for high by 30% and for very high by 40%. + +We cache block header data in an in-memory cache, so we don't have to fetch it every time we estimate gas. The cache has capacity equal to `gas_price_estimation_blocks` and every time we add a new element, we remove one that is least frequently used and oldest (with block number being a constant and chain always moving forward it makes no sense to keep old blocks). + +It's important to know that in order to use congestion metrics we need to fetch at least 80% of the requested blocks. If that fails, we will skip this part of the estimation and only adjust the gas price based on priority. + +For both transaction types if any of the steps fails, we fallback to hardcoded values. + +### DOT graphs + +There are multiple ways of visualising DOT graphs: + +- `xdot` application [recommended] +- VSCode Extensions +- online viewers + +### xdot + +To install simply run `homebrew install xdot` and then run `xdot `. This tool seems to be the best for the job, since the viewer is interactive and supports tooltips, which in our case contain extra tracing information. + +### VSCode Extensions + +There are multiple extensions that can be used to view DOT files in VSCode. We recommend using [Graphviz Preview](https://marketplace.visualstudio.com/items?itemName=EFanZh.graphviz-preview). The downside is that it doesn't support tooltips. + +### Goland + +We were unable to find any (working) plugins for DOT graph visualization. If you do know any, please let us know. + +### Online viewers + +There's at least a dozen of them available, but none of them support tooltips and most can't handle our multi-line labels. These two are known to work, though: + +- [Devtools/daily](https://www.devtoolsdaily.com/graphviz/) +- [Sketchviz](https://sketchviz.com/) + +### Using multiple keys + +If you want to use existing multiple keys (instead of ephemeral ones) you can pass them as part of the network configuration. In that case it's recommended to **not** read them from TOML file. If you need to read them for the filesystem/os it's best if you use environment variables. +Once you've read them in a safe manner you should programmatically add them to Seth's Config struct (which safe parts can be read from TOML file). You can either add them directly to `Network`, if it's already set up, or you can add them to `Networks` slice to the network you intend to use. + +For example you could start by reading the TOML configuration first: + +```go +cfg, err := seth.ReadCfg() +if err != nil { + log.Fatal(err) +} +``` + +Then read the private keys in a safe manner. For example from a secure vault or environment variables: + +```go +var privateKeys []string +var err error +privateKeys, err = some_utils.ReadPrivateKeysFromEnv() +if err != nil { + log.Fatal(err) +} +``` + +and then add them to the `Network` you plan to use. Let's assume it's called `Sepolia`: + +```go +for i, network := range cfg.Networks { + if network.Name == "Sepolia" { + cfg.Networks[i].PrivateKeys = privateKeys + } +} +``` + +Or if you aren't using `[[Networks]]` in your TOML config and have just a single `Network`: + +```go +cfg.Network.PrivateKeys = privateKeys +``` + +Or... you can use the convenience function `AppendPksToNetwork()` to have them added to both the `Network` and `Networks` slice: + +```go +added := cfg.AppendPksToNetwork(privateKeys, "Sepolia") +if !added { + log.Fatal("Network Sepolia not found in the config") +} +``` + +Finally, proceed to create a new Seth instance: + +```go +seth, err := seth.NewClientWithConfig(cfg) +if err != nil { + log.Fatal(err) +} +``` + +A working example can be found [here](examples/example_test.go) as `TestSmokeExampleMultiKeyFromEnv` test. + +Currently, there's no safe way to pass multiple keys to CLI. In that case TOML is the only way to go, but you should be mindful that if you commit the TOML file with keys in it, you should assume they are compromised and all funds on them are lost. + +### Experimental features + +In order to enable an experimental feature you need to pass its name in config. It's a global config, you cannot enable it per-network. Example: + +```toml +# other settings before... +tracing_level = "reverted" +trace_outputs = ["console"] +experiments_enabled = ["slow_funds_return", "eip_1559_fee_equalizer"] +``` + +Here's what they do: + +- `slow_funds_return` will work only in `core` and when enabled it changes tx priority to `slow` and increases transaction timeout to 30 minutes. +- `eip_1559_fee_equalizer` in case of EIP-1559 transactions if it detects that historical base fee and suggested/historical tip are more than 3 orders of magnitude apart, it will use the higher value for both (this helps in cases where base fee is almost 0 and transaction is never processed). + +## Gas bumping for slow transactions + +Seth has built-in gas bumping mechanism for slow transactions. If a transaction is not mined within a certain time frame (`Network`'s transaction timeout), Seth will automatically bump the gas price and resubmit the transaction. This feature is disabled by default and can be enabled by setting the `[gas_bumps] retries` to a non-zero number: + +```toml +[gas_bumps] +retries = 5 +``` + +Once enabled, by default the amount, by which gas price is bumped depends on `gas_price_estimation_tx_priority` setting and is calculated as follows: + +- `Priority_Fast`: 30% increase +- `Priority_Standard`: 15% increase +- `Priority_Slow`: 5% increase +- everything else: no increase + +You can cap max gas price by settings (in wei): + +```toml +[gas_bumps] +max_gas_price = 1000000000000 +``` + +Once the gas price bump would go above the limit we stop bumping and use the last gas price that was below the limit. + +How gas price is calculated depends on transaction type: + +- for legacy transactions it's just the gas price +- for EIP-1559 transactions it's the sum of gas fee cap and tip cap +- for Blob transactions (EIP-4844) it's the sum of gas fee cap and tip cap and max fee per blob +- for AccessList transactions (EIP-2930) it's just the gas price + +Please note that Blob and AccessList support remains experimental and is not tested. + +If you want to use a custom bumping strategy, you can use a function with [GasBumpStrategyFn](retry.go) type. Here's an example of a custom strategy that bumps the gas price by 100% for every retry: + +```go +var customGasBumpStrategyFn = func(gasPrice *big.Int) *big.Int { + return new(big.Int).Mul(gasPrice, big.NewInt(2)) +} +``` + +To use this strategy, you need to pass it to the `WithGasBumping` function in the `ClientBuilder`: + +```go +var hundredGwei in64 = 100_000_000_000 +client, err := builder. + // other settings... + WithGasBumping(5, hundredGwei, customGasBumpStrategyFn). + Build() +``` + +Or set it directly on Seth's config: + +```go +// assuming sethClient is already created +sethClient.Config.GasBumps.StrategyFn = customGasBumpStrategyFn +``` + +Since strategy function only accepts a single parameter, if you want to base its behaviour on anything else than that you will need to capture these values from the context, in which you define the strategy function. For example, you can use a closure to capture the initial gas price: + +```go +gasOracleClient := NewGasOracleClient() + +var oracleGasBumpStrategyFn = func(gasPrice *big.Int) *big.Int { + // get the current gas price from the oracle + suggestedGasPrice := gasOracleClient.GetCurrentGasPrice() + + // if oracle suggests a higher gas price, use it + if suggestedGasPrice.Cmp(gasPrice) == 1 { + return suggestedGasPrice + } + + // otherwise bump by 100% + return new(big.Int).Mul(gasPrice, big.NewInt(2)) +} +``` + +Same strategy is applied to all types of transactions, regardless whether it's gas price, gas fee cap, gas tip cap or max blob fee. + +When enabled, gas bumping is used in two places: + +- during contract deployment via `DeployContract` function +- inside `Decode()` function + +It is recommended to decrease transaction timeout when using gas bumping, as it will be effectively increased by the number of retries. So if you were running with 5 minutes timeout and 0 retries, you should set it to 1 minute and 5 retries +or 30 seconds and 10 retries. + +Don't worry if while bumping logic executes previous transaction gets mined. In that case sending replacement transaction with higher gas will fail (because it is using the same nonce as original transaction) and we will retry waiting for the mining of the original transaction. + +**Gas bumping is only applied for submitted transaction. If transaction was rejected by the node (e.g. because of too low base fee) we will not bump the gas price nor try to submit it, because original transaction submission happens outside of Seth.** + +## CLI + +You can either define the network you want to interact with in your TOML config and then refer it in the CLI command, or you can pass all network parameters via env vars. Most of the examples below show how to use the former approach. + +### Manual gas price estimation + +In order to adjust gas price for a transaction, you can use `seth gas` command + +```sh +seth -n Fuji gas -b 10000 -tp 0.99 +``` + +This will analyze last 10k blocks and give you 25/50/75/99th/Max percentiles for base fees and tip fees + +`-tp 0.99` requests the 99th tip percentile across all the transaction in one block and calculates 25/50/75/99th/Max across all blocks + +### Block Stats + +If you need to get some insights into network stats and create a realistic load/chaos profile with simulators (`anvil` as an example), you can use `stats` CLI command + +#### Define your network in `seth.toml` + +Edit your `seth.toml` + +```toml +[[networks]] +name = "MyCustomNetwork" +urls_secret = ["..."] + +[block_stats] +rpc_requests_per_second_limit = 5 +``` + +Then check the stats for the last N blocks + +```sh +seth -n MyCustomNetwork stats -s -10 +``` + +To check stats for the interval (A, B) + +```sh +seth -n MyCustomNetwork stats -s A -e B +``` + +#### Pass all network parameters via env vars + +If you don't have a network defined in the TOML you can still use the CLI by providing the RPC url via cmd arg. + +Then check the stats for the last N blocks + +```sh +seth -u "https://my-rpc.network.io" stats -s -10 +``` + +To check stats for the interval (A, B) + +```sh +seth -u "https://my-rpc.network.io" stats -s A -e B +``` + +Results can help you to understand if network is stable, what is avg block time, gas price, block utilization and transactions per second. + +```toml +# Stats +perc_95_tps = 8.0 +perc_95_block_duration = '3s' +perc_95_block_gas_used = 1305450 +perc_95_block_gas_limit = 15000000 +perc_95_block_base_fee = 25000000000 +avg_tps = 2.433333333333333 +avg_block_duration = '2s' +avg_block_gas_used = 493233 +avg_block_gas_limit = 15000000 +avg_block_base_fee = 25000000000 + +# Recommended performance/chaos test parameters +duration = '2m0s' +block_gas_base_fee_initial_value = 25000000000 +block_gas_base_fee_bump_percentage = '100.00% (no bump required)' +block_gas_usage_percentage = '3.28822000% gas used (no congestion)' +avg_tps = 3.0 +max_tps = 8.0 +``` + +### Single transaction tracing + +You can trace a single transaction using `seth trace` command. Example with `seth` alias mentioned before: + +```sh +seth -u "https://my-rpc.network.io" trace -t 0x4c21294bf4c0a19de16e0fca74e1ea1687ba96c3cab64f6fca5640fb7b84df65 +``` + +or if you want to use a predefined-network: + +```sh +seth -n=Geth trace -t 0x4c21294bf4c0a19de16e0fca74e1ea1687ba96c3cab64f6fca5640fb7b84df65 +``` + +### Bulk transaction tracing + +You can trace multiple transactions at once using `seth trace` command for a predefined network named `Geth`. Example: + +```sh +seth -n=Geth trace -f reverted_transactions.json +``` + +or by passing all the RPC parameter with a flag: + +```sh +seth -u "https://my-rpc.network.io" trace -f reverted_transactions.json +``` + +You need to pass a file with a list of transaction hashes to trace. The file should be a JSON array of transaction hashes, like this: + +```json +[ + "0x...", + "0x...", + "0x...", + ... +] +``` + +(Note that currently Seth automatically creates `reverted_transactions__.json` with all reverted transactions, so you can use this file as input for the `trace` command.) diff --git a/seth/abi_finder.go b/seth/abi_finder.go new file mode 100644 index 000000000..b7316c442 --- /dev/null +++ b/seth/abi_finder.go @@ -0,0 +1,146 @@ +package seth + +import ( + "strings" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" +) + +type ABIFinder struct { + ContractMap ContractMap + ContractStore *ContractStore +} + +type ABIFinderResult struct { + ABI abi.ABI + Method *abi.Method + DuplicateCount int + contractName string +} + +func (a *ABIFinderResult) ContractName() string { + return strings.TrimSuffix(a.contractName, ".abi") +} + +func NewABIFinder(contractMap ContractMap, contractStore *ContractStore) ABIFinder { + return ABIFinder{ + ContractMap: contractMap, + ContractStore: contractStore, + } +} + +// FindABIByMethod finds the ABI method and instance for the given contract address and signature +// If the contract address is known, it will use the ABI instance that is known to be at the address. +// If the contract address is not known, it will iterate over all known ABIs and check if any of them +// has a method with the given signature. If there are duplicates we will use the first ABI that matched. +func (a *ABIFinder) FindABIByMethod(address string, signature []byte) (ABIFinderResult, error) { + result := ABIFinderResult{} + stringSignature := common.Bytes2Hex(signature) + + // let's start by checking if we already know what contract is at the address being called, + // so that we don't have to search all known ABIs. If we have a match, let's double check + // that it's correct. If it's not we will stop and return an error + if a.ContractMap.IsKnownAddress(address) { + contractName := a.ContractMap.GetContractName(address) + abiInstanceCandidate, ok := a.ContractStore.ABIs[contractName+".abi"] + if !ok { + err := errors.New(ErrNoAbiFound) + L.Err(err). + Str("Contract", contractName). + Str("Address", address). + Msg("ABI not found, even though contract is known. This should not happen. Contract map might be corrupted") + return ABIFinderResult{}, err + } + + methodCandidate, err := abiInstanceCandidate.MethodById(signature) + if err != nil { + // this might still be a valid, case when contract A and B share the same method signature, + // and we have already traced that call to contract A (when in fact it was contract B). If + // the next call we are tracing is to contract B, then the ABI we have selected (belonging to A), + // won't have it. In this case we should just continue and try to find the method in other ABIs. + // In that case we should update our mapping, as now we came across a method that's (hopefully) + // unique to contract B. + for correctedContractName, correctedAbi := range a.ContractStore.ABIs { + correctedMethod, abiErr := correctedAbi.MethodById(signature) + if abiErr == nil { + L.Debug(). + Str("Address", address). + Str("Old ABI", contractName). + Str("New ABI", correctedContractName). + Str("Signature", stringSignature). + Msgf("Updating contract mapping as previous one was based on non-unique method signature") + + a.ContractMap.AddContract(address, correctedContractName) + + result.Method = correctedMethod + result.ABI = correctedAbi + result.contractName = correctedContractName + result.DuplicateCount = a.getDuplicateCount(signature) + + return result, nil + } + } + + L.Err(err). + Str("Signature", stringSignature). + Str("Supposed contract", contractName). + Str("Supposed address", address). + Msg("Method not found in known ABI instance. This should not happen. Contract map might be corrupted") + + return ABIFinderResult{}, err + } + + result.Method = methodCandidate + result.ABI = abiInstanceCandidate + result.contractName = contractName + result.DuplicateCount = 0 // we know the exact contract, so the duplicates here do not matter + + return result, nil + } + + // if we do not know what contract is at given address we need to iterate over all known ABIs + // and check if any of them has a method with the given signature (this might gave false positives, + // when more than one contract has the same method signature, but we can't do anything about it) + // In any case this should happen only when we did not deploy the contract via Seth (as otherwise we + // know the address of the contract and can map it to the correct ABI instance). + // If there are duplicates we will use the first ABI that matched. + for abiName, abiInstanceCandidate := range a.ContractStore.ABIs { + methodCandidate, err := abiInstanceCandidate.MethodById(signature) + if err != nil { + L.Trace(). + Err(err). + Str("Signature", stringSignature). + Msg("Method not found") + continue + } + + a.ContractMap.AddContract(address, abiName) + + result.ABI = abiInstanceCandidate + result.Method = methodCandidate + result.contractName = abiName + result.DuplicateCount = a.getDuplicateCount(signature) + + break + } + + if result.Method == nil { + return ABIFinderResult{}, errors.New(ErrNoABIMethod) + } + + return result, nil +} + +func (a *ABIFinder) getDuplicateCount(signature []byte) int { + count := 0 + for _, abiInstance := range a.ContractStore.ABIs { + _, err := abiInstance.MethodById(signature) + if err == nil { + count++ + } + } + + return count - 1 +} diff --git a/seth/block_stats.go b/seth/block_stats.go new file mode 100644 index 000000000..33bfde4a0 --- /dev/null +++ b/seth/block_stats.go @@ -0,0 +1,299 @@ +package seth + +import ( + "context" + "fmt" + "math" + "math/big" + "sort" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/pelletier/go-toml/v2" + "go.uber.org/ratelimit" + "golang.org/x/sync/errgroup" +) + +type BlockStatsConfig struct { + RPCRateLimit int `toml:"rpc_requests_per_second_limit"` +} + +func (cfg *BlockStatsConfig) Validate() error { + if cfg.RPCRateLimit == 0 { + cfg.RPCRateLimit = 3 + } + return nil +} + +// BlockStats is a block stats calculator +type BlockStats struct { + Limiter ratelimit.Limiter + Client *Client +} + +// NewBlockStats creates a new instance of BlockStats +func NewBlockStats(c *Client) (*BlockStats, error) { + return &BlockStats{ + Limiter: ratelimit.New(c.Cfg.BlockStatsConfig.RPCRateLimit, ratelimit.WithoutSlack), + Client: c, + }, nil +} + +// Stats fetches and logs the blocks' statistics from startBlock to endBlock +func (cs *BlockStats) Stats(startBlock *big.Int, endBlock *big.Int) error { + // Get the latest block number if endBlock is nil or if startBlock is negative + var latestBlockNumber *big.Int + if endBlock == nil || startBlock.Sign() < 0 { + header, err := cs.Client.Client.HeaderByNumber(context.Background(), nil) + if err != nil { + return fmt.Errorf("failed to get the latest block header: %v", err) + } + latestBlockNumber = header.Number + } + + // Handle case where startBlock is negative + if startBlock.Sign() < 0 { + startBlock = new(big.Int).Add(latestBlockNumber, startBlock) + } + + if endBlock.Int64() == 0 { + endBlock = latestBlockNumber + } + if endBlock != nil && startBlock.Int64() > endBlock.Int64() { + return fmt.Errorf("start block is less than the end block") + } + L.Info(). + Int64("EndBlock", endBlock.Int64()). + Int64("StartBlock", startBlock.Int64()). + Msg("Calculating stats for blocks interval") + + blocks := make([]*types.Block, 0) + blockMu := &sync.Mutex{} + eg := &errgroup.Group{} + for bn := startBlock.Int64(); bn < endBlock.Int64(); bn++ { + bn := bn + eg.Go(func() error { + cs.Limiter.Take() + block, err := cs.Client.Client.BlockByNumber(context.Background(), big.NewInt(bn)) + if err != nil { + // invalid blocks on some networks, ignore them for now + if strings.Contains(err.Error(), "value overflows uint256") { + L.Error().Err(err).Int64("BlockNumber", bn).Msg("skipped block") + return nil + // that means we need a raw RPC adapter, some chains has block formats that can't be marshalled with + // any version of go-ethereum + } else if strings.Contains(err.Error(), "transaction type not supported") { + L.Error().Err(err).Int64("BlockNumber", bn).Msg("skipped block") + return nil + } + return err + } + blockMu.Lock() + blocks = append(blocks, block) + blockMu.Unlock() + return nil + }) + } + if err := eg.Wait(); err != nil { + return err + } + sort.SliceStable(blocks, func(i, j int) bool { + return blocks[i].Number().Int64() < blocks[j].Number().Int64() + }) + return cs.CalculateBlockDurations(blocks) +} + +// CalculateBlockDurations calculates and logs the duration, TPS, gas used, and gas limit between each consecutive block +func (cs *BlockStats) CalculateBlockDurations(blocks []*types.Block) error { + if len(blocks) == 0 { + return fmt.Errorf("no blocks no analyze") + } + var ( + durations []time.Duration + tpsValues []float64 + gasUsedValues []uint64 + gasLimitValues []uint64 + blockBaseFeeValues []uint64 + blockSizeValues []uint64 + ) + totalDuration := time.Duration(0) + totalTransactions := 0 + totalGasUsed := uint64(0) + totalGasLimit := uint64(0) + totalBaseFee := uint64(0) + totalSize := uint64(0) + + for i := 1; i < len(blocks); i++ { + duration := time.Unix(int64(blocks[i].Time()), 0).Sub(time.Unix(int64(blocks[i-1].Time()), 0)) + durations = append(durations, duration) + totalDuration += duration + + transactions := len(blocks[i].Transactions()) + totalTransactions += transactions + + gasUsed := blocks[i].GasUsed() + gasLimit := blocks[i].GasLimit() + blockBaseFee := blocks[i].BaseFee().Uint64() + blockSize := blocks[i].Size() + gasUsedValues = append(gasUsedValues, gasUsed) + gasLimitValues = append(gasLimitValues, gasLimit) + blockBaseFeeValues = append(blockBaseFeeValues, blockBaseFee) + blockSizeValues = append(blockSizeValues, blockSize) + totalGasUsed += gasUsed + totalGasLimit += gasLimit + totalBaseFee += blockBaseFee + totalSize += blockSize + + var tps float64 + if duration.Seconds() > 0 { + tps = float64(transactions) / duration.Seconds() + } else { + tps = 0 + } + tpsValues = append(tpsValues, tps) + + L.Debug(). + Uint64("BlockNumber", blocks[i].Number().Uint64()). + Time("BlockTime", time.Unix(int64(blocks[i].Time()), 0)). + Str("Duration", duration.String()). + Float64("GasUsedPercentage", calculateRatioPercentage(blocks[i].GasUsed(), blocks[i].GasLimit())). + Float64("TPS", tps). + Uint64("BlockGasFee", blocks[i].BaseFee().Uint64()). + Uint64("BlockGasTip", blocks[i].BaseFee().Uint64()). + Uint64("BlockSize", blocks[i].Size()). + Uint64("GasUsed", gasUsed). + Uint64("GasLimit", gasLimit). + Msg("Block info") + } + + // Calculate average values + averageTPS := float64(totalTransactions) / totalDuration.Seconds() + averageDuration := totalDuration / time.Duration(len(durations)) + averageGasUsed := totalGasUsed / uint64(len(gasUsedValues)) + averageGasLimit := totalGasLimit / uint64(len(gasLimitValues)) + averageBlockBaseFee := totalBaseFee / uint64(len(blockBaseFeeValues)) + averageBlockSize := totalSize / uint64(len(blockSizeValues)) + + // Calculate 95th percentile + sort.Slice(durations, func(i, j int) bool { return durations[i] < durations[j] }) + sort.Float64s(tpsValues) + sort.Slice(gasUsedValues, func(i, j int) bool { return gasUsedValues[i] < gasUsedValues[j] }) + sort.Slice(gasLimitValues, func(i, j int) bool { return gasLimitValues[i] < gasLimitValues[j] }) + sort.Slice(blockBaseFeeValues, func(i, j int) bool { return blockBaseFeeValues[i] < blockBaseFeeValues[j] }) + sort.Slice(blockSizeValues, func(i, j int) bool { return blockSizeValues[i] < blockSizeValues[j] }) + + index95 := int(0.95 * float64(len(durations))) + + percentile95Duration := durations[index95] + percentile95TPS := tpsValues[index95] + percentile95GasUsed := gasUsedValues[index95] + percentile95GasLimit := gasLimitValues[index95] + percentile95BlockBaseFee := blockBaseFeeValues[index95] + percentile95BlockSize := blockSizeValues[index95] + + L.Debug(). + Int("Blocks", len(blocks)). + Float64("AverageTPS", averageTPS). + Dur("AvgBlockDuration", averageDuration). + Uint64("AvgBlockGasUsed", averageGasUsed). + Uint64("AvgBlockGasLimit", averageGasLimit). + Uint64("AvgBlockBaseFee", averageBlockBaseFee). + Uint64("AvgBlockSize", averageBlockSize). + Dur("95thBlockDuration", percentile95Duration). + Float64("95thTPS", percentile95TPS). + Uint64("95thBlockGasUsed", percentile95GasUsed). + Uint64("95thBlockGasLimit", percentile95GasLimit). + Uint64("95thBlockBaseFee", percentile95BlockBaseFee). + Uint64("95thBlockSize", percentile95BlockSize). + Float64("RequiredGasBumpPercentage", calculateRatioPercentage(percentile95BlockBaseFee, averageBlockBaseFee)). + Msg("Summary") + + type stats struct { + Blocks int `toml:"blocks"` + Perc95TPS float64 `toml:"perc_95_tps"` + Perc95BlockDuration string `toml:"perc_95_block_duration"` + Perc95BlockGasUsed uint64 `toml:"perc_95_block_gas_used"` + Perc95BlockGasLimit uint64 `toml:"perc_95_block_gas_limit"` + Perc95BlockBaseFee uint64 `toml:"perc_95_block_base_fee"` + Perc95BlockSize uint64 `toml:"perc_95_block_size"` + AvgTPS float64 `toml:"avg_tps"` + AvgBlockDuration string `toml:"avg_block_duration"` + AvgBlockGasUsed uint64 `toml:"avg_block_gas_used"` + AvgBlockGasLimit uint64 `toml:"avg_block_gas_limit"` + AvgBlockBaseFee uint64 `toml:"avg_block_base_fee"` + AvgBlockSize uint64 `toml:"avg_block_size"` + } + + type performanceTestStats struct { + Duration string `toml:"duration"` + GasInitialValue uint64 `toml:"avg_block_gas_base_fee_initial_value"` + GasBaseFeeBumpPercentage string `toml:"avg_block_gas_base_fee_bump_percentage"` + GasUsagePercentage string `toml:"avg_block_gas_usage_percentage"` + TPSStable float64 `toml:"avg_tps"` + TPSMax float64 `toml:"max_tps"` + } + + tomlCfg := stats{ + Blocks: len(blocks), + Perc95TPS: percentile95TPS, + Perc95BlockDuration: percentile95Duration.String(), + Perc95BlockGasUsed: percentile95GasUsed, + Perc95BlockGasLimit: percentile95GasLimit, + Perc95BlockBaseFee: percentile95BlockBaseFee, + Perc95BlockSize: percentile95BlockSize, + AvgTPS: averageTPS, + AvgBlockDuration: averageDuration.String(), + AvgBlockGasUsed: averageGasUsed, + AvgBlockGasLimit: averageGasLimit, + AvgBlockBaseFee: averageBlockBaseFee, + AvgBlockSize: averageBlockSize, + } + + var bumpMsg string + bump := calculateRatioPercentage(percentile95BlockBaseFee, averageBlockBaseFee) + if bump == 100.0 { + bumpMsg = fmt.Sprintf("%.2f%% (no bump required)", bump) + } else { + bumpMsg = fmt.Sprintf("%.2f%% (multiply)", bump) + } + var blockGasUsagePercentageMsg string + blockGasUsagePerc := calculateRatioPercentage(averageGasUsed, averageGasLimit) + if blockGasUsagePerc >= 100 { + blockGasUsagePercentageMsg = fmt.Sprintf("%.8f%% gas used (network is congested)", blockGasUsagePerc) + } else { + blockGasUsagePercentageMsg = fmt.Sprintf("%.8f%% gas used (no congestion)", blockGasUsagePerc) + } + + perfStats := performanceTestStats{ + Duration: totalDuration.String(), + GasInitialValue: averageBlockBaseFee, + TPSStable: math.Ceil(averageTPS), + TPSMax: math.Ceil(percentile95TPS), + GasUsagePercentage: blockGasUsagePercentageMsg, + GasBaseFeeBumpPercentage: bumpMsg, + } + + marshalled, err := toml.Marshal(tomlCfg) + if err != nil { + return err + } + L.Info().Msgf("Stats:\n%s", string(marshalled)) + + marshalled, err = toml.Marshal(perfStats) + if err != nil { + return err + } + L.Info().Msgf("Recommended performance/chaos test parameters:\n%s", string(marshalled)) + return nil +} + +// calculateRatioPercentage calculates the ratio between two uint64 values and returns it as a percentage +func calculateRatioPercentage(value1, value2 uint64) float64 { + if value2 == 0 { + return 0.0 + } + ratio := float64(value1) / float64(value2) * 100 + return ratio +} diff --git a/seth/client.go b/seth/client.go new file mode 100644 index 000000000..5a75998cd --- /dev/null +++ b/seth/client.go @@ -0,0 +1,1386 @@ +package seth + +import ( + "context" + "crypto/ecdsa" + verr "errors" + "fmt" + "math/big" + "net/http" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/avast/retry-go" + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/rpc" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/pkg/errors" + "github.com/rs/zerolog" + "golang.org/x/sync/errgroup" +) + +const ( + ErrEmptyConfigPath = "toml config path is empty, set SETH_CONFIG_PATH" + ErrCreateABIStore = "failed to create ABI store" + ErrReadingKeys = "failed to read keys" + ErrCreateNonceManager = "failed to create nonce manager" + ErrCreateTracer = "failed to create tracer" + ErrReadContractMap = "failed to read deployed contract map" + ErrNoKeyLoaded = "failed to load private key" + ErrRpcHealthCheckFailed = "RPC health check failed ¯\\_(ツ)_/¯" + ErrContractDeploymentFailed = "contract deployment failed" + + ContractMapFilePattern = "deployed_contracts_%s_%s.toml" + RevertedTransactionsFilePattern = "reverted_transactions_%s_%s.json" +) + +var ( + // Amount of funds that will be left on the root key, when splitting funds between ephemeral addresses + ZeroInt64 int64 + + TracingLevel_None = "NONE" + TracingLevel_Reverted = "REVERTED" + TracingLevel_All = "ALL" + + TraceOutput_Console = "console" + TraceOutput_JSON = "json" + TraceOutput_DOT = "dot" +) + +// Client is a vanilla go-ethereum client with enhanced debug logging +type Client struct { + Cfg *Config + Client *ethclient.Client + Addresses []common.Address + PrivateKeys []*ecdsa.PrivateKey + ChainID int64 + URL string + Context context.Context + CancelFunc context.CancelFunc + Errors []error + ContractStore *ContractStore + NonceManager *NonceManager + Tracer *Tracer + ContractAddressToNameMap ContractMap + ABIFinder *ABIFinder + HeaderCache *LFUHeaderCache +} + +// NewClientWithConfig creates a new seth client with all deps setup from config +func NewClientWithConfig(cfg *Config) (*Client, error) { + initDefaultLogging() + + err := ValidateConfig(cfg) + if err != nil { + return nil, err + } + + L.Debug().Msgf("Using tracing level: %s", cfg.TracingLevel) + + cfg.setEphemeralAddrs() + cs, err := NewContractStore(filepath.Join(cfg.ConfigDir, cfg.ABIDir), filepath.Join(cfg.ConfigDir, cfg.BINDir)) + if err != nil { + return nil, errors.Wrap(err, ErrCreateABIStore) + } + if cfg.ephemeral { + // we don't care about any other keys, only the root key + // you should not use ephemeral mode with more than 1 key + if len(cfg.Network.PrivateKeys) > 1 { + L.Warn().Msg("Ephemeral mode is enabled, but more than 1 key is loaded. Only the first key will be used") + } + cfg.Network.PrivateKeys = cfg.Network.PrivateKeys[:1] + pkeys, err := NewEphemeralKeys(*cfg.EphemeralAddrs) + if err != nil { + return nil, err + } + cfg.Network.PrivateKeys = append(cfg.Network.PrivateKeys, pkeys...) + } + addrs, pkeys, err := cfg.ParseKeys() + if err != nil { + return nil, errors.Wrap(err, ErrReadingKeys) + } + nm, err := NewNonceManager(cfg, addrs, pkeys) + if err != nil { + return nil, errors.Wrap(err, ErrCreateNonceManager) + } + + if !cfg.IsSimulatedNetwork() && cfg.SaveDeployedContractsMap && cfg.ContractMapFile == "" { + cfg.ContractMapFile = cfg.GenerateContractMapFileName() + } + + // this part is kind of duplicated in NewClientRaw, but we need to create contract map before creating Tracer + // so that both the tracer and client have references to the same map + contractAddressToNameMap := NewEmptyContractMap() + contractAddressToNameMap.addressMap = make(map[string]string) + if !cfg.IsSimulatedNetwork() { + contractAddressToNameMap.addressMap, err = LoadDeployedContracts(cfg.ContractMapFile) + if err != nil { + return nil, errors.Wrap(err, ErrReadContractMap) + } + } else { + L.Debug().Msg("Simulated network, contract map won't be read from file") + } + + abiFinder := NewABIFinder(contractAddressToNameMap, cs) + if len(cfg.Network.URLs) == 0 { + return nil, fmt.Errorf("at least one url should be present in config in 'secret_urls = []'") + } + tr, err := NewTracer(cs, &abiFinder, cfg, contractAddressToNameMap, addrs) + if err != nil { + return nil, errors.Wrap(err, ErrCreateTracer) + } + + return NewClientRaw( + cfg, + addrs, + pkeys, + WithContractStore(cs), + WithNonceManager(nm), + WithTracer(tr), + WithContractMap(contractAddressToNameMap), + WithABIFinder(&abiFinder), + ) +} + +func ValidateConfig(cfg *Config) error { + if cfg.Network.GasPriceEstimationEnabled { + if cfg.Network.GasPriceEstimationBlocks == 0 { + return errors.New("when automating gas estimation is enabled blocks must be greater than 0. fix it or disable gas estimation") + } + cfg.Network.GasPriceEstimationTxPriority = strings.ToLower(cfg.Network.GasPriceEstimationTxPriority) + + if cfg.Network.GasPriceEstimationTxPriority == "" { + cfg.Network.GasPriceEstimationTxPriority = Priority_Standard + } + + switch cfg.Network.GasPriceEstimationTxPriority { + case Priority_Degen: + case Priority_Fast: + case Priority_Standard: + case Priority_Slow: + default: + return errors.New("when automating gas estimation is enabled priority must be fast, standard or slow. fix it or disable gas estimation") + } + + } + + if cfg.Network.GasLimit != 0 { + L.Warn(). + Msg("Gas limit is set, this will override the gas limit set by the network. This option should be used **ONLY** if node is incapable of estimating gas limit itself, which happens only with very old versions") + } + + if cfg.TracingLevel == "" { + cfg.TracingLevel = TracingLevel_Reverted + } + + cfg.TracingLevel = strings.ToUpper(cfg.TracingLevel) + + switch cfg.TracingLevel { + case TracingLevel_None: + case TracingLevel_Reverted: + case TracingLevel_All: + default: + return errors.New("tracing level must be one of: NONE, REVERTED, ALL") + } + + for _, output := range cfg.TraceOutputs { + switch strings.ToLower(output) { + case TraceOutput_Console: + case TraceOutput_JSON: + case TraceOutput_DOT: + default: + return errors.New("trace output must be one of: console, json, dot") + } + } + + if cfg.Network.DialTimeout == nil { + cfg.Network.DialTimeout = &Duration{D: DefaultDialTimeout} + } + + return nil +} + +// NewClient creates a new raw seth client with all deps setup from env vars +func NewClient() (*Client, error) { + cfg, err := ReadConfig() + if err != nil { + return nil, err + } + return NewClientWithConfig(cfg) +} + +// NewClientRaw creates a new raw seth client without dependencies +func NewClientRaw( + cfg *Config, + addrs []common.Address, + pkeys []*ecdsa.PrivateKey, + opts ...ClientOpt, +) (*Client, error) { + if len(cfg.Network.URLs) == 0 { + return nil, errors.New("no RPC URL provided") + } + if len(cfg.Network.URLs) > 1 { + L.Warn().Msg("Multiple RPC URLs provided, only the first one will be used") + } + ctx, cancel := context.WithTimeout(context.Background(), cfg.Network.DialTimeout.Duration()) + defer cancel() + rpcClient, err := rpc.DialOptions(ctx, + cfg.FirstNetworkURL(), + rpc.WithHeaders(cfg.RPCHeaders), + rpc.WithHTTPClient(&http.Client{ + Transport: NewLoggingTransport(), + }), + ) + if err != nil { + return nil, fmt.Errorf("failed to connect RPC client to '%s' due to: %w", cfg.FirstNetworkURL(), err) + } + client := ethclient.NewClient(rpcClient) + + chainId, err := client.ChainID(context.Background()) + if err != nil { + return nil, errors.Wrap(err, "failed to get chain ID") + } + cfg.Network.ChainID = chainId.String() + cID, err := strconv.Atoi(cfg.Network.ChainID) + if err != nil { + return nil, err + } + ctx, cancelFunc := context.WithCancel(context.Background()) + c := &Client{ + Cfg: cfg, + Client: client, + Addresses: addrs, + PrivateKeys: pkeys, + URL: cfg.FirstNetworkURL(), + ChainID: int64(cID), + Context: ctx, + CancelFunc: cancelFunc, + } + for _, o := range opts { + o(c) + } + + if c.ContractAddressToNameMap.addressMap == nil { + c.ContractAddressToNameMap = NewEmptyContractMap() + if !cfg.IsSimulatedNetwork() { + c.ContractAddressToNameMap.addressMap, err = LoadDeployedContracts(cfg.ContractMapFile) + if err != nil { + return nil, errors.Wrap(err, ErrReadContractMap) + } + if len(c.ContractAddressToNameMap.addressMap) > 0 { + L.Info(). + Int("Size", len(c.ContractAddressToNameMap.addressMap)). + Str("File name", cfg.ContractMapFile). + Msg("No contract map provided, read it from file") + } else { + L.Info(). + Msg("No contract map provided and no file found, created new one") + } + } else { + L.Debug().Msg("Simulated network, contract map won't be read from file") + L.Info(). + Msg("No contract map provided and no file found, created new one") + } + } else { + L.Info(). + Int("Size", len(c.ContractAddressToNameMap.addressMap)). + Msg("Contract map was provided") + } + if c.NonceManager != nil { + c.NonceManager.Client = c + if len(c.Cfg.Network.PrivateKeys) > 0 { + if err := c.NonceManager.UpdateNonces(); err != nil { + return nil, err + } + } + } + + if cfg.CheckRpcHealthOnStart { + if c.NonceManager == nil { + L.Warn().Msg("Nonce manager is not set, RPC health check will be skipped. Client will most probably fail on first transaction") + } else { + if err := c.checkRPCHealth(); err != nil { + return nil, err + } + } + } + + cfg.setEphemeralAddrs() + + L.Info(). + Str("NetworkName", cfg.Network.Name). + Interface("Addresses", addrs). + Str("RPC", cfg.FirstNetworkURL()). + Str("ChainID", cfg.Network.ChainID). + Int64("Ephemeral keys", *cfg.EphemeralAddrs). + Msg("Created new client") + + if cfg.ephemeral { + gasPrice, err := c.GetSuggestedLegacyFees(context.Background(), Priority_Standard) + if err != nil { + gasPrice = big.NewInt(c.Cfg.Network.GasPrice) + } + + bd, err := c.CalculateSubKeyFunding(*cfg.EphemeralAddrs, gasPrice.Int64(), *cfg.RootKeyFundsBuffer) + if err != nil { + return nil, err + } + L.Warn().Msg("Ephemeral mode, all funds will be lost!") + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + eg, egCtx := errgroup.WithContext(ctx) + // root key is element 0 in ephemeral + for _, addr := range c.Addresses[1:] { + addr := addr + eg.Go(func() error { + return c.TransferETHFromKey(egCtx, 0, addr.Hex(), bd.AddrFunding, gasPrice) + }) + } + if err := eg.Wait(); err != nil { + return nil, err + } + } + + if c.Cfg.TracingLevel != TracingLevel_None && c.Tracer == nil { + if c.ContractStore == nil { + cs, err := NewContractStore(filepath.Join(cfg.ConfigDir, cfg.ABIDir), filepath.Join(cfg.ConfigDir, cfg.BINDir)) + if err != nil { + return nil, errors.Wrap(err, ErrCreateABIStore) + } + c.ContractStore = cs + } + if c.ABIFinder == nil { + abiFinder := NewABIFinder(c.ContractAddressToNameMap, c.ContractStore) + c.ABIFinder = &abiFinder + } + tr, err := NewTracer(c.ContractStore, c.ABIFinder, cfg, c.ContractAddressToNameMap, addrs) + if err != nil { + return nil, errors.Wrap(err, ErrCreateTracer) + } + + c.Tracer = tr + } + + now := time.Now().Format("2006-01-02-15-04-05") + c.Cfg.revertedTransactionsFile = filepath.Join(c.Cfg.ArtifactsDir, fmt.Sprintf(RevertedTransactionsFilePattern, c.Cfg.Network.Name, now)) + + if c.Cfg.Network.GasPriceEstimationEnabled { + L.Debug().Msg("Gas estimation is enabled") + L.Debug().Msg("Initializing LFU block header cache") + c.HeaderCache = NewLFUBlockCache(c.Cfg.Network.GasPriceEstimationBlocks) + + if c.Cfg.Network.EIP1559DynamicFees { + L.Debug().Msg("Checking if EIP-1559 is supported by the network") + c.CalculateGasEstimations(GasEstimationRequest{ + GasEstimationEnabled: true, + FallbackGasPrice: c.Cfg.Network.GasPrice, + FallbackGasFeeCap: c.Cfg.Network.GasFeeCap, + FallbackGasTipCap: c.Cfg.Network.GasTipCap, + Priority: Priority_Standard, + }) + } + } + + // if gas bumping is enabled, but no strategy is set, we set the default one; otherwise we set the no-op strategy (defensive programming to avoid NPE) + if c.Cfg.GasBump != nil && c.Cfg.GasBump.StrategyFn == nil { + if c.Cfg.GasBumpRetries() != 0 { + c.Cfg.GasBump.StrategyFn = PriorityBasedGasBumpingStrategyFn(c.Cfg.Network.GasPriceEstimationTxPriority) + } else { + c.Cfg.GasBump.StrategyFn = NoOpGasBumpStrategyFn + } + } + + return c, nil +} + +func (m *Client) checkRPCHealth() error { + L.Info().Str("RPC node", m.URL).Msg("---------------- !!!!! ----------------> Checking RPC health") + ctx, cancel := context.WithTimeout(context.Background(), m.Cfg.Network.TxnTimeout.Duration()) + defer cancel() + + gasPrice, err := m.GetSuggestedLegacyFees(context.Background(), Priority_Standard) + if err != nil { + gasPrice = big.NewInt(m.Cfg.Network.GasPrice) + } + + err = m.TransferETHFromKey(ctx, 0, m.Addresses[0].Hex(), big.NewInt(10_000), gasPrice) + if err != nil { + return errors.Wrap(err, ErrRpcHealthCheckFailed) + } + + L.Info().Msg("RPC health check passed <---------------- !!!!! ----------------") + return nil +} + +// Decode waits for transaction to be minted, then decodes transaction inputs, outputs, logs and events and +// depending on 'tracing_level' it either returns immediately or if the level matches it traces all calls. +// Where tracing results go depends on the 'trace_outputs' field in the config. +// If transaction was reverted the error returned will be revert error, not decoding error (that one, if any, will be logged). +// At the same time we also return decoded transaction, so contrary to go convention you might get both error and result. +// Last, but not least, if gas bumps are enabled, we will try to bump gas on transaction timeout and resubmit it with higher gas. +func (m *Client) Decode(tx *types.Transaction, txErr error) (*DecodedTransaction, error) { + if len(m.Errors) > 0 { + return nil, verr.Join(m.Errors...) + } + + // do not try to decode ABI error if contract deployment failed, because the error is not related to ABI + if txErr != nil { + //try to decode revert reason + reason, decodingErr := m.DecodeCustomABIErr(txErr) + + if decodingErr == nil { + return nil, errors.Wrap(txErr, reason) + } + + L.Trace(). + Msg("Skipping decoding, transaction submission failed. Nothing to decode") + return nil, txErr + } + + if tx == nil { + L.Trace(). + Msg("Skipping decoding, because transaction is nil. Nothing to decode") + return nil, nil + } + + l := L.With().Str("Transaction", tx.Hash().Hex()).Logger() + + // if transaction was not mined, we will retry it with gas bumping, but only if gas bumping is enabled + // and if the transaction was not mined in time, other errors will be returned as is + var receipt *types.Receipt + err := retry.Do( + func() error { + var err error + ctx, cancel := context.WithTimeout(context.Background(), m.Cfg.Network.TxnTimeout.Duration()) + receipt, err = m.WaitMined(ctx, l, m.Client, tx) + cancel() + + return err + }, retry.OnRetry(func(i uint, retryErr error) { + replacementTx, replacementErr := prepareReplacementTransaction(m, tx) + if replacementErr != nil { + L.Debug().Str("Replacement error", replacementErr.Error()).Str("Current error", retryErr.Error()).Uint("Attempt", i).Msg("Failed to prepare replacement transaction. Retrying without the original one") + return + } + L.Debug().Str("Current error", retryErr.Error()).Uint("Attempt", i).Msg("Waiting for transaction to be confirmed after gas bump") + tx = replacementTx + }), + retry.DelayType(retry.FixedDelay), + // unless attempts is at least 1 retry.Do won't execute at all + retry.Attempts(func() uint { + if m.Cfg.GasBumpRetries() == 0 { + return 1 + } + return m.Cfg.GasBumpRetries() + }()), + retry.RetryIf(func(err error) bool { + return m.Cfg.GasBumpRetries() != 0 && errors.Is(err, context.DeadlineExceeded) + }), + ) + + if err != nil { + L.Trace(). + Err(err). + Msg("Skipping decoding, because transaction was not minted. Nothing to decode") + return nil, err + } + + var revertErr error + if receipt.Status == 0 { + revertErr = m.callAndGetRevertReason(tx, receipt) + } + + decoded, decodeErr := m.decodeTransaction(l, tx, receipt) + + if decodeErr != nil && errors.Is(decodeErr, errors.New(ErrNoABIMethod)) { + if m.Cfg.hasOutput(TraceOutput_JSON) { + L.Trace(). + Err(decodeErr). + Msg("Failed to decode transaction. Saving transaction data hash as JSON") + + err = CreateOrAppendToJsonArray(m.Cfg.revertedTransactionsFile, tx.Hash().Hex()) + if err != nil { + l.Warn(). + Err(err). + Str("TXHash", tx.Hash().Hex()). + Msg("Failed to save reverted transaction hash to file") + } else { + l.Trace(). + Str("TXHash", tx.Hash().Hex()). + Msg("Saved reverted transaction to file") + } + } + m.printDecodedTXData(l, decoded) + return decoded, revertErr + } + + if m.Cfg.TracingLevel == TracingLevel_None { + L.Trace(). + Str("Transaction Hash", tx.Hash().Hex()). + Msg("Tracing level is NONE, skipping decoding") + m.printDecodedTXData(l, decoded) + return decoded, revertErr + } + + if m.Cfg.TracingLevel == TracingLevel_All || (m.Cfg.TracingLevel == TracingLevel_Reverted && revertErr != nil) { + traceErr := m.Tracer.TraceGethTX(decoded.Hash, revertErr) + if traceErr != nil { + if m.Cfg.hasOutput(TraceOutput_JSON) { + L.Trace(). + Err(traceErr). + Msg("Failed to trace call, but decoding was successful. Saving decoded data as JSON") + + path, saveErr := saveAsJson(decoded, filepath.Join(m.Cfg.ArtifactsDir, "traces"), decoded.Hash) + if saveErr != nil { + L.Warn(). + Err(saveErr). + Msg("Failed to save decoded call as JSON") + } else { + L.Trace(). + Str("Path", path). + Str("Tx hash", decoded.Hash). + Msg("Saved decoded transaction data to JSON") + } + } + + if strings.Contains(traceErr.Error(), "debug_traceTransaction does not exist") { + L.Warn(). + Err(err). + Msg("Debug API is either disabled or not available on the node. Disabling tracing") + + m.Cfg.TracingLevel = TracingLevel_None + } + + m.printDecodedTXData(l, decoded) + return decoded, revertErr + } + + if m.Cfg.hasOutput(TraceOutput_JSON) { + path, saveErr := saveAsJson(m.Tracer.GetDecodedCalls(decoded.Hash), filepath.Join(m.Cfg.ArtifactsDir, "traces"), decoded.Hash) + if saveErr != nil { + L.Warn(). + Err(saveErr). + Msg("Failed to save decoded call as JSON") + } else { + L.Trace(). + Str("Path", path). + Str("Tx hash", decoded.Hash). + Msg("Saved decoded call data to JSON") + } + } + } else { + L.Trace(). + Str("Transaction Hash", tx.Hash().Hex()). + Str("Tracing level", m.Cfg.TracingLevel). + Bool("Was reverted?", revertErr != nil). + Msg("Transaction doesn't match tracing level, skipping decoding") + } + + return decoded, revertErr +} + +func (m *Client) TransferETHFromKey(ctx context.Context, fromKeyNum int, to string, value *big.Int, gasPrice *big.Int) error { + if fromKeyNum > len(m.PrivateKeys) || fromKeyNum > len(m.Addresses) { + return errors.Wrap(errors.New(ErrNoKeyLoaded), fmt.Sprintf("requested key: %d", fromKeyNum)) + } + toAddr := common.HexToAddress(to) + ctx, chainCancel := context.WithTimeout(ctx, m.Cfg.Network.TxnTimeout.Duration()) + defer chainCancel() + + chainID, err := m.Client.NetworkID(ctx) + if err != nil { + return errors.Wrap(err, "failed to get network ID") + } + + var gasLimit int64 + //nolint + gasLimitRaw, err := m.EstimateGasLimitForFundTransfer(m.Addresses[fromKeyNum], common.HexToAddress(to), value) + if err != nil { + gasLimit = m.Cfg.Network.TransferGasFee + } else { + gasLimit = int64(gasLimitRaw) + } + + if gasPrice == nil { + gasPrice = big.NewInt(m.Cfg.Network.GasPrice) + } + + rawTx := &types.LegacyTx{ + Nonce: m.NonceManager.NextNonce(m.Addresses[fromKeyNum]).Uint64(), + To: &toAddr, + Value: value, + Gas: uint64(gasLimit), + GasPrice: gasPrice, + } + L.Debug().Interface("TransferTx", rawTx).Send() + signedTx, err := types.SignNewTx(m.PrivateKeys[fromKeyNum], types.NewEIP155Signer(chainID), rawTx) + if err != nil { + return errors.Wrap(err, "failed to sign tx") + } + + ctx, sendCancel := context.WithTimeout(ctx, m.Cfg.Network.TxnTimeout.Duration()) + defer sendCancel() + err = m.Client.SendTransaction(ctx, signedTx) + if err != nil { + return errors.Wrap(err, "failed to send transaction") + } + l := L.With().Str("Transaction", signedTx.Hash().Hex()).Logger() + l.Info(). + Int("FromKeyNum", fromKeyNum). + Str("To", to). + Interface("Value", value). + Msg("Send ETH") + _, err = m.WaitMined(ctx, l, m.Client, signedTx) + if err != nil { + return err + } + return err +} + +// WaitMined the same as bind.WaitMined, awaits transaction receipt until timeout +func (m *Client) WaitMined(ctx context.Context, l zerolog.Logger, b bind.DeployBackend, tx *types.Transaction) (*types.Receipt, error) { + queryTicker := time.NewTicker(time.Second) + defer queryTicker.Stop() + ctx, cancel := context.WithTimeout(ctx, m.Cfg.Network.TxnTimeout.Duration()) + defer cancel() + for { + receipt, err := b.TransactionReceipt(ctx, tx.Hash()) + if err == nil { + l.Info(). + Int64("BlockNumber", receipt.BlockNumber.Int64()). + Str("TX", tx.Hash().String()). + Msg("Transaction receipt found") + return receipt, nil + } else if errors.Is(err, ethereum.NotFound) { + l.Debug(). + Str("TX", tx.Hash().String()). + Msg("Awaiting transaction") + } else { + l.Warn(). + Err(err). + Str("TX", tx.Hash().String()). + Msg("Failed to get receipt") + } + select { + case <-ctx.Done(): + l.Error().Err(err).Msg("Transaction context is done") + return nil, ctx.Err() + case <-queryTicker.C: + } + } +} + +/* ClientOpts client functional options */ + +// ClientOpt is a client functional option +type ClientOpt func(c *Client) + +// WithContractStore ContractStore functional option +func WithContractStore(as *ContractStore) ClientOpt { + return func(c *Client) { + c.ContractStore = as + } +} + +// WithContractMap contractAddressToNameMap functional option +func WithContractMap(contractAddressToNameMap ContractMap) ClientOpt { + return func(c *Client) { + c.ContractAddressToNameMap = contractAddressToNameMap + } +} + +// WithABIFinder ABIFinder functional option +func WithABIFinder(abiFinder *ABIFinder) ClientOpt { + return func(c *Client) { + c.ABIFinder = abiFinder + } +} + +// WithNonceManager NonceManager functional option +func WithNonceManager(nm *NonceManager) ClientOpt { + return func(c *Client) { + c.NonceManager = nm + } +} + +// WithTracer Tracer functional option +func WithTracer(t *Tracer) ClientOpt { + return func(c *Client) { + c.Tracer = t + } +} + +/* CallOpts function options */ + +// CallOpt is a functional option for bind.CallOpts +type CallOpt func(o *bind.CallOpts) + +// WithPending sets pending option for bind.CallOpts +func WithPending(pending bool) CallOpt { + return func(o *bind.CallOpts) { + o.Pending = pending + } +} + +// WithBlockNumber sets blockNumber option for bind.CallOpts +func WithBlockNumber(bn uint64) CallOpt { + return func(o *bind.CallOpts) { + o.BlockNumber = big.NewInt(int64(bn)) + } +} + +// NewCallOpts returns a new sequential call options wrapper +func (m *Client) NewCallOpts(o ...CallOpt) *bind.CallOpts { + co := &bind.CallOpts{ + Pending: false, + From: m.Addresses[0], + } + for _, f := range o { + f(co) + } + return co +} + +// NewCallKeyOpts returns a new sequential call options wrapper from the key N +func (m *Client) NewCallKeyOpts(keyNum int, o ...CallOpt) *bind.CallOpts { + co := &bind.CallOpts{ + Pending: false, + From: m.Addresses[keyNum], + } + for _, f := range o { + f(co) + } + return co +} + +// TransactOpt is a wrapper for bind.TransactOpts +type TransactOpt func(o *bind.TransactOpts) + +// WithValue sets value option for bind.TransactOpts +func WithValue(value *big.Int) TransactOpt { + return func(o *bind.TransactOpts) { + o.Value = value + } +} + +// WithGasPrice sets gasPrice option for bind.TransactOpts +func WithGasPrice(gasPrice *big.Int) TransactOpt { + return func(o *bind.TransactOpts) { + o.GasPrice = gasPrice + } +} + +// WithGasLimit sets gasLimit option for bind.TransactOpts +func WithGasLimit(gasLimit uint64) TransactOpt { + return func(o *bind.TransactOpts) { + o.GasLimit = gasLimit + } +} + +// WithNoSend sets noSend option for bind.TransactOpts +func WithNoSend(noSend bool) TransactOpt { + return func(o *bind.TransactOpts) { + o.NoSend = noSend + } +} + +// WithNonce sets nonce option for bind.TransactOpts +func WithNonce(nonce *big.Int) TransactOpt { + return func(o *bind.TransactOpts) { + o.Nonce = nonce + } +} + +// WithGasFeeCap sets gasFeeCap option for bind.TransactOpts +func WithGasFeeCap(gasFeeCap *big.Int) TransactOpt { + return func(o *bind.TransactOpts) { + o.GasFeeCap = gasFeeCap + } +} + +// WithGasTipCap sets gasTipCap option for bind.TransactOpts +func WithGasTipCap(gasTipCap *big.Int) TransactOpt { + return func(o *bind.TransactOpts) { + o.GasTipCap = gasTipCap + } +} + +type ContextErrorKey struct{} + +// NewTXOpts returns a new transaction options wrapper, +// Sets gas price/fee tip/cap and gas limit either based on TOML config or estimations. +func (m *Client) NewTXOpts(o ...TransactOpt) *bind.TransactOpts { + opts, nonce, estimations := m.getProposedTransactionOptions(0) + m.configureTransactionOpts(opts, nonce.PendingNonce, estimations, o...) + L.Debug(). + Interface("Nonce", opts.Nonce). + Interface("Value", opts.Value). + Interface("GasPrice", opts.GasPrice). + Interface("GasFeeCap", opts.GasFeeCap). + Interface("GasTipCap", opts.GasTipCap). + Uint64("GasLimit", opts.GasLimit). + Msg("New transaction options") + return opts +} + +// NewTXKeyOpts returns a new transaction options wrapper, +// sets opts.GasPrice and opts.GasLimit from seth.toml or override with options +func (m *Client) NewTXKeyOpts(keyNum int, o ...TransactOpt) *bind.TransactOpts { + if keyNum > len(m.Addresses) || keyNum < 0 { + errText := fmt.Sprintf("keyNum is out of range. Expected %d-%d. Got: %d", 0, len(m.Addresses)-1, keyNum) + if keyNum == TimeoutKeyNum { + errText += " (this is a probably because, we didn't manage to find any synced key before timeout)" + } + + err := errors.New(errText) + m.Errors = append(m.Errors, err) + opts := &bind.TransactOpts{} + + // can't return nil, otherwise RPC wrapper will panic and we might lose funds on testnets/mainnets, that's why + // error is passed in Context here to avoid panic, whoever is using Seth should make sure that there is no error + // present in Context before using *bind.TransactOpts + opts.Context = context.WithValue(context.Background(), ContextErrorKey{}, err) + + return opts + } + L.Debug(). + Interface("KeyNum", keyNum). + Interface("Address", m.Addresses[keyNum]). + Msg("Estimating transaction") + opts, nonceStatus, estimations := m.getProposedTransactionOptions(keyNum) + + m.configureTransactionOpts(opts, nonceStatus.PendingNonce, estimations, o...) + L.Debug(). + Interface("KeyNum", keyNum). + Interface("Nonce", opts.Nonce). + Interface("Value", opts.Value). + Interface("GasPrice", opts.GasPrice). + Interface("GasFeeCap", opts.GasFeeCap). + Interface("GasTipCap", opts.GasTipCap). + Uint64("GasLimit", opts.GasLimit). + Msg("New transaction options") + return opts +} + +// AnySyncedKey returns the first synced key +func (m *Client) AnySyncedKey() int { + return m.NonceManager.anySyncedKey() +} + +type GasEstimations struct { + GasPrice *big.Int + GasTipCap *big.Int + GasFeeCap *big.Int +} + +type NonceStatus struct { + LastNonce uint64 + PendingNonce uint64 +} + +func (m *Client) getNonceStatus(address common.Address) (NonceStatus, error) { + ctx, cancel := context.WithTimeout(context.Background(), m.Cfg.Network.TxnTimeout.Duration()) + defer cancel() + pendingNonce, err := m.Client.PendingNonceAt(ctx, address) + if err != nil { + L.Error().Err(err).Msg("Failed to get pending nonce") + return NonceStatus{}, err + } + + lastNonce, err := m.Client.NonceAt(ctx, address, nil) + if err != nil { + return NonceStatus{}, err + } + + return NonceStatus{ + LastNonce: lastNonce, + PendingNonce: pendingNonce, + }, nil +} + +// getProposedTransactionOptions gets all the tx info that network proposed +func (m *Client) getProposedTransactionOptions(keyNum int) (*bind.TransactOpts, NonceStatus, GasEstimations) { + nonceStatus, err := m.getNonceStatus(m.Addresses[keyNum]) + if err != nil { + m.Errors = append(m.Errors, err) + // can't return nil, otherwise RPC wrapper will panic + ctx := context.WithValue(context.Background(), ContextErrorKey{}, err) + + return &bind.TransactOpts{Context: ctx}, NonceStatus{}, GasEstimations{} + } + + var ctx context.Context + + if m.Cfg.PendingNonceProtectionEnabled { + if nonceStatus.PendingNonce > nonceStatus.LastNonce { + errMsg := ` +pending nonce for key %d is higher than last nonce, there are %d pending transactions. + +This issue is caused by one of two things: +1. You are using the same keyNum in multiple goroutines, which is not supported. Each goroutine should use an unique keyNum. +2. You have stuck transaction(s). Speed them up by sending replacement transactions with higher gas price before continuing, otherwise future transactions most probably will also get stuck. +` + err := fmt.Errorf(errMsg, keyNum, nonceStatus.PendingNonce-nonceStatus.LastNonce) + m.Errors = append(m.Errors, err) + // can't return nil, otherwise RPC wrapper will panic and we might lose funds on testnets/mainnets, that's why + // error is passed in Context here to avoid panic, whoever is using Seth should make sure that there is no error + // present in Context before using *bind.TransactOpts + ctx = context.WithValue(context.Background(), ContextErrorKey{}, err) + } + L.Debug(). + Msg("Pending nonce protection is enabled. Nonce status is OK") + } + + estimations := m.CalculateGasEstimations(m.NewDefaultGasEstimationRequest()) + + L.Debug(). + Interface("KeyNum", keyNum). + Uint64("Nonce", nonceStatus.PendingNonce). + Interface("GasEstimations", estimations). + Msg("Proposed transaction options") + + opts, err := bind.NewKeyedTransactorWithChainID(m.PrivateKeys[keyNum], big.NewInt(m.ChainID)) + if err != nil { + err = errors.Wrapf(err, "failed to create transactor for key %d", keyNum) + m.Errors = append(m.Errors, err) + // can't return nil, otherwise RPC wrapper will panic and we might lose funds on testnets/mainnets, that's why + // error is passed in Context here to avoid panic, whoever is using Seth should make sure that there is no error + // present in Context before using *bind.TransactOpts + ctx := context.WithValue(context.Background(), ContextErrorKey{}, err) + + return &bind.TransactOpts{Context: ctx}, NonceStatus{}, GasEstimations{} + } + + if ctx != nil { + opts.Context = ctx + } + + return opts, nonceStatus, estimations +} + +type GasEstimationRequest struct { + GasEstimationEnabled bool + FallbackGasPrice int64 + FallbackGasFeeCap int64 + FallbackGasTipCap int64 + Priority string +} + +// NewDefaultGasEstimationRequest creates a new default gas estimation request based on current network configuration +func (m *Client) NewDefaultGasEstimationRequest() GasEstimationRequest { + return GasEstimationRequest{ + GasEstimationEnabled: m.Cfg.Network.GasPriceEstimationEnabled, + FallbackGasPrice: m.Cfg.Network.GasPrice, + FallbackGasFeeCap: m.Cfg.Network.GasFeeCap, + FallbackGasTipCap: m.Cfg.Network.GasTipCap, + Priority: m.Cfg.Network.GasPriceEstimationTxPriority, + } +} + +// CalculateGasEstimations calculates gas estimations (price, tip/cap) or uses hardcoded values if estimation is disabled, +// estimation errors or network is a simulated one. +func (m *Client) CalculateGasEstimations(request GasEstimationRequest) GasEstimations { + estimations := GasEstimations{} + + if m.Cfg.IsSimulatedNetwork() || !request.GasEstimationEnabled { + estimations.GasPrice = big.NewInt(request.FallbackGasPrice) + estimations.GasFeeCap = big.NewInt(request.FallbackGasFeeCap) + estimations.GasTipCap = big.NewInt(request.FallbackGasTipCap) + + return estimations + } + + ctx, cancel := context.WithTimeout(context.Background(), m.Cfg.Network.TxnTimeout.Duration()) + defer cancel() + + var disableEstimationsIfNeeded = func(err error) { + if strings.Contains(err.Error(), ZeroGasSuggestedErr) { + L.Warn().Msg("Received incorrect gas estimations. Disabling them and reverting to hardcoded values. Remember to update your config!") + m.Cfg.Network.GasPriceEstimationEnabled = false + } + } + + var calculateLegacyFees = func() { + gasPrice, err := m.GetSuggestedLegacyFees(ctx, request.Priority) + if err != nil { + disableEstimationsIfNeeded(err) + L.Warn().Err(err).Msg("Failed to get suggested Legacy fees. Using hardcoded values") + estimations.GasPrice = big.NewInt(request.FallbackGasPrice) + } else { + estimations.GasPrice = gasPrice + } + } + + if m.Cfg.Network.EIP1559DynamicFees { + maxFee, priorityFee, err := m.GetSuggestedEIP1559Fees(ctx, request.Priority) + if err != nil { + L.Warn().Err(err).Msg("Failed to get suggested EIP1559 fees. Using hardcoded values") + estimations.GasFeeCap = big.NewInt(request.FallbackGasFeeCap) + estimations.GasTipCap = big.NewInt(request.FallbackGasTipCap) + + disableEstimationsIfNeeded(err) + + if strings.Contains(err.Error(), "method eth_maxPriorityFeePerGas") || strings.Contains(err.Error(), "method eth_maxFeePerGas") || strings.Contains(err.Error(), "method eth_feeHistory") || strings.Contains(err.Error(), "expected input list for types.txdata") { + L.Warn().Msg("EIP1559 fees are not supported by the network. Switching to Legacy fees. Remember to update your config!") + if m.Cfg.Network.GasPrice == 0 { + L.Warn().Msg("Gas price is 0. If Legacy estimations fail, there will no fallback price and transactions will start fail. Set gas price in config and disable EIP1559DynamicFees") + } + m.Cfg.Network.EIP1559DynamicFees = false + calculateLegacyFees() + } + } else { + estimations.GasFeeCap = maxFee + estimations.GasTipCap = priorityFee + } + } else { + calculateLegacyFees() + } + + return estimations +} + +// EstimateGasLimitForFundTransfer estimates gas limit for fund transfer +func (m *Client) EstimateGasLimitForFundTransfer(from, to common.Address, amount *big.Int) (uint64, error) { + ctx, cancel := context.WithTimeout(context.Background(), m.Cfg.Network.TxnTimeout.Duration()) + defer cancel() + gasLimit, err := m.Client.EstimateGas(ctx, ethereum.CallMsg{ + From: from, + To: &to, + Value: amount, + }) + if err != nil { + L.Warn().Err(err).Msg("Failed to estimate gas for fund transfer.") + return 0, errors.Wrapf(err, "failed to estimate gas for fund transfer") + } + return gasLimit, nil +} + +// configureTransactionOpts configures transaction for legacy or type-2 +func (m *Client) configureTransactionOpts( + opts *bind.TransactOpts, + nonce uint64, + estimations GasEstimations, + o ...TransactOpt, +) *bind.TransactOpts { + opts.Nonce = big.NewInt(int64(nonce)) + opts.GasPrice = estimations.GasPrice + opts.GasLimit = m.Cfg.Network.GasLimit + + if m.Cfg.Network.EIP1559DynamicFees { + opts.GasPrice = nil + opts.GasTipCap = estimations.GasTipCap + opts.GasFeeCap = estimations.GasFeeCap + } + for _, f := range o { + f(opts) + } + return opts +} + +// ContractLoader is a helper struct for loading contracts +type ContractLoader[T any] struct { + Client *Client +} + +// NewContractLoader creates a new contract loader +func NewContractLoader[T any](client *Client) *ContractLoader[T] { + return &ContractLoader[T]{ + Client: client, + } +} + +// LoadContract loads contract by name, address, ABI loader and wrapper init function, it adds contract ABI to Seth Contract Store and address to Contract Map. Thanks to that we can easily +// trace and debug interactions with the contract. Signatures of functions passed to this method were chosen to conform to Geth wrappers' GetAbi() and NewXXXContract() functions. +func (cl *ContractLoader[T]) LoadContract(name string, address common.Address, abiLoadFn func() (*abi.ABI, error), wrapperInitFn func(common.Address, bind.ContractBackend) (*T, error)) (*T, error) { + abiData, err := abiLoadFn() + if err != nil { + return new(T), err + } + cl.Client.ContractStore.AddABI(name, *abiData) + cl.Client.ContractAddressToNameMap.AddContract(address.Hex(), name) + + return wrapperInitFn(address, cl.Client.Client) +} + +// DeployContract deploys contract using ABI and bytecode passed to it, waits for transaction to be minted and contract really +// available at the address, so that when the method returns it's safe to interact with it. It also saves the contract address and ABI name +// to the contract map, so that we can use that, when tracing transactions. It is suggested to use name identical to the name of the contract Solidity file. +func (m *Client) DeployContract(auth *bind.TransactOpts, name string, abi abi.ABI, bytecode []byte, params ...interface{}) (DeploymentData, error) { + L.Info(). + Msgf("Started deploying %s contract", name) + + if auth.Context != nil { + if err, ok := auth.Context.Value(ContextErrorKey{}).(error); ok { + return DeploymentData{}, errors.Wrapf(err, "aborted contract deployment for %s, because context passed in transaction options had an error set", name) + } + } + + address, tx, contract, err := bind.DeployContract(auth, abi, bytecode, m.Client, params...) + if err != nil { + return DeploymentData{}, wrapErrInMessageWithASuggestion(err) + } + + L.Info(). + Str("Address", address.Hex()). + Str("TXHash", tx.Hash().Hex()). + Msgf("Waiting for %s contract deployment to finish", name) + + m.ContractAddressToNameMap.AddContract(address.Hex(), name) + + if _, ok := m.ContractStore.GetABI(name); !ok { + m.ContractStore.AddABI(name, abi) + } + + // retry is needed both for gas bumping and for waiting for deployment to finish (sometimes there's no code at address the first time we check) + if err := retry.Do( + func() error { + ctx, cancel := context.WithTimeout(context.Background(), m.Cfg.Network.TxnTimeout.Duration()) + _, err := bind.WaitDeployed(ctx, m.Client, tx) + cancel() + + // let's make sure that deployment transaction was successful, before retrying + if err != nil && !errors.Is(err, context.DeadlineExceeded) { + ctx, cancel := context.WithTimeout(context.Background(), m.Cfg.Network.TxnTimeout.Duration()) + receipt, mineErr := bind.WaitMined(ctx, m.Client, tx) + if mineErr != nil { + cancel() + return mineErr + } + cancel() + + if receipt.Status == 0 { + return errors.New("deployment transaction was reverted") + } + } + + return err + }, retry.OnRetry(func(i uint, retryErr error) { + switch { + case errors.Is(retryErr, context.DeadlineExceeded): + replacementTx, replacementErr := prepareReplacementTransaction(m, tx) + if replacementErr != nil { + L.Debug().Str("Current error", retryErr.Error()).Str("Replacement error", replacementErr.Error()).Uint("Attempt", i+1).Msg("Failed to prepare replacement transaction for contract deployment. Retrying with the original one") + return + } + tx = replacementTx + default: + // do nothing, just wait again until it's mined + } + L.Debug().Str("Current error", retryErr.Error()).Uint("Attempt", i+1).Msg("Waiting for contract to be deployed") + }), + retry.DelayType(retry.FixedDelay), + // if gas bump retries are set to 0, we still want to retry 10 times, because what we will be retrying will be other errors (no code at address, etc.) + // downside is that if retries are enabled and their number is low other retry errors will be retried only that number of times + // (we could have custom logic for different retry count per error, but that seemed like an overkill, so it wasn't implemented) + retry.Attempts(func() uint { + if m.Cfg.GasBumpRetries() != 0 { + return m.Cfg.GasBumpRetries() + } + return 10 + }()), + retry.RetryIf(func(err error) bool { + return strings.Contains(strings.ToLower(err.Error()), "no contract code at given address") || + strings.Contains(strings.ToLower(err.Error()), "no contract code after deployment") || + (m.Cfg.GasBumpRetries() != 0 && errors.Is(err, context.DeadlineExceeded)) + }), + ); err != nil { + // pass this specific error, so that Decode knows that it's not the actual revert reason + _, _ = m.Decode(tx, errors.New(ErrContractDeploymentFailed)) + + return DeploymentData{}, wrapErrInMessageWithASuggestion(m.rewriteDeploymentError(err)) + } + + L.Info(). + Str("Address", address.Hex()). + Str("TXHash", tx.Hash().Hex()). + Msgf("Deployed %s contract", name) + + if !m.Cfg.ShouldSaveDeployedContractMap() { + return DeploymentData{Address: address, Transaction: tx, BoundContract: contract}, nil + } + + if err := SaveDeployedContract(m.Cfg.ContractMapFile, name, address.Hex()); err != nil { + L.Warn(). + Err(err). + Msg("Failed to save deployed contract address to file") + } + + return DeploymentData{Address: address, Transaction: tx, BoundContract: contract}, nil +} + +// rewriteDeploymentError makes some known errors more human friendly +func (m *Client) rewriteDeploymentError(err error) error { + var maybeRetryErr retry.Error + switch { + case errors.As(err, &maybeRetryErr): + areAllTimeouts := false + for _, e := range maybeRetryErr.WrappedErrors() { + if !errors.Is(e, context.DeadlineExceeded) { + break + } + areAllTimeouts = true + } + + if areAllTimeouts { + newErr := retry.Error{} + for range maybeRetryErr.WrappedErrors() { + newErr = append(newErr, fmt.Errorf("deployment transaction was not mined within %s", m.Cfg.Network.TxnTimeout.Duration().String())) + } + err = newErr + } + case errors.Is(err, context.DeadlineExceeded): + { + err = fmt.Errorf("deployment transaction was not mined within %s", m.Cfg.Network.TxnTimeout.Duration().String()) + } + } + + return err +} + +type DeploymentData struct { + Address common.Address + Transaction *types.Transaction + BoundContract *bind.BoundContract +} + +// DeployContractFromContractStore deploys contract from Seth's Contract Store, waits for transaction to be minted and contract really +// available at the address, so that when the method returns it's safe to interact with it. It also saves the contract address and ABI name +// to the contract map, so that we can use that, when tracing transactions. Name by which you refer the contract should be the same as the +// name of ABI file (you can omit the .abi suffix). +func (m *Client) DeployContractFromContractStore(auth *bind.TransactOpts, name string, params ...interface{}) (DeploymentData, error) { + if m.ContractStore == nil { + return DeploymentData{}, errors.New("ABIStore is nil") + } + + name = strings.TrimSuffix(name, ".abi") + name = strings.TrimSuffix(name, ".bin") + + contractAbi, ok := m.ContractStore.ABIs[name+".abi"] + if !ok { + return DeploymentData{}, errors.New("ABI not found") + } + + bytecode, ok := m.ContractStore.BINs[name+".bin"] + if !ok { + return DeploymentData{}, errors.New("BIN not found") + } + + data, err := m.DeployContract(auth, name, contractAbi, bytecode, params...) + if err != nil { + return DeploymentData{}, err + } + + return data, nil +} + +func (m *Client) SaveDecodedCallsAsJson(dirname string) error { + return m.Tracer.SaveDecodedCallsAsJson(dirname) +} + +type TransactionLog struct { + Topics []common.Hash + Data []byte +} + +func (t TransactionLog) GetTopics() []common.Hash { + return t.Topics +} + +func (t TransactionLog) GetData() []byte { + return t.Data +} + +func (m *Client) decodeContractLogs(l zerolog.Logger, logs []types.Log, a abi.ABI) ([]DecodedTransactionLog, error) { + l.Trace().Msg("Decoding events") + var eventsParsed []DecodedTransactionLog + for _, lo := range logs { + for _, evSpec := range a.Events { + if evSpec.ID.Hex() == lo.Topics[0].Hex() { + d := TransactionLog{lo.Topics, lo.Data} + l.Trace().Str("Name", evSpec.RawName).Str("Signature", evSpec.Sig).Msg("Unpacking event") + eventsMap, topicsMap, err := decodeEventFromLog(l, a, evSpec, d) + if err != nil { + return nil, errors.Wrap(err, ErrDecodeLog) + } + parsedEvent := decodedLogFromMaps(&DecodedTransactionLog{}, eventsMap, topicsMap) + if decodedTransactionLog, ok := parsedEvent.(*DecodedTransactionLog); ok { + decodedTransactionLog.Signature = evSpec.Sig + m.mergeLogMeta(decodedTransactionLog, lo) + eventsParsed = append(eventsParsed, *decodedTransactionLog) + l.Trace().Interface("Log", parsedEvent).Msg("Transaction log") + } else { + l.Trace(). + Str("Actual type", fmt.Sprintf("%T", decodedTransactionLog)). + Msg("Failed to cast decoded event to DecodedCommonLog") + } + } + } + } + return eventsParsed, nil +} + +// WaitUntilNoPendingTxForRootKey waits until there's no pending transaction for root key. If after timeout there are still pending transactions, it returns error. +func (m *Client) WaitUntilNoPendingTxForRootKey(timeout time.Duration) error { + return m.WaitUntilNoPendingTx(m.MustGetRootKeyAddress(), timeout) +} + +// WaitUntilNoPendingTxForKeyNum waits until there's no pending transaction for key at index `keyNum`. If index is out of range or +// if after timeout there are still pending transactions, it returns error. +func (m *Client) WaitUntilNoPendingTxForKeyNum(keyNum int, timeout time.Duration) error { + if keyNum > len(m.Addresses)-1 || keyNum < 0 { + return fmt.Errorf("keyNum is out of range. Expected %d-%d. Got: %d", 0, len(m.Addresses)-1, keyNum) + } + return m.WaitUntilNoPendingTx(m.Addresses[keyNum], timeout) +} + +// WaitUntilNoPendingTx waits until there's no pending transaction for address. If after timeout there are still pending transactions, it returns error. +func (m *Client) WaitUntilNoPendingTx(address common.Address, timeout time.Duration) error { + ticker := time.NewTicker(1 * time.Second) + defer ticker.Stop() + waitTimeout := time.NewTimer(timeout) + defer waitTimeout.Stop() + + for { + select { + case <-waitTimeout.C: + return fmt.Errorf("after '%s' address '%s' still had pending transactions", timeout, address) + case <-ticker.C: + nonceStatus, err := m.getNonceStatus(address) + // if there is an error, we can't be sure if there are pending transactions or not, let's retry on next tick + if err != nil { + L.Debug().Err(err).Msg("Failed to get nonce status") + continue + } + L.Debug().Msgf("Nonce status for address %s: %v", address.Hex(), nonceStatus) + + if nonceStatus.PendingNonce > nonceStatus.LastNonce { + L.Debug().Uint64("Pending transactions", nonceStatus.PendingNonce-nonceStatus.LastNonce).Msgf("There are still pending transactions for %s", address.Hex()) + continue + } + + return nil + } + } +} + +// mergeLogMeta add metadata from log +func (m *Client) mergeLogMeta(pe *DecodedTransactionLog, l types.Log) { + pe.Address = l.Address + pe.Topics = make([]string, 0) + for _, t := range l.Topics { + pe.Topics = append(pe.Topics, t.String()) + } + pe.BlockNumber = l.BlockNumber + pe.Index = l.Index + pe.TXHash = l.TxHash.Hex() + pe.TXIndex = l.TxIndex + pe.Removed = l.Removed +} diff --git a/seth/client_api_test.go b/seth/client_api_test.go new file mode 100644 index 000000000..222b3ed13 --- /dev/null +++ b/seth/client_api_test.go @@ -0,0 +1,359 @@ +package seth_test + +import ( + "context" + "math/big" + "sync" + "testing" + "time" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/pkg/errors" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" + "github.com/smartcontractkit/chainlink-testing-framework/seth/test_utils" +) + +func TestAPI(t *testing.T) { + c := newClientWithEphemeralAddresses(t) + + t.Cleanup(func() { + err := c.NonceManager.UpdateNonces() + require.NoError(t, err, "failed to update nonces") + err = seth.ReturnFunds(c, c.Addresses[0].Hex()) + require.NoError(t, err, "failed to return funds") + }) + + type test struct { + name string + EIP1559Enabled bool + transactionOpts []seth.TransactOpt + callOpts []seth.CallOpt + } + + bn, err := c.Client.BlockNumber(context.Background()) + require.NoError(t, err) + weiValue := big.NewInt(1) + overriddenGasPrice := big.NewInt(c.Cfg.Network.GasPrice + 1) + overriddenGasFeeCap := big.NewInt(c.Cfg.Network.GasFeeCap + 1) + overriddenGasTipCap := big.NewInt(c.Cfg.Network.GasTipCap + 1) + overriddenGasLimit := uint64(c.Cfg.Network.GasLimit) + 1 + + tests := []test{ + { + name: "default tx gas opts from cfg", + }, + { + name: "custom legacy tx opts override", + transactionOpts: []seth.TransactOpt{ + seth.WithGasPrice(overriddenGasPrice), + seth.WithGasLimit(overriddenGasLimit), + }, + }, + { + name: "custom EIP-1559 tx opts override", + transactionOpts: []seth.TransactOpt{ + seth.WithGasFeeCap(overriddenGasFeeCap), + seth.WithGasTipCap(overriddenGasTipCap), + }, + EIP1559Enabled: true, + }, + { + name: "with value override", + transactionOpts: []seth.TransactOpt{ + seth.WithValue(weiValue), + }, + }, + { + name: "custom call opts", + callOpts: []seth.CallOpt{ + seth.WithPending(true), + seth.WithBlockNumber(bn), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + var dtx *seth.DecodedTransaction + c.Cfg.Network.EIP1559DynamicFees = tc.EIP1559Enabled + if tc.name == "with value override" { + dtx, err = c.Decode( + TestEnv.DebugContract.Pay(c.NewTXOpts(tc.transactionOpts...)), + ) + require.NoError(t, err) + require.Equal(t, weiValue, dtx.Transaction.Value()) + } else { + dtx, err = c.Decode( + TestEnv.DebugContract.Set(c.NewTXOpts(tc.transactionOpts...), big.NewInt(1)), + ) + } + require.NoError(t, err) + require.NotEmpty(t, dtx.Transaction) + require.NotEmpty(t, dtx.Receipt) + val, err := TestEnv.DebugContract.Get(c.NewCallOpts(tc.callOpts...)) + require.NoError(t, err) + require.Equal(t, big.NewInt(1), val) + }) + } +} + +func TestAPINonces(t *testing.T) { + c := newClientWithEphemeralAddresses(t) + + t.Cleanup(func() { + err := c.NonceManager.UpdateNonces() + require.NoError(t, err, "failed to update nonces") + err = seth.ReturnFunds(c, c.Addresses[0].Hex()) + require.NoError(t, err, "failed to return funds") + }) + + type test struct { + name string + EIP1559Enabled bool + transactionOpts []seth.TransactOpt + callOpts []seth.CallOpt + } + + pnonce, err := c.Client.PendingNonceAt(context.Background(), c.Addresses[0]) + require.NoError(t, err) + + tests := []test{ + { + name: "with nonce override", + transactionOpts: []seth.TransactOpt{ + seth.WithNonce(big.NewInt(int64(pnonce))), + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + c.Cfg.Network.EIP1559DynamicFees = tc.EIP1559Enabled + _, err := c.Decode( + TestEnv.DebugContract.Set(c.NewTXOpts(tc.transactionOpts...), big.NewInt(1)), + ) + require.NoError(t, err) + val, err := TestEnv.DebugContract.Get(c.NewCallOpts(tc.callOpts...)) + require.NoError(t, err) + require.Equal(t, big.NewInt(1), val) + }) + } +} + +func TestAPISeqErrors(t *testing.T) { + c := newClientWithEphemeralAddresses(t) + + t.Cleanup(func() { + err := c.NonceManager.UpdateNonces() + require.NoError(t, err, "failed to update nonces") + err = seth.ReturnFunds(c, c.Addresses[0].Hex()) + require.NoError(t, err, "failed to return funds") + }) + + type test struct { + name string + } + + tests := []test{ + { + name: "raise previous call error first", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + c.Errors = append(c.Errors, errors.New("previous call error")) + _, err := c.Decode( + TestEnv.DebugContract.Set(c.NewTXOpts(), big.NewInt(1)), + ) + require.Error(t, err) + }) + } +} + +func TestAPIConfig(t *testing.T) { + cfg, err := seth.ReadConfig() + require.NoError(t, err) + addrs, pkeys, err := cfg.ParseKeys() + require.NoError(t, err) + c, err := seth.NewClientRaw(cfg, addrs, pkeys) + require.NoError(t, err) + + type test struct { + name string + } + + tests := []test{ + { + name: "can run without ABI", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + _, err := c.Decode( + TestEnv.DebugContract.Set(c.NewTXOpts(), big.NewInt(1)), + ) + require.NoError(t, err) + val, err := TestEnv.DebugContract.Get(c.NewCallOpts()) + require.NoError(t, err) + require.Equal(t, big.NewInt(1), val) + }) + } +} + +func TestAPIKeys(t *testing.T) { + type test struct { + name string + } + + keyCount := 60 + c := test_utils.NewClientWithAddresses(t, keyCount, nil) + + t.Cleanup(func() { + err := c.NonceManager.UpdateNonces() + require.NoError(t, err, "failed to update nonces") + err = seth.ReturnFunds(c, c.Addresses[0].Hex()) + require.NoError(t, err, "failed to return funds") + }) + + tests := []test{ + { + name: "multiple separate keys used", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + wg := &sync.WaitGroup{} + for i := 1; i < 61; i++ { + i := i + wg.Add(1) + go func() { + defer wg.Done() + _, err := c.Decode( + TestEnv.DebugContract.AddCounter(c.NewTXKeyOpts(i), big.NewInt(0), big.NewInt(1)), + ) + require.NoError(t, err) + }() + } + wg.Wait() + for i := 1; i < 61; i++ { + val, err := TestEnv.DebugContract.GetCounter(c.NewCallOpts(), big.NewInt(0)) + require.NoError(t, err) + require.Equal(t, big.NewInt(60), val) + } + }) + } +} + +func TestAPISyncKeysPool(t *testing.T) { + type test struct { + name string + counterIdx int64 + keys int + batchGroup int + runs int + shouldFail bool + } + + tests := []test{ + { + name: "get an error if there are no keys in the pool until timeout", + shouldFail: true, + keys: 2, + batchGroup: 6, + runs: 1, + }, + { + name: "realistic workload for 1s chain (default Geth)", + counterIdx: 1, + keys: 60, + batchGroup: 15, + runs: 10, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + t.Setenv(seth.LogLevelEnvVar, "trace") + c := test_utils.NewClientWithAddresses(t, tc.keys, nil) + + t.Cleanup(func() { + err := c.NonceManager.UpdateNonces() + require.NoError(t, err, "failed to update nonces") + err = seth.ReturnFunds(c, c.Addresses[0].Hex()) + require.NoError(t, err, "failed to return funds") + }) + + if tc.shouldFail { + c.NonceManager.SyncedKeys = make(chan *seth.KeyNonce) + } + for i := 0; i < tc.runs; i++ { + wg := &sync.WaitGroup{} + for i := 0; i < tc.batchGroup; i++ { + wg.Add(1) + go func() { + defer wg.Done() + keyNum := c.AnySyncedKey() + var err error + if keyNum == -80001 { + err = errors.New("-1 keyNum was returned, which means there was a syncing timeout") + } else { + _, err = c.Decode( + TestEnv.DebugContract.AddCounter(c.NewTXKeyOpts(keyNum), big.NewInt(tc.counterIdx), big.NewInt(1)), + ) + } + + if tc.shouldFail { + require.Error(t, err) + return + } + require.NoError(t, err) + }() + } + wg.Wait() + } + val, err := TestEnv.DebugContract.GetCounter(c.NewCallOpts(), big.NewInt(1)) + require.NoError(t, err) + if !tc.shouldFail { + require.Equal(t, big.NewInt(int64(tc.batchGroup*tc.runs)), val) + } + }) + } +} + +func TestManualAPIReconnect(t *testing.T) { + c := newClientWithEphemeralAddresses(t) + + t.Cleanup(func() { + err := c.NonceManager.UpdateNonces() + require.NoError(t, err, "failed to update nonces") + err = seth.ReturnFunds(c, c.Addresses[0].Hex()) + require.NoError(t, err, "failed to return funds") + }) + + type test struct { + name string + transactionOpts []seth.TransactOpt + } + + tests := []test{ + { + name: "can reconnect", + }, + } + + for _, tc := range tests { + for i := 0; i < 20; i++ { + _, err := c.RetryTxAndDecode(func() (*types.Transaction, error) { + return TestEnv.DebugContract.Set(c.NewTXOpts(tc.transactionOpts...), big.NewInt(1)) + }) + require.NoError(t, err) + time.Sleep(1 * time.Second) + } + } +} diff --git a/seth/client_builder.go b/seth/client_builder.go new file mode 100644 index 000000000..0470ddaf9 --- /dev/null +++ b/seth/client_builder.go @@ -0,0 +1,246 @@ +package seth + +import ( + "time" +) + +type ClientBuilder struct { + config *Config +} + +// NewClientBuilder creates a new ClientBuilder with reasonable default values. You only need to pass private key(s) and RPC URL to build a usable config. +func NewClientBuilder() *ClientBuilder { + network := &Network{ + Name: DefaultNetworkName, + EIP1559DynamicFees: true, + TxnTimeout: MustMakeDuration(5 * time.Minute), + DialTimeout: MustMakeDuration(DefaultDialTimeout), + TransferGasFee: DefaultTransferGasFee, + GasPriceEstimationEnabled: true, + GasPriceEstimationBlocks: 200, + GasPriceEstimationTxPriority: Priority_Standard, + GasPrice: DefaultGasPrice, + GasFeeCap: DefaultGasFeeCap, + GasTipCap: DefaultGasTipCap, + } + + return &ClientBuilder{ + config: &Config{ + ArtifactsDir: "seth_artifacts", + EphemeralAddrs: &ZeroInt64, + RootKeyFundsBuffer: &ZeroInt64, + Network: network, + Networks: []*Network{network}, + TracingLevel: TracingLevel_Reverted, + TraceOutputs: []string{TraceOutput_Console, TraceOutput_DOT}, + ExperimentsEnabled: []string{}, + CheckRpcHealthOnStart: true, + BlockStatsConfig: &BlockStatsConfig{RPCRateLimit: 10}, + NonceManager: &NonceManagerCfg{KeySyncRateLimitSec: 10, KeySyncRetries: 3, KeySyncTimeout: MustMakeDuration(60 * time.Second), KeySyncRetryDelay: MustMakeDuration(5 * time.Second)}, + GasBump: &GasBumpConfig{ + Retries: 10, + }, + }, + } +} + +// WithRpcUrl sets the RPC URL for the config. +// Default value is an empty string (which is an incorrect value). +func (c *ClientBuilder) WithRpcUrl(url string) *ClientBuilder { + c.config.Network.URLs = []string{url} + // defensive programming + if len(c.config.Networks) == 0 { + c.config.Networks = append(c.config.Networks, c.config.Network) + } else { + c.config.Networks[0].URLs = []string{url} + } + return c +} + +// WithPrivateKeys sets the private keys for the config. At least one is required to build a valid config. +// Default value is an empty slice (which is an incorrect value). +func (c *ClientBuilder) WithPrivateKeys(pks []string) *ClientBuilder { + c.config.Network.PrivateKeys = pks + // defensive programming + if len(c.config.Networks) == 0 { + c.config.Networks = append(c.config.Networks, c.config.Network) + } else { + c.config.Networks[0].PrivateKeys = pks + } + return c +} + +// WithNetworkName sets the network name, useful mostly for debugging and logging. +// Default value is "default". +func (c *ClientBuilder) WithNetworkName(name string) *ClientBuilder { + c.config.Network.Name = name + // defensive programming + if len(c.config.Networks) == 0 { + c.config.Networks = append(c.config.Networks, c.config.Network) + } else { + c.config.Networks[0].Name = name + } + return c +} + +// WithGasPriceEstimations enables or disables gas price estimations, sets the number of blocks to use for estimation or transaction priority. +// Even with estimations enabled you should still either set legacy gas price with `WithLegacyGasPrice()` or EIP-1559 dynamic fees with `WithDynamicGasPrices()` +// ss they will be used as fallback values, if the estimations fail. +// Following priorities are supported: "slow", "standard" and "fast" +// Default values are true for enabled, 200 blocks for estimation and "standard" for priority. +func (c *ClientBuilder) WithGasPriceEstimations(enabled bool, estimationBlocks uint64, txPriority string) *ClientBuilder { + c.config.Network.GasPriceEstimationEnabled = enabled + c.config.Network.GasPriceEstimationBlocks = estimationBlocks + c.config.Network.GasPriceEstimationTxPriority = txPriority + // defensive programming + if len(c.config.Networks) == 0 { + c.config.Networks = append(c.config.Networks, c.config.Network) + } else { + c.config.Networks[0].GasPriceEstimationEnabled = enabled + c.config.Networks[0].GasPriceEstimationBlocks = estimationBlocks + c.config.Networks[0].GasPriceEstimationTxPriority = txPriority + } + return c +} + +// WithEIP1559DynamicFees enables or disables EIP-1559 dynamic fees. If enabled, you should set gas fee cap and gas tip cap with `WithDynamicGasPrices()` +// Default value is true. +func (c *ClientBuilder) WithEIP1559DynamicFees(enabled bool) *ClientBuilder { + c.config.Network.EIP1559DynamicFees = enabled + // defensive programming + if len(c.config.Networks) == 0 { + c.config.Networks = append(c.config.Networks, c.config.Network) + } else { + c.config.Networks[0].EIP1559DynamicFees = enabled + } + return c +} + +// WithLegacyGasPrice sets the gas price for legacy transactions that will be used only if EIP-1559 dynamic fees are disabled. +// Default value is 1 gwei. +func (c *ClientBuilder) WithLegacyGasPrice(gasPrice int64) *ClientBuilder { + c.config.Network.GasPrice = gasPrice + // defensive programming + if len(c.config.Networks) == 0 { + c.config.Networks = append(c.config.Networks, c.config.Network) + } else { + c.config.Networks[0].GasPrice = gasPrice + } + return c +} + +// WithDynamicGasPrices sets the gas fee cap and gas tip cap for EIP-1559 dynamic fees. These values will be used only if EIP-1559 dynamic fees are enabled. +// Default values are 150 gwei for gas fee cap and 50 gwei for gas tip cap. +func (c *ClientBuilder) WithDynamicGasPrices(gasFeeCap, gasTipCap int64) *ClientBuilder { + c.config.Network.GasFeeCap = gasFeeCap + c.config.Network.GasTipCap = gasTipCap + // defensive programming + if len(c.config.Networks) == 0 { + c.config.Networks = append(c.config.Networks, c.config.Network) + } else { + c.config.Networks[0].GasFeeCap = gasFeeCap + c.config.Networks[0].GasTipCap = gasTipCap + } + return c +} + +// WithTransferGasFee sets the gas fee for transfer transactions. This value is used, when sending funds to ephemeral keys or returning funds to root private key. +// Default value is 21_000 wei. +func (c *ClientBuilder) WithTransferGasFee(gasFee int64) *ClientBuilder { + c.config.Network.TransferGasFee = gasFee + // defensive programming + if len(c.config.Networks) == 0 { + c.config.Networks = append(c.config.Networks, c.config.Network) + } else { + c.config.Networks[0].TransferGasFee = gasFee + } + return c +} + +// WithGasBumping sets the number of retries for gas bumping and max gas price. You can also provide a custom bumping strategy. If the transaction is not mined within this number of retries, it will be considered failed. +// If the gas price is bumped to a value higher than max gas price, no more gas bumping will be attempted and previous gas price will be used by all subsequent attempts. If set to 0 max price is not checked. +// Default value is 10 retries, no max gas price and a default bumping strategy (with gas increase % based on gas_price_estimation_tx_priority) +func (c *ClientBuilder) WithGasBumping(retries uint, maxGasPrice int64, customBumpingStrategy GasBumpStrategyFn) *ClientBuilder { + c.config.GasBump = &GasBumpConfig{ + Retries: retries, + MaxGasPrice: maxGasPrice, + StrategyFn: customBumpingStrategy, + } + return c +} + +// WithTransactionTimeout sets the timeout for transactions. If the transaction is not mined within this time, it will be considered failed. +// Default value is 5 minutes. +func (c *ClientBuilder) WithTransactionTimeout(timeout time.Duration) *ClientBuilder { + c.config.Network.TxnTimeout = MustMakeDuration(timeout) + // defensive programming + if len(c.config.Networks) == 0 { + c.config.Networks = append(c.config.Networks, c.config.Network) + } else { + c.config.Networks[0].TxnTimeout = MustMakeDuration(timeout) + } + return c +} + +// WithRpcDialTimeout sets the timeout for dialing the RPC server. If the connection is not established within this time, it will be considered failed. +// Default value is 1 minute. +func (c *ClientBuilder) WithRpcDialTimeout(timeout time.Duration) *ClientBuilder { + c.config.Network.DialTimeout = MustMakeDuration(timeout) + // defensive programming + if len(c.config.Networks) == 0 { + c.config.Networks = append(c.config.Networks, c.config.Network) + } else { + c.config.Networks[0].DialTimeout = MustMakeDuration(timeout) + } + return c +} + +// WithEphemeralAddresses sets the number of ephemeral addresses to generate and the amount of funds to keep in the root private key. +// Default values are 0 for ephemeral addresses and 0 for root key funds buffer. +func (c *ClientBuilder) WithEphemeralAddresses(ephemeralAddressCount, rootKeyBufferAmount int64) *ClientBuilder { + c.config.EphemeralAddrs = &ephemeralAddressCount + c.config.RootKeyFundsBuffer = &rootKeyBufferAmount + + return c +} + +// WithTracing sets the tracing level and outputs. Tracing level can be one of: "all", "reverted", "none". Outputs can be one or more of: "console", "dot" or "json". +// Default values are "reverted" and ["console", "dot"]. +func (c *ClientBuilder) WithTracing(level string, outputs []string) *ClientBuilder { + c.config.TracingLevel = level + c.config.TraceOutputs = outputs + return c +} + +// WithProtections enables or disables nonce protection (fails, when key has a pending transaction and you try to submit another one) and node health check on startup. +// Default values are false for nonce protection and true for node health check. +func (c *ClientBuilder) WithProtections(pendingNonceProtectionEnabled, nodeHealthStartupCheck bool) *ClientBuilder { + c.config.PendingNonceProtectionEnabled = pendingNonceProtectionEnabled + c.config.CheckRpcHealthOnStart = nodeHealthStartupCheck + return c +} + +// WithArtifactsFolder sets the folder where the Seth artifacts such as DOT graphs or JSON will be saved. +// Default value is "seth_artifacts". +func (c *ClientBuilder) WithArtifactsFolder(folder string) *ClientBuilder { + c.config.ArtifactsDir = folder + return c +} + +// WithNonceManager sets the rate limit for key sync, number of retries, timeout and retry delay. +// Default values are 10 calls per second, 3 retires, 60s timeout and 5s retry delay. +func (c *ClientBuilder) WithNonceManager(rateLimitSec int, retries uint, timeout, retryDelay time.Duration) *ClientBuilder { + c.config.NonceManager = &NonceManagerCfg{ + KeySyncRateLimitSec: rateLimitSec, + KeySyncRetries: retries, + KeySyncTimeout: MustMakeDuration(timeout), + KeySyncRetryDelay: MustMakeDuration(retryDelay), + } + + return c +} + +// Build creates a new Client from the builder. +func (c *ClientBuilder) Build() (*Client, error) { + return NewClientWithConfig(c.config) +} diff --git a/seth/client_contract_map_test.go b/seth/client_contract_map_test.go new file mode 100644 index 000000000..25f0311a9 --- /dev/null +++ b/seth/client_contract_map_test.go @@ -0,0 +1,167 @@ +package seth_test + +import ( + "crypto/ecdsa" + "os" + "testing" + + "github.com/barkimedes/go-deepcopy" + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" + "github.com/smartcontractkit/chainlink-testing-framework/seth/test_utils" +) + +func TestContractMapSavesDeployedContractsToFileAndReadsThem(t *testing.T) { + file, err := os.CreateTemp("", "deployed_contracts.toml") + require.NoError(t, err, "failed to create temp file") + + err = seth.SaveDeployedContract(file.Name(), "contractName", "0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82") + require.NoError(t, err, "failed to save deployed contract") + + contracts, err := seth.LoadDeployedContracts(file.Name()) + require.NoError(t, err, "failed to load deployed contracts") + + require.Equal(t, map[string]string{"0x0DCd1Bf9A1b36cE34237eEaFef220932846BCD82": "contractName"}, contracts) +} + +func TestContractMapDoesNotErrorWhenReadingNonExistentFile(t *testing.T) { + _, err := seth.LoadDeployedContracts("nonexistent.toml") + require.NoError(t, err, "reading from non-existent file should not error") +} + +func TestContractMapErrorsWheneadingInvalidTomlFile(t *testing.T) { + file, err := os.CreateTemp("", "invalid_contracts.toml") + require.NoError(t, err, "failed to create temp file") + + _, err = file.WriteString("invalid toml") + require.NoError(t, err, "failed to write invalid toml") + + _, err = seth.LoadDeployedContracts(file.Name()) + require.Error(t, err, "expected error reading invalid toml file") +} + +func TestContractMapErrorsWhenReadingMalformedAddress(t *testing.T) { + file, err := os.CreateTemp("", "malformed_address.toml") + require.NoError(t, err, "failed to create temp file") + + err = seth.SaveDeployedContract(file.Name(), "contractName", "malformed") + require.NoError(t, err, "failed to save deployed contract") + + _, err = seth.LoadDeployedContracts(file.Name()) + require.Error(t, err, "expected error reading malformed address") + require.Contains(t, err.Error(), "hex string without 0x prefix", "expected error reading malformed address") +} + +func TestContractMapNonSimulatedClientSavesAndReadsContractMap(t *testing.T) { + file, err := os.CreateTemp("", "deployed_contracts.toml") + require.NoError(t, err, "failed to create temp file") + + client, err := seth.NewClient() + require.NoError(t, err, "failed to create client") + + client.Cfg.SaveDeployedContractsMap = true + client.Cfg.ContractMapFile = file.Name() + // change network name so that is not treated as simulated + client.Cfg.Network.Name = "geth2" + data, err := client.DeployContractFromContractStore(client.NewTXOpts(), "NetworkDebugSubContract") + require.NoError(t, err, "failed to deploy contract") + + cfg := client.Cfg + newNonSimulatedClient, err := seth.NewClientRaw(cfg, client.Addresses, client.PrivateKeys) + require.NoError(t, err, "failed to create new client") + require.Equal(t, 1, newNonSimulatedClient.ContractAddressToNameMap.Size(), "expected contract map to be saved") + + expectedMap := seth.NewContractMap(map[string]string{data.Address.Hex(): "NetworkDebugSubContract"}) + require.Equal(t, expectedMap, newNonSimulatedClient.ContractAddressToNameMap, "expected contract map to be saved") + + cfg.Network.Name = seth.GETH + newSimulatedClient, err := seth.NewClientRaw(cfg, client.Addresses, client.PrivateKeys) + require.NoError(t, err, "failed to create new client") + require.Equal(t, 0, newSimulatedClient.ContractAddressToNameMap.Size(), "expected contract map to be saved") +} + +func TestContractMapSimulatedClientDoesntSaveContractMap(t *testing.T) { + client, err := seth.NewClient() + require.NoError(t, err, "failed to create client") + + client.Cfg.SaveDeployedContractsMap = true + _, err = client.DeployContractFromContractStore(client.NewTXOpts(), "NetworkDebugSubContract") + require.NoError(t, err, "failed to deploy contract") + + _, err = os.Stat(client.Cfg.GenerateContractMapFileName()) + require.Error(t, err, "contract file should not be saved for simulated network") + require.True(t, os.IsNotExist(err), "contract file should not be saved for simulated network") +} + +func TestContractMapNewClientIsCreatedEvenIfNoContractMapFileExists(t *testing.T) { + cfg, err := test_utils.CopyConfig(TestEnv.Client.Cfg) + require.NoError(t, err, "failed to copy config") + + cfg.SaveDeployedContractsMap = true + // change network name so that is not treated as simulated + cfg.Network.Name = "geth2" + cfg.ContractMapFile = cfg.GenerateContractMapFileName() + nm, err := seth.NewNonceManager(cfg, TestEnv.Client.Addresses, TestEnv.Client.PrivateKeys) + require.NoError(t, err, "failed to create nonce manager") + + newClient, err := seth.NewClientRaw(cfg, TestEnv.Client.Addresses, TestEnv.Client.PrivateKeys, seth.WithNonceManager(nm), seth.WithContractStore(TestEnv.Client.ContractStore)) + require.NoError(t, err, "failed to create new client") + require.Equal(t, 0, newClient.ContractAddressToNameMap.Size(), "expected contract map to be saved") + + _, err = newClient.DeployContractFromContractStore(newClient.NewTXOpts(), "NetworkDebugSubContract") + require.NoError(t, err, "failed to deploy contract") + + t.Cleanup(func() { + _ = os.Remove(cfg.ContractMapFile) + }) + + // make sure deployed contract is present in the contract map + require.Equal(t, 1, newClient.ContractAddressToNameMap.Size(), "expected contract map to be saved") + + // make sure that new client instance loads map from existing file instead of creating a new one + newClient, err = seth.NewClientRaw(cfg, TestEnv.Client.Addresses, TestEnv.Client.PrivateKeys, seth.WithNonceManager(nm), seth.WithContractStore(TestEnv.Client.ContractStore)) + require.NoError(t, err, "failed to create new client") + require.Equal(t, 1, newClient.ContractAddressToNameMap.Size(), "expected contract map to be saved") +} + +func TestContractMapNewClientIsNotCreatedWhenCorruptedContractMapFileExists(t *testing.T) { + file, err := os.CreateTemp("", "deployed_contracts.toml") + require.NoError(t, err, "failed to create temp file") + + err = os.WriteFile(file.Name(), []byte("invalid toml"), 0600) + require.NoError(t, err, "failed to write invalid toml") + + cfg, err := test_utils.CopyConfig(TestEnv.Client.Cfg) + require.NoError(t, err, "failed to copy config") + addresses := deepcopy.MustAnything(TestEnv.Client.Addresses).([]common.Address) + pks := deepcopy.MustAnything(TestEnv.Client.PrivateKeys).([]*ecdsa.PrivateKey) + // change network name so that is not treated as simulated + cfg.Network.Name = "geth2" + cfg.ContractMapFile = file.Name() + newClient, err := seth.NewClientRaw(cfg, addresses, pks) + require.Error(t, err, "succeeded in creation of new client") + require.Contains(t, err.Error(), seth.ErrReadContractMap, "expected error reading invalid toml") + require.Nil(t, newClient, "expected new client to be nil") +} + +func TestContractMapNewClientIsNotCreatedWhenCorruptedContractMapFileExists_InvalidAddress(t *testing.T) { + file, err := os.CreateTemp("", "deployed_contracts.toml") + require.NoError(t, err, "failed to create temp file") + + err = seth.SaveDeployedContract(file.Name(), "contractName", "malformed") + require.NoError(t, err, "failed to write invalid toml") + + cfg, err := test_utils.CopyConfig(TestEnv.Client.Cfg) + require.NoError(t, err, "failed to copy config") + addresses := deepcopy.MustAnything(TestEnv.Client.Addresses).([]common.Address) + pks := deepcopy.MustAnything(TestEnv.Client.PrivateKeys).([]*ecdsa.PrivateKey) + // change network name so that is not treated as simulated + cfg.Network.Name = "geth2" + cfg.ContractMapFile = file.Name() + newClient, err := seth.NewClientRaw(cfg, addresses, pks) + require.Error(t, err, "succeeded in creation of new client") + require.Contains(t, err.Error(), seth.ErrReadContractMap, "expected error reading invalid contract address") + require.Nil(t, newClient, "expected new client to be nil") +} diff --git a/seth/client_decode_test.go b/seth/client_decode_test.go new file mode 100644 index 000000000..d0e4adf54 --- /dev/null +++ b/seth/client_decode_test.go @@ -0,0 +1,236 @@ +package seth_test + +import ( + "math/big" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" +) + +func TestSmokeDebugReverts(t *testing.T) { + c := newClient(t) + + type test struct { + name string + method string + output map[string]string + } + + tests := []test{ + { + name: "revert with require", + method: "alwaysRevertsRequire", + output: map[string]string{ + seth.GETH: "execution reverted: always revert error", + seth.ANVIL: "execution reverted: revert: always revert error", + }, + }, + { + name: "revert with assert(panic)", + method: "alwaysRevertsAssert", + output: map[string]string{ + seth.GETH: "execution reverted: assert(false)", + seth.ANVIL: "execution reverted: panic: assertion failed (0x01)", + }, + }, + { + name: "revert with a custom err", + method: "alwaysRevertsCustomError", + output: map[string]string{ + seth.GETH: "error type: CustomErr, error values: [12 21]", + }, + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if tc.name == "revert with a custom err" && (c.Cfg.Network.Name == "Mumbai" || c.Cfg.Network.Name == "Fuji") { + t.Skip("typed errors are not supported\nnodes payload of rpc.DataError is empty and tx fails on send, not on execution") + } + _, err := c.Decode(TestEnv.DebugContractRaw.Transact(c.NewTXOpts(), tc.method)) + require.Error(t, err) + var expectedOutput = tc.output[seth.GETH] + if c.Cfg.Network.Name != seth.GETH { + if eo, ok := tc.output[c.Cfg.Network.Name]; ok { + expectedOutput = eo + } + } + require.Equal(t, expectedOutput, err.Error()) + }) + } +} + +func TestSmokeDebugData(t *testing.T) { + c := newClient(t) + c.Cfg.TracingLevel = seth.TracingLevel_All + + type test struct { + name string + method string + params []interface{} + output seth.DecodedTransaction + write bool + } + + tests := []test{ + { + name: "test named inputs/outputs", + method: "emitNamedInputsOutputs", + params: []interface{}{big.NewInt(1337), "test"}, + write: true, + output: seth.DecodedTransaction{ + CommonData: seth.CommonData{ + Input: map[string]interface{}{ + "inputVal1": big.NewInt(1337), + "inputVal2": "test", + }, + }, + }, + }, + // TODO: https://docs.soliditylang.org/en/v0.8.19/control-structures.html read and figure out if + // decoding anynymous + named is heavily used and needed, usually people name params and omit output names + { + name: "test anonymous inputs/outputs", + method: "emitInputsOutputs", + params: []interface{}{big.NewInt(1337), "test"}, + write: true, + output: seth.DecodedTransaction{ + CommonData: seth.CommonData{ + Input: map[string]interface{}{ + "inputVal1": big.NewInt(1337), + "inputVal2": "test", + }, + }, + }, + }, + { + name: "test one log no index", + method: "emitNoIndexEvent", + write: true, + output: seth.DecodedTransaction{ + Events: []seth.DecodedTransactionLog{ + { + DecodedCommonLog: seth.DecodedCommonLog{ + EventData: map[string]interface{}{ + "sender": c.Addresses[0], + }, + }, + }, + }, + }, + }, + { + name: "test one log index", + method: "emitOneIndexEvent", + write: true, + output: seth.DecodedTransaction{ + Events: []seth.DecodedTransactionLog{ + { + DecodedCommonLog: seth.DecodedCommonLog{ + EventData: map[string]interface{}{ + "a": big.NewInt(83), + }, + }, + }, + }, + }, + }, + { + name: "test two log index", + method: "emitTwoIndexEvent", + write: true, + output: seth.DecodedTransaction{ + Events: []seth.DecodedTransactionLog{ + { + DecodedCommonLog: seth.DecodedCommonLog{ + EventData: map[string]interface{}{ + "roundId": big.NewInt(1), + "startedBy": c.Addresses[0], + }, + }, + }, + }, + }, + }, + { + name: "test three log index", + method: "emitThreeIndexEvent", + write: true, + output: seth.DecodedTransaction{ + Events: []seth.DecodedTransactionLog{ + { + DecodedCommonLog: seth.DecodedCommonLog{ + EventData: map[string]interface{}{ + "roundId": big.NewInt(1), + "startedAt": big.NewInt(3), + "startedBy": c.Addresses[0], + }, + }, + }, + }, + }, + }, + { + name: "test log no index string", + method: "emitNoIndexEventString", + write: true, + output: seth.DecodedTransaction{ + Events: []seth.DecodedTransactionLog{ + { + DecodedCommonLog: seth.DecodedCommonLog{ + EventData: map[string]interface{}{ + "str": "myString", + }, + }, + }, + }, + }, + }, + // emitNoIndexStructEvent + { + name: "test log struct", + method: "emitNoIndexStructEvent", + write: true, + output: seth.DecodedTransaction{ + Events: []seth.DecodedTransactionLog{ + { + DecodedCommonLog: seth.DecodedCommonLog{ + EventData: map[string]interface{}{ + "a": struct { + Name string `json:"name"` + Balance uint64 `json:"balance"` + DailyLimit *big.Int `json:"dailyLimit"` + }{ + Name: "John", + Balance: 5, + DailyLimit: big.NewInt(10), + }, + }, + }, + }, + }, + }, + }, + // TODO: another case - figure out if indexed strings are used by anyone in events + // https://ethereum.stackexchange.com/questions/6840/indexed-event-with-string-not-getting-logged + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + if tc.write { + dtx, err := c.Decode( + TestEnv.DebugContractRaw.Transact(c.NewTXOpts(), tc.method, tc.params...), + ) + require.NoError(t, err) + require.Equal(t, dtx.Input, tc.output.Input) + require.Equal(t, dtx.Output, tc.output.Output) + for i, e := range tc.output.Events { + require.NotNil(t, dtx.Events[i]) + require.Equal(t, dtx.Events[i].EventData, e.EventData) + } + } + }) + } +} diff --git a/seth/client_helpers.go b/seth/client_helpers.go new file mode 100644 index 000000000..162dd8e77 --- /dev/null +++ b/seth/client_helpers.go @@ -0,0 +1,44 @@ +package seth + +import ( + "crypto/ecdsa" + "errors" + + "github.com/ethereum/go-ethereum/common" +) + +// MustGetRootKeyAddress returns the root key address from the client configuration. If no addresses are found, it panics. +// Root key address is the first address in the list of addresses. +func (m *Client) MustGetRootKeyAddress() common.Address { + if len(m.Addresses) == 0 { + panic("no addresses found in the client configuration") + } + return m.Addresses[0] +} + +// GetRootKeyAddress returns the root key address from the client configuration. If no addresses are found, it returns an error. +// Root key address is the first address in the list of addresses. +func (m *Client) GetRootKeyAddress() (common.Address, error) { + if len(m.Addresses) == 0 { + return common.Address{}, errors.New("no addresses found in the client configuration") + } + return m.Addresses[0], nil +} + +// MustGetRootPrivateKey returns the private key of root key/address from the client configuration. If no private keys are found, it panics. +// Root private key is the first private key in the list of private keys. +func (m *Client) MustGetRootPrivateKey() *ecdsa.PrivateKey { + if len(m.PrivateKeys) == 0 { + panic("no private keys found in the client configuration") + } + return m.PrivateKeys[0] +} + +// GetRootPrivateKey returns the private key of root key/address from the client configuration. If no private keys are found, it returns an error. +// Root private key is the first private key in the list of private keys. +func (m *Client) GetRootPrivateKey() (*ecdsa.PrivateKey, error) { + if len(m.PrivateKeys) == 0 { + return nil, errors.New("no private keys found in the client configuration") + } + return m.PrivateKeys[0], nil +} diff --git a/seth/client_main_test.go b/seth/client_main_test.go new file mode 100644 index 000000000..0bb339c52 --- /dev/null +++ b/seth/client_main_test.go @@ -0,0 +1,217 @@ +package seth_test + +import ( + "context" + "math/big" + "os" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" + network_debug_contract "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/debug" + link_token "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/link" + network_sub_contract "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/sub" +) + +/* + Some tests should be run on testnets/mainnets, so we are deploying the contract only once, + for these types of tests it's always a choice between funds/speed of tests + + If you need unique setup, just use NewDebugContractSetup in tests +*/ + +func init() { + _ = os.Setenv("SETH_CONFIG_PATH", "seth.toml") +} + +var ( + TestEnv TestEnvironment +) + +type TestEnvironment struct { + Client *seth.Client + DebugContract *network_debug_contract.NetworkDebugContract + DebugSubContract *network_sub_contract.NetworkDebugSubContract + LinkTokenContract *link_token.LinkToken + DebugContractAddress common.Address + DebugSubContractAddress common.Address + DebugContractRaw *bind.BoundContract + ContractMap seth.ContractMap +} + +func newClient(t *testing.T) *seth.Client { + c, err := seth.NewClient() + require.NoError(t, err, "failed to initialize seth") + + return c +} + +func newClientWithEphemeralAddresses(t *testing.T) *seth.Client { + cfg, err := seth.ReadConfig() + require.NoError(t, err, "failed to read config") + + var sixty int64 = 60 + cfg.EphemeralAddrs = &sixty + + c, err := seth.NewClientWithConfig(cfg) + require.NoError(t, err, "failed to initialize seth") + + return c +} + +func TestDeploymentLinkTokenFromGethWrapperExample(t *testing.T) { + c, err := seth.NewClient() + require.NoError(t, err, "failed to initialize seth") + abi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + contractData, err := c.DeployContract(c.NewTXOpts(), "LinkToken", *abi, []byte(link_token.LinkTokenMetaData.Bin)) + require.NoError(t, err, "failed to deploy link token contract from wrapper's ABI/BIN") + + contract, err := link_token.NewLinkToken(contractData.Address, c.Client) + require.NoError(t, err, "failed to create debug contract instance") + + _, err = c.Decode(contract.Mint(c.NewTXOpts(), common.Address{}, big.NewInt(1))) + require.NoError(t, err, "failed to decode transaction") +} + +func TestDeploymentAbortedWhenContextHasError(t *testing.T) { + c, err := seth.NewClient() + require.NoError(t, err, "failed to initialize seth") + abi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + + opts := c.NewTXOpts() + opts.Context = context.WithValue(context.Background(), seth.ContextErrorKey{}, errors.New("context error")) + + _, err = c.DeployContract(opts, "LinkToken", *abi, []byte(link_token.LinkTokenMetaData.Bin)) + require.Error(t, err, "did not abort deployment of link token contract due to context error") + require.Contains(t, err.Error(), "aborted contract deployment for", "incorrect context error") +} + +func newClientWithContractMapFromEnv(t *testing.T) *seth.Client { + c := newClient(t) + if TestEnv.ContractMap.Size() == 0 { + t.Fatal("contract map is empty") + } + + // create a copy of the map, so we don't have problem with side effects of modifying client's map + // impacting the global, underlying one + contractMap := seth.NewEmptyContractMap() + for k, v := range TestEnv.ContractMap.GetContractMap() { + contractMap.AddContract(k, v) + } + + c.ContractAddressToNameMap = contractMap + + // now let's recreate the Tracer, so that it has the same contract map + tracer, err := seth.NewTracer(c.ContractStore, c.ABIFinder, c.Cfg, contractMap, c.Addresses) + require.NoError(t, err, "failed to create tracer") + + c.Tracer = tracer + c.ABIFinder.ContractMap = contractMap + + return c +} + +func NewDebugContractSetup() ( + *seth.Client, + *network_debug_contract.NetworkDebugContract, + common.Address, + common.Address, + *bind.BoundContract, + error, +) { + cfg, err := seth.ReadConfig() + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, err + } + cs, err := seth.NewContractStore("./contracts/abi", "./contracts/bin") + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, err + } + addrs, pkeys, err := cfg.ParseKeys() + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, err + } + contractMap := seth.NewEmptyContractMap() + + abiFinder := seth.NewABIFinder(contractMap, cs) + tracer, err := seth.NewTracer(cs, &abiFinder, cfg, contractMap, addrs) + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, err + } + + nm, err := seth.NewNonceManager(cfg, addrs, pkeys) + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, errors.Wrap(err, seth.ErrCreateNonceManager) + } + + c, err := seth.NewClientRaw(cfg, addrs, pkeys, seth.WithContractStore(cs), seth.WithTracer(tracer), seth.WithNonceManager(nm)) + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, err + } + subData, err := c.DeployContractFromContractStore(c.NewTXOpts(), "NetworkDebugSubContract.abi") + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, err + } + data, err := c.DeployContractFromContractStore(c.NewTXOpts(), "NetworkDebugContract.abi", subData.Address) + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, err + } + contract, err := network_debug_contract.NewNetworkDebugContract(data.Address, c.Client) + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, err + } + return c, contract, data.Address, subData.Address, data.BoundContract, nil +} + +func TestMain(m *testing.M) { + if skip := os.Getenv("SKIP_MAIN_CONFIG"); skip == "" { + var err error + client, debugContract, debugContractAddress, debugSubContractAddress, debugContractRaw, err := NewDebugContractSetup() + if err != nil { + panic(err) + } + + linkTokenAbi, err := link_token.LinkTokenMetaData.GetAbi() + if err != nil { + panic(err) + } + linkDeploymentData, err := client.DeployContract(client.NewTXOpts(), "LinkToken", *linkTokenAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + if err != nil { + panic(err) + } + linkToken, err := link_token.NewLinkToken(linkDeploymentData.Address, client.Client) + if err != nil { + panic(err) + } + linkAbi, err := link_token.LinkTokenMetaData.GetAbi() + if err != nil { + panic(err) + } + client.ContractStore.AddABI("LinkToken", *linkAbi) + + contractMap := seth.NewEmptyContractMap() + for k, v := range client.ContractAddressToNameMap.GetContractMap() { + contractMap.AddContract(k, v) + } + + TestEnv = TestEnvironment{ + Client: client, + DebugContract: debugContract, + LinkTokenContract: linkToken, + DebugContractAddress: debugContractAddress, + DebugSubContractAddress: debugSubContractAddress, + DebugContractRaw: debugContractRaw, + ContractMap: contractMap, + } + } else { + seth.L.Warn().Msg("Skipping main suite setup") + } + + os.Exit(m.Run()) +} diff --git a/seth/client_test.go b/seth/client_test.go new file mode 100644 index 000000000..37893c594 --- /dev/null +++ b/seth/client_test.go @@ -0,0 +1,72 @@ +package seth_test + +import ( + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" + link_token "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/link" +) + +func TestRPCHealthCheckEnabled_Node_OK(t *testing.T) { + cfg, err := seth.ReadConfig() + require.NoError(t, err, "failed to read config") + cfg.CheckRpcHealthOnStart = true + + _, err = seth.NewClientWithConfig(cfg) + require.NoError(t, err, "failed to initialise seth") +} + +func TestRPCHealthCheckDisabled_Node_OK(t *testing.T) { + cfg, err := seth.ReadConfig() + require.NoError(t, err, "failed to read config") + cfg.CheckRpcHealthOnStart = false + + _, err = seth.NewClientWithConfig(cfg) + require.NoError(t, err, "failed to initialise seth") +} + +func TestRPCHealthCheckEnabled_Node_Unhealthy(t *testing.T) { + cfg, err := seth.ReadConfig() + require.NoError(t, err, "failed to read config") + + newPks, err := seth.NewEphemeralKeys(1) + require.NoError(t, err, "failed to create ephemeral keys") + + cfg.CheckRpcHealthOnStart = true + cfg.Network.PrivateKeys = []string{newPks[0]} + + _, err = seth.NewClientWithConfig(cfg) + require.Error(t, err, "expected error when connecting to unhealthy node") + require.Contains(t, err.Error(), seth.ErrRpcHealthCheckFailed, "expected error message when connecting to dead node") +} + +func TestRPCHealthCheckDisabled_Node_Unhealthy(t *testing.T) { + cfg, err := seth.ReadConfig() + require.NoError(t, err, "failed to read config") + + newPks, err := seth.NewEphemeralKeys(1) + require.NoError(t, err, "failed to create ephemeral keys") + + cfg.CheckRpcHealthOnStart = false + cfg.Network.PrivateKeys = []string{newPks[0]} + + _, err = seth.NewClientWithConfig(cfg) + require.NoError(t, err, "expected health check to be skipped") +} + +func TestContractLoader(t *testing.T) { + c, err := seth.NewClient() + require.NoError(t, err, "failed to initialise seth") + + loader := seth.NewContractLoader[link_token.LinkToken](c) + + contract, err := loader.LoadContract("LinkToken", TestEnv.LinkTokenContract.Address(), link_token.LinkTokenMetaData.GetAbi, link_token.NewLinkToken) + require.NoError(t, err, "failed to load contract") + + owner, err := contract.Owner(c.NewCallOpts()) + require.NoError(t, err, "failed to call loaded LINK contract") + require.NotEqual(t, common.Address{}, owner, "expected owner to be set") +} diff --git a/seth/client_trace_test.go b/seth/client_trace_test.go new file mode 100644 index 000000000..40a33d10d --- /dev/null +++ b/seth/client_trace_test.go @@ -0,0 +1,1796 @@ +package seth_test + +import ( + "encoding/json" + "fmt" + "io" + "math/big" + "os" + "path/filepath" + "strings" + "testing" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" + network_debug_contract "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/debug" + link_token "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/link" + "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/link_token_interface" + "github.com/smartcontractkit/chainlink-testing-framework/seth/test_utils" +) + +const ( + NoAnvilSupport = "Anvil doesn't support tracing" + FailedToDecode = "failed to decode transaction" +) + +func SkipAnvil(t *testing.T, c *seth.Client) { + if c.Cfg.Network.Name == "Anvil" { + t.Skip(NoAnvilSupport) + } +} + +// since we uploaded the contracts via Seth, we have the contract address in the map +// and we can trace the calls correctly even though both calls have the same signature +func TestTraceContractTracingSameMethodSignatures_UploadedViaSeth(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + var x int64 = 2 + var y int64 = 4 + tx, err := c.Decode(TestEnv.DebugContract.Trace(c.NewTXOpts(), big.NewInt(x), big.NewInt(y))) + require.NoError(t, err, FailedToDecode) + + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + require.Equal(t, 2, len(c.Tracer.GetDecodedCalls(tx.Hash)), "expected 2 decoded calls for this transaction") + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + + firstExpectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "3e41f135", + CallType: "CALL", + Method: "trace(int256,int256)", + Input: map[string]interface{}{"x": big.NewInt(x), "y": big.NewInt(y)}, + Output: map[string]interface{}{"0": big.NewInt(y + 2)}, + }, + Events: []seth.DecodedCommonLog{ + { + Signature: "TwoIndexEvent(uint256,address)", + EventData: map[string]interface{}{"roundId": big.NewInt(y), "startedBy": c.Addresses[0]}, + Address: TestEnv.DebugContractAddress, + Topics: []string{ + "0x33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b5", + "0x0000000000000000000000000000000000000000000000000000000000000004", + "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266", + }, + }, + }, + Comment: "", + } + + require.EqualValues(t, firstExpectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "first decoded call does not match") + + secondExpectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + ToAddress: strings.ToLower(TestEnv.DebugSubContractAddress.Hex()), + From: "NetworkDebugContract", + To: "NetworkDebugSubContract", + CommonData: seth.CommonData{ + Signature: "3e41f135", + ParentSignature: "3e41f135", + CallType: "CALL", + NestingLevel: 1, + Method: "trace(int256,int256)", + Input: map[string]interface{}{"x": big.NewInt(x), "y": big.NewInt(y)}, + Output: map[string]interface{}{"0": big.NewInt(y + 4)}, + }, + Comment: "", + } + + actualSecondEvents := c.Tracer.GetDecodedCalls(tx.Hash)[1].Events + c.Tracer.GetDecodedCalls(tx.Hash)[1].Events = nil + + require.EqualValues(t, secondExpectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[1], "second decoded call does not match") + require.Equal(t, 1, len(actualSecondEvents), "second decoded call events count does not match") + require.Equal(t, 3, len(actualSecondEvents[0].Topics), "second decoded event topics count does not match") + + expectedSecondEvents := []seth.DecodedCommonLog{ + { + Signature: "TwoIndexEvent(uint256,address)", + EventData: map[string]interface{}{"roundId": big.NewInt(6), "startedBy": TestEnv.DebugContractAddress}, + Address: TestEnv.DebugSubContractAddress, + Topics: []string{ + "0x33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b5", + "0x0000000000000000000000000000000000000000000000000000000000000006", + // this one changes dynamically depending on sender address + // "0x000000000000000000000000c351628eb244ec633d5f21fbd6621e1a683b1181", + }, + }, + } + actualSecondEvents[0].Topics = actualSecondEvents[0].Topics[0:2] + require.EqualValues(t, expectedSecondEvents, actualSecondEvents, "second decoded call events do not match") +} + +// we test a scenario, where because two contracts have the same method signature, both addresses +// were mapped to the same contract name (it doesn't happen always, it all depends on how data is ordered +// in the maps and that depends on addresses generated). We show that even if the initial mapping is incorrect, +// once we trace a transaction with different method signature, the mapping is corrected and the second transaction +// is traced correctly. +func TestTraceContractTracingSameMethodSignatures_UploadedManually(t *testing.T) { + c := newClient(t) + SkipAnvil(t, c) + + for k := range c.ContractAddressToNameMap.GetContractMap() { + delete(c.ContractAddressToNameMap.GetContractMap(), k) + } + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + // let's simulate this case, because it doesn't happen always, it all depends on the order of the + // contract map, which is non-deterministic (hash map with keys being dynamically generated addresses) + c.ContractAddressToNameMap.AddContract(TestEnv.DebugContractAddress.Hex(), "NetworkDebugContract") + c.ContractAddressToNameMap.AddContract(TestEnv.DebugSubContractAddress.Hex(), "NetworkDebugContract") + + var x int64 = 2 + var y int64 = 4 + + diffSigTx, txErr := c.Decode(TestEnv.DebugContract.TraceDifferent(c.NewTXOpts(), big.NewInt(x), big.NewInt(y))) + require.NoError(t, txErr, FailedToDecode) + sameSigTx, txErr := c.Decode(TestEnv.DebugContract.Trace(c.NewTXOpts(), big.NewInt(x), big.NewInt(y))) + require.NoError(t, txErr, FailedToDecode) + + require.NotNil(t, c.Tracer.GetDecodedCalls(diffSigTx.Hash), "expected decoded calls to contain the diffSig transaction hash") + require.Equal(t, 2, len(c.Tracer.GetDecodedCalls(diffSigTx.Hash)), "expected 2 decoded calls for diffSig transaction") + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + + firstDiffSigCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "30985bcc", + CallType: "CALL", + Method: "traceDifferent(int256,int256)", + Input: map[string]interface{}{"x": big.NewInt(x), "y": big.NewInt(y)}, + Output: map[string]interface{}{"0": big.NewInt(y + 2)}, + }, + Events: []seth.DecodedCommonLog{ + { + Signature: "OneIndexEvent(uint256)", + EventData: map[string]interface{}{"a": big.NewInt(x)}, + Address: TestEnv.DebugContractAddress, + Topics: []string{ + "0xeace1be0b97ec11f959499c07b9f60f0cc47bf610b28fda8fb0e970339cf3b35", + "0x0000000000000000000000000000000000000000000000000000000000000002", + }, + }, + }, + Comment: "", + } + + require.EqualValues(t, firstDiffSigCall, c.Tracer.GetDecodedCalls(diffSigTx.Hash)[0], "first diffSig decoded call does not match") + + secondDiffSigCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + ToAddress: strings.ToLower(TestEnv.DebugSubContractAddress.Hex()), + From: "NetworkDebugContract", + To: "NetworkDebugSubContract", + CommonData: seth.CommonData{ + Signature: "047c4425", + ParentSignature: "30985bcc", + NestingLevel: 1, + CallType: "CALL", + Method: "traceOneInt(int256)", + Input: map[string]interface{}{"x": big.NewInt(x + 2)}, + Output: map[string]interface{}{"r": big.NewInt(y + 3)}, + }, + Comment: "", + } + + c.Tracer.GetDecodedCalls(diffSigTx.Hash)[1].Events = nil + require.EqualValues(t, secondDiffSigCall, c.Tracer.GetDecodedCalls(diffSigTx.Hash)[1], "second diffSig decoded call does not match") + + require.Equal(t, 2, len(c.Tracer.GetAllDecodedCalls()), "expected 2 decoded transactons") + require.NotNil(t, c.Tracer.GetDecodedCalls(sameSigTx.Hash), "expected decoded calls to contain the sameSig transaction hash") + require.Equal(t, 2, len(c.Tracer.GetDecodedCalls(sameSigTx.Hash)), "expected 2 decoded calls for sameSig transaction") + + firstSameSigCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "3e41f135", + CallType: "CALL", + Method: "trace(int256,int256)", + Input: map[string]interface{}{"x": big.NewInt(x), "y": big.NewInt(y)}, + Output: map[string]interface{}{"0": big.NewInt(y + 2)}, + }, + Events: []seth.DecodedCommonLog{ + { + Signature: "TwoIndexEvent(uint256,address)", + EventData: map[string]interface{}{"roundId": big.NewInt(y), "startedBy": c.Addresses[0]}, + Address: TestEnv.DebugContractAddress, + Topics: []string{ + "0x33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b5", + "0x0000000000000000000000000000000000000000000000000000000000000004", + "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266", + }, + }, + }, + Comment: "", + } + + require.EqualValues(t, firstSameSigCall, c.Tracer.GetDecodedCalls(sameSigTx.Hash)[0], "first sameSig decoded call does not match") + + secondSameSigCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + ToAddress: strings.ToLower(TestEnv.DebugSubContractAddress.Hex()), + From: "NetworkDebugContract", + To: "NetworkDebugSubContract", + CommonData: seth.CommonData{ + Signature: "3e41f135", + ParentSignature: "3e41f135", + NestingLevel: 1, + CallType: "CALL", + Method: "trace(int256,int256)", + Input: map[string]interface{}{"x": big.NewInt(x), "y": big.NewInt(y)}, + Output: map[string]interface{}{"0": big.NewInt(y + 4)}, + }, + } + + actualSecondEvents := c.Tracer.GetDecodedCalls(sameSigTx.Hash)[1].Events + c.Tracer.GetDecodedCalls(sameSigTx.Hash)[1].Events = nil + + require.EqualValues(t, secondSameSigCall, c.Tracer.GetDecodedCalls(sameSigTx.Hash)[1], "second sameSig decoded call does not match") + require.Equal(t, 1, len(actualSecondEvents), "second sameSig decoded call events count does not match") + require.Equal(t, 3, len(actualSecondEvents[0].Topics), "second sameSig decoded event topics count does not match") + + expectedSecondEvents := []seth.DecodedCommonLog{ + { + Signature: "TwoIndexEvent(uint256,address)", + EventData: map[string]interface{}{"roundId": big.NewInt(6), "startedBy": TestEnv.DebugContractAddress}, + Address: TestEnv.DebugSubContractAddress, + Topics: []string{ + "0x33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b5", + "0x0000000000000000000000000000000000000000000000000000000000000006", + // third topic changes dynamically depending on sender address + // "0x000000000000000000000000c351628eb244ec633d5f21fbd6621e1a683b1181", + }, + }, + } + actualSecondEvents[0].Topics = actualSecondEvents[0].Topics[0:2] + require.EqualValues(t, expectedSecondEvents, actualSecondEvents, "second sameSig decoded call events do not match") +} + +func TestTraceContractTracingSameMethodSignaturesWarningInComment_UploadedManually(t *testing.T) { + c := newClient(t) + SkipAnvil(t, c) + + c.ContractAddressToNameMap = seth.NewEmptyContractMap() + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + sameSigTx, err := c.Decode(TestEnv.DebugContract.Trace(c.NewTXOpts(), big.NewInt(2), big.NewInt(2))) + require.NoError(t, err, "failed to send transaction") + + require.NotNil(t, c.Tracer.GetDecodedCalls(sameSigTx.Hash), "expected decoded calls to contain the transaction hash") + require.Equal(t, 2, len(c.Tracer.GetDecodedCalls(sameSigTx.Hash)), "expected 2 decoded calls for transaction") + require.Equal(t, "potentially inaccurate - method present in 1 other contracts", c.Tracer.GetDecodedCalls(sameSigTx.Hash)[1].Comment, "expected comment to be set") +} + +func TestTraceContractTracingWithCallback_UploadedViaSeth(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + // As this test might fail if run multiple times due to non-deterministic addressed in contract mapping + // which sometime causes the call to be traced and sometimes not (it all depends on the order of + // addresses in the map), I just remove potentially problematic ABI. + delete(c.ContractStore.ABIs, "DebugContractCallback.abi") + + var x int64 = 2 + var y int64 = 4 + tx, txErr := c.Decode(TestEnv.DebugContract.TraceSubWithCallback(c.NewTXOpts(), big.NewInt(x), big.NewInt(y))) + require.NoError(t, txErr, FailedToDecode) + + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + require.Equal(t, 3, len(c.Tracer.GetDecodedCalls(tx.Hash)), "expected 2 decoded calls for test transaction") + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + + firstExpectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + CallType: "CALL", + Signature: "3837a75e", + Method: "traceSubWithCallback(int256,int256)", + Input: map[string]interface{}{"x": big.NewInt(x), "y": big.NewInt(y)}, + Output: map[string]interface{}{"0": big.NewInt(y + 4)}, + }, + Events: []seth.DecodedCommonLog{ + { + Signature: "TwoIndexEvent(uint256,address)", + EventData: map[string]interface{}{"roundId": big.NewInt(1), "startedBy": c.Addresses[0]}, + Address: TestEnv.DebugContractAddress, + Topics: []string{ + "0x33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b5", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266", + }, + }, + }, + Comment: "", + } + + require.EqualValues(t, firstExpectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "first decoded call does not match") + + require.Equal(t, 2, len(c.Tracer.GetDecodedCalls(tx.Hash)[1].Events), "second decoded call events count does not match") + require.Equal(t, 3, len(c.Tracer.GetDecodedCalls(tx.Hash)[1].Events[0].Topics), "second decoded first event topics count does not match") + + separatedTopcis := c.Tracer.GetDecodedCalls(tx.Hash)[1].Events[0].Topics + separatedTopcis = separatedTopcis[0:2] + c.Tracer.GetDecodedCalls(tx.Hash)[1].Events[0].Topics = nil + + secondExpectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + ToAddress: strings.ToLower(TestEnv.DebugSubContractAddress.Hex()), + From: "NetworkDebugContract", + To: "NetworkDebugSubContract", + CommonData: seth.CommonData{ + Signature: "fa8fca7a", + CallType: "CALL", + Method: "traceWithCallback(int256,int256)", + NestingLevel: 1, + ParentSignature: "3837a75e", + Input: map[string]interface{}{"x": big.NewInt(x), "y": big.NewInt(y + 2)}, + Output: map[string]interface{}{"0": big.NewInt(y + 2)}, + }, + Events: []seth.DecodedCommonLog{ + { + Signature: "TwoIndexEvent(uint256,address)", + EventData: map[string]interface{}{"roundId": big.NewInt(6), "startedBy": TestEnv.DebugContractAddress}, + Address: TestEnv.DebugSubContractAddress, + }, + { + Signature: "OneIndexEvent(uint256)", + EventData: map[string]interface{}{"a": big.NewInt(y + 2)}, + Address: TestEnv.DebugSubContractAddress, + Topics: []string{ + "0xeace1be0b97ec11f959499c07b9f60f0cc47bf610b28fda8fb0e970339cf3b35", + "0x0000000000000000000000000000000000000000000000000000000000000006", + }, + }, + }, + Comment: "", + } + + require.EqualValues(t, secondExpectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[1], "second decoded call does not match") + + expectedTopics := []string{ + "0x33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b5", + "0x0000000000000000000000000000000000000000000000000000000000000006", + // third topic is dynamic (sender address), skip it + // "0x00000000000000000000000056fc17a65ccfec6b7ad0ade9bd9416cb365b9be8", + } + + require.EqualValues(t, expectedTopics, separatedTopcis, "second decoded first event topics do not match") + + thirdExpectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(TestEnv.DebugSubContractAddress.Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "NetworkDebugSubContract", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "fbcb8d07", + CallType: "CALL", + Method: "callbackMethod(int256)", + ParentSignature: "fa8fca7a", + NestingLevel: 2, + Input: map[string]interface{}{"x": big.NewInt(x + y)}, + Output: map[string]interface{}{"0": big.NewInt(y + x)}, + }, + Events: []seth.DecodedCommonLog{ + { + Signature: "CallbackEvent(int256)", + EventData: map[string]interface{}{"a": big.NewInt(y + 2)}, + Address: TestEnv.DebugContractAddress, + Topics: []string{ + "0xb16dba9242e1aa07ccc47228094628f72c8cc9699ee45d5bc8d67b84d3038c68", + "0x0000000000000000000000000000000000000000000000000000000000000006", + }, + }, + }, + } + require.EqualValues(t, thirdExpectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[2], "third decoded call does not match") +} + +// Here we show that partial tracing works even if we don't have the ABI for the contract. +// We still try to decode what we can even without ABI and that we can decode the other call +// for which we do have ABI. +func TestTraceContractTracingUnknownAbi(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + // simulate missing ABI + delete(c.ContractAddressToNameMap.GetContractMap(), strings.ToLower(TestEnv.DebugContractAddress.Hex())) + delete(c.ContractStore.ABIs, "NetworkDebugContract.abi") + + var x int64 = 2 + var y int64 = 4 + tx, txErr := c.Decode(TestEnv.DebugContract.TraceDifferent(c.NewTXOpts(), big.NewInt(x), big.NewInt(y))) + require.NoError(t, txErr, FailedToDecode) + + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + require.Equal(t, 2, len(c.Tracer.GetDecodedCalls(tx.Hash)), "expected 2 decoded calls for test transaction") + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + + firstExpectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: seth.UNKNOWN, + CommonData: seth.CommonData{ + Signature: "30985bcc", + CallType: "CALL", + Method: seth.UNKNOWN, + Input: make(map[string]interface{}), + Output: make(map[string]interface{}), + }, + Events: []seth.DecodedCommonLog{}, + Comment: seth.CommentMissingABI, + } + + require.EqualValues(t, firstExpectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "first decoded call does not match") + + secondExpectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + ToAddress: strings.ToLower(TestEnv.DebugSubContractAddress.Hex()), + From: seth.UNKNOWN, + To: "NetworkDebugSubContract", + CommonData: seth.CommonData{ + Signature: "047c4425", + CallType: "CALL", + ParentSignature: "30985bcc", + NestingLevel: 1, + Method: "traceOneInt(int256)", + Input: map[string]interface{}{"x": big.NewInt(x + 2)}, + Output: map[string]interface{}{"r": big.NewInt(y + 3)}, + }, + Comment: "", + } + + c.Tracer.GetDecodedCalls(tx.Hash)[1].Events = nil + require.EqualValues(t, secondExpectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[1], "second decoded call does not match") +} + +func TestTraceContractTracingNamedInputsAndOutputs(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + x := big.NewInt(1000) + var testString = "string" + tx, txErr := c.Decode(TestEnv.DebugContract.EmitNamedInputsOutputs(c.NewTXOpts(), x, testString)) + require.NoError(t, txErr, FailedToDecode) + + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "45f0c9e6", + CallType: "CALL", + Method: "emitNamedInputsOutputs(uint256,string)", + Input: map[string]interface{}{"inputVal1": x, "inputVal2": testString}, + Output: map[string]interface{}{"outputVal1": x, "outputVal2": testString}, + }, + Comment: "", + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingNamedInputsAnonymousOutputs(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + x := big.NewInt(1001) + var testString = "string" + tx, txErr := c.Decode(TestEnv.DebugContract.EmitInputsOutputs(c.NewTXOpts(), x, testString)) + require.NoError(t, txErr, "failed to send transaction") + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "d7a80205", + CallType: "CALL", + Method: "emitInputsOutputs(uint256,string)", + Input: map[string]interface{}{"inputVal1": x, "inputVal2": testString}, + Output: map[string]interface{}{"0": x, "1": testString}, + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +// Shows that when output mixes named and unnamed parameters, we can still decode the transaction, +// but that named outputs become unnamed and referenced by their index. +func TestTraceContractTracingIntInputsWithoutLength(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + x := big.NewInt(1001) + y := big.NewInt(2) + z := big.NewInt(26) + tx, txErr := c.Decode(TestEnv.DebugContract.EmitInts(c.NewTXOpts(), x, y, z)) + require.NoError(t, txErr, "failed to send transaction") + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "9e099652", + CallType: "CALL", + Method: "emitInts(int256,int128,uint256)", + Input: map[string]interface{}{"first": x, "second": y, "third": z}, + Output: map[string]interface{}{"0": x, "1": y, "2": z}, + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingAddressInputAndOutput(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + address := c.Addresses[0] + tx, txErr := c.Decode(TestEnv.DebugContract.EmitAddress(c.NewTXOpts(), address)) + require.NoError(t, txErr, "failed to send transaction") + + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "ec5c3ede", + CallType: "CALL", + Method: "emitAddress(address)", + Input: map[string]interface{}{"addr": address}, + Output: map[string]interface{}{"0": address}, + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingBytes32InputAndOutput(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + addrAsBytes := c.Addresses[0].Bytes() + addrAsBytes = append(addrAsBytes, c.Addresses[0].Bytes()...) + var bytes32 = [32]byte(addrAsBytes) + tx, txErr := c.Decode(TestEnv.DebugContract.EmitBytes32(c.NewTXOpts(), bytes32)) + require.NoError(t, txErr, FailedToDecode) + + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "33311ef3", + CallType: "CALL", + Method: "emitBytes32(bytes32)", + Input: map[string]interface{}{"input": bytes32}, + Output: map[string]interface{}{"output": bytes32}, + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingUint256ArrayInputAndOutput(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + uint256Array := []*big.Int{big.NewInt(1), big.NewInt(19271), big.NewInt(261), big.NewInt(271911), big.NewInt(821762721)} + tx, txErr := c.Decode(TestEnv.DebugContract.ProcessUintArray(c.NewTXOpts(), uint256Array)) + require.NoError(t, txErr, FailedToDecode) + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + var output []*big.Int + for _, x := range uint256Array { + output = append(output, big.NewInt(0).Add(x, big.NewInt(1))) + } + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "12d91233", + CallType: "CALL", + Method: "processUintArray(uint256[])", + Input: map[string]interface{}{"input": uint256Array}, + Output: map[string]interface{}{"0": output}, + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingAddressArrayInputAndOutput(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + addressArray := []common.Address{c.Addresses[0], TestEnv.DebugSubContractAddress} + tx, txErr := c.Decode(TestEnv.DebugContract.ProcessAddressArray(c.NewTXOpts(), addressArray)) + require.NoError(t, txErr, FailedToDecode) + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "e1111f79", + CallType: "CALL", + Method: "processAddressArray(address[])", + Input: map[string]interface{}{"input": addressArray}, + Output: map[string]interface{}{"0": addressArray}, + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingStructWithDynamicFieldsInputAndOutput(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + data := network_debug_contract.NetworkDebugContractData{ + Name: "my awesome name", + Values: []*big.Int{big.NewInt(2), big.NewInt(266810), big.NewInt(473878233)}, + } + tx, txErr := c.Decode(TestEnv.DebugContract.ProcessDynamicData(c.NewTXOpts(), data)) + require.NoError(t, txErr, FailedToDecode) + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + expected := struct { + Name string `json:"name"` + Values []*big.Int `json:"values"` + }{ + Name: data.Name, + Values: data.Values, + } + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "7fdc8fe1", + CallType: "CALL", + Method: "processDynamicData((string,uint256[]))", + Input: map[string]interface{}{"data": expected}, + Output: map[string]interface{}{"0": expected}, + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingStructArrayWithDynamicFieldsInputAndOutput(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + data := network_debug_contract.NetworkDebugContractData{ + Name: "my awesome name", + Values: []*big.Int{big.NewInt(2), big.NewInt(266810), big.NewInt(473878233)}, + } + dataArray := [3]network_debug_contract.NetworkDebugContractData{data, data, data} + tx, txErr := c.Decode(TestEnv.DebugContract.ProcessFixedDataArray(c.NewTXOpts(), dataArray)) + require.NoError(t, txErr, FailedToDecode) + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + input := [3]struct { + Name string `json:"name"` + Values []*big.Int `json:"values"` + }{ + { + Name: data.Name, + Values: data.Values, + }, + { + Name: data.Name, + Values: data.Values, + }, + { + Name: data.Name, + Values: data.Values, + }, + } + + output := [2]struct { + Name string `json:"name"` + Values []*big.Int `json:"values"` + }{ + { + Name: data.Name, + Values: data.Values, + }, + { + Name: data.Name, + Values: data.Values, + }, + } + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "99adad2e", + CallType: "CALL", + Method: "processFixedDataArray((string,uint256[])[3])", + Input: map[string]interface{}{"data": input}, + Output: map[string]interface{}{"0": output}, + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingNestedStructsWithDynamicFieldsInputAndOutput(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + data := network_debug_contract.NetworkDebugContractNestedData{ + Data: network_debug_contract.NetworkDebugContractData{ + Name: "my awesome name", + Values: []*big.Int{big.NewInt(2), big.NewInt(266810), big.NewInt(473878233)}, + }, + DynamicBytes: []byte("dynamic bytes"), + } + tx, txErr := c.Decode(TestEnv.DebugContract.ProcessNestedData(c.NewTXOpts(), data)) + require.NoError(t, txErr, "failed to send transaction") + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + input := struct { + Data struct { + Name string `json:"name"` + Values []*big.Int `json:"values"` + } `json:"data"` + DynamicBytes []byte `json:"dynamicBytes"` + }{ + struct { + Name string `json:"name"` + Values []*big.Int `json:"values"` + }{ + Name: data.Data.Name, + Values: data.Data.Values, + }, + data.DynamicBytes, + } + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "7f12881c", + CallType: "CALL", + Method: "processNestedData(((string,uint256[]),bytes))", + Input: map[string]interface{}{"data": input}, + Output: map[string]interface{}{"0": input}, + }, + Comment: "", + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingNestedStructsWithDynamicFieldsInputAndStructOutput(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + data := network_debug_contract.NetworkDebugContractData{ + Name: "my awesome name", + Values: []*big.Int{big.NewInt(2), big.NewInt(266810), big.NewInt(473878233)}, + } + tx, txErr := c.Decode(TestEnv.DebugContract.ProcessNestedData0(c.NewTXOpts(), data)) + require.NoError(t, txErr, "failed to send transaction") + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + input := struct { + Name string `json:"name"` + Values []*big.Int `json:"values"` + }{ + Name: data.Name, + Values: data.Values, + } + + hash := crypto.Keccak256Hash([]byte(input.Name)) + + output := struct { + Data struct { + Name string `json:"name"` + Values []*big.Int `json:"values"` + } `json:"data"` + DynamicBytes []byte `json:"dynamicBytes"` + }{ + struct { + Name string `json:"name"` + Values []*big.Int `json:"values"` + }{ + Name: data.Name, + Values: data.Values, + }, + hash.Bytes(), + } + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "f499af2a", + CallType: "CALL", + Method: "processNestedData((string,uint256[]))", + Input: map[string]interface{}{"data": input}, + Output: map[string]interface{}{"0": output}, + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingPayable(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + var value int64 = 1000 + tx, txErr := c.Decode(TestEnv.DebugContract.Pay(c.NewTXOpts(seth.WithValue(big.NewInt(value))))) + require.NoError(t, txErr, FailedToDecode) + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "1b9265b8", + CallType: "CALL", + Method: "pay()", + Output: map[string]interface{}{}, + }, + Value: value, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingFallback(t *testing.T) { + t.Skip("Need to investigate further how to support it, the call succeeds, but we fail to decode it") + // our ABIFinder doesn't know anything about fallback, but maybe we should use it, when everything else fails? + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + tx, txErr := c.Decode(TestEnv.DebugContractRaw.RawTransact(c.NewTXOpts(), []byte("iDontExist"))) + require.NoError(t, txErr, FailedToDecode) + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transaction") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "1b9265b8", + CallType: "CALL", + Method: "pay()", + Output: map[string]interface{}{}, + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingReceive(t *testing.T) { + t.Skip("Need to investigate further how to support it, the call succreds, but we fail to match the signature as input is 0x") + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + value := big.NewInt(29121) + tx, txErr := c.Decode(TestEnv.DebugContract.Receive(c.NewTXOpts(seth.WithValue(value)))) + require.NoError(t, txErr, FailedToDecode) + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transaction") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "1b9265b8", + Method: "pay()", + CallType: "CALL", + Output: map[string]interface{}{}, + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingEnumInputAndOutput(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + var status uint8 = 1 // Active + tx, txErr := c.Decode(TestEnv.DebugContract.SetStatus(c.NewTXOpts(), status)) + require.NoError(t, txErr, FailedToDecode) + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "2e49d78b", + CallType: "CALL", + Method: "setStatus(uint8)", + Input: map[string]interface{}{"status": status}, + Output: map[string]interface{}{"0": status}, + }, + Comment: "", + Events: []seth.DecodedCommonLog{ + { + Signature: "CurrentStatus(uint8)", + EventData: map[string]interface{}{"status": status}, + Address: TestEnv.DebugContractAddress, + Topics: []string{ + "0xbea054406fdf249b05d1aef1b5f848d62d902d94389fca702b2d8337677c359a", + "0x0000000000000000000000000000000000000000000000000000000000000001", + }, + }, + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingNonIndexedEventParameter(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + tx, txErr := c.Decode(TestEnv.DebugContract.EmitNoIndexEventString(c.NewTXOpts())) + require.NoError(t, txErr, "failed to send transaction") + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "788c4772", + CallType: "CALL", + Method: "emitNoIndexEventString()", + Input: nil, + Output: map[string]interface{}{}, + }, + Comment: "", + Events: []seth.DecodedCommonLog{ + { + Signature: "NoIndexEventString(string)", + EventData: map[string]interface{}{"str": "myString"}, + Address: TestEnv.DebugContractAddress, + Topics: []string{ + "0x25b7adba1b046a19379db4bc06aa1f2e71604d7b599a0ee8783d58110f00e16a", + }, + }, + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingEventThreeIndexedParameters(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + tx, txErr := c.Decode(TestEnv.DebugContract.EmitThreeIndexEvent(c.NewTXOpts())) + require.NoError(t, txErr, FailedToDecode) + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "aa3fdcf4", + CallType: "CALL", + Method: "emitThreeIndexEvent()", + Input: nil, + Output: map[string]interface{}{}, + }, + Comment: "", + Events: []seth.DecodedCommonLog{ + { + Signature: "ThreeIndexEvent(uint256,address,uint256)", + EventData: map[string]interface{}{"roundId": big.NewInt(1), "startedAt": big.NewInt(3), "startedBy": c.Addresses[0]}, + Address: TestEnv.DebugContractAddress, + Topics: []string{ + "0x5660e8f93f0146f45abcd659e026b75995db50053cbbca4d7f365934ade68bf3", + "0x0000000000000000000000000000000000000000000000000000000000000001", + "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "0x0000000000000000000000000000000000000000000000000000000000000003", + }, + }, + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingEventFourMixedParameters(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + tx, txErr := c.Decode(TestEnv.DebugContract.EmitFourParamMixedEvent(c.NewTXOpts())) + require.NoError(t, txErr, FailedToDecode) + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "c2124b22", + CallType: "CALL", + Method: "emitFourParamMixedEvent()", + Input: nil, + Output: map[string]interface{}{}, + }, + Comment: "", + Events: []seth.DecodedCommonLog{ + { + Signature: "ThreeIndexAndOneNonIndexedEvent(uint256,address,uint256,string)", + EventData: map[string]interface{}{"roundId": big.NewInt(2), "startedAt": big.NewInt(3), "startedBy": c.Addresses[0], "dataId": "some id"}, + Address: TestEnv.DebugContractAddress, + Topics: []string{ + "0x56c2ea44ba516098cee0c181dd9d8db262657368b6e911e83ae0ccfae806c73d", + "0x0000000000000000000000000000000000000000000000000000000000000002", + "0x000000000000000000000000f39fd6e51aad88f6f4ce6ab8827279cfffb92266", + "0x0000000000000000000000000000000000000000000000000000000000000003", + }, + }, + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractAll(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_All + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + revertedTx, txErr := TestEnv.DebugContract.AlwaysRevertsCustomError(c.NewTXOpts()) + require.NoError(t, txErr, "transaction sending should not fail") + _, decodeErr := c.Decode(revertedTx, txErr) + require.Error(t, decodeErr, "transaction should have reverted") + require.Equal(t, "error type: CustomErr, error values: [12 21]", decodeErr.Error(), "expected error message to contain the reverted error type and values") + + okTx, txErr := TestEnv.DebugContract.AddCounter(c.NewTXOpts(), big.NewInt(1), big.NewInt(2)) + require.NoError(t, txErr, "transaction should not have reverted") + _, decodeErr = c.Decode(okTx, txErr) + require.NoError(t, decodeErr, "transaction decoding should not err") + require.Equal(t, 2, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "5e9c80d6", + CallType: "CALL", + Method: "alwaysRevertsCustomError()", + Output: map[string]interface{}{}, + Error: "execution reverted", + }, + } + + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(revertedTx.Hash().Hex())[0], "reverted decoded call does not match") + + expectedCall = &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "23515760", + CallType: "CALL", + Method: "addCounter(int256,int256)", + Output: map[string]interface{}{"value": big.NewInt(2)}, + Input: map[string]interface{}{"idx": big.NewInt(1), "x": big.NewInt(2)}, + }, + } + + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(okTx.Hash().Hex())[0], "successful decoded call does not match") +} + +func TestTraceContractOnlyReverted(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_Reverted + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + revertedTx, txErr := TestEnv.DebugContract.AlwaysRevertsCustomError(c.NewTXOpts()) + require.NoError(t, txErr, "transaction sending should not fail") + _, decodeErr := c.Decode(revertedTx, txErr) + require.Error(t, decodeErr, "transaction should have reverted") + require.Equal(t, "error type: CustomErr, error values: [12 21]", decodeErr.Error(), "expected error message to contain the reverted error type and values") + + okTx, txErr := TestEnv.DebugContract.AddCounter(c.NewTXOpts(), big.NewInt(1), big.NewInt(2)) + require.NoError(t, txErr, "transaction should not have reverted") + _, decodeErr = c.Decode(okTx, txErr) + require.NoError(t, decodeErr, "transaction decoding should not err") + + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "5e9c80d6", + CallType: "CALL", + Method: "alwaysRevertsCustomError()", + Output: map[string]interface{}{}, + Error: "execution reverted", + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(revertedTx.Hash().Hex())[0], "decoded call does not match") +} + +func TestTraceContractNone(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + // when this level nothing is ever traced or debugged + c.Cfg.TracingLevel = seth.TracingLevel_None + + revertedTx, txErr := TestEnv.DebugContract.AlwaysRevertsCustomError(c.NewTXOpts()) + require.NoError(t, txErr, "transaction sending should not fail") + _, decodeErr := c.Decode(revertedTx, txErr) + require.Error(t, decodeErr, "transaction should have reverted") + require.Equal(t, "error type: CustomErr, error values: [12 21]", decodeErr.Error(), "expected error message to contain the reverted error type and values") + + okTx, txErr := TestEnv.DebugContract.AddCounter(c.NewTXOpts(), big.NewInt(1), big.NewInt(2)) + require.NoError(t, txErr, "transaction should not have reverted") + _, decodeErr = c.Decode(okTx, txErr) + require.NoError(t, decodeErr, "transaction decoding should not err") + + require.Empty(t, c.Tracer.GetAllDecodedCalls(), "expected 1 decoded transaction") +} + +func TestTraceContractRevertedErrNoValues(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_Reverted + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + tx, txErr := TestEnv.DebugContract.AlwaysRevertsCustomErrorNoValues(c.NewTXOpts()) + require.NoError(t, txErr, "transaction should have reverted") + _, decodeErr := c.Decode(tx, txErr) + require.Error(t, decodeErr, "transaction should have reverted") + require.Equal(t, "error type: CustomErrNoValues, error values: []", decodeErr.Error(), "expected error message to contain the reverted error type and values") + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "b600141f", + CallType: "CALL", + Method: "alwaysRevertsCustomErrorNoValues()", + Output: map[string]interface{}{}, + Error: "execution reverted", + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash().Hex())[0], "decoded call does not match") +} + +func TestTraceCallRevertFunctionInTheContract(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_Reverted + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + tx, txErr := TestEnv.DebugContract.CallRevertFunctionInTheContract(c.NewTXOpts()) + require.NoError(t, txErr, "transaction should have reverted") + _, decodeErr := c.Decode(tx, txErr) + require.Error(t, decodeErr, "transaction should have reverted") + require.Equal(t, "error type: CustomErr, error values: [12 21]", decodeErr.Error(), "expected error message to contain the reverted error type and values") + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "9349d00b", + CallType: "CALL", + Method: "callRevertFunctionInTheContract()", + Output: map[string]interface{}{}, + Error: "execution reverted", + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash().Hex())[0], "decoded call does not match") +} + +func TestTraceCallRevertFunctionInSubContract(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_Reverted + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + x := big.NewInt(1001) + y := big.NewInt(2) + tx, txErr := TestEnv.DebugContract.CallRevertFunctionInSubContract(c.NewTXOpts(), x, y) + require.NoError(t, txErr, "transaction should have reverted") + _, decodeErr := c.Decode(tx, txErr) + require.Error(t, decodeErr, "transaction should have reverted") + require.Equal(t, "error type: CustomErr, error values: [1001 2]", decodeErr.Error(), "expected error message to contain the reverted error type and values") + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transaction") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "11b3c478", + CallType: "CALL", + Method: "callRevertFunctionInSubContract(uint256,uint256)", + Input: map[string]interface{}{"x": x, "y": y}, + Output: map[string]interface{}{}, + Error: "execution reverted", + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash().Hex())[0], "decoded call does not match") +} + +func TestTraceCallRevertInCallback(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_Reverted + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + linkAbi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + c.ContractStore.AddABI("LinkToken", *linkAbi) + c.ContractStore.AddBIN("LinkToken", common.FromHex(link_token.LinkTokenMetaData.Bin)) + + amount := big.NewInt(0) + tx, txErr := TestEnv.LinkTokenContract.TransferAndCall(c.NewTXOpts(), TestEnv.DebugContractAddress, amount, []byte{}) + require.NoError(t, txErr, "transaction should have reverted") + _, decodeErr := c.Decode(tx, txErr) + require.Error(t, decodeErr, "transaction should have reverted") + require.Equal(t, "error type: CustomErr, error values: [99 101]", decodeErr.Error(), "expected error message to contain the reverted error type and values") +} + +func TestTraceOldPragmaNoRevertReason(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TracingLevel = seth.TracingLevel_Reverted + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + // this is old Link contract used on Ethereum Mainnet that's in pragma 0.4 + linkAbi, err := link_token_interface.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + + data, err := c.DeployContract(c.NewTXOpts(), "LinkTokenInterface", *linkAbi, common.FromHex(link_token_interface.LinkTokenMetaData.Bin)) + require.NoError(t, err, "failed to deploy contract") + + instance, err := link_token_interface.NewLinkToken(data.Address, c.Client) + require.NoError(t, err, "failed to create contract instance") + + amount := big.NewInt(0) + tx, txErr := instance.TransferAndCall(c.NewTXOpts(), TestEnv.DebugContractAddress, amount, []byte{}) + require.NoError(t, txErr, "transaction should have reverted") + _, decodeErr := c.Decode(tx, txErr) + require.Error(t, decodeErr, "transaction should have reverted") + require.Equal(t, "execution reverted", decodeErr.Error(), "expected error message to contain the reverted error type and values") +} + +func TestTraceeRevertReasonNonRootSender(t *testing.T) { + cBeta := newClientWithContractMapFromEnv(t) + SkipAnvil(t, cBeta) + + cfg := cBeta.Cfg + one := int64(1) + cfg.EphemeralAddrs = &one + cfg.TracingLevel = seth.TracingLevel_Reverted + cfg.TraceOutputs = []string{seth.TraceOutput_Console} + + c, err := seth.NewClientWithConfig(cfg) + require.NoError(t, err, "failed to create client") + + x := big.NewInt(1001) + y := big.NewInt(2) + tx, txErr := TestEnv.DebugContract.CallRevertFunctionInSubContract(c.NewTXKeyOpts(1), x, y) + require.NoError(t, txErr, "transaction should have reverted") + _, decodeErr := c.Decode(tx, txErr) + require.Error(t, decodeErr, "transaction should have reverted") + require.Equal(t, "error type: CustomErr, error values: [1001 2]", decodeErr.Error(), "expected error message to contain the reverted error type and values") + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transaction") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[1].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "11b3c478", + CallType: "CALL", + Method: "callRevertFunctionInSubContract(uint256,uint256)", + Input: map[string]interface{}{"x": x, "y": y}, + Output: map[string]interface{}{}, + Error: "execution reverted", + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash().Hex())[0], "decoded call does not match") +} + +func TestTraceContractTracingClientInitialisesTracerIfTracingIsEnabled(t *testing.T) { + cfg, err := test_utils.CopyConfig(TestEnv.Client.Cfg) + require.NoError(t, err, "failed to copy config") + + as, err := seth.NewContractStore(filepath.Join(cfg.ConfigDir, cfg.ABIDir), filepath.Join(cfg.ConfigDir, cfg.BINDir)) + require.NoError(t, err, "failed to create contract store") + + nm, err := seth.NewNonceManager(cfg, TestEnv.Client.Addresses, TestEnv.Client.PrivateKeys) + require.NoError(t, err, "failed to create nonce manager") + + cfg.TracingLevel = seth.TracingLevel_All + cfg.TraceOutputs = []string{seth.TraceOutput_Console} + cfg.Network.TxnTimeout = seth.MustMakeDuration(time.Duration(5 * time.Second)) + + c, err := seth.NewClientRaw( + cfg, + TestEnv.Client.Addresses, + TestEnv.Client.PrivateKeys, + seth.WithContractStore(as), + seth.WithNonceManager(nm), + ) + require.NoError(t, err, "failed to create client") + SkipAnvil(t, c) + + x := big.NewInt(1001) + y := big.NewInt(2) + z := big.NewInt(26) + tx, txErr := c.Decode(TestEnv.DebugContract.EmitInts(c.NewTXOpts(), x, y, z)) + require.NoError(t, txErr, "failed to send transaction") + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "9e099652", + CallType: "CALL", + Method: "emitInts(int256,int128,uint256)", + Input: map[string]interface{}{"first": x, "second": y, "third": z}, + Output: map[string]interface{}{"0": x, "1": y, "2": z}, + }, + } + + removeGasDataFromDecodedCalls(c.Tracer.GetAllDecodedCalls()) + require.EqualValues(t, expectedCall, c.Tracer.GetDecodedCalls(tx.Hash)[0], "decoded call does not match") +} + +func TestTraceContractTracingSaveToJson(t *testing.T) { + cfg, err := test_utils.CopyConfig(TestEnv.Client.Cfg) + require.NoError(t, err, "failed to copy config") + + as, err := seth.NewContractStore(filepath.Join(cfg.ConfigDir, cfg.ABIDir), filepath.Join(cfg.ConfigDir, cfg.BINDir)) + require.NoError(t, err, "failed to create contract store") + + nm, err := seth.NewNonceManager(cfg, TestEnv.Client.Addresses, TestEnv.Client.PrivateKeys) + require.NoError(t, err, "failed to create nonce manager") + + cfg.TracingLevel = seth.TracingLevel_All + cfg.TraceOutputs = []string{seth.TraceOutput_JSON} + cfg.Network.TxnTimeout = seth.MustMakeDuration(time.Duration(5 * time.Second)) + + c, err := seth.NewClientRaw( + cfg, + TestEnv.Client.Addresses, + TestEnv.Client.PrivateKeys, + seth.WithContractStore(as), + seth.WithNonceManager(nm), + ) + require.NoError(t, err, "failed to create client") + SkipAnvil(t, c) + + x := big.NewInt(1001) + y := big.NewInt(2) + z := big.NewInt(26) + tx, txErr := c.Decode(TestEnv.DebugContract.EmitInts(c.NewTXOpts(), x, y, z)) + require.NoError(t, txErr, "failed to send transaction") + require.Equal(t, 1, len(c.Tracer.GetAllDecodedCalls()), "expected 1 decoded transacton") + require.NotNil(t, c.Tracer.GetDecodedCalls(tx.Hash), "expected decoded calls to contain the transaction hash") + + fileName := filepath.Join(c.Cfg.ArtifactsDir, "traces", fmt.Sprintf("%s.json", tx.Hash)) + t.Cleanup(func() { + _ = os.Remove(fileName) + }) + + expectedCall := &seth.DecodedCall{ + FromAddress: strings.ToLower(c.Addresses[0].Hex()), + ToAddress: strings.ToLower(TestEnv.DebugContractAddress.Hex()), + From: "you", + To: "NetworkDebugContract", + CommonData: seth.CommonData{ + Signature: "9e099652", + CallType: "CALL", + Method: "emitInts(int256,int128,uint256)", + Input: map[string]interface{}{"first": 1001.0, "second": 2.0, "third": 26.0}, + Output: map[string]interface{}{"0": 1001.0, "1": 2.0, "2": 26.0}, + }, + Comment: "", + } + + f, err := os.OpenFile(fileName, os.O_RDONLY, 0666) + require.NoError(t, err, "expected trace file to exist") + + var readCall []seth.DecodedCall + + defer func() { _ = f.Close() }() + b, _ := io.ReadAll(f) + err = json.Unmarshal(b, &readCall) + require.NoError(t, err, "failed to unmarshal trace file") + + removeGasDataFromDecodedCalls(map[string][]*seth.DecodedCall{tx.Hash: {&readCall[0]}}) + + require.Equal(t, 1, len(readCall), "expected 1 decoded transaction") + require.EqualValues(t, expectedCall, &readCall[0], "decoded call does not match one read from file") +} + +func TestTraceContractTracingSaveToDot(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TraceOutputs = []string{seth.TraceOutput_DOT} + c.Cfg.TracingLevel = seth.TracingLevel_All + + linkTokenAbi, err := link_token.LinkTokenMetaData.GetAbi() + if err != nil { + panic(err) + } + linkDeploymentData, err := c.DeployContract(c.NewTXOpts(), "LinkToken", *linkTokenAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + if err != nil { + panic(err) + } + linkToken, err := link_token.NewLinkToken(linkDeploymentData.Address, c.Client) + if err != nil { + panic(err) + } + + _, err = c.Decode(linkToken.GrantMintRole(c.NewTXOpts(), c.MustGetRootKeyAddress())) + if err != nil { + fmt.Println("failed to grant mint LINK role") + os.Exit(1) + } + + _, err = c.Decode(linkToken.Mint(c.NewTXOpts(), c.MustGetRootKeyAddress(), big.NewInt(1000000000000000000))) + if err != nil { + fmt.Println("failed to mint LINK") + os.Exit(1) + } + + debugAbi, err := abi.JSON(strings.NewReader(network_debug_contract.NetworkDebugContractMetaData.ABI)) + if err != nil { + fmt.Println("failed to get debug contract ABI") + os.Exit(1) + } + + var x int64 = 6 + var y int64 = 5 + + req, err := debugAbi.Pack( + "traceWithValidate", + big.NewInt(x), + big.NewInt(y), + ) + + if err != nil { + fmt.Println("failed to pack arguments") + os.Exit(1) + } + + amount := big.NewInt(10) + decodedTx, decodeErr := c.Decode(linkToken.TransferAndCall(c.NewTXOpts(), TestEnv.DebugContractAddress, amount, req)) + require.NoError(t, decodeErr, "transaction should not have reverted") + + fileName := filepath.Join(c.Cfg.ArtifactsDir, "dot_graphs", fmt.Sprintf("%s.dot", decodedTx.Hash)) + t.Cleanup(func() { + _ = os.Remove(fileName) + }) + + f, err := os.OpenFile(fileName, os.O_RDONLY, 0666) + require.NoError(t, err, "expected trace file to exist") + require.NotNil(t, f, "expected file to exist") + s, err := f.Stat() + require.NoError(t, err, "expected file to exist") + require.Greater(t, s.Size(), int64(0), "expected file to have content") +} + +func TestTraceVariousCallTypesAndNestingLevels(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + c.Cfg.TraceOutputs = []string{seth.TraceOutput_Console} + c.Cfg.TracingLevel = seth.TracingLevel_All + + linkTokenAbi, err := link_token.LinkTokenMetaData.GetAbi() + if err != nil { + panic(err) + } + linkDeploymentData, err := c.DeployContract(c.NewTXOpts(), "LinkToken", *linkTokenAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + if err != nil { + panic(err) + } + linkToken, err := link_token.NewLinkToken(linkDeploymentData.Address, c.Client) + if err != nil { + panic(err) + } + + _, err = c.Decode(linkToken.GrantMintRole(c.NewTXOpts(), c.MustGetRootKeyAddress())) + if err != nil { + fmt.Println("failed to grant mint LINK role") + os.Exit(1) + } + + _, err = c.Decode(linkToken.Mint(c.NewTXOpts(), c.MustGetRootKeyAddress(), big.NewInt(1000000000000000000))) + if err != nil { + fmt.Println("failed to mint LINK") + os.Exit(1) + } + + debugAbi, err := abi.JSON(strings.NewReader(network_debug_contract.NetworkDebugContractMetaData.ABI)) + if err != nil { + fmt.Println("failed to get debug contract ABI") + os.Exit(1) + } + + var x int64 = 6 + var y int64 = 5 + + req, err := debugAbi.Pack( + "traceWithValidate", + big.NewInt(x), + big.NewInt(y), + ) + + if err != nil { + fmt.Println("failed to pack arguments") + os.Exit(1) + } + + amount := big.NewInt(10) + decodedTx, decodeErr := c.Decode(linkToken.TransferAndCall(c.NewTXOpts(), TestEnv.DebugContractAddress, amount, req)) + require.NoError(t, decodeErr, "transaction should not have reverted") + require.Equal(t, 3, len(c.Tracer.GetAllDecodedCalls()), "expected 3 decoded transaction") + require.Equal(t, 9, len(c.Tracer.GetDecodedCalls(decodedTx.Hash)), "expected 9 decoded transaction for tx: "+decodedTx.Hash) + + require.Equal(t, "CALL", c.Tracer.GetDecodedCalls(decodedTx.Hash)[0].CallType, "expected call type to be CALL") + require.Equal(t, 0, c.Tracer.GetDecodedCalls(decodedTx.Hash)[0].NestingLevel, "expected nesting level to be 0") + + require.Equal(t, "CALL", c.Tracer.GetDecodedCalls(decodedTx.Hash)[1].CallType, "expected call type to be CALL") + require.Equal(t, 1, c.Tracer.GetDecodedCalls(decodedTx.Hash)[1].NestingLevel, "expected nesting level to be 1") + + require.Equal(t, "DELEGATECALL", c.Tracer.GetDecodedCalls(decodedTx.Hash)[2].CallType, "expected call type to be DELEGATECALL") + require.Equal(t, 2, c.Tracer.GetDecodedCalls(decodedTx.Hash)[2].NestingLevel, "expected nesting level to be 2") + + require.Equal(t, "CALL", c.Tracer.GetDecodedCalls(decodedTx.Hash)[3].CallType, "expected call type to be CALL") + require.Equal(t, 3, c.Tracer.GetDecodedCalls(decodedTx.Hash)[3].NestingLevel, "expected nesting level to be 3") + + require.Equal(t, "STATICCALL", c.Tracer.GetDecodedCalls(decodedTx.Hash)[4].CallType, "expected call type to be STATICCALL") + require.Equal(t, 2, c.Tracer.GetDecodedCalls(decodedTx.Hash)[4].NestingLevel, "expected nesting level to be 2") + + require.Equal(t, "STATICCALL", c.Tracer.GetDecodedCalls(decodedTx.Hash)[5].CallType, "expected call type to be STATICCALL") + require.Equal(t, 3, c.Tracer.GetDecodedCalls(decodedTx.Hash)[5].NestingLevel, "expected nesting level to be 3") + + require.Equal(t, "CALL", c.Tracer.GetDecodedCalls(decodedTx.Hash)[6].CallType, "expected call type to be CALL") + require.Equal(t, 2, c.Tracer.GetDecodedCalls(decodedTx.Hash)[6].NestingLevel, "expected nesting level to be 2") + + require.Equal(t, "CALL", c.Tracer.GetDecodedCalls(decodedTx.Hash)[7].CallType, "expected call type to be CALL") + require.Equal(t, 3, c.Tracer.GetDecodedCalls(decodedTx.Hash)[7].NestingLevel, "expected nesting level to be 3") + + require.Equal(t, "CALL", c.Tracer.GetDecodedCalls(decodedTx.Hash)[8].CallType, "expected call type to be CALL") + require.Equal(t, 4, c.Tracer.GetDecodedCalls(decodedTx.Hash)[8].NestingLevel, "expected nesting level to be 4") +} + +func removeGasDataFromDecodedCalls(decodedCall map[string][]*seth.DecodedCall) { + for _, decodedCalls := range decodedCall { + for _, call := range decodedCalls { + call.GasUsed = 0 + call.GasLimit = 0 + } + } +} diff --git a/seth/cmd/seth.go b/seth/cmd/seth.go new file mode 100644 index 000000000..9df4e0669 --- /dev/null +++ b/seth/cmd/seth.go @@ -0,0 +1,307 @@ +package seth + +import ( + "context" + "fmt" + "math/big" + "os" + "path/filepath" + + "github.com/ethereum/go-ethereum/rpc" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/ethclient" + "github.com/pelletier/go-toml/v2" + "github.com/pkg/errors" + "github.com/urfave/cli/v2" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" +) + +const ( + ErrNoNetwork = "no network specified, use -n flag. Ex.: 'seth -n Geth stats' or -u and -c flags. Ex.: 'seth -u http://localhost:8545 -c 1337 stats'" +) + +var C *seth.Client + +func RunCLI(args []string) error { + app := &cli.App{ + Name: "seth", + Version: "v1.0.0", + Usage: "seth CLI", + UsageText: `utility to create and control Ethereum keys and give you more debug info about chains`, + Flags: []cli.Flag{ + &cli.StringFlag{Name: "networkName", Aliases: []string{"n"}}, + &cli.StringFlag{Name: "url", Aliases: []string{"u"}}, + }, + Before: func(cCtx *cli.Context) error { + networkName := cCtx.String("networkName") + url := cCtx.String("url") + if networkName == "" && url == "" { + return errors.New(ErrNoNetwork) + } + if networkName != "" { + _ = os.Setenv(seth.NETWORK_ENV_VAR, networkName) + } + if url != "" { + _ = os.Setenv(seth.URL_ENV_VAR, url) + } + if cCtx.Args().Len() > 0 && cCtx.Args().First() != "trace" { + var err error + switch cCtx.Args().First() { + case "gas", "stats": + var cfg *seth.Config + var pk string + _, pk, err = seth.NewAddress() + if err != nil { + return err + } + + err = os.Setenv(seth.ROOT_PRIVATE_KEY_ENV_VAR, pk) + if err != nil { + return err + } + + cfg, err = seth.ReadConfig() + if err != nil { + return err + } + C, err = seth.NewClientWithConfig(cfg) + if err != nil { + return err + } + case "trace": + return nil + } + if err != nil { + return err + } + } + return nil + }, + Commands: []*cli.Command{ + { + Name: "stats", + HelpName: "stats", + Aliases: []string{"s"}, + Description: "get various network related stats", + Flags: []cli.Flag{ + &cli.Int64Flag{Name: "start_block", Aliases: []string{"s"}}, + &cli.Int64Flag{Name: "end_block", Aliases: []string{"e"}}, + }, + Action: func(cCtx *cli.Context) error { + start := cCtx.Int64("start_block") + end := cCtx.Int64("end_block") + if start == 0 { + return fmt.Errorf("at least start block should be defined, ex.: -s -10") + } + if start > 0 && end == 0 { + return fmt.Errorf("invalid block params. Last N blocks example: -s -10, interval example: -s 10 -e 20") + } + cs, err := seth.NewBlockStats(C) + if err != nil { + return err + } + return cs.Stats(big.NewInt(start), big.NewInt(end)) + }, + }, + { + Name: "gas", + HelpName: "gas", + Aliases: []string{"g"}, + Description: "get various info about gas prices", + Flags: []cli.Flag{ + &cli.Int64Flag{Name: "blocks", Aliases: []string{"b"}}, + &cli.Float64Flag{Name: "tipPercentile", Aliases: []string{"tp"}}, + }, + Action: func(cCtx *cli.Context) error { + ge := seth.NewGasEstimator(C) + blocks := cCtx.Uint64("blocks") + tipPerc := cCtx.Float64("tipPercentile") + stats, err := ge.Stats(blocks, tipPerc) + if err != nil { + return err + } + seth.L.Info(). + Interface("Max", stats.GasPrice.Max). + Interface("99", stats.GasPrice.Perc99). + Interface("75", stats.GasPrice.Perc75). + Interface("50", stats.GasPrice.Perc50). + Interface("25", stats.GasPrice.Perc25). + Msg("Base fee (Wei)") + seth.L.Info(). + Interface("Max", stats.TipCap.Max). + Interface("99", stats.TipCap.Perc99). + Interface("75", stats.TipCap.Perc75). + Interface("50", stats.TipCap.Perc50). + Interface("25", stats.TipCap.Perc25). + Msg("Priority fee (Wei)") + seth.L.Info(). + Interface("GasPrice", stats.SuggestedGasPrice). + Msg("Suggested gas price now") + seth.L.Info(). + Interface("GasTipCap", stats.SuggestedGasTipCap). + Msg("Suggested gas tip cap now") + + type asTomlCfg struct { + GasPrice int64 `toml:"gas_price"` + GasTip int64 `toml:"gas_tip_cap"` + GasFee int64 `toml:"gas_fee_cap"` + } + + tomlCfg := asTomlCfg{ + GasPrice: stats.SuggestedGasPrice.Int64(), + GasTip: stats.SuggestedGasTipCap.Int64(), + GasFee: stats.SuggestedGasPrice.Int64() + stats.SuggestedGasTipCap.Int64(), + } + + marshalled, err := toml.Marshal(tomlCfg) + if err != nil { + return err + } + + seth.L.Info().Msgf("Fallback prices for TOML config:\n%s", string(marshalled)) + + return err + }, + }, + { + Name: "trace", + HelpName: "trace", + Aliases: []string{"t"}, + Description: "trace transactions loaded from JSON file", + Flags: []cli.Flag{ + &cli.StringFlag{Name: "file", Aliases: []string{"f"}}, + &cli.StringFlag{Name: "txHash", Aliases: []string{"t"}}, + }, + Action: func(cCtx *cli.Context) error { + file := cCtx.String("file") + txHash := cCtx.String("txHash") + + if file == "" && txHash == "" { + return fmt.Errorf("no file or transaction hash specified, use -f or -t flags") + } + + if file != "" && txHash != "" { + return fmt.Errorf("both file and transaction hash specified, use only one") + } + + var transactions []string + if file != "" { + err := seth.OpenJsonFileAsStruct(file, &transactions) + if err != nil { + return err + } + } else { + transactions = append(transactions, txHash) + } + + _ = os.Setenv(seth.LogLevelEnvVar, "debug") + + cfgPath := os.Getenv(seth.CONFIG_FILE_ENV_VAR) + if cfgPath == "" { + return errors.New(seth.ErrEmptyConfigPath) + } + var cfg *seth.Config + d, err := os.ReadFile(cfgPath) + if err != nil { + return errors.Wrap(err, seth.ErrReadSethConfig) + } + err = toml.Unmarshal(d, &cfg) + if err != nil { + return errors.Wrap(err, seth.ErrUnmarshalSethConfig) + } + absPath, err := filepath.Abs(cfgPath) + if err != nil { + return err + } + cfg.ConfigDir = filepath.Dir(absPath) + + selectedNetwork := os.Getenv(seth.NETWORK_ENV_VAR) + if selectedNetwork != "" { + for _, n := range cfg.Networks { + if n.Name == selectedNetwork { + cfg.Network = n + break + } + } + if cfg.Network == nil { + return fmt.Errorf("network %s not defined in the TOML file", selectedNetwork) + } + } else { + url := os.Getenv(seth.URL_ENV_VAR) + + if url == "" { + return fmt.Errorf("network not selected, set %s=... or %s=..., check TOML config for available networks", seth.NETWORK_ENV_VAR, seth.URL_ENV_VAR) + } + + //look for default network + for _, n := range cfg.Networks { + if n.Name == seth.DefaultNetworkName { + cfg.Network = n + cfg.Network.Name = selectedNetwork + cfg.Network.URLs = []string{url} + break + } + } + + if cfg.Network == nil { + return fmt.Errorf("default network not defined in the TOML file") + } + + if cfg.Network.DialTimeout == nil { + cfg.Network.DialTimeout = &seth.Duration{D: seth.DefaultDialTimeout} + } + ctx, cancel := context.WithTimeout(context.Background(), cfg.Network.DialTimeout.Duration()) + defer cancel() + rpcClient, err := rpc.DialOptions(ctx, cfg.FirstNetworkURL(), rpc.WithHeaders(cfg.RPCHeaders)) + if err != nil { + return fmt.Errorf("failed to connect RPC client to '%s' due to: %w", cfg.FirstNetworkURL(), err) + } + client := ethclient.NewClient(rpcClient) + defer client.Close() + + if cfg.Network.Name == seth.DefaultNetworkName { + chainId, err := client.ChainID(context.Background()) + if err != nil { + return errors.Wrap(err, "failed to get chain ID") + } + cfg.Network.ChainID = chainId.String() + } + } + + zero := int64(0) + cfg.EphemeralAddrs = &zero + cfg.TracingLevel = seth.TracingLevel_All + if cfg.Network.DialTimeout == nil { + cfg.Network.DialTimeout = &seth.Duration{D: seth.DefaultDialTimeout} + } + + client, err := seth.NewClientWithConfig(cfg) + if err != nil { + return err + } + + seth.L.Info().Msgf("Tracing transactions from %s file", file) + + for _, txHash := range transactions { + seth.L.Info().Msgf("Tracing transaction %s", txHash) + ctx, cancel := context.WithTimeout(context.Background(), cfg.Network.TxnTimeout.Duration()) + tx, _, err := client.Client.TransactionByHash(ctx, common.HexToHash(txHash)) + cancel() + if err != nil { + return errors.Wrapf(err, "failed to get transaction %s", txHash) + } + + _, err = client.Decode(tx, nil) + if err != nil { + seth.L.Info().Msgf("Possible revert reason: %s", err.Error()) + } + } + return err + }, + }, + }, + } + return app.Run(args) +} diff --git a/seth/cmd/seth/seth.go b/seth/cmd/seth/seth.go new file mode 100644 index 000000000..efab91a8c --- /dev/null +++ b/seth/cmd/seth/seth.go @@ -0,0 +1,13 @@ +package main + +import ( + "os" + + seth "github.com/smartcontractkit/chainlink-testing-framework/seth/cmd" +) + +func main() { + if err := seth.RunCLI(os.Args); err != nil { + panic(err) + } +} diff --git a/seth/config.go b/seth/config.go new file mode 100644 index 000000000..dd1b449e2 --- /dev/null +++ b/seth/config.go @@ -0,0 +1,303 @@ +package seth + +import ( + "crypto/ecdsa" + "fmt" + "net/http" + "os" + "path/filepath" + "strings" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" + "github.com/pelletier/go-toml/v2" + "github.com/pkg/errors" +) + +const ( + ErrReadSethConfig = "failed to read TOML config for seth" + ErrUnmarshalSethConfig = "failed to unmarshal TOML config for seth" + ErrEmptyRootPrivateKey = "no root private key were set, set %s=..." + + GETH = "Geth" + ANVIL = "Anvil" + + CONFIG_FILE_ENV_VAR = "SETH_CONFIG_PATH" + + ROOT_PRIVATE_KEY_ENV_VAR = "SETH_ROOT_PRIVATE_KEY" + NETWORK_ENV_VAR = "SETH_NETWORK" + URL_ENV_VAR = "SETH_URL" + + DefaultNetworkName = "Default" + DefaultDialTimeout = 1 * time.Minute + + DefaultTransferGasFee = 21_000 + DefaultGasPrice = 1_000_000_000 // 1 Gwei + DefaultGasFeeCap = 100_000_000_000 // 100 Gwei + DefaultGasTipCap = 50_000_000_000 // 50 Gwei +) + +type Config struct { + // internal fields + revertedTransactionsFile string + ephemeral bool + RPCHeaders http.Header + + // external fields + // ArtifactDir is the directory where all artifacts generated by seth are stored (e.g. transaction traces) + ArtifactsDir string `toml:"artifacts_dir"` + EphemeralAddrs *int64 `toml:"ephemeral_addresses_number"` + RootKeyFundsBuffer *int64 `toml:"root_key_funds_buffer"` + ABIDir string `toml:"abi_dir"` + BINDir string `toml:"bin_dir"` + ContractMapFile string `toml:"contract_map_file"` + SaveDeployedContractsMap bool `toml:"save_deployed_contracts_map"` + Network *Network `toml:"network"` + Networks []*Network `toml:"networks"` + NonceManager *NonceManagerCfg `toml:"nonce_manager"` + TracingLevel string `toml:"tracing_level"` + TraceOutputs []string `toml:"trace_outputs"` + PendingNonceProtectionEnabled bool `toml:"pending_nonce_protection_enabled"` + ConfigDir string `toml:"abs_path"` + ExperimentsEnabled []string `toml:"experiments_enabled"` + CheckRpcHealthOnStart bool `toml:"check_rpc_health_on_start"` + BlockStatsConfig *BlockStatsConfig `toml:"block_stats"` + GasBump *GasBumpConfig `toml:"gas_bump"` +} + +type GasBumpConfig struct { + Retries uint `toml:"retries"` + MaxGasPrice int64 `toml:"max_gas_price"` + StrategyFn GasBumpStrategyFn `toml:"-"` +} + +// GasBumpRetries returns the number of retries for gas bumping +func (c *Config) GasBumpRetries() uint { + if c.GasBump == nil { + return 0 + } + + return c.GasBump.Retries +} + +// HasMaxBumpGasPrice returns true if the max gas price for gas bumping is set +func (c *Config) HasMaxBumpGasPrice() bool { + return c.GasBump != nil && c.GasBump.MaxGasPrice > 0 +} + +type NonceManagerCfg struct { + KeySyncRateLimitSec int `toml:"key_sync_rate_limit_per_sec"` + KeySyncTimeout *Duration `toml:"key_sync_timeout"` + KeySyncRetries uint `toml:"key_sync_retries"` + KeySyncRetryDelay *Duration `toml:"key_sync_retry_delay"` +} + +type Network struct { + Name string `toml:"name"` + URLs []string `toml:"urls_secret"` + EIP1559DynamicFees bool `toml:"eip_1559_dynamic_fees"` + GasPrice int64 `toml:"gas_price"` + GasFeeCap int64 `toml:"gas_fee_cap"` + GasTipCap int64 `toml:"gas_tip_cap"` + GasLimit uint64 `toml:"gas_limit"` + TxnTimeout *Duration `toml:"transaction_timeout"` + DialTimeout *Duration `toml:"dial_timeout"` + TransferGasFee int64 `toml:"transfer_gas_fee"` + PrivateKeys []string `toml:"private_keys_secret"` + GasPriceEstimationEnabled bool `toml:"gas_price_estimation_enabled"` + GasPriceEstimationBlocks uint64 `toml:"gas_price_estimation_blocks"` + GasPriceEstimationTxPriority string `toml:"gas_price_estimation_tx_priority"` + + // derivative vars + ChainID string +} + +// DefaultClient returns a Client with reasonable default config with the specified RPC URL and private keys. You should pass at least 1 private key. +// It assumes that network is EIP-1559 compatible (if it's not, the client will later automatically update its configuration to reflect it). +func DefaultClient(rpcUrl string, privateKeys []string) (*Client, error) { + return NewClientBuilder().WithRpcUrl(rpcUrl).WithPrivateKeys(privateKeys).Build() +} + +// ReadConfig reads the TOML config file from location specified by env var "SETH_CONFIG_PATH" and returns a Config struct +func ReadConfig() (*Config, error) { + cfgPath := os.Getenv(CONFIG_FILE_ENV_VAR) + if cfgPath == "" { + return nil, errors.New(ErrEmptyConfigPath) + } + var cfg *Config + d, err := os.ReadFile(cfgPath) + if err != nil { + return nil, errors.Wrap(err, ErrReadSethConfig) + } + err = toml.Unmarshal(d, &cfg) + if err != nil { + return nil, errors.Wrap(err, ErrUnmarshalSethConfig) + } + absPath, err := filepath.Abs(cfgPath) + if err != nil { + return nil, err + } + cfg.ConfigDir = filepath.Dir(absPath) + selectedNetwork := os.Getenv(NETWORK_ENV_VAR) + if selectedNetwork != "" { + for _, n := range cfg.Networks { + if n.Name == selectedNetwork { + cfg.Network = n + break + } + } + } + + if cfg.Network == nil { + L.Debug().Msgf("Network %s not found in TOML, trying to use URL", selectedNetwork) + url := os.Getenv(URL_ENV_VAR) + + if url == "" { + return nil, fmt.Errorf("network not selected, set %s=... or %s=..., check TOML config for available networks", NETWORK_ENV_VAR, URL_ENV_VAR) + } + + //look for default network + for _, n := range cfg.Networks { + if n.Name == DefaultNetworkName { + cfg.Network = n + cfg.Network.Name = selectedNetwork + cfg.Network.URLs = []string{url} + + if selectedNetwork == "" { + L.Warn().Msg("No network name provided, using default network") + cfg.Network.Name = DefaultNetworkName + } + + break + } + } + + if cfg.Network == nil { + return nil, fmt.Errorf("default network not defined in the TOML file") + } + } + + rootPrivateKey := os.Getenv(ROOT_PRIVATE_KEY_ENV_VAR) + if rootPrivateKey == "" { + return nil, errors.Errorf(ErrEmptyRootPrivateKey, ROOT_PRIVATE_KEY_ENV_VAR) + } + cfg.Network.PrivateKeys = append(cfg.Network.PrivateKeys, rootPrivateKey) + if cfg.Network.DialTimeout == nil { + cfg.Network.DialTimeout = &Duration{D: DefaultDialTimeout} + } + L.Trace().Interface("Config", cfg).Msg("Parsed seth config") + return cfg, nil +} + +// FirstNetworkURL returns first network URL +func (c *Config) FirstNetworkURL() string { + return c.Network.URLs[0] +} + +// ParseKeys parses private keys from the config +func (c *Config) ParseKeys() ([]common.Address, []*ecdsa.PrivateKey, error) { + addresses := make([]common.Address, 0) + privKeys := make([]*ecdsa.PrivateKey, 0) + for _, k := range c.Network.PrivateKeys { + privateKey, err := crypto.HexToECDSA(k) + if err != nil { + return nil, nil, err + } + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return nil, nil, err + } + pubKeyAddress := crypto.PubkeyToAddress(*publicKeyECDSA) + addresses = append(addresses, pubKeyAddress) + privKeys = append(privKeys, privateKey) + } + return addresses, privKeys, nil +} + +// IsSimulatedNetwork returns true if the network is simulated (i.e. Geth or Anvil) +func (c *Config) IsSimulatedNetwork() bool { + networkName := strings.ToLower(c.Network.Name) + return networkName == strings.ToLower(GETH) || networkName == strings.ToLower(ANVIL) +} + +// GenerateContractMapFileName generates a file name for the contract map +func (c *Config) GenerateContractMapFileName() string { + networkName := strings.ToLower(c.Network.Name) + now := time.Now().Format("2006-01-02-15-04-05") + return fmt.Sprintf(ContractMapFilePattern, networkName, now) +} + +// ShouldSaveDeployedContractMap returns true if the contract map should be saved (i.e. not a simulated network and functionality is enabled) +func (c *Config) ShouldSaveDeployedContractMap() bool { + return !c.IsSimulatedNetwork() && c.SaveDeployedContractsMap +} + +func (c *Config) setEphemeralAddrs() { + if c.EphemeralAddrs == nil { + c.EphemeralAddrs = &ZeroInt64 + } + + if *c.EphemeralAddrs == 0 { + c.ephemeral = false + } else { + c.ephemeral = true + } + + if c.RootKeyFundsBuffer == nil { + c.RootKeyFundsBuffer = &ZeroInt64 + } +} + +const ( + Experiment_SlowFundsReturn = "slow_funds_return" + Experiment_Eip1559FeeEqualier = "eip_1559_fee_equalizer" +) + +// IsExperimentEnabled returns true if the experiment is enabled +func (c *Config) IsExperimentEnabled(experiment string) bool { + for _, e := range c.ExperimentsEnabled { + if e == experiment { + return true + } + } + return false +} + +// AppendPksToNetwork appends private keys to the network with the specified name and returns "true" if the network was updated. +func (c *Config) AppendPksToNetwork(pks []string, name string) bool { + if c.Network != nil && strings.EqualFold(c.Network.Name, name) { + c.Network.PrivateKeys = append(c.Network.PrivateKeys, pks...) + + return true + } + + for _, n := range c.Networks { + if strings.EqualFold(n.Name, name) { + n.PrivateKeys = append(c.Network.PrivateKeys, pks...) + return true + } + } + + return false +} + +// GetMaxConcurrency returns the maximum number of concurrent transactions. Root key is excluded from the count. +func (c *Config) GetMaxConcurrency() int { + if c.ephemeral { + return int(*c.EphemeralAddrs) + } + + return len(c.Network.PrivateKeys) - 1 +} + +func (c *Config) hasOutput(output string) bool { + for _, o := range c.TraceOutputs { + if strings.EqualFold(o, output) { + return true + } + } + + return false +} diff --git a/seth/config_test.go b/seth/config_test.go new file mode 100644 index 000000000..168df9256 --- /dev/null +++ b/seth/config_test.go @@ -0,0 +1,216 @@ +package seth_test + +import ( + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" + link_token "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/link" +) + +func TestConfig_DefaultClient(t *testing.T) { + client, err := seth.DefaultClient("ws://localhost:8546", []string{"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"}) + require.NoError(t, err, "failed to create client with default config") + require.Equal(t, 1, len(client.PrivateKeys), "expected 1 private key") + + linkAbi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get LINK ABI") + + _, err = client.DeployContract(client.NewTXOpts(), "LinkToken", *linkAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + require.NoError(t, err, "failed to deploy LINK contract") +} + +func TestConfig_Default_TwoPks(t *testing.T) { + client, err := seth.DefaultClient("ws://localhost:8546", []string{"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80", "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"}) + require.NoError(t, err, "failed to create client with default config") + require.Equal(t, 2, len(client.PrivateKeys), "expected 2 private keys") + + linkAbi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get LINK ABI") + + _, err = client.DeployContract(client.NewTXOpts(), "LinkToken", *linkAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + require.NoError(t, err, "failed to deploy LINK contract") +} + +func TestConfig_MinimalBuilder(t *testing.T) { + builder := seth.NewClientBuilder() + + client, err := builder.WithRpcUrl("ws://localhost:8546"). + WithPrivateKeys([]string{"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"}). + Build() + require.NoError(t, err, "failed to build client") + + require.Equal(t, 1, len(client.PrivateKeys), "expected 1 private key") + + linkAbi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get LINK ABI") + + _, err = client.DeployContract(client.NewTXOpts(), "LinkToken", *linkAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + require.NoError(t, err, "failed to deploy LINK contract") +} + +func TestConfig_MaximalBuilder(t *testing.T) { + builder := seth.NewClientBuilder() + + client, err := builder. + // network + WithNetworkName("my network"). + WithRpcUrl("ws://localhost:8546"). + WithPrivateKeys([]string{"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"}). + WithRpcDialTimeout(10*time.Second). + WithTransactionTimeout(1*time.Minute). + // addresses + WithEphemeralAddresses(10, 10). + // tracing + WithTracing(seth.TracingLevel_All, []string{seth.TraceOutput_Console}). + // protections + WithProtections(true, true). + // artifacts folder + WithArtifactsFolder("some_folder"). + // nonce manager + WithNonceManager(10, 3, 60, 5). + Build() + + require.NoError(t, err, "failed to build client") + require.NoError(t, err, "failed to create client") + require.Equal(t, 11, len(client.PrivateKeys), "expected 11 private keys") + + t.Cleanup(func() { + err = seth.ReturnFunds(client, client.Addresses[0].Hex()) + require.NoError(t, err, "failed to return funds") + }) + + linkAbi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get LINK ABI") + + _, err = client.DeployContract(client.NewTXOpts(), "LinkToken", *linkAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + require.NoError(t, err, "failed to deploy LINK contract") +} + +func TestConfig_LegacyGas_No_Estimations(t *testing.T) { + builder := seth.NewClientBuilder() + + client, err := builder. + // network + WithNetworkName("my network"). + WithRpcUrl("ws://localhost:8546"). + WithPrivateKeys([]string{"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"}). + // Gas price and estimations + WithLegacyGasPrice(710_000_000). + WithGasPriceEstimations(false, 0, ""). + Build() + require.NoError(t, err, "failed to build client") + require.Equal(t, 1, len(client.PrivateKeys), "expected 1 private key") + + linkAbi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get LINK ABI") + + _, err = client.DeployContract(client.NewTXOpts(), "LinkToken", *linkAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + require.NoError(t, err, "failed to deploy LINK contract") +} + +func TestConfig_Eip1559Gas_With_Estimations(t *testing.T) { + builder := seth.NewClientBuilder() + + client, err := builder. + // network + WithNetworkName("my network"). + WithRpcUrl("ws://localhost:8546"). + WithPrivateKeys([]string{"ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80"}). + // Gas price and estimations + WithEIP1559DynamicFees(true). + WithDynamicGasPrices(120_000_000_000, 44_000_000_000). + WithGasPriceEstimations(false, 10, seth.Priority_Fast). + Build() + + require.NoError(t, err, "failed to build client") + require.Equal(t, 1, len(client.PrivateKeys), "expected 1 private key") + + linkAbi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get LINK ABI") + + _, err = client.DeployContract(client.NewTXOpts(), "LinkToken", *linkAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + require.NoError(t, err, "failed to deploy LINK contract") +} + +func TestConfigAppendPkToEmptyNetwork(t *testing.T) { + networkName := "network" + cfg := &seth.Config{ + Network: &seth.Network{ + Name: networkName, + }, + } + + added := cfg.AppendPksToNetwork([]string{"pk"}, networkName) + require.True(t, added, "should have added pk to network") + require.Equal(t, []string{"pk"}, cfg.Network.PrivateKeys, "network should have 1 pk") +} + +func TestConfigAppendPkToEmptySharedNetwork(t *testing.T) { + networkName := "network" + network := &seth.Network{ + Name: networkName, + } + cfg := &seth.Config{ + Network: network, + Networks: []*seth.Network{network}, + } + + added := cfg.AppendPksToNetwork([]string{"pk"}, networkName) + require.True(t, added, "should have added pk to network") + require.Equal(t, []string{"pk"}, cfg.Network.PrivateKeys, "network should have 1 pk") + require.Equal(t, []string{"pk"}, cfg.Networks[0].PrivateKeys, "network should have 1 pk") +} + +func TestConfigAppendPkToNetworkWithPk(t *testing.T) { + networkName := "network" + cfg := &seth.Config{ + Network: &seth.Network{ + Name: networkName, + PrivateKeys: []string{"pk1"}, + }, + } + + added := cfg.AppendPksToNetwork([]string{"pk2"}, networkName) + require.True(t, added, "should have added pk to network") + require.Equal(t, []string{"pk1", "pk2"}, cfg.Network.PrivateKeys, "network should have 2 pks") +} + +func TestConfigAppendPkToMissingNetwork(t *testing.T) { + networkName := "network" + cfg := &seth.Config{ + Network: &seth.Network{ + Name: "some_other", + }, + } + + added := cfg.AppendPksToNetwork([]string{"pk"}, networkName) + require.False(t, added, "should have not added pk to network") + require.Equal(t, 0, len(cfg.Network.PrivateKeys), "network should have 0 pks") +} + +func TestConfigAppendPkToInactiveNetwork(t *testing.T) { + networkName := "network" + cfg := &seth.Config{ + Network: &seth.Network{ + Name: "some_other", + }, + Networks: []*seth.Network{ + { + Name: "some_other", + }, + { + Name: networkName, + }, + }, + } + + added := cfg.AppendPksToNetwork([]string{"pk"}, networkName) + require.True(t, added, "should have added pk to network") + require.Equal(t, 0, len(cfg.Network.PrivateKeys), "network should have 0 pks") + require.Equal(t, 0, len(cfg.Networks[0].PrivateKeys), "network should have 0 pks") + require.Equal(t, []string{"pk"}, cfg.Networks[1].PrivateKeys, "network should have 1 pk") +} diff --git a/seth/contract_map.go b/seth/contract_map.go new file mode 100644 index 000000000..1f629ff5f --- /dev/null +++ b/seth/contract_map.go @@ -0,0 +1,119 @@ +package seth + +import ( + "io" + "os" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/common" + "github.com/pelletier/go-toml/v2" +) + +type ContractMap struct { + mu *sync.RWMutex + addressMap map[string]string +} + +func NewEmptyContractMap() ContractMap { + return ContractMap{ + mu: &sync.RWMutex{}, + addressMap: map[string]string{}, + } +} + +func NewContractMap(contracts map[string]string) ContractMap { + return ContractMap{ + mu: &sync.RWMutex{}, + addressMap: contracts, + } +} + +func (c ContractMap) GetContractMap() map[string]string { + return c.addressMap +} + +func (c ContractMap) IsKnownAddress(addr string) bool { + c.mu.Lock() + defer c.mu.Unlock() + return c.addressMap[strings.ToLower(addr)] != "" +} + +func (c ContractMap) GetContractName(addr string) string { + c.mu.Lock() + defer c.mu.Unlock() + return c.addressMap[strings.ToLower(addr)] +} + +func (c ContractMap) GetContractAddress(addr string) string { + if addr == UNKNOWN { + return UNKNOWN + } + + c.mu.Lock() + defer c.mu.Unlock() + for k, v := range c.addressMap { + if v == addr { + return k + } + } + return UNKNOWN +} + +func (c ContractMap) AddContract(addr, name string) { + if addr == UNKNOWN { + return + } + + name = strings.TrimSuffix(name, ".abi") + c.mu.Lock() + defer c.mu.Unlock() + c.addressMap[strings.ToLower(addr)] = name +} + +func (c ContractMap) Size() int { + return len(c.addressMap) +} + +func SaveDeployedContract(filename, contractName, address string) error { + file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0600) + + if err != nil { + return err + } + defer file.Close() + + v := map[string]string{ + address: contractName, + } + + marhalled, err := toml.Marshal(v) + if err != nil { + return err + } + + _, err = file.WriteString(string(marhalled)) + return err +} + +func LoadDeployedContracts(filename string) (map[string]string, error) { + tomlFile, err := os.Open(filename) + if err != nil { + return map[string]string{}, nil + } + defer tomlFile.Close() + + b, _ := io.ReadAll(tomlFile) + rawContracts := map[common.Address]string{} + err = toml.Unmarshal(b, &rawContracts) + if err != nil { + return map[string]string{}, err + } + + contracts := map[string]string{} + for k, v := range rawContracts { + contracts[k.Hex()] = v + } + + return contracts, nil +} diff --git a/seth/contract_store.go b/seth/contract_store.go new file mode 100644 index 000000000..49c3697fe --- /dev/null +++ b/seth/contract_store.go @@ -0,0 +1,130 @@ +package seth + +import ( + "os" + "path/filepath" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" +) + +const ( + ErrOpenABIFile = "failed to open ABI file" + ErrParseABI = "failed to parse ABI file" + ErrOpenBINFile = "failed to open BIN file" +) + +// ContractStore contains all ABIs that are used in decoding. It might also contain contract bytecode for deployment +type ContractStore struct { + ABIs ABIStore + BINs map[string][]byte + mu *sync.RWMutex +} + +type ABIStore map[string]abi.ABI + +func (c *ContractStore) GetABI(name string) (*abi.ABI, bool) { + if !strings.HasSuffix(name, ".abi") { + name = name + ".abi" + } + + c.mu.Lock() + defer c.mu.Unlock() + + abi, ok := c.ABIs[name] + return &abi, ok +} + +func (c *ContractStore) AddABI(name string, abi abi.ABI) { + if !strings.HasSuffix(name, ".abi") { + name = name + ".abi" + } + + c.mu.Lock() + defer c.mu.Unlock() + + c.ABIs[name] = abi +} + +func (c *ContractStore) GetBIN(name string) ([]byte, bool) { + if !strings.HasSuffix(name, ".bin") { + name = name + ".bin" + } + + c.mu.Lock() + defer c.mu.Unlock() + + bin, ok := c.BINs[name] + return bin, ok +} + +func (c *ContractStore) AddBIN(name string, bin []byte) { + if !strings.HasSuffix(name, ".bin") { + name = name + ".bin" + } + + c.mu.Lock() + defer c.mu.Unlock() + + c.BINs[name] = bin +} + +// NewContractStore creates a new Contract store +func NewContractStore(abiPath, binPath string) (*ContractStore, error) { + cs := &ContractStore{ABIs: make(ABIStore), BINs: make(map[string][]byte), mu: &sync.RWMutex{}} + + if abiPath != "" { + files, err := os.ReadDir(abiPath) + if err != nil { + return nil, err + } + var foundABI bool + for _, f := range files { + if strings.HasSuffix(f.Name(), ".abi") { + L.Debug().Str("File", f.Name()).Msg("ABI file loaded") + ff, err := os.Open(filepath.Join(abiPath, f.Name())) + if err != nil { + return nil, errors.Wrap(err, ErrOpenABIFile) + } + a, err := abi.JSON(ff) + if err != nil { + return nil, errors.Wrap(err, ErrParseABI) + } + cs.ABIs[f.Name()] = a + foundABI = true + } + } + if !foundABI { + L.Warn().Msg("No ABI files found") + L.Warn().Msg("You will need to provide the bytecode manually, when deploying contracts") + } + } + + if binPath != "" { + files, err := os.ReadDir(binPath) + if err != nil { + return nil, err + } + var foundBIN bool + for _, f := range files { + if strings.HasSuffix(f.Name(), ".bin") { + L.Debug().Str("File", f.Name()).Msg("BIN file loaded") + bin, err := os.ReadFile(filepath.Join(binPath, f.Name())) + if err != nil { + return nil, errors.Wrap(err, ErrOpenBINFile) + } + cs.BINs[f.Name()] = common.FromHex(string(bin)) + foundBIN = true + } + } + if !foundBIN { + L.Warn().Msg("No BIN files found") + L.Warn().Msg("You will need to provide the bytecode manually, when deploying contracts") + } + } + + return cs, nil +} diff --git a/seth/contract_store_test.go b/seth/contract_store_test.go new file mode 100644 index 000000000..eaa054a33 --- /dev/null +++ b/seth/contract_store_test.go @@ -0,0 +1,104 @@ +package seth_test + +import ( + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" +) + +func TestSmokeContractABIStore(t *testing.T) { + + type test struct { + name string + abiPath string + err string + } + + tests := []test{ + { + name: "can load the ABI", + abiPath: "./contracts/abi", + }, + { + name: "can't open the ABI path", + abiPath: "dasdsadd", + err: "open dasdsadd: no such file or directory", + }, + { + name: "empty ABI dir", + abiPath: "./contracts/emptyContractDir", + }, + { + name: "invalid ABI inside dir", + abiPath: "./contracts/invalidContractDir", + err: "failed to parse ABI file: invalid character ':' after array element", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + cs, err := seth.NewContractStore(tc.abiPath, tc.abiPath) + if err == nil { + require.NotNil(t, cs.ABIs, "ABIs should not be nil") + require.NotNil(t, cs.BINs, "BINs should not be nil") + require.Equal(t, make(map[string][]uint8), cs.BINs) + err = errors.New("") + } + require.Equal(t, tc.err, err.Error()) + }) + } +} + +func TestSmokeContractBINStore(t *testing.T) { + + type test struct { + name string + abiPath string + binPath string + binFound bool + err string + } + + tests := []test{ + { + name: "can load the ABI and BIN", + abiPath: "./contracts/abi", + binPath: "./contracts/bin", + binFound: true, + }, + { + name: "can't open the BIN path", + abiPath: "./contracts/abi", + binPath: "./contract/i-don't-exist", + err: "open ./contract/i-don't-exist: no such file or directory", + }, + { + name: "empty BIN dir", + abiPath: "./contracts/abi", + binPath: "./contracts/emptyContractDir", + }, + } + + for _, tc := range tests { + t.Run(tc.name, func(t *testing.T) { + var err error + cs, err := seth.NewContractStore(tc.abiPath, tc.binPath) + if err == nil { + require.NotEmpty(t, cs.ABIs, "ABIs should not be empty") + err = errors.New("") + if tc.binFound { + require.NotEmpty(t, cs.BINs, "BINs should not be empty") + } else { + require.Empty(t, cs.BINs, "BINs should be empty") + } + } else { + require.Nil(t, cs, "ContractStore should be nil") + } + require.Equal(t, tc.err, err.Error(), "error should match") + }) + } +} diff --git a/seth/contracts/NetworkDebugContract.sol b/seth/contracts/NetworkDebugContract.sol new file mode 100644 index 000000000..c9e3d650d --- /dev/null +++ b/seth/contracts/NetworkDebugContract.sol @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +import "./NetworkDebugSubContract.sol"; + +contract NetworkDebugContract { + int256 public storedData; + + mapping(address => int256) public storedDataMap; + mapping(int256 => int256) public counterMap; + + NetworkDebugSubContract public subContract; + + uint256 private data; + + constructor(address subAddr) { + subContract = NetworkDebugSubContract(subAddr); + data = 256; + } + + /* + Basic types events + 1 index by default is selector signature, topic 0 + */ + + event NoIndexEventString(string str); + event NoIndexEvent(address sender); + event OneIndexEvent(uint indexed a); + event IsValidEvent(bool success); + event TwoIndexEvent(uint256 indexed roundId, address indexed startedBy); + event ThreeIndexEvent(uint256 indexed roundId, address indexed startedBy, uint256 indexed startedAt); + event ThreeIndexAndOneNonIndexedEvent(uint256 indexed roundId, address indexed startedBy, uint256 indexed startedAt, string dataId); + event CallbackEvent(int256 indexed a); + + /* Struct events */ + + struct Account { + string name; + uint64 balance; + uint dailyLimit; + } + + event NoIndexStructEvent(Account a); + + /* Errors */ + + error CustomErr(uint256 available, uint256 required); + error CustomErrNoValues(); + error CustomErrWithMessage(string message); + + /* Getters/Setters */ + function setMap(int256 x) public returns (int256 value) { + storedDataMap[msg.sender] = x; + return x; + } + + function getMap() public view returns (int256 data) { + return storedDataMap[msg.sender]; + } + + function set(int256 x) public returns (int256 value) { + storedData = x; + return x; + } + + function addCounter(int256 idx, int256 x) public returns (int256 value) { + counterMap[idx] += x; + return x; + } + + function getCounter(int256 idx) public view returns (int256 data) { + return counterMap[idx]; + } + + function resetCounter(int256 idx) public { + counterMap[idx] = 0; + } + + function get() public view returns (int256 data) { + return storedData; + } + + function trace(int256 x, int256 y) public returns (int256) { + subContract.trace(x, y); + emit TwoIndexEvent(uint256(y), address(msg.sender)); + return x + y; + } + + function traceDifferent(int x, int256 y) public returns (int256) { + subContract.traceOneInt(y); + emit OneIndexEvent(uint(x)); + return x + y; + } + + function validate(int x, int256 y) public returns (bool) { + emit IsValidEvent(x > y); + return x > y; + } + + function traceWithValidate(int x, int256 y) public payable returns (int256) { + if (validate(x, y)) { + subContract.trace(x, y); + emit TwoIndexEvent(uint256(y), address(msg.sender)); + return x + y; + } + + revert CustomErrWithMessage("first int was not greater than second int"); + } + + function traceYetDifferent(int x, int256 y) public returns (int256) { + subContract.trace(x, y); + emit TwoIndexEvent(uint256(y), address(msg.sender)); + return x + y; + } + + /* Events */ + + function emitNoIndexEventString() public { + emit NoIndexEventString("myString"); + } + + function emitNoIndexEvent() public { + emit NoIndexEvent(msg.sender); + } + + function emitOneIndexEvent() public { + emit OneIndexEvent(83); + } + + function emitTwoIndexEvent() public { + emit TwoIndexEvent(1, address(msg.sender)); + } + + function emitThreeIndexEvent() public { + emit ThreeIndexEvent(1, address(msg.sender), 3); + } + + function emitFourParamMixedEvent() public { + emit ThreeIndexAndOneNonIndexedEvent(2, address(msg.sender), 3, "some id"); + } + + function emitNoIndexStructEvent() public { + emit NoIndexStructEvent(Account("John", 5, 10)); + } + + /* Reverts */ + + function alwaysRevertsRequire() public { + require(false, "always revert error"); + } + + function alwaysRevertsAssert() public { + assert(false); + } + + function alwaysRevertsCustomError() public { + revert CustomErr({ + available: 12, + required: 21 + }); + } + + function alwaysRevertsCustomErrorNoValues() public { + revert CustomErrNoValues(); + } + + /* Inputs/Outputs */ + + function emitNamedInputsOutputs(uint256 inputVal1, string memory inputVal2) public returns (uint256 outputVal1, string memory outputVal2) { + return (inputVal1, inputVal2); + } + + function emitInputsOutputs(uint256 inputVal1, string memory inputVal2) public returns (uint256, string memory) { + return (inputVal1, inputVal2); + } + + function emitInputs(uint256 inputVal1, string memory inputVal2) public { + return; + } + + function emitOutputs() public returns (uint256, string memory) { + return (31337, "outputVal1"); + } + + function emitNamedOutputs() public returns (uint256 outputVal1, string memory outputVal2) { + return (31337, "outputVal1"); + } + + function emitInts(int first, int128 second, uint third) public returns (int, int128 outputVal1, uint outputVal2) { + return (first, second, third); + } + + function emitAddress(address addr) public returns (address) { + return addr; + } + + function emitBytes32(bytes32 input) public returns (bytes32 output) { + return input; + } + + function processUintArray(uint256[] memory input) public returns (uint256[] memory) { + uint256[] memory output = new uint256[](input.length); + for (uint i = 0; i < input.length; i++) { + output[i] = input[i] + 1; + } + return output; + } + + function processAddressArray(address[] memory input) public returns (address[] memory) { + return input; + } + + // struct with dynamic fields + struct Data { + string name; + uint256[] values; + } + + function processDynamicData(Data calldata data) public returns (Data calldata) { + return data; + } + + function processFixedDataArray(Data[3] calldata data) public returns (Data[2] memory) { + Data[2] memory output; + + output[0] = data[0]; + output[1] = data[1]; + + return output; + } + + struct NestedData { + Data data; + bytes dynamicBytes; + } + + function processNestedData(NestedData calldata data) public returns (NestedData memory) { + return data; + } + + /* Overload of processNestedData */ + function processNestedData(Data calldata data) public returns (NestedData memory) { + bytes32 hashedData = keccak256(abi.encodePacked(data.name)); + + bytes memory convertedData = new bytes(32); + for (uint i = 0; i < 32; i++) { + convertedData[i] = hashedData[i]; + } + + return NestedData(data, convertedData); + } + + function pay() public payable {} + + /* Fallback and receive */ + + event EtherReceived(address sender, uint amount); + + fallback() external payable { + emit EtherReceived(msg.sender, msg.value); + } + + event Received(address caller, uint amount, string message); + + receive() external payable { + emit Received(msg.sender, msg.value, "Received Ether"); + } + + /* Enums */ + + enum Status { Pending, Active, Completed, Cancelled } + Status public currentStatus; + + event CurrentStatus(Status indexed status); + + function setStatus(Status status) public returns (Status) { + currentStatus = status; + emit CurrentStatus(currentStatus); + return status; + } + + function traceSubWithCallback(int256 x, int256 y) public returns (int256) { + y = y + 2; + subContract.traceWithCallback(x, y); + emit TwoIndexEvent(1, address(msg.sender)); + return x + y; + } + + function callRevertFunctionInSubContract(uint256 x, uint256 y) public { + subContract.alwaysRevertsCustomError(x, y); + } + + + function callRevertFunctionInTheContract() public { + alwaysRevertsCustomError(); + } + + /* Static call */ + function getData() external view returns (uint256) { + return data; + } + + function performStaticCall() external view returns (uint256) { + address self = address(this); + + // Perform a static call to getData function + (bool success, bytes memory returnData) = self.staticcall( + abi.encodeWithSelector(this.getData.selector) + ); + + require(success, "Static call failed"); + + uint256 result = abi.decode(returnData, (uint256)); + + return result; + } + + /* Callback function */ + function callbackMethod(int x) external returns (int) { + emit CallbackEvent(x); + return x; + } + + /* ERC677 token transfer */ + + event CallDataLength(uint256 length); + + function onTokenTransfer(address sender, uint256 amount, bytes calldata data) external { + emit CallDataLength(data.length); + + if (data.length == 0) { + revert CustomErr({ + available: 99, + required: 101 + }); + } + (bool success, bytes memory returnData) = address(this).delegatecall(data); + + if (!success) { + if (returnData.length > 0) { + assembly { + let returndata_size := mload(returnData) + revert(add(32, returnData), returndata_size) + } + } else { + revert CustomErrWithMessage("delegatecall failed with no reason"); + } + } + this.performStaticCall(); + + bytes4 selector = bytes4(data[:4]); + if (selector == this.traceYetDifferent.selector) { + revert CustomErrWithMessage("oh oh oh it's magic!"); + } + + this.traceSubWithCallback(1, 2); + } +} diff --git a/seth/contracts/NetworkDebugSubContract.sol b/seth/contracts/NetworkDebugSubContract.sol new file mode 100644 index 000000000..58049ccc3 --- /dev/null +++ b/seth/contracts/NetworkDebugSubContract.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.19; + +interface DebugContractCallback { + function callbackMethod(int x) external returns (int); +} + +contract NetworkDebugSubContract { + int256 storedData; + + /* + Basic types events + 1 index by default is selector signature, topic 0 + */ + + event NoIndexEventString(string str); + event NoIndexEvent(address sender); + event OneIndexEvent(uint indexed a); + event TwoIndexEvent(uint256 indexed roundId, address indexed startedBy); + event ThreeIndexEvent(uint256 indexed roundId, address indexed startedBy, uint256 startedAt); + + /* Struct events */ + + struct Account { + string name; + uint64 balance; + uint dailyLimit; + } + event NoIndexStructEvent(Account a); + + /* Errors */ + + error CustomErr(uint256 available, uint256 required); + + function trace(int256 x, int256 y) public returns (int256) { + y = y + 2; + emit TwoIndexEvent(uint256(y), address(msg.sender)); + return x + y; + } + + function traceWithCallback(int256 x, int256 y) public returns (int256) { + emit TwoIndexEvent(uint256(y), address(msg.sender)); + int256 response = DebugContractCallback(msg.sender).callbackMethod(y); + emit OneIndexEvent(uint(response)); + return response; + } + + function traceOneInt(int256 x) public returns (int256 r) { + emit NoIndexEvent(msg.sender); + return x + 3; + } + + function alwaysRevertsCustomError(uint256 x, uint256 y) public { + revert CustomErr({ + available: x, + required: y + }); + } + + function pay() public payable {} +} diff --git a/seth/contracts/abi/DebugContractCallback.abi b/seth/contracts/abi/DebugContractCallback.abi new file mode 100644 index 000000000..994cf1563 --- /dev/null +++ b/seth/contracts/abi/DebugContractCallback.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"int256","name":"x","type":"int256"}],"name":"callbackMethod","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/seth/contracts/abi/LinkTokenLegacy.abi b/seth/contracts/abi/LinkTokenLegacy.abi new file mode 100644 index 000000000..972e02cc3 --- /dev/null +++ b/seth/contracts/abi/LinkTokenLegacy.abi @@ -0,0 +1 @@ +[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_value","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_from","type":"address"},{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"},{"name":"_data","type":"bytes"}],"name":"transferAndCall","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"}],"name":"balanceOf","outputs":[{"name":"balance","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"_to","type":"address"},{"name":"_value","type":"uint256"}],"name":"transfer","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"_spender","type":"address"},{"name":"_addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[{"name":"success","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"remaining","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"},{"indexed":false,"name":"data","type":"bytes"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"owner","type":"address"},{"indexed":true,"name":"spender","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Approval","type":"event"}] diff --git a/seth/contracts/abi/LinkTokenModern.abi b/seth/contracts/abi/LinkTokenModern.abi new file mode 100644 index 000000000..c82a2a721 --- /dev/null +++ b/seth/contracts/abi/LinkTokenModern.abi @@ -0,0 +1 @@ +[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"supplyAfterMint","type":"uint256"}],"name":"MaxSupplyExceeded","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"SenderNotBurner","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"SenderNotMinter","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"burner","type":"address"}],"name":"BurnAccessGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"burner","type":"address"}],"name":"BurnAccessRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"}],"name":"MintAccessGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"minter","type":"address"}],"name":"MintAccessRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferRequested","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"data","type":"bytes"}],"name":"Transfer","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burnFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseApproval","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getBurners","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMinters","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"burner","type":"address"}],"name":"grantBurnRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"burnAndMinter","type":"address"}],"name":"grantMintAndBurnRoles","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter","type":"address"}],"name":"grantMintRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseApproval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"burner","type":"address"}],"name":"isBurner","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"minter","type":"address"}],"name":"isMinter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"burner","type":"address"}],"name":"revokeBurnRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"minter","type":"address"}],"name":"revokeMintRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"transferAndCall","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"}] diff --git a/seth/contracts/abi/NetworkDebugContract.abi b/seth/contracts/abi/NetworkDebugContract.abi new file mode 100644 index 000000000..b7cf1bd7a --- /dev/null +++ b/seth/contracts/abi/NetworkDebugContract.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address","name":"subAddr","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"CustomErr","type":"error"},{"inputs":[],"name":"CustomErrNoValues","type":"error"},{"inputs":[{"internalType":"string","name":"message","type":"string"}],"name":"CustomErrWithMessage","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"length","type":"uint256"}],"name":"CallDataLength","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"int256","name":"a","type":"int256"}],"name":"CallbackEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"enum NetworkDebugContract.Status","name":"status","type":"uint8"}],"name":"CurrentStatus","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"EtherReceived","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bool","name":"success","type":"bool"}],"name":"IsValidEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"NoIndexEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"str","type":"string"}],"name":"NoIndexEventString","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint64","name":"balance","type":"uint64"},{"internalType":"uint256","name":"dailyLimit","type":"uint256"}],"indexed":false,"internalType":"struct NetworkDebugContract.Account","name":"a","type":"tuple"}],"name":"NoIndexStructEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"a","type":"uint256"}],"name":"OneIndexEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"caller","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"string","name":"message","type":"string"}],"name":"Received","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":true,"internalType":"address","name":"startedBy","type":"address"},{"indexed":true,"internalType":"uint256","name":"startedAt","type":"uint256"},{"indexed":false,"internalType":"string","name":"dataId","type":"string"}],"name":"ThreeIndexAndOneNonIndexedEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":true,"internalType":"address","name":"startedBy","type":"address"},{"indexed":true,"internalType":"uint256","name":"startedAt","type":"uint256"}],"name":"ThreeIndexEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":true,"internalType":"address","name":"startedBy","type":"address"}],"name":"TwoIndexEvent","type":"event"},{"stateMutability":"payable","type":"fallback"},{"inputs":[{"internalType":"int256","name":"idx","type":"int256"},{"internalType":"int256","name":"x","type":"int256"}],"name":"addCounter","outputs":[{"internalType":"int256","name":"value","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"alwaysRevertsAssert","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"alwaysRevertsCustomError","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"alwaysRevertsCustomErrorNoValues","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"alwaysRevertsRequire","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"name":"callRevertFunctionInSubContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"callRevertFunctionInTheContract","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"x","type":"int256"}],"name":"callbackMethod","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"","type":"int256"}],"name":"counterMap","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"currentStatus","outputs":[{"internalType":"enum NetworkDebugContract.Status","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"emitAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"input","type":"bytes32"}],"name":"emitBytes32","outputs":[{"internalType":"bytes32","name":"output","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitFourParamMixedEvent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"inputVal1","type":"uint256"},{"internalType":"string","name":"inputVal2","type":"string"}],"name":"emitInputs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"inputVal1","type":"uint256"},{"internalType":"string","name":"inputVal2","type":"string"}],"name":"emitInputsOutputs","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"first","type":"int256"},{"internalType":"int128","name":"second","type":"int128"},{"internalType":"uint256","name":"third","type":"uint256"}],"name":"emitInts","outputs":[{"internalType":"int256","name":"","type":"int256"},{"internalType":"int128","name":"outputVal1","type":"int128"},{"internalType":"uint256","name":"outputVal2","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"inputVal1","type":"uint256"},{"internalType":"string","name":"inputVal2","type":"string"}],"name":"emitNamedInputsOutputs","outputs":[{"internalType":"uint256","name":"outputVal1","type":"uint256"},{"internalType":"string","name":"outputVal2","type":"string"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitNamedOutputs","outputs":[{"internalType":"uint256","name":"outputVal1","type":"uint256"},{"internalType":"string","name":"outputVal2","type":"string"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitNoIndexEvent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitNoIndexEventString","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitNoIndexStructEvent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitOneIndexEvent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitOutputs","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitThreeIndexEvent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitTwoIndexEvent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"get","outputs":[{"internalType":"int256","name":"data","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"idx","type":"int256"}],"name":"getCounter","outputs":[{"internalType":"int256","name":"data","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getData","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMap","outputs":[{"internalType":"int256","name":"data","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onTokenTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pay","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"performStaticCall","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"input","type":"address[]"}],"name":"processAddressArray","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"internalType":"struct NetworkDebugContract.Data","name":"data","type":"tuple"}],"name":"processDynamicData","outputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"internalType":"struct NetworkDebugContract.Data","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"internalType":"struct NetworkDebugContract.Data[3]","name":"data","type":"tuple[3]"}],"name":"processFixedDataArray","outputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"internalType":"struct NetworkDebugContract.Data[2]","name":"","type":"tuple[2]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"internalType":"struct NetworkDebugContract.Data","name":"data","type":"tuple"},{"internalType":"bytes","name":"dynamicBytes","type":"bytes"}],"internalType":"struct NetworkDebugContract.NestedData","name":"data","type":"tuple"}],"name":"processNestedData","outputs":[{"components":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"internalType":"struct NetworkDebugContract.Data","name":"data","type":"tuple"},{"internalType":"bytes","name":"dynamicBytes","type":"bytes"}],"internalType":"struct NetworkDebugContract.NestedData","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"internalType":"struct NetworkDebugContract.Data","name":"data","type":"tuple"}],"name":"processNestedData","outputs":[{"components":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint256[]","name":"values","type":"uint256[]"}],"internalType":"struct NetworkDebugContract.Data","name":"data","type":"tuple"},{"internalType":"bytes","name":"dynamicBytes","type":"bytes"}],"internalType":"struct NetworkDebugContract.NestedData","name":"","type":"tuple"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"input","type":"uint256[]"}],"name":"processUintArray","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"idx","type":"int256"}],"name":"resetCounter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"x","type":"int256"}],"name":"set","outputs":[{"internalType":"int256","name":"value","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"x","type":"int256"}],"name":"setMap","outputs":[{"internalType":"int256","name":"value","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"enum NetworkDebugContract.Status","name":"status","type":"uint8"}],"name":"setStatus","outputs":[{"internalType":"enum NetworkDebugContract.Status","name":"","type":"uint8"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"storedData","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"storedDataMap","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"subContract","outputs":[{"internalType":"contract NetworkDebugSubContract","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"name":"trace","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"name":"traceDifferent","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"name":"traceSubWithCallback","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"name":"traceWithValidate","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"name":"traceYetDifferent","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"name":"validate","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file diff --git a/seth/contracts/abi/NetworkDebugSubContract.abi b/seth/contracts/abi/NetworkDebugSubContract.abi new file mode 100644 index 000000000..a06fc6200 --- /dev/null +++ b/seth/contracts/abi/NetworkDebugSubContract.abi @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"CustomErr","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"NoIndexEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"str","type":"string"}],"name":"NoIndexEventString","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint64","name":"balance","type":"uint64"},{"internalType":"uint256","name":"dailyLimit","type":"uint256"}],"indexed":false,"internalType":"struct NetworkDebugSubContract.Account","name":"a","type":"tuple"}],"name":"NoIndexStructEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"a","type":"uint256"}],"name":"OneIndexEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":true,"internalType":"address","name":"startedBy","type":"address"},{"indexed":false,"internalType":"uint256","name":"startedAt","type":"uint256"}],"name":"ThreeIndexEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":true,"internalType":"address","name":"startedBy","type":"address"}],"name":"TwoIndexEvent","type":"event"},{"inputs":[{"internalType":"uint256","name":"x","type":"uint256"},{"internalType":"uint256","name":"y","type":"uint256"}],"name":"alwaysRevertsCustomError","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pay","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"name":"trace","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"x","type":"int256"}],"name":"traceOneInt","outputs":[{"internalType":"int256","name":"r","type":"int256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256","name":"x","type":"int256"},{"internalType":"int256","name":"y","type":"int256"}],"name":"traceWithCallback","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"nonpayable","type":"function"}] \ No newline at end of file diff --git a/seth/contracts/bin/DebugContractCallback.bin b/seth/contracts/bin/DebugContractCallback.bin new file mode 100644 index 000000000..e69de29bb diff --git a/seth/contracts/bin/NetworkDebugContract.bin b/seth/contracts/bin/NetworkDebugContract.bin new file mode 100644 index 000000000..4ee99e999 --- /dev/null +++ b/seth/contracts/bin/NetworkDebugContract.bin @@ -0,0 +1 @@ +60806040523480156200001157600080fd5b5060405162004015380380620040158339818101604052810190620000379190620000f2565b80600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101006004819055505062000124565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620000ba826200008d565b9050919050565b620000cc81620000ad565b8114620000d857600080fd5b50565b600081519050620000ec81620000c1565b92915050565b6000602082840312156200010b576200010a62000088565b5b60006200011b84828501620000db565b91505092915050565b613ee180620001346000396000f3fe6080604052600436106102e85760003560e01c8063788c477211610190578063b1ae9d85116100dc578063e5c19b2d11610095578063ef8a92351161006f578063ef8a923514610bc8578063f3396bd914610bf3578063f499af2a14610c1c578063fbcb8d0714610c5957610328565b8063e5c19b2d14610b11578063e8116e2814610b4e578063ec5c3ede14610b8b57610328565b8063b1ae9d8514610a0d578063b600141f14610a3d578063c0d06d8914610a54578063c2124b2214610a7f578063d7a8020514610a96578063e1111f7914610ad457610328565b80639349d00b116101495780639e099652116101235780639e09965214610963578063a4c0ed36146109a2578063aa3fdcf4146109cb578063ad3de14c146109e257610328565b80639349d00b146108f857806395a81a4c1461090f57806399adad2e1461092657610328565b8063788c4772146107fb5780637f12881c146108125780637fdc8fe11461084f57806381b375a01461088c5780638db611be146108b55780638f856296146108e157610328565b806333311ef31161024f57806358379d71116102085780636284117d116101e25780636284117d1461075057806362c270e11461078d5780636d4ce63c146107a45780637014c81d146107cf57610328565b806358379d71146106bf5780635921483f146106fc5780635e9c80d61461073957610328565b806333311ef3146105625780633837a75e1461059f5780633bc5de30146105dc5780633e41f1351461060757806345f0c9e61461064457806348ad9fe81461068257610328565b806323515760116102a1578063235157601461043e578063256560d51461047b5780632a1afcd9146104925780632e49d78b146104bd57806330985bcc146104fa5780633170428e1461053757610328565b806304d8215b1461036357806306595f75146103a057806311b3c478146103b757806312d91233146103e05780631b9265b81461041d5780631e31d0a81461042757610328565b36610328577f59e04c3f0d44b7caf6e8ef854b61d9a51cf1960d7a88ff6356cc5e946b4b5832333460405161031e9291906120c3565b60405180910390a1005b7f1e57e3bb474320be3d2c77138f75b7c3941292d647f5f9634e33a8e94e0e069b33346040516103599291906120ff565b60405180910390a1005b34801561036f57600080fd5b5061038a60048036038101906103859190612172565b610c96565b60405161039791906121cd565b60405180910390f35b3480156103ac57600080fd5b506103b5610cdc565b005b3480156103c357600080fd5b506103de60048036038101906103d99190612214565b610d1f565b005b3480156103ec57600080fd5b50610407600480360381019061040291906123ad565b610db2565b60405161041491906124b4565b60405180910390f35b610425610e71565b005b34801561043357600080fd5b5061043c610e73565b005b34801561044a57600080fd5b5061046560048036038101906104609190612172565b610eba565b60405161047291906124e5565b60405180910390f35b34801561048757600080fd5b50610490610eef565b005b34801561049e57600080fd5b506104a7610f00565b6040516104b491906124e5565b60405180910390f35b3480156104c957600080fd5b506104e460048036038101906104df9190612525565b610f06565b6040516104f191906125c9565b60405180910390f35b34801561050657600080fd5b50610521600480360381019061051c9190612172565b610f88565b60405161052e91906124e5565b60405180910390f35b34801561054357600080fd5b5061054c61106a565b60405161055991906125e4565b60405180910390f35b34801561056e57600080fd5b5061058960048036038101906105849190612635565b6111a9565b6040516105969190612671565b60405180910390f35b3480156105ab57600080fd5b506105c660048036038101906105c19190612172565b6111b3565b6040516105d391906124e5565b60405180910390f35b3480156105e857600080fd5b506105f16112be565b6040516105fe91906125e4565b60405180910390f35b34801561061357600080fd5b5061062e60048036038101906106299190612172565b6112c8565b60405161063b91906124e5565b60405180910390f35b34801561065057600080fd5b5061066b60048036038101906106669190612741565b6113c3565b60405161067992919061280b565b60405180910390f35b34801561068e57600080fd5b506106a960048036038101906106a49190612867565b6113d4565b6040516106b691906124e5565b60405180910390f35b3480156106cb57600080fd5b506106e660048036038101906106e19190612172565b6113ec565b6040516106f391906124e5565b60405180910390f35b34801561070857600080fd5b50610723600480360381019061071e9190612894565b6114e7565b60405161073091906124e5565b60405180910390f35b34801561074557600080fd5b5061074e611504565b005b34801561075c57600080fd5b5061077760048036038101906107729190612894565b611545565b60405161078491906124e5565b60405180910390f35b34801561079957600080fd5b506107a261155d565b005b3480156107b057600080fd5b506107b96115f1565b6040516107c691906124e5565b60405180910390f35b3480156107db57600080fd5b506107e46115fa565b6040516107f292919061280b565b60405180910390f35b34801561080757600080fd5b5061081061163f565b005b34801561081e57600080fd5b50610839600480360381019061083491906128e5565b611676565b6040516108469190612ac4565b60405180910390f35b34801561085b57600080fd5b5061087660048036038101906108719190612b05565b61168f565b6040516108839190612d12565b60405180910390f35b34801561089857600080fd5b506108b360048036038101906108ae9190612741565b611698565b005b3480156108c157600080fd5b506108ca61169c565b6040516108d892919061280b565b60405180910390f35b3480156108ed57600080fd5b506108f66116e1565b005b34801561090457600080fd5b5061090d611711565b005b34801561091b57600080fd5b5061092461171b565b005b34801561093257600080fd5b5061094d60048036038101906109489190612d56565b611754565b60405161095a9190612e55565b60405180910390f35b34801561096f57600080fd5b5061098a60048036038101906109859190612eb0565b6117ff565b60405161099993929190612f12565b60405180910390f35b3480156109ae57600080fd5b506109c960048036038101906109c49190612fa4565b611816565b005b3480156109d757600080fd5b506109e0611b02565b005b3480156109ee57600080fd5b506109f7611b4b565b604051610a0491906124e5565b60405180910390f35b610a276004803603810190610a229190612172565b611b92565b604051610a3491906124e5565b60405180910390f35b348015610a4957600080fd5b50610a52611cdc565b005b348015610a6057600080fd5b50610a69611d0e565b604051610a769190613077565b60405180910390f35b348015610a8b57600080fd5b50610a94611d34565b005b348015610aa257600080fd5b50610abd6004803603810190610ab89190612741565b611d86565b604051610acb92919061280b565b60405180910390f35b348015610ae057600080fd5b50610afb6004803603810190610af69190613155565b611d97565b604051610b08919061325c565b60405180910390f35b348015610b1d57600080fd5b50610b386004803603810190610b339190612894565b611da1565b604051610b4591906124e5565b60405180910390f35b348015610b5a57600080fd5b50610b756004803603810190610b709190612894565b611db2565b604051610b8291906124e5565b60405180910390f35b348015610b9757600080fd5b50610bb26004803603810190610bad9190612867565b611e00565b604051610bbf919061327e565b60405180910390f35b348015610bd457600080fd5b50610bdd611e0a565b604051610bea91906125c9565b60405180910390f35b348015610bff57600080fd5b50610c1a6004803603810190610c159190612894565b611e1d565b005b348015610c2857600080fd5b50610c436004803603810190610c3e9190612b05565b611e39565b604051610c509190612ac4565b60405180910390f35b348015610c6557600080fd5b50610c806004803603810190610c7b9190612894565b611f6e565b604051610c8d91906124e5565b60405180910390f35b60007fdfac7500004753b91139af55816e7eade36d96faec68b343f77ed66b89912a7b828413604051610cc991906121cd565b60405180910390a1818313905092915050565b6000610d1d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d14906132e5565b60405180910390fd5b565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166311abb00283836040518363ffffffff1660e01b8152600401610d7c929190613305565b600060405180830381600087803b158015610d9657600080fd5b505af1158015610daa573d6000803e3d6000fd5b505050505050565b60606000825167ffffffffffffffff811115610dd157610dd061226a565b5b604051908082528060200260200182016040528015610dff5781602001602082028036833780820191505090505b50905060005b8351811015610e67576001848281518110610e2357610e2261332e565b5b6020026020010151610e35919061338c565b828281518110610e4857610e4761332e565b5b6020026020010181815250508080610e5f906133c0565b915050610e05565b5080915050919050565b565b3373ffffffffffffffffffffffffffffffffffffffff1660017f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a3565b600081600260008581526020019081526020016000206000828254610edf9190613408565b9250508190555081905092915050565b6000610efe57610efd61344c565b5b565b60005481565b600081600560006101000a81548160ff02191690836003811115610f2d57610f2c612552565b5b0217905550600560009054906101000a900460ff166003811115610f5457610f53612552565b5b7fbea054406fdf249b05d1aef1b5f848d62d902d94389fca702b2d8337677c359a60405160405180910390a2819050919050565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663047c4425836040518263ffffffff1660e01b8152600401610fe591906124e5565b6020604051808303816000875af1158015611004573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110289190613490565b50827feace1be0b97ec11f959499c07b9f60f0cc47bf610b28fda8fb0e970339cf3b3560405160405180910390a281836110629190613408565b905092915050565b6000803090506000808273ffffffffffffffffffffffffffffffffffffffff16633bc5de3060e01b604051602401604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161110291906134f9565b600060405180830381855afa9150503d806000811461113d576040519150601f19603f3d011682016040523d82523d6000602084013e611142565b606091505b509150915081611187576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161117e9061355c565b60405180910390fd5b60008180602001905181019061119d9190613591565b90508094505050505090565b6000819050919050565b60006002826111c29190613408565b9150600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fa8fca7a84846040518363ffffffff1660e01b81526004016112219291906135be565b6020604051808303816000875af1158015611240573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112649190613490565b503373ffffffffffffffffffffffffffffffffffffffff1660017f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a381836112b69190613408565b905092915050565b6000600454905090565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633e41f13584846040518363ffffffff1660e01b81526004016113279291906135be565b6020604051808303816000875af1158015611346573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061136a9190613490565b503373ffffffffffffffffffffffffffffffffffffffff16827f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a381836113bb9190613408565b905092915050565b600060608383915091509250929050565b60016020528060005260406000206000915090505481565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633e41f13584846040518363ffffffff1660e01b815260040161144b9291906135be565b6020604051808303816000875af115801561146a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061148e9190613490565b503373ffffffffffffffffffffffffffffffffffffffff16827f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a381836114df9190613408565b905092915050565b600060026000838152602001908152602001600020549050919050565b600c60156040517f4a2eaf7e00000000000000000000000000000000000000000000000000000000815260040161153c92919061365d565b60405180910390fd5b60026020528060005260406000206000915090505481565b7febe3ff7e2071d351bf2e65b4fccd24e3ae99485f02468f1feecf7d64dc04418860405180606001604052806040518060400160405280600481526020017f4a6f686e000000000000000000000000000000000000000000000000000000008152508152602001600567ffffffffffffffff168152602001600a8152506040516115e791906136f9565b60405180910390a1565b60008054905090565b60006060617a696040518060400160405280600a81526020017f6f757470757456616c3100000000000000000000000000000000000000000000815250915091509091565b7f25b7adba1b046a19379db4bc06aa1f2e71604d7b599a0ee8783d58110f00e16a60405161166c90613767565b60405180910390a1565b61167e611fa5565b8161168890613942565b9050919050565b36819050919050565b5050565b60006060617a696040518060400160405280600a81526020017f6f757470757456616c3100000000000000000000000000000000000000000000815250915091509091565b60537feace1be0b97ec11f959499c07b9f60f0cc47bf610b28fda8fb0e970339cf3b3560405160405180910390a2565b611719611504565b565b7f33bc9bae48dbe1e057f264b3fc6a1dacdcceacb3ba28d937231c70e068a02f1c3360405161174a919061327e565b60405180910390a1565b61175c611fc5565b611764611fc5565b826000600381106117785761177761332e565b5b6020028101906117889190613964565b6117919061398c565b816000600281106117a5576117a461332e565b5b6020020181905250826001600381106117c1576117c061332e565b5b6020028101906117d19190613964565b6117da9061398c565b816001600281106117ee576117ed61332e565b5b602002018190525080915050919050565b600080600085858592509250925093509350939050565b7f962c5df4c8ad201a4f54a88f47715bb2cf291d019e350e2dff50ca6fc0f5d0ed8282905060405161184891906125e4565b60405180910390a16000828290500361189c57606360656040517f4a2eaf7e000000000000000000000000000000000000000000000000000000008152600401611893929190613a15565b60405180910390fd5b6000803073ffffffffffffffffffffffffffffffffffffffff1684846040516118c6929190613a63565b600060405180830381855af49150503d8060008114611901576040519150601f19603f3d011682016040523d82523d6000602084013e611906565b606091505b50915091508161195e576000815111156119235780518082602001fd5b6040517f2350eb5200000000000000000000000000000000000000000000000000000000815260040161195590613aee565b60405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff16633170428e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119cd9190613591565b50600084846000906004926119e493929190613b18565b906119ef9190613b97565b90506358379d7160e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603611a78576040517f2350eb52000000000000000000000000000000000000000000000000000000008152600401611a6f90613c42565b60405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff16633837a75e600160026040518363ffffffff1660e01b8152600401611ab5929190613cd8565b6020604051808303816000875af1158015611ad4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af89190613490565b5050505050505050565b60033373ffffffffffffffffffffffffffffffffffffffff1660017f5660e8f93f0146f45abcd659e026b75995db50053cbbca4d7f365934ade68bf360405160405180910390a4565b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905090565b6000611b9e8383610c96565b15611c9b57600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633e41f13584846040518363ffffffff1660e01b8152600401611c009291906135be565b6020604051808303816000875af1158015611c1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c439190613490565b503373ffffffffffffffffffffffffffffffffffffffff16827f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a38183611c949190613408565b9050611cd6565b6040517f2350eb52000000000000000000000000000000000000000000000000000000008152600401611ccd90613d73565b60405180910390fd5b92915050565b6040517fa0c2d2db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60033373ffffffffffffffffffffffffffffffffffffffff1660027f56c2ea44ba516098cee0c181dd9d8db262657368b6e911e83ae0ccfae806c73d604051611d7c90613ddf565b60405180910390a4565b600060608383915091509250929050565b6060819050919050565b600081600081905550819050919050565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550819050919050565b6000819050919050565b600560009054906101000a900460ff1681565b6000600260008381526020019081526020016000208190555050565b611e41611fa5565b6000828060000190611e539190613dff565b604051602001611e64929190613e92565b6040516020818303038152906040528051906020012090506000602067ffffffffffffffff811115611e9957611e9861226a565b5b6040519080825280601f01601f191660200182016040528015611ecb5781602001600182028036833780820191505090505b50905060005b6020811015611f4657828160208110611eed57611eec61332e565b5b1a60f81b828281518110611f0457611f0361332e565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508080611f3e906133c0565b915050611ed1565b50604051806040016040528085611f5c9061398c565b81526020018281525092505050919050565b6000817fb16dba9242e1aa07ccc47228094628f72c8cc9699ee45d5bc8d67b84d3038c6860405160405180910390a2819050919050565b6040518060400160405280611fb8611ff2565b8152602001606081525090565b60405180604001604052806002905b611fdc611ff2565b815260200190600190039081611fd45790505090565b604051806040016040528060608152602001606081525090565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006120378261200c565b9050919050565b6120478161202c565b82525050565b6000819050919050565b6120608161204d565b82525050565b600082825260208201905092915050565b7f5265636569766564204574686572000000000000000000000000000000000000600082015250565b60006120ad600e83612066565b91506120b882612077565b602082019050919050565b60006060820190506120d8600083018561203e565b6120e56020830184612057565b81810360408301526120f6816120a0565b90509392505050565b6000604082019050612114600083018561203e565b6121216020830184612057565b9392505050565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b61214f8161213c565b811461215a57600080fd5b50565b60008135905061216c81612146565b92915050565b6000806040838503121561218957612188612132565b5b60006121978582860161215d565b92505060206121a88582860161215d565b9150509250929050565b60008115159050919050565b6121c7816121b2565b82525050565b60006020820190506121e260008301846121be565b92915050565b6121f18161204d565b81146121fc57600080fd5b50565b60008135905061220e816121e8565b92915050565b6000806040838503121561222b5761222a612132565b5b6000612239858286016121ff565b925050602061224a858286016121ff565b9150509250929050565b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6122a282612259565b810181811067ffffffffffffffff821117156122c1576122c061226a565b5b80604052505050565b60006122d4612128565b90506122e08282612299565b919050565b600067ffffffffffffffff821115612300576122ff61226a565b5b602082029050602081019050919050565b600080fd5b6000612329612324846122e5565b6122ca565b9050808382526020820190506020840283018581111561234c5761234b612311565b5b835b81811015612375578061236188826121ff565b84526020840193505060208101905061234e565b5050509392505050565b600082601f83011261239457612393612254565b5b81356123a4848260208601612316565b91505092915050565b6000602082840312156123c3576123c2612132565b5b600082013567ffffffffffffffff8111156123e1576123e0612137565b5b6123ed8482850161237f565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b61242b8161204d565b82525050565b600061243d8383612422565b60208301905092915050565b6000602082019050919050565b6000612461826123f6565b61246b8185612401565b935061247683612412565b8060005b838110156124a757815161248e8882612431565b975061249983612449565b92505060018101905061247a565b5085935050505092915050565b600060208201905081810360008301526124ce8184612456565b905092915050565b6124df8161213c565b82525050565b60006020820190506124fa60008301846124d6565b92915050565b6004811061250d57600080fd5b50565b60008135905061251f81612500565b92915050565b60006020828403121561253b5761253a612132565b5b600061254984828501612510565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6004811061259257612591612552565b5b50565b60008190506125a382612581565b919050565b60006125b382612595565b9050919050565b6125c3816125a8565b82525050565b60006020820190506125de60008301846125ba565b92915050565b60006020820190506125f96000830184612057565b92915050565b6000819050919050565b612612816125ff565b811461261d57600080fd5b50565b60008135905061262f81612609565b92915050565b60006020828403121561264b5761264a612132565b5b600061265984828501612620565b91505092915050565b61266b816125ff565b82525050565b60006020820190506126866000830184612662565b92915050565b600080fd5b600067ffffffffffffffff8211156126ac576126ab61226a565b5b6126b582612259565b9050602081019050919050565b82818337600083830152505050565b60006126e46126df84612691565b6122ca565b905082815260208101848484011115612700576126ff61268c565b5b61270b8482856126c2565b509392505050565b600082601f83011261272857612727612254565b5b81356127388482602086016126d1565b91505092915050565b6000806040838503121561275857612757612132565b5b6000612766858286016121ff565b925050602083013567ffffffffffffffff81111561278757612786612137565b5b61279385828601612713565b9150509250929050565b600081519050919050565b60005b838110156127c65780820151818401526020810190506127ab565b60008484015250505050565b60006127dd8261279d565b6127e78185612066565b93506127f78185602086016127a8565b61280081612259565b840191505092915050565b60006040820190506128206000830185612057565b818103602083015261283281846127d2565b90509392505050565b6128448161202c565b811461284f57600080fd5b50565b6000813590506128618161283b565b92915050565b60006020828403121561287d5761287c612132565b5b600061288b84828501612852565b91505092915050565b6000602082840312156128aa576128a9612132565b5b60006128b88482850161215d565b91505092915050565b600080fd5b6000604082840312156128dc576128db6128c1565b5b81905092915050565b6000602082840312156128fb576128fa612132565b5b600082013567ffffffffffffffff81111561291957612918612137565b5b612925848285016128c6565b91505092915050565b600082825260208201905092915050565b600061294a8261279d565b612954818561292e565b93506129648185602086016127a8565b61296d81612259565b840191505092915050565b600082825260208201905092915050565b6000612994826123f6565b61299e8185612978565b93506129a983612412565b8060005b838110156129da5781516129c18882612431565b97506129cc83612449565b9250506001810190506129ad565b5085935050505092915050565b60006040830160008301518482036000860152612a04828261293f565b91505060208301518482036020860152612a1e8282612989565b9150508091505092915050565b600081519050919050565b600082825260208201905092915050565b6000612a5282612a2b565b612a5c8185612a36565b9350612a6c8185602086016127a8565b612a7581612259565b840191505092915050565b60006040830160008301518482036000860152612a9d82826129e7565b91505060208301518482036020860152612ab78282612a47565b9150508091505092915050565b60006020820190508181036000830152612ade8184612a80565b905092915050565b600060408284031215612afc57612afb6128c1565b5b81905092915050565b600060208284031215612b1b57612b1a612132565b5b600082013567ffffffffffffffff811115612b3957612b38612137565b5b612b4584828501612ae6565b91505092915050565b600080fd5b600080fd5b600080fd5b60008083356001602003843603038112612b7a57612b79612b58565b5b83810192508235915060208301925067ffffffffffffffff821115612ba257612ba1612b4e565b5b600182023603831315612bb857612bb7612b53565b5b509250929050565b6000612bcc838561292e565b9350612bd98385846126c2565b612be283612259565b840190509392505050565b60008083356001602003843603038112612c0a57612c09612b58565b5b83810192508235915060208301925067ffffffffffffffff821115612c3257612c31612b4e565b5b602082023603831315612c4857612c47612b53565b5b509250929050565b600080fd5b82818337505050565b6000612c6a8385612978565b93507f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115612c9d57612c9c612c50565b5b602083029250612cae838584612c55565b82840190509392505050565b600060408301612ccd6000840184612b5d565b8583036000870152612ce0838284612bc0565b92505050612cf16020840184612bed565b8583036020870152612d04838284612c5e565b925050508091505092915050565b60006020820190508181036000830152612d2c8184612cba565b905092915050565b600081905082602060030282011115612d5057612d4f612311565b5b92915050565b600060208284031215612d6c57612d6b612132565b5b600082013567ffffffffffffffff811115612d8a57612d89612137565b5b612d9684828501612d34565b91505092915050565b600060029050919050565b600081905092915050565b6000819050919050565b6000612dcb83836129e7565b905092915050565b6000602082019050919050565b6000612deb82612d9f565b612df58185612daa565b935083602082028501612e0785612db5565b8060005b85811015612e435784840389528151612e248582612dbf565b9450612e2f83612dd3565b925060208a01995050600181019050612e0b565b50829750879550505050505092915050565b60006020820190508181036000830152612e6f8184612de0565b905092915050565b600081600f0b9050919050565b612e8d81612e77565b8114612e9857600080fd5b50565b600081359050612eaa81612e84565b92915050565b600080600060608486031215612ec957612ec8612132565b5b6000612ed78682870161215d565b9350506020612ee886828701612e9b565b9250506040612ef9868287016121ff565b9150509250925092565b612f0c81612e77565b82525050565b6000606082019050612f2760008301866124d6565b612f346020830185612f03565b612f416040830184612057565b949350505050565b600080fd5b60008083601f840112612f6457612f63612254565b5b8235905067ffffffffffffffff811115612f8157612f80612f49565b5b602083019150836001820283011115612f9d57612f9c612311565b5b9250929050565b60008060008060608587031215612fbe57612fbd612132565b5b6000612fcc87828801612852565b9450506020612fdd878288016121ff565b935050604085013567ffffffffffffffff811115612ffe57612ffd612137565b5b61300a87828801612f4e565b925092505092959194509250565b6000819050919050565b600061303d6130386130338461200c565b613018565b61200c565b9050919050565b600061304f82613022565b9050919050565b600061306182613044565b9050919050565b61307181613056565b82525050565b600060208201905061308c6000830184613068565b92915050565b600067ffffffffffffffff8211156130ad576130ac61226a565b5b602082029050602081019050919050565b60006130d16130cc84613092565b6122ca565b905080838252602082019050602084028301858111156130f4576130f3612311565b5b835b8181101561311d57806131098882612852565b8452602084019350506020810190506130f6565b5050509392505050565b600082601f83011261313c5761313b612254565b5b813561314c8482602086016130be565b91505092915050565b60006020828403121561316b5761316a612132565b5b600082013567ffffffffffffffff81111561318957613188612137565b5b61319584828501613127565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6131d38161202c565b82525050565b60006131e583836131ca565b60208301905092915050565b6000602082019050919050565b60006132098261319e565b61321381856131a9565b935061321e836131ba565b8060005b8381101561324f57815161323688826131d9565b9750613241836131f1565b925050600181019050613222565b5085935050505092915050565b6000602082019050818103600083015261327681846131fe565b905092915050565b6000602082019050613293600083018461203e565b92915050565b7f616c7761797320726576657274206572726f7200000000000000000000000000600082015250565b60006132cf601383612066565b91506132da82613299565b602082019050919050565b600060208201905081810360008301526132fe816132c2565b9050919050565b600060408201905061331a6000830185612057565b6133276020830184612057565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006133978261204d565b91506133a28361204d565b92508282019050808211156133ba576133b961335d565b5b92915050565b60006133cb8261204d565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036133fd576133fc61335d565b5b600182019050919050565b60006134138261213c565b915061341e8361213c565b9250828201905082811215600083121683821260008412151617156134465761344561335d565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b60008151905061348a81612146565b92915050565b6000602082840312156134a6576134a5612132565b5b60006134b48482850161347b565b91505092915050565b600081905092915050565b60006134d382612a2b565b6134dd81856134bd565b93506134ed8185602086016127a8565b80840191505092915050565b600061350582846134c8565b915081905092915050565b7f5374617469632063616c6c206661696c65640000000000000000000000000000600082015250565b6000613546601283612066565b915061355182613510565b602082019050919050565b6000602082019050818103600083015261357581613539565b9050919050565b60008151905061358b816121e8565b92915050565b6000602082840312156135a7576135a6612132565b5b60006135b58482850161357c565b91505092915050565b60006040820190506135d360008301856124d6565b6135e060208301846124d6565b9392505050565b6000819050919050565b600061360c613607613602846135e7565b613018565b61204d565b9050919050565b61361c816135f1565b82525050565b6000819050919050565b600061364761364261363d84613622565b613018565b61204d565b9050919050565b6136578161362c565b82525050565b60006040820190506136726000830185613613565b61367f602083018461364e565b9392505050565b600067ffffffffffffffff82169050919050565b6136a381613686565b82525050565b600060608301600083015184820360008601526136c6828261293f565b91505060208301516136db602086018261369a565b5060408301516136ee6040860182612422565b508091505092915050565b6000602082019050818103600083015261371381846136a9565b905092915050565b7f6d79537472696e67000000000000000000000000000000000000000000000000600082015250565b6000613751600883612066565b915061375c8261371b565b602082019050919050565b6000602082019050818103600083015261378081613744565b9050919050565b600080fd5b600080fd5b6000604082840312156137a7576137a6613787565b5b6137b160406122ca565b9050600082013567ffffffffffffffff8111156137d1576137d061378c565b5b6137dd84828501612713565b600083015250602082013567ffffffffffffffff8111156138015761380061378c565b5b61380d8482850161237f565b60208301525092915050565b600067ffffffffffffffff8211156138345761383361226a565b5b61383d82612259565b9050602081019050919050565b600061385d61385884613819565b6122ca565b9050828152602081018484840111156138795761387861268c565b5b6138848482856126c2565b509392505050565b600082601f8301126138a1576138a0612254565b5b81356138b184826020860161384a565b91505092915050565b6000604082840312156138d0576138cf613787565b5b6138da60406122ca565b9050600082013567ffffffffffffffff8111156138fa576138f961378c565b5b61390684828501613791565b600083015250602082013567ffffffffffffffff81111561392a5761392961378c565b5b6139368482850161388c565b60208301525092915050565b600061394e36836138ba565b9050919050565b600080fd5b600080fd5b600080fd5b6000823560016040038336030381126139805761397f613955565b5b80830191505092915050565b60006139983683613791565b9050919050565b6000819050919050565b60006139c46139bf6139ba8461399f565b613018565b61204d565b9050919050565b6139d4816139a9565b82525050565b6000819050919050565b60006139ff6139fa6139f5846139da565b613018565b61204d565b9050919050565b613a0f816139e4565b82525050565b6000604082019050613a2a60008301856139cb565b613a376020830184613a06565b9392505050565b6000613a4a83856134bd565b9350613a578385846126c2565b82840190509392505050565b6000613a70828486613a3e565b91508190509392505050565b7f64656c656761746563616c6c206661696c65642077697468206e6f207265617360008201527f6f6e000000000000000000000000000000000000000000000000000000000000602082015250565b6000613ad8602283612066565b9150613ae382613a7c565b604082019050919050565b60006020820190508181036000830152613b0781613acb565b9050919050565b600080fd5b600080fd5b60008085851115613b2c57613b2b613b0e565b5b83861115613b3d57613b3c613b13565b5b6001850283019150848603905094509492505050565b600082905092915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b600082821b905092915050565b6000613ba38383613b53565b82613bae8135613b5e565b92506004821015613bee57613be97fffffffff0000000000000000000000000000000000000000000000000000000083600403600802613b8a565b831692505b505092915050565b7f6f68206f68206f682069742773206d6167696321000000000000000000000000600082015250565b6000613c2c601483612066565b9150613c3782613bf6565b602082019050919050565b60006020820190508181036000830152613c5b81613c1f565b9050919050565b6000819050919050565b6000613c87613c82613c7d84613c62565b613018565b61213c565b9050919050565b613c9781613c6c565b82525050565b6000819050919050565b6000613cc2613cbd613cb884613c9d565b613018565b61213c565b9050919050565b613cd281613ca7565b82525050565b6000604082019050613ced6000830185613c8e565b613cfa6020830184613cc9565b9392505050565b7f666972737420696e7420776173206e6f742067726561746572207468616e207360008201527f65636f6e6420696e740000000000000000000000000000000000000000000000602082015250565b6000613d5d602983612066565b9150613d6882613d01565b604082019050919050565b60006020820190508181036000830152613d8c81613d50565b9050919050565b7f736f6d6520696400000000000000000000000000000000000000000000000000600082015250565b6000613dc9600783612066565b9150613dd482613d93565b602082019050919050565b60006020820190508181036000830152613df881613dbc565b9050919050565b60008083356001602003843603038112613e1c57613e1b613955565b5b80840192508235915067ffffffffffffffff821115613e3e57613e3d61395a565b5b602083019250600182023603831315613e5a57613e5961395f565b5b509250929050565b600081905092915050565b6000613e798385613e62565b9350613e868385846126c2565b82840190509392505050565b6000613e9f828486613e6d565b9150819050939250505056fea264697066735822122095801437fe2af153ec90ad58a4678a194b7fb3b777ee30438995ef0f23596f7664736f6c63430008130033 \ No newline at end of file diff --git a/seth/contracts/bin/NetworkDebugSubContract.bin b/seth/contracts/bin/NetworkDebugSubContract.bin new file mode 100644 index 000000000..83359ec0e --- /dev/null +++ b/seth/contracts/bin/NetworkDebugSubContract.bin @@ -0,0 +1 @@ +608060405234801561001057600080fd5b506105f4806100206000396000f3fe60806040526004361061004a5760003560e01c8063047c44251461004f57806311abb0021461008c5780631b9265b8146100b55780633e41f135146100bf578063fa8fca7a146100fc575b600080fd5b34801561005b57600080fd5b5061007660048036038101906100719190610368565b610139565b60405161008391906103a4565b60405180910390f35b34801561009857600080fd5b506100b360048036038101906100ae91906103f5565b610186565b005b6100bd6101c5565b005b3480156100cb57600080fd5b506100e660048036038101906100e19190610435565b6101c7565b6040516100f391906103a4565b60405180910390f35b34801561010857600080fd5b50610123600480360381019061011e9190610435565b610230565b60405161013091906103a4565b60405180910390f35b60007f33bc9bae48dbe1e057f264b3fc6a1dacdcceacb3ba28d937231c70e068a02f1c3360405161016a91906104b6565b60405180910390a160038261017f9190610500565b9050919050565b81816040517f4a2eaf7e0000000000000000000000000000000000000000000000000000000081526004016101bc929190610553565b60405180910390fd5b565b60006002826101d69190610500565b91503373ffffffffffffffffffffffffffffffffffffffff16827f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a381836102289190610500565b905092915050565b60003373ffffffffffffffffffffffffffffffffffffffff16827f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a360003373ffffffffffffffffffffffffffffffffffffffff1663fbcb8d07846040518263ffffffff1660e01b81526004016102b191906103a4565b6020604051808303816000875af11580156102d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102f49190610591565b9050807feace1be0b97ec11f959499c07b9f60f0cc47bf610b28fda8fb0e970339cf3b3560405160405180910390a28091505092915050565b600080fd5b6000819050919050565b61034581610332565b811461035057600080fd5b50565b6000813590506103628161033c565b92915050565b60006020828403121561037e5761037d61032d565b5b600061038c84828501610353565b91505092915050565b61039e81610332565b82525050565b60006020820190506103b96000830184610395565b92915050565b6000819050919050565b6103d2816103bf565b81146103dd57600080fd5b50565b6000813590506103ef816103c9565b92915050565b6000806040838503121561040c5761040b61032d565b5b600061041a858286016103e0565b925050602061042b858286016103e0565b9150509250929050565b6000806040838503121561044c5761044b61032d565b5b600061045a85828601610353565b925050602061046b85828601610353565b9150509250929050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006104a082610475565b9050919050565b6104b081610495565b82525050565b60006020820190506104cb60008301846104a7565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061050b82610332565b915061051683610332565b92508282019050828112156000831216838212600084121516171561053e5761053d6104d1565b5b92915050565b61054d816103bf565b82525050565b60006040820190506105686000830185610544565b6105756020830184610544565b9392505050565b60008151905061058b8161033c565b92915050565b6000602082840312156105a7576105a661032d565b5b60006105b58482850161057c565b9150509291505056fea2646970667358221220baf1ca0ebe5c3f70cf36de50eba34b796fa2141e3773a505c3199b335b0023c564736f6c63430008130033 \ No newline at end of file diff --git a/seth/contracts/bind/debug/NetworkDebugContract.go b/seth/contracts/bind/debug/NetworkDebugContract.go new file mode 100644 index 000000000..d2e14bd02 --- /dev/null +++ b/seth/contracts/bind/debug/NetworkDebugContract.go @@ -0,0 +1,3286 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package network_debug_contract + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// NetworkDebugContractAccount is an auto generated low-level Go binding around an user-defined struct. +type NetworkDebugContractAccount struct { + Name string + Balance uint64 + DailyLimit *big.Int +} + +// NetworkDebugContractData is an auto generated low-level Go binding around an user-defined struct. +type NetworkDebugContractData struct { + Name string + Values []*big.Int +} + +// NetworkDebugContractNestedData is an auto generated low-level Go binding around an user-defined struct. +type NetworkDebugContractNestedData struct { + Data NetworkDebugContractData + DynamicBytes []byte +} + +// NetworkDebugContractMetaData contains all meta data concerning the NetworkDebugContract contract. +var NetworkDebugContractMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"address\",\"name\":\"subAddr\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"required\",\"type\":\"uint256\"}],\"name\":\"CustomErr\",\"type\":\"error\"},{\"inputs\":[],\"name\":\"CustomErrNoValues\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"CustomErrWithMessage\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"CallDataLength\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"int256\",\"name\":\"a\",\"type\":\"int256\"}],\"name\":\"CallbackEvent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"enumNetworkDebugContract.Status\",\"name\":\"status\",\"type\":\"uint8\"}],\"name\":\"CurrentStatus\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"EtherReceived\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"name\":\"IsValidEvent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"NoIndexEvent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"str\",\"type\":\"string\"}],\"name\":\"NoIndexEventString\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"uint64\",\"name\":\"balance\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"dailyLimit\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"structNetworkDebugContract.Account\",\"name\":\"a\",\"type\":\"tuple\"}],\"name\":\"NoIndexStructEvent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"a\",\"type\":\"uint256\"}],\"name\":\"OneIndexEvent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"caller\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"message\",\"type\":\"string\"}],\"name\":\"Received\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"roundId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"startedBy\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"startedAt\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"string\",\"name\":\"dataId\",\"type\":\"string\"}],\"name\":\"ThreeIndexAndOneNonIndexedEvent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"roundId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"startedBy\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"startedAt\",\"type\":\"uint256\"}],\"name\":\"ThreeIndexEvent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"roundId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"startedBy\",\"type\":\"address\"}],\"name\":\"TwoIndexEvent\",\"type\":\"event\"},{\"stateMutability\":\"payable\",\"type\":\"fallback\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"idx\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"x\",\"type\":\"int256\"}],\"name\":\"addCounter\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"value\",\"type\":\"int256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"alwaysRevertsAssert\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"alwaysRevertsCustomError\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"alwaysRevertsCustomErrorNoValues\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"alwaysRevertsRequire\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"name\":\"callRevertFunctionInSubContract\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"callRevertFunctionInTheContract\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"x\",\"type\":\"int256\"}],\"name\":\"callbackMethod\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"name\":\"counterMap\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"currentStatus\",\"outputs\":[{\"internalType\":\"enumNetworkDebugContract.Status\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"addr\",\"type\":\"address\"}],\"name\":\"emitAddress\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes32\",\"name\":\"input\",\"type\":\"bytes32\"}],\"name\":\"emitBytes32\",\"outputs\":[{\"internalType\":\"bytes32\",\"name\":\"output\",\"type\":\"bytes32\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emitFourParamMixedEvent\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"inputVal1\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"inputVal2\",\"type\":\"string\"}],\"name\":\"emitInputs\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"inputVal1\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"inputVal2\",\"type\":\"string\"}],\"name\":\"emitInputsOutputs\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"first\",\"type\":\"int256\"},{\"internalType\":\"int128\",\"name\":\"second\",\"type\":\"int128\"},{\"internalType\":\"uint256\",\"name\":\"third\",\"type\":\"uint256\"}],\"name\":\"emitInts\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"},{\"internalType\":\"int128\",\"name\":\"outputVal1\",\"type\":\"int128\"},{\"internalType\":\"uint256\",\"name\":\"outputVal2\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"inputVal1\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"inputVal2\",\"type\":\"string\"}],\"name\":\"emitNamedInputsOutputs\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"outputVal1\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"outputVal2\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emitNamedOutputs\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"outputVal1\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"outputVal2\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emitNoIndexEvent\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emitNoIndexEventString\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emitNoIndexStructEvent\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emitOneIndexEvent\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emitOutputs\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"},{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emitThreeIndexEvent\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"emitTwoIndexEvent\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"get\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"data\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"idx\",\"type\":\"int256\"}],\"name\":\"getCounter\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"data\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getData\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getMap\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"data\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"onTokenTransfer\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pay\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"performStaticCall\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address[]\",\"name\":\"input\",\"type\":\"address[]\"}],\"name\":\"processAddressArray\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"}],\"internalType\":\"structNetworkDebugContract.Data\",\"name\":\"data\",\"type\":\"tuple\"}],\"name\":\"processDynamicData\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"}],\"internalType\":\"structNetworkDebugContract.Data\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"}],\"internalType\":\"structNetworkDebugContract.Data[3]\",\"name\":\"data\",\"type\":\"tuple[3]\"}],\"name\":\"processFixedDataArray\",\"outputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"}],\"internalType\":\"structNetworkDebugContract.Data[2]\",\"name\":\"\",\"type\":\"tuple[2]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"}],\"internalType\":\"structNetworkDebugContract.Data\",\"name\":\"data\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"dynamicBytes\",\"type\":\"bytes\"}],\"internalType\":\"structNetworkDebugContract.NestedData\",\"name\":\"data\",\"type\":\"tuple\"}],\"name\":\"processNestedData\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"}],\"internalType\":\"structNetworkDebugContract.Data\",\"name\":\"data\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"dynamicBytes\",\"type\":\"bytes\"}],\"internalType\":\"structNetworkDebugContract.NestedData\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"}],\"internalType\":\"structNetworkDebugContract.Data\",\"name\":\"data\",\"type\":\"tuple\"}],\"name\":\"processNestedData\",\"outputs\":[{\"components\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"uint256[]\",\"name\":\"values\",\"type\":\"uint256[]\"}],\"internalType\":\"structNetworkDebugContract.Data\",\"name\":\"data\",\"type\":\"tuple\"},{\"internalType\":\"bytes\",\"name\":\"dynamicBytes\",\"type\":\"bytes\"}],\"internalType\":\"structNetworkDebugContract.NestedData\",\"name\":\"\",\"type\":\"tuple\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256[]\",\"name\":\"input\",\"type\":\"uint256[]\"}],\"name\":\"processUintArray\",\"outputs\":[{\"internalType\":\"uint256[]\",\"name\":\"\",\"type\":\"uint256[]\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"idx\",\"type\":\"int256\"}],\"name\":\"resetCounter\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"x\",\"type\":\"int256\"}],\"name\":\"set\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"value\",\"type\":\"int256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"x\",\"type\":\"int256\"}],\"name\":\"setMap\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"value\",\"type\":\"int256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"enumNetworkDebugContract.Status\",\"name\":\"status\",\"type\":\"uint8\"}],\"name\":\"setStatus\",\"outputs\":[{\"internalType\":\"enumNetworkDebugContract.Status\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"storedData\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"name\":\"storedDataMap\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"subContract\",\"outputs\":[{\"internalType\":\"contractNetworkDebugSubContract\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"x\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"y\",\"type\":\"int256\"}],\"name\":\"trace\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"x\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"y\",\"type\":\"int256\"}],\"name\":\"traceDifferent\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"x\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"y\",\"type\":\"int256\"}],\"name\":\"traceSubWithCallback\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"x\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"y\",\"type\":\"int256\"}],\"name\":\"traceWithValidate\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"x\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"y\",\"type\":\"int256\"}],\"name\":\"traceYetDifferent\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"x\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"y\",\"type\":\"int256\"}],\"name\":\"validate\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"stateMutability\":\"payable\",\"type\":\"receive\"}]", + Bin: "0x60806040523480156200001157600080fd5b5060405162004015380380620040158339818101604052810190620000379190620000f2565b80600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506101006004819055505062000124565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620000ba826200008d565b9050919050565b620000cc81620000ad565b8114620000d857600080fd5b50565b600081519050620000ec81620000c1565b92915050565b6000602082840312156200010b576200010a62000088565b5b60006200011b84828501620000db565b91505092915050565b613ee180620001346000396000f3fe6080604052600436106102e85760003560e01c8063788c477211610190578063b1ae9d85116100dc578063e5c19b2d11610095578063ef8a92351161006f578063ef8a923514610bc8578063f3396bd914610bf3578063f499af2a14610c1c578063fbcb8d0714610c5957610328565b8063e5c19b2d14610b11578063e8116e2814610b4e578063ec5c3ede14610b8b57610328565b8063b1ae9d8514610a0d578063b600141f14610a3d578063c0d06d8914610a54578063c2124b2214610a7f578063d7a8020514610a96578063e1111f7914610ad457610328565b80639349d00b116101495780639e099652116101235780639e09965214610963578063a4c0ed36146109a2578063aa3fdcf4146109cb578063ad3de14c146109e257610328565b80639349d00b146108f857806395a81a4c1461090f57806399adad2e1461092657610328565b8063788c4772146107fb5780637f12881c146108125780637fdc8fe11461084f57806381b375a01461088c5780638db611be146108b55780638f856296146108e157610328565b806333311ef31161024f57806358379d71116102085780636284117d116101e25780636284117d1461075057806362c270e11461078d5780636d4ce63c146107a45780637014c81d146107cf57610328565b806358379d71146106bf5780635921483f146106fc5780635e9c80d61461073957610328565b806333311ef3146105625780633837a75e1461059f5780633bc5de30146105dc5780633e41f1351461060757806345f0c9e61461064457806348ad9fe81461068257610328565b806323515760116102a1578063235157601461043e578063256560d51461047b5780632a1afcd9146104925780632e49d78b146104bd57806330985bcc146104fa5780633170428e1461053757610328565b806304d8215b1461036357806306595f75146103a057806311b3c478146103b757806312d91233146103e05780631b9265b81461041d5780631e31d0a81461042757610328565b36610328577f59e04c3f0d44b7caf6e8ef854b61d9a51cf1960d7a88ff6356cc5e946b4b5832333460405161031e9291906120c3565b60405180910390a1005b7f1e57e3bb474320be3d2c77138f75b7c3941292d647f5f9634e33a8e94e0e069b33346040516103599291906120ff565b60405180910390a1005b34801561036f57600080fd5b5061038a60048036038101906103859190612172565b610c96565b60405161039791906121cd565b60405180910390f35b3480156103ac57600080fd5b506103b5610cdc565b005b3480156103c357600080fd5b506103de60048036038101906103d99190612214565b610d1f565b005b3480156103ec57600080fd5b50610407600480360381019061040291906123ad565b610db2565b60405161041491906124b4565b60405180910390f35b610425610e71565b005b34801561043357600080fd5b5061043c610e73565b005b34801561044a57600080fd5b5061046560048036038101906104609190612172565b610eba565b60405161047291906124e5565b60405180910390f35b34801561048757600080fd5b50610490610eef565b005b34801561049e57600080fd5b506104a7610f00565b6040516104b491906124e5565b60405180910390f35b3480156104c957600080fd5b506104e460048036038101906104df9190612525565b610f06565b6040516104f191906125c9565b60405180910390f35b34801561050657600080fd5b50610521600480360381019061051c9190612172565b610f88565b60405161052e91906124e5565b60405180910390f35b34801561054357600080fd5b5061054c61106a565b60405161055991906125e4565b60405180910390f35b34801561056e57600080fd5b5061058960048036038101906105849190612635565b6111a9565b6040516105969190612671565b60405180910390f35b3480156105ab57600080fd5b506105c660048036038101906105c19190612172565b6111b3565b6040516105d391906124e5565b60405180910390f35b3480156105e857600080fd5b506105f16112be565b6040516105fe91906125e4565b60405180910390f35b34801561061357600080fd5b5061062e60048036038101906106299190612172565b6112c8565b60405161063b91906124e5565b60405180910390f35b34801561065057600080fd5b5061066b60048036038101906106669190612741565b6113c3565b60405161067992919061280b565b60405180910390f35b34801561068e57600080fd5b506106a960048036038101906106a49190612867565b6113d4565b6040516106b691906124e5565b60405180910390f35b3480156106cb57600080fd5b506106e660048036038101906106e19190612172565b6113ec565b6040516106f391906124e5565b60405180910390f35b34801561070857600080fd5b50610723600480360381019061071e9190612894565b6114e7565b60405161073091906124e5565b60405180910390f35b34801561074557600080fd5b5061074e611504565b005b34801561075c57600080fd5b5061077760048036038101906107729190612894565b611545565b60405161078491906124e5565b60405180910390f35b34801561079957600080fd5b506107a261155d565b005b3480156107b057600080fd5b506107b96115f1565b6040516107c691906124e5565b60405180910390f35b3480156107db57600080fd5b506107e46115fa565b6040516107f292919061280b565b60405180910390f35b34801561080757600080fd5b5061081061163f565b005b34801561081e57600080fd5b50610839600480360381019061083491906128e5565b611676565b6040516108469190612ac4565b60405180910390f35b34801561085b57600080fd5b5061087660048036038101906108719190612b05565b61168f565b6040516108839190612d12565b60405180910390f35b34801561089857600080fd5b506108b360048036038101906108ae9190612741565b611698565b005b3480156108c157600080fd5b506108ca61169c565b6040516108d892919061280b565b60405180910390f35b3480156108ed57600080fd5b506108f66116e1565b005b34801561090457600080fd5b5061090d611711565b005b34801561091b57600080fd5b5061092461171b565b005b34801561093257600080fd5b5061094d60048036038101906109489190612d56565b611754565b60405161095a9190612e55565b60405180910390f35b34801561096f57600080fd5b5061098a60048036038101906109859190612eb0565b6117ff565b60405161099993929190612f12565b60405180910390f35b3480156109ae57600080fd5b506109c960048036038101906109c49190612fa4565b611816565b005b3480156109d757600080fd5b506109e0611b02565b005b3480156109ee57600080fd5b506109f7611b4b565b604051610a0491906124e5565b60405180910390f35b610a276004803603810190610a229190612172565b611b92565b604051610a3491906124e5565b60405180910390f35b348015610a4957600080fd5b50610a52611cdc565b005b348015610a6057600080fd5b50610a69611d0e565b604051610a769190613077565b60405180910390f35b348015610a8b57600080fd5b50610a94611d34565b005b348015610aa257600080fd5b50610abd6004803603810190610ab89190612741565b611d86565b604051610acb92919061280b565b60405180910390f35b348015610ae057600080fd5b50610afb6004803603810190610af69190613155565b611d97565b604051610b08919061325c565b60405180910390f35b348015610b1d57600080fd5b50610b386004803603810190610b339190612894565b611da1565b604051610b4591906124e5565b60405180910390f35b348015610b5a57600080fd5b50610b756004803603810190610b709190612894565b611db2565b604051610b8291906124e5565b60405180910390f35b348015610b9757600080fd5b50610bb26004803603810190610bad9190612867565b611e00565b604051610bbf919061327e565b60405180910390f35b348015610bd457600080fd5b50610bdd611e0a565b604051610bea91906125c9565b60405180910390f35b348015610bff57600080fd5b50610c1a6004803603810190610c159190612894565b611e1d565b005b348015610c2857600080fd5b50610c436004803603810190610c3e9190612b05565b611e39565b604051610c509190612ac4565b60405180910390f35b348015610c6557600080fd5b50610c806004803603810190610c7b9190612894565b611f6e565b604051610c8d91906124e5565b60405180910390f35b60007fdfac7500004753b91139af55816e7eade36d96faec68b343f77ed66b89912a7b828413604051610cc991906121cd565b60405180910390a1818313905092915050565b6000610d1d576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610d14906132e5565b60405180910390fd5b565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166311abb00283836040518363ffffffff1660e01b8152600401610d7c929190613305565b600060405180830381600087803b158015610d9657600080fd5b505af1158015610daa573d6000803e3d6000fd5b505050505050565b60606000825167ffffffffffffffff811115610dd157610dd061226a565b5b604051908082528060200260200182016040528015610dff5781602001602082028036833780820191505090505b50905060005b8351811015610e67576001848281518110610e2357610e2261332e565b5b6020026020010151610e35919061338c565b828281518110610e4857610e4761332e565b5b6020026020010181815250508080610e5f906133c0565b915050610e05565b5080915050919050565b565b3373ffffffffffffffffffffffffffffffffffffffff1660017f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a3565b600081600260008581526020019081526020016000206000828254610edf9190613408565b9250508190555081905092915050565b6000610efe57610efd61344c565b5b565b60005481565b600081600560006101000a81548160ff02191690836003811115610f2d57610f2c612552565b5b0217905550600560009054906101000a900460ff166003811115610f5457610f53612552565b5b7fbea054406fdf249b05d1aef1b5f848d62d902d94389fca702b2d8337677c359a60405160405180910390a2819050919050565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663047c4425836040518263ffffffff1660e01b8152600401610fe591906124e5565b6020604051808303816000875af1158015611004573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110289190613490565b50827feace1be0b97ec11f959499c07b9f60f0cc47bf610b28fda8fb0e970339cf3b3560405160405180910390a281836110629190613408565b905092915050565b6000803090506000808273ffffffffffffffffffffffffffffffffffffffff16633bc5de3060e01b604051602401604051602081830303815290604052907bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166020820180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff838183161783525050505060405161110291906134f9565b600060405180830381855afa9150503d806000811461113d576040519150601f19603f3d011682016040523d82523d6000602084013e611142565b606091505b509150915081611187576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161117e9061355c565b60405180910390fd5b60008180602001905181019061119d9190613591565b90508094505050505090565b6000819050919050565b60006002826111c29190613408565b9150600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fa8fca7a84846040518363ffffffff1660e01b81526004016112219291906135be565b6020604051808303816000875af1158015611240573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112649190613490565b503373ffffffffffffffffffffffffffffffffffffffff1660017f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a381836112b69190613408565b905092915050565b6000600454905090565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633e41f13584846040518363ffffffff1660e01b81526004016113279291906135be565b6020604051808303816000875af1158015611346573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061136a9190613490565b503373ffffffffffffffffffffffffffffffffffffffff16827f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a381836113bb9190613408565b905092915050565b600060608383915091509250929050565b60016020528060005260406000206000915090505481565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633e41f13584846040518363ffffffff1660e01b815260040161144b9291906135be565b6020604051808303816000875af115801561146a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061148e9190613490565b503373ffffffffffffffffffffffffffffffffffffffff16827f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a381836114df9190613408565b905092915050565b600060026000838152602001908152602001600020549050919050565b600c60156040517f4a2eaf7e00000000000000000000000000000000000000000000000000000000815260040161153c92919061365d565b60405180910390fd5b60026020528060005260406000206000915090505481565b7febe3ff7e2071d351bf2e65b4fccd24e3ae99485f02468f1feecf7d64dc04418860405180606001604052806040518060400160405280600481526020017f4a6f686e000000000000000000000000000000000000000000000000000000008152508152602001600567ffffffffffffffff168152602001600a8152506040516115e791906136f9565b60405180910390a1565b60008054905090565b60006060617a696040518060400160405280600a81526020017f6f757470757456616c3100000000000000000000000000000000000000000000815250915091509091565b7f25b7adba1b046a19379db4bc06aa1f2e71604d7b599a0ee8783d58110f00e16a60405161166c90613767565b60405180910390a1565b61167e611fa5565b8161168890613942565b9050919050565b36819050919050565b5050565b60006060617a696040518060400160405280600a81526020017f6f757470757456616c3100000000000000000000000000000000000000000000815250915091509091565b60537feace1be0b97ec11f959499c07b9f60f0cc47bf610b28fda8fb0e970339cf3b3560405160405180910390a2565b611719611504565b565b7f33bc9bae48dbe1e057f264b3fc6a1dacdcceacb3ba28d937231c70e068a02f1c3360405161174a919061327e565b60405180910390a1565b61175c611fc5565b611764611fc5565b826000600381106117785761177761332e565b5b6020028101906117889190613964565b6117919061398c565b816000600281106117a5576117a461332e565b5b6020020181905250826001600381106117c1576117c061332e565b5b6020028101906117d19190613964565b6117da9061398c565b816001600281106117ee576117ed61332e565b5b602002018190525080915050919050565b600080600085858592509250925093509350939050565b7f962c5df4c8ad201a4f54a88f47715bb2cf291d019e350e2dff50ca6fc0f5d0ed8282905060405161184891906125e4565b60405180910390a16000828290500361189c57606360656040517f4a2eaf7e000000000000000000000000000000000000000000000000000000008152600401611893929190613a15565b60405180910390fd5b6000803073ffffffffffffffffffffffffffffffffffffffff1684846040516118c6929190613a63565b600060405180830381855af49150503d8060008114611901576040519150601f19603f3d011682016040523d82523d6000602084013e611906565b606091505b50915091508161195e576000815111156119235780518082602001fd5b6040517f2350eb5200000000000000000000000000000000000000000000000000000000815260040161195590613aee565b60405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff16633170428e6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156119a9573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906119cd9190613591565b50600084846000906004926119e493929190613b18565b906119ef9190613b97565b90506358379d7160e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191603611a78576040517f2350eb52000000000000000000000000000000000000000000000000000000008152600401611a6f90613c42565b60405180910390fd5b3073ffffffffffffffffffffffffffffffffffffffff16633837a75e600160026040518363ffffffff1660e01b8152600401611ab5929190613cd8565b6020604051808303816000875af1158015611ad4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611af89190613490565b5050505050505050565b60033373ffffffffffffffffffffffffffffffffffffffff1660017f5660e8f93f0146f45abcd659e026b75995db50053cbbca4d7f365934ade68bf360405160405180910390a4565b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905090565b6000611b9e8383610c96565b15611c9b57600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633e41f13584846040518363ffffffff1660e01b8152600401611c009291906135be565b6020604051808303816000875af1158015611c1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611c439190613490565b503373ffffffffffffffffffffffffffffffffffffffff16827f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a38183611c949190613408565b9050611cd6565b6040517f2350eb52000000000000000000000000000000000000000000000000000000008152600401611ccd90613d73565b60405180910390fd5b92915050565b6040517fa0c2d2db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60033373ffffffffffffffffffffffffffffffffffffffff1660027f56c2ea44ba516098cee0c181dd9d8db262657368b6e911e83ae0ccfae806c73d604051611d7c90613ddf565b60405180910390a4565b600060608383915091509250929050565b6060819050919050565b600081600081905550819050919050565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550819050919050565b6000819050919050565b600560009054906101000a900460ff1681565b6000600260008381526020019081526020016000208190555050565b611e41611fa5565b6000828060000190611e539190613dff565b604051602001611e64929190613e92565b6040516020818303038152906040528051906020012090506000602067ffffffffffffffff811115611e9957611e9861226a565b5b6040519080825280601f01601f191660200182016040528015611ecb5781602001600182028036833780820191505090505b50905060005b6020811015611f4657828160208110611eed57611eec61332e565b5b1a60f81b828281518110611f0457611f0361332e565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053508080611f3e906133c0565b915050611ed1565b50604051806040016040528085611f5c9061398c565b81526020018281525092505050919050565b6000817fb16dba9242e1aa07ccc47228094628f72c8cc9699ee45d5bc8d67b84d3038c6860405160405180910390a2819050919050565b6040518060400160405280611fb8611ff2565b8152602001606081525090565b60405180604001604052806002905b611fdc611ff2565b815260200190600190039081611fd45790505090565b604051806040016040528060608152602001606081525090565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006120378261200c565b9050919050565b6120478161202c565b82525050565b6000819050919050565b6120608161204d565b82525050565b600082825260208201905092915050565b7f5265636569766564204574686572000000000000000000000000000000000000600082015250565b60006120ad600e83612066565b91506120b882612077565b602082019050919050565b60006060820190506120d8600083018561203e565b6120e56020830184612057565b81810360408301526120f6816120a0565b90509392505050565b6000604082019050612114600083018561203e565b6121216020830184612057565b9392505050565b6000604051905090565b600080fd5b600080fd5b6000819050919050565b61214f8161213c565b811461215a57600080fd5b50565b60008135905061216c81612146565b92915050565b6000806040838503121561218957612188612132565b5b60006121978582860161215d565b92505060206121a88582860161215d565b9150509250929050565b60008115159050919050565b6121c7816121b2565b82525050565b60006020820190506121e260008301846121be565b92915050565b6121f18161204d565b81146121fc57600080fd5b50565b60008135905061220e816121e8565b92915050565b6000806040838503121561222b5761222a612132565b5b6000612239858286016121ff565b925050602061224a858286016121ff565b9150509250929050565b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b6122a282612259565b810181811067ffffffffffffffff821117156122c1576122c061226a565b5b80604052505050565b60006122d4612128565b90506122e08282612299565b919050565b600067ffffffffffffffff821115612300576122ff61226a565b5b602082029050602081019050919050565b600080fd5b6000612329612324846122e5565b6122ca565b9050808382526020820190506020840283018581111561234c5761234b612311565b5b835b81811015612375578061236188826121ff565b84526020840193505060208101905061234e565b5050509392505050565b600082601f83011261239457612393612254565b5b81356123a4848260208601612316565b91505092915050565b6000602082840312156123c3576123c2612132565b5b600082013567ffffffffffffffff8111156123e1576123e0612137565b5b6123ed8482850161237f565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b61242b8161204d565b82525050565b600061243d8383612422565b60208301905092915050565b6000602082019050919050565b6000612461826123f6565b61246b8185612401565b935061247683612412565b8060005b838110156124a757815161248e8882612431565b975061249983612449565b92505060018101905061247a565b5085935050505092915050565b600060208201905081810360008301526124ce8184612456565b905092915050565b6124df8161213c565b82525050565b60006020820190506124fa60008301846124d6565b92915050565b6004811061250d57600080fd5b50565b60008135905061251f81612500565b92915050565b60006020828403121561253b5761253a612132565b5b600061254984828501612510565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b6004811061259257612591612552565b5b50565b60008190506125a382612581565b919050565b60006125b382612595565b9050919050565b6125c3816125a8565b82525050565b60006020820190506125de60008301846125ba565b92915050565b60006020820190506125f96000830184612057565b92915050565b6000819050919050565b612612816125ff565b811461261d57600080fd5b50565b60008135905061262f81612609565b92915050565b60006020828403121561264b5761264a612132565b5b600061265984828501612620565b91505092915050565b61266b816125ff565b82525050565b60006020820190506126866000830184612662565b92915050565b600080fd5b600067ffffffffffffffff8211156126ac576126ab61226a565b5b6126b582612259565b9050602081019050919050565b82818337600083830152505050565b60006126e46126df84612691565b6122ca565b905082815260208101848484011115612700576126ff61268c565b5b61270b8482856126c2565b509392505050565b600082601f83011261272857612727612254565b5b81356127388482602086016126d1565b91505092915050565b6000806040838503121561275857612757612132565b5b6000612766858286016121ff565b925050602083013567ffffffffffffffff81111561278757612786612137565b5b61279385828601612713565b9150509250929050565b600081519050919050565b60005b838110156127c65780820151818401526020810190506127ab565b60008484015250505050565b60006127dd8261279d565b6127e78185612066565b93506127f78185602086016127a8565b61280081612259565b840191505092915050565b60006040820190506128206000830185612057565b818103602083015261283281846127d2565b90509392505050565b6128448161202c565b811461284f57600080fd5b50565b6000813590506128618161283b565b92915050565b60006020828403121561287d5761287c612132565b5b600061288b84828501612852565b91505092915050565b6000602082840312156128aa576128a9612132565b5b60006128b88482850161215d565b91505092915050565b600080fd5b6000604082840312156128dc576128db6128c1565b5b81905092915050565b6000602082840312156128fb576128fa612132565b5b600082013567ffffffffffffffff81111561291957612918612137565b5b612925848285016128c6565b91505092915050565b600082825260208201905092915050565b600061294a8261279d565b612954818561292e565b93506129648185602086016127a8565b61296d81612259565b840191505092915050565b600082825260208201905092915050565b6000612994826123f6565b61299e8185612978565b93506129a983612412565b8060005b838110156129da5781516129c18882612431565b97506129cc83612449565b9250506001810190506129ad565b5085935050505092915050565b60006040830160008301518482036000860152612a04828261293f565b91505060208301518482036020860152612a1e8282612989565b9150508091505092915050565b600081519050919050565b600082825260208201905092915050565b6000612a5282612a2b565b612a5c8185612a36565b9350612a6c8185602086016127a8565b612a7581612259565b840191505092915050565b60006040830160008301518482036000860152612a9d82826129e7565b91505060208301518482036020860152612ab78282612a47565b9150508091505092915050565b60006020820190508181036000830152612ade8184612a80565b905092915050565b600060408284031215612afc57612afb6128c1565b5b81905092915050565b600060208284031215612b1b57612b1a612132565b5b600082013567ffffffffffffffff811115612b3957612b38612137565b5b612b4584828501612ae6565b91505092915050565b600080fd5b600080fd5b600080fd5b60008083356001602003843603038112612b7a57612b79612b58565b5b83810192508235915060208301925067ffffffffffffffff821115612ba257612ba1612b4e565b5b600182023603831315612bb857612bb7612b53565b5b509250929050565b6000612bcc838561292e565b9350612bd98385846126c2565b612be283612259565b840190509392505050565b60008083356001602003843603038112612c0a57612c09612b58565b5b83810192508235915060208301925067ffffffffffffffff821115612c3257612c31612b4e565b5b602082023603831315612c4857612c47612b53565b5b509250929050565b600080fd5b82818337505050565b6000612c6a8385612978565b93507f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115612c9d57612c9c612c50565b5b602083029250612cae838584612c55565b82840190509392505050565b600060408301612ccd6000840184612b5d565b8583036000870152612ce0838284612bc0565b92505050612cf16020840184612bed565b8583036020870152612d04838284612c5e565b925050508091505092915050565b60006020820190508181036000830152612d2c8184612cba565b905092915050565b600081905082602060030282011115612d5057612d4f612311565b5b92915050565b600060208284031215612d6c57612d6b612132565b5b600082013567ffffffffffffffff811115612d8a57612d89612137565b5b612d9684828501612d34565b91505092915050565b600060029050919050565b600081905092915050565b6000819050919050565b6000612dcb83836129e7565b905092915050565b6000602082019050919050565b6000612deb82612d9f565b612df58185612daa565b935083602082028501612e0785612db5565b8060005b85811015612e435784840389528151612e248582612dbf565b9450612e2f83612dd3565b925060208a01995050600181019050612e0b565b50829750879550505050505092915050565b60006020820190508181036000830152612e6f8184612de0565b905092915050565b600081600f0b9050919050565b612e8d81612e77565b8114612e9857600080fd5b50565b600081359050612eaa81612e84565b92915050565b600080600060608486031215612ec957612ec8612132565b5b6000612ed78682870161215d565b9350506020612ee886828701612e9b565b9250506040612ef9868287016121ff565b9150509250925092565b612f0c81612e77565b82525050565b6000606082019050612f2760008301866124d6565b612f346020830185612f03565b612f416040830184612057565b949350505050565b600080fd5b60008083601f840112612f6457612f63612254565b5b8235905067ffffffffffffffff811115612f8157612f80612f49565b5b602083019150836001820283011115612f9d57612f9c612311565b5b9250929050565b60008060008060608587031215612fbe57612fbd612132565b5b6000612fcc87828801612852565b9450506020612fdd878288016121ff565b935050604085013567ffffffffffffffff811115612ffe57612ffd612137565b5b61300a87828801612f4e565b925092505092959194509250565b6000819050919050565b600061303d6130386130338461200c565b613018565b61200c565b9050919050565b600061304f82613022565b9050919050565b600061306182613044565b9050919050565b61307181613056565b82525050565b600060208201905061308c6000830184613068565b92915050565b600067ffffffffffffffff8211156130ad576130ac61226a565b5b602082029050602081019050919050565b60006130d16130cc84613092565b6122ca565b905080838252602082019050602084028301858111156130f4576130f3612311565b5b835b8181101561311d57806131098882612852565b8452602084019350506020810190506130f6565b5050509392505050565b600082601f83011261313c5761313b612254565b5b813561314c8482602086016130be565b91505092915050565b60006020828403121561316b5761316a612132565b5b600082013567ffffffffffffffff81111561318957613188612137565b5b61319584828501613127565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b6131d38161202c565b82525050565b60006131e583836131ca565b60208301905092915050565b6000602082019050919050565b60006132098261319e565b61321381856131a9565b935061321e836131ba565b8060005b8381101561324f57815161323688826131d9565b9750613241836131f1565b925050600181019050613222565b5085935050505092915050565b6000602082019050818103600083015261327681846131fe565b905092915050565b6000602082019050613293600083018461203e565b92915050565b7f616c7761797320726576657274206572726f7200000000000000000000000000600082015250565b60006132cf601383612066565b91506132da82613299565b602082019050919050565b600060208201905081810360008301526132fe816132c2565b9050919050565b600060408201905061331a6000830185612057565b6133276020830184612057565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b60006133978261204d565b91506133a28361204d565b92508282019050808211156133ba576133b961335d565b5b92915050565b60006133cb8261204d565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82036133fd576133fc61335d565b5b600182019050919050565b60006134138261213c565b915061341e8361213c565b9250828201905082811215600083121683821260008412151617156134465761344561335d565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b60008151905061348a81612146565b92915050565b6000602082840312156134a6576134a5612132565b5b60006134b48482850161347b565b91505092915050565b600081905092915050565b60006134d382612a2b565b6134dd81856134bd565b93506134ed8185602086016127a8565b80840191505092915050565b600061350582846134c8565b915081905092915050565b7f5374617469632063616c6c206661696c65640000000000000000000000000000600082015250565b6000613546601283612066565b915061355182613510565b602082019050919050565b6000602082019050818103600083015261357581613539565b9050919050565b60008151905061358b816121e8565b92915050565b6000602082840312156135a7576135a6612132565b5b60006135b58482850161357c565b91505092915050565b60006040820190506135d360008301856124d6565b6135e060208301846124d6565b9392505050565b6000819050919050565b600061360c613607613602846135e7565b613018565b61204d565b9050919050565b61361c816135f1565b82525050565b6000819050919050565b600061364761364261363d84613622565b613018565b61204d565b9050919050565b6136578161362c565b82525050565b60006040820190506136726000830185613613565b61367f602083018461364e565b9392505050565b600067ffffffffffffffff82169050919050565b6136a381613686565b82525050565b600060608301600083015184820360008601526136c6828261293f565b91505060208301516136db602086018261369a565b5060408301516136ee6040860182612422565b508091505092915050565b6000602082019050818103600083015261371381846136a9565b905092915050565b7f6d79537472696e67000000000000000000000000000000000000000000000000600082015250565b6000613751600883612066565b915061375c8261371b565b602082019050919050565b6000602082019050818103600083015261378081613744565b9050919050565b600080fd5b600080fd5b6000604082840312156137a7576137a6613787565b5b6137b160406122ca565b9050600082013567ffffffffffffffff8111156137d1576137d061378c565b5b6137dd84828501612713565b600083015250602082013567ffffffffffffffff8111156138015761380061378c565b5b61380d8482850161237f565b60208301525092915050565b600067ffffffffffffffff8211156138345761383361226a565b5b61383d82612259565b9050602081019050919050565b600061385d61385884613819565b6122ca565b9050828152602081018484840111156138795761387861268c565b5b6138848482856126c2565b509392505050565b600082601f8301126138a1576138a0612254565b5b81356138b184826020860161384a565b91505092915050565b6000604082840312156138d0576138cf613787565b5b6138da60406122ca565b9050600082013567ffffffffffffffff8111156138fa576138f961378c565b5b61390684828501613791565b600083015250602082013567ffffffffffffffff81111561392a5761392961378c565b5b6139368482850161388c565b60208301525092915050565b600061394e36836138ba565b9050919050565b600080fd5b600080fd5b600080fd5b6000823560016040038336030381126139805761397f613955565b5b80830191505092915050565b60006139983683613791565b9050919050565b6000819050919050565b60006139c46139bf6139ba8461399f565b613018565b61204d565b9050919050565b6139d4816139a9565b82525050565b6000819050919050565b60006139ff6139fa6139f5846139da565b613018565b61204d565b9050919050565b613a0f816139e4565b82525050565b6000604082019050613a2a60008301856139cb565b613a376020830184613a06565b9392505050565b6000613a4a83856134bd565b9350613a578385846126c2565b82840190509392505050565b6000613a70828486613a3e565b91508190509392505050565b7f64656c656761746563616c6c206661696c65642077697468206e6f207265617360008201527f6f6e000000000000000000000000000000000000000000000000000000000000602082015250565b6000613ad8602283612066565b9150613ae382613a7c565b604082019050919050565b60006020820190508181036000830152613b0781613acb565b9050919050565b600080fd5b600080fd5b60008085851115613b2c57613b2b613b0e565b5b83861115613b3d57613b3c613b13565b5b6001850283019150848603905094509492505050565b600082905092915050565b60007fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b600082821b905092915050565b6000613ba38383613b53565b82613bae8135613b5e565b92506004821015613bee57613be97fffffffff0000000000000000000000000000000000000000000000000000000083600403600802613b8a565b831692505b505092915050565b7f6f68206f68206f682069742773206d6167696321000000000000000000000000600082015250565b6000613c2c601483612066565b9150613c3782613bf6565b602082019050919050565b60006020820190508181036000830152613c5b81613c1f565b9050919050565b6000819050919050565b6000613c87613c82613c7d84613c62565b613018565b61213c565b9050919050565b613c9781613c6c565b82525050565b6000819050919050565b6000613cc2613cbd613cb884613c9d565b613018565b61213c565b9050919050565b613cd281613ca7565b82525050565b6000604082019050613ced6000830185613c8e565b613cfa6020830184613cc9565b9392505050565b7f666972737420696e7420776173206e6f742067726561746572207468616e207360008201527f65636f6e6420696e740000000000000000000000000000000000000000000000602082015250565b6000613d5d602983612066565b9150613d6882613d01565b604082019050919050565b60006020820190508181036000830152613d8c81613d50565b9050919050565b7f736f6d6520696400000000000000000000000000000000000000000000000000600082015250565b6000613dc9600783612066565b9150613dd482613d93565b602082019050919050565b60006020820190508181036000830152613df881613dbc565b9050919050565b60008083356001602003843603038112613e1c57613e1b613955565b5b80840192508235915067ffffffffffffffff821115613e3e57613e3d61395a565b5b602083019250600182023603831315613e5a57613e5961395f565b5b509250929050565b600081905092915050565b6000613e798385613e62565b9350613e868385846126c2565b82840190509392505050565b6000613e9f828486613e6d565b9150819050939250505056fea264697066735822122095801437fe2af153ec90ad58a4678a194b7fb3b777ee30438995ef0f23596f7664736f6c63430008130033", +} + +// NetworkDebugContractABI is the input ABI used to generate the binding from. +// Deprecated: Use NetworkDebugContractMetaData.ABI instead. +var NetworkDebugContractABI = NetworkDebugContractMetaData.ABI + +// NetworkDebugContractBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use NetworkDebugContractMetaData.Bin instead. +var NetworkDebugContractBin = NetworkDebugContractMetaData.Bin + +// DeployNetworkDebugContract deploys a new Ethereum contract, binding an instance of NetworkDebugContract to it. +func DeployNetworkDebugContract(auth *bind.TransactOpts, backend bind.ContractBackend, subAddr common.Address) (common.Address, *types.Transaction, *NetworkDebugContract, error) { + parsed, err := NetworkDebugContractMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(NetworkDebugContractBin), backend, subAddr) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &NetworkDebugContract{NetworkDebugContractCaller: NetworkDebugContractCaller{contract: contract}, NetworkDebugContractTransactor: NetworkDebugContractTransactor{contract: contract}, NetworkDebugContractFilterer: NetworkDebugContractFilterer{contract: contract}}, nil +} + +// NetworkDebugContract is an auto generated Go binding around an Ethereum contract. +type NetworkDebugContract struct { + NetworkDebugContractCaller // Read-only binding to the contract + NetworkDebugContractTransactor // Write-only binding to the contract + NetworkDebugContractFilterer // Log filterer for contract events +} + +// NetworkDebugContractCaller is an auto generated read-only Go binding around an Ethereum contract. +type NetworkDebugContractCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// NetworkDebugContractTransactor is an auto generated write-only Go binding around an Ethereum contract. +type NetworkDebugContractTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// NetworkDebugContractFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type NetworkDebugContractFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// NetworkDebugContractSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type NetworkDebugContractSession struct { + Contract *NetworkDebugContract // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// NetworkDebugContractCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type NetworkDebugContractCallerSession struct { + Contract *NetworkDebugContractCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// NetworkDebugContractTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type NetworkDebugContractTransactorSession struct { + Contract *NetworkDebugContractTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// NetworkDebugContractRaw is an auto generated low-level Go binding around an Ethereum contract. +type NetworkDebugContractRaw struct { + Contract *NetworkDebugContract // Generic contract binding to access the raw methods on +} + +// NetworkDebugContractCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type NetworkDebugContractCallerRaw struct { + Contract *NetworkDebugContractCaller // Generic read-only contract binding to access the raw methods on +} + +// NetworkDebugContractTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type NetworkDebugContractTransactorRaw struct { + Contract *NetworkDebugContractTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewNetworkDebugContract creates a new instance of NetworkDebugContract, bound to a specific deployed contract. +func NewNetworkDebugContract(address common.Address, backend bind.ContractBackend) (*NetworkDebugContract, error) { + contract, err := bindNetworkDebugContract(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &NetworkDebugContract{NetworkDebugContractCaller: NetworkDebugContractCaller{contract: contract}, NetworkDebugContractTransactor: NetworkDebugContractTransactor{contract: contract}, NetworkDebugContractFilterer: NetworkDebugContractFilterer{contract: contract}}, nil +} + +// NewNetworkDebugContractCaller creates a new read-only instance of NetworkDebugContract, bound to a specific deployed contract. +func NewNetworkDebugContractCaller(address common.Address, caller bind.ContractCaller) (*NetworkDebugContractCaller, error) { + contract, err := bindNetworkDebugContract(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &NetworkDebugContractCaller{contract: contract}, nil +} + +// NewNetworkDebugContractTransactor creates a new write-only instance of NetworkDebugContract, bound to a specific deployed contract. +func NewNetworkDebugContractTransactor(address common.Address, transactor bind.ContractTransactor) (*NetworkDebugContractTransactor, error) { + contract, err := bindNetworkDebugContract(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &NetworkDebugContractTransactor{contract: contract}, nil +} + +// NewNetworkDebugContractFilterer creates a new log filterer instance of NetworkDebugContract, bound to a specific deployed contract. +func NewNetworkDebugContractFilterer(address common.Address, filterer bind.ContractFilterer) (*NetworkDebugContractFilterer, error) { + contract, err := bindNetworkDebugContract(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &NetworkDebugContractFilterer{contract: contract}, nil +} + +// bindNetworkDebugContract binds a generic wrapper to an already deployed contract. +func bindNetworkDebugContract(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := NetworkDebugContractMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_NetworkDebugContract *NetworkDebugContractRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _NetworkDebugContract.Contract.NetworkDebugContractCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_NetworkDebugContract *NetworkDebugContractRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.NetworkDebugContractTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_NetworkDebugContract *NetworkDebugContractRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.NetworkDebugContractTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_NetworkDebugContract *NetworkDebugContractCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _NetworkDebugContract.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_NetworkDebugContract *NetworkDebugContractTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_NetworkDebugContract *NetworkDebugContractTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.contract.Transact(opts, method, params...) +} + +// CounterMap is a free data retrieval call binding the contract method 0x6284117d. +// +// Solidity: function counterMap(int256 ) view returns(int256) +func (_NetworkDebugContract *NetworkDebugContractCaller) CounterMap(opts *bind.CallOpts, arg0 *big.Int) (*big.Int, error) { + var out []interface{} + err := _NetworkDebugContract.contract.Call(opts, &out, "counterMap", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// CounterMap is a free data retrieval call binding the contract method 0x6284117d. +// +// Solidity: function counterMap(int256 ) view returns(int256) +func (_NetworkDebugContract *NetworkDebugContractSession) CounterMap(arg0 *big.Int) (*big.Int, error) { + return _NetworkDebugContract.Contract.CounterMap(&_NetworkDebugContract.CallOpts, arg0) +} + +// CounterMap is a free data retrieval call binding the contract method 0x6284117d. +// +// Solidity: function counterMap(int256 ) view returns(int256) +func (_NetworkDebugContract *NetworkDebugContractCallerSession) CounterMap(arg0 *big.Int) (*big.Int, error) { + return _NetworkDebugContract.Contract.CounterMap(&_NetworkDebugContract.CallOpts, arg0) +} + +// CurrentStatus is a free data retrieval call binding the contract method 0xef8a9235. +// +// Solidity: function currentStatus() view returns(uint8) +func (_NetworkDebugContract *NetworkDebugContractCaller) CurrentStatus(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _NetworkDebugContract.contract.Call(opts, &out, "currentStatus") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +// CurrentStatus is a free data retrieval call binding the contract method 0xef8a9235. +// +// Solidity: function currentStatus() view returns(uint8) +func (_NetworkDebugContract *NetworkDebugContractSession) CurrentStatus() (uint8, error) { + return _NetworkDebugContract.Contract.CurrentStatus(&_NetworkDebugContract.CallOpts) +} + +// CurrentStatus is a free data retrieval call binding the contract method 0xef8a9235. +// +// Solidity: function currentStatus() view returns(uint8) +func (_NetworkDebugContract *NetworkDebugContractCallerSession) CurrentStatus() (uint8, error) { + return _NetworkDebugContract.Contract.CurrentStatus(&_NetworkDebugContract.CallOpts) +} + +// Get is a free data retrieval call binding the contract method 0x6d4ce63c. +// +// Solidity: function get() view returns(int256 data) +func (_NetworkDebugContract *NetworkDebugContractCaller) Get(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _NetworkDebugContract.contract.Call(opts, &out, "get") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// Get is a free data retrieval call binding the contract method 0x6d4ce63c. +// +// Solidity: function get() view returns(int256 data) +func (_NetworkDebugContract *NetworkDebugContractSession) Get() (*big.Int, error) { + return _NetworkDebugContract.Contract.Get(&_NetworkDebugContract.CallOpts) +} + +// Get is a free data retrieval call binding the contract method 0x6d4ce63c. +// +// Solidity: function get() view returns(int256 data) +func (_NetworkDebugContract *NetworkDebugContractCallerSession) Get() (*big.Int, error) { + return _NetworkDebugContract.Contract.Get(&_NetworkDebugContract.CallOpts) +} + +// GetCounter is a free data retrieval call binding the contract method 0x5921483f. +// +// Solidity: function getCounter(int256 idx) view returns(int256 data) +func (_NetworkDebugContract *NetworkDebugContractCaller) GetCounter(opts *bind.CallOpts, idx *big.Int) (*big.Int, error) { + var out []interface{} + err := _NetworkDebugContract.contract.Call(opts, &out, "getCounter", idx) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetCounter is a free data retrieval call binding the contract method 0x5921483f. +// +// Solidity: function getCounter(int256 idx) view returns(int256 data) +func (_NetworkDebugContract *NetworkDebugContractSession) GetCounter(idx *big.Int) (*big.Int, error) { + return _NetworkDebugContract.Contract.GetCounter(&_NetworkDebugContract.CallOpts, idx) +} + +// GetCounter is a free data retrieval call binding the contract method 0x5921483f. +// +// Solidity: function getCounter(int256 idx) view returns(int256 data) +func (_NetworkDebugContract *NetworkDebugContractCallerSession) GetCounter(idx *big.Int) (*big.Int, error) { + return _NetworkDebugContract.Contract.GetCounter(&_NetworkDebugContract.CallOpts, idx) +} + +// GetData is a free data retrieval call binding the contract method 0x3bc5de30. +// +// Solidity: function getData() view returns(uint256) +func (_NetworkDebugContract *NetworkDebugContractCaller) GetData(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _NetworkDebugContract.contract.Call(opts, &out, "getData") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetData is a free data retrieval call binding the contract method 0x3bc5de30. +// +// Solidity: function getData() view returns(uint256) +func (_NetworkDebugContract *NetworkDebugContractSession) GetData() (*big.Int, error) { + return _NetworkDebugContract.Contract.GetData(&_NetworkDebugContract.CallOpts) +} + +// GetData is a free data retrieval call binding the contract method 0x3bc5de30. +// +// Solidity: function getData() view returns(uint256) +func (_NetworkDebugContract *NetworkDebugContractCallerSession) GetData() (*big.Int, error) { + return _NetworkDebugContract.Contract.GetData(&_NetworkDebugContract.CallOpts) +} + +// GetMap is a free data retrieval call binding the contract method 0xad3de14c. +// +// Solidity: function getMap() view returns(int256 data) +func (_NetworkDebugContract *NetworkDebugContractCaller) GetMap(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _NetworkDebugContract.contract.Call(opts, &out, "getMap") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// GetMap is a free data retrieval call binding the contract method 0xad3de14c. +// +// Solidity: function getMap() view returns(int256 data) +func (_NetworkDebugContract *NetworkDebugContractSession) GetMap() (*big.Int, error) { + return _NetworkDebugContract.Contract.GetMap(&_NetworkDebugContract.CallOpts) +} + +// GetMap is a free data retrieval call binding the contract method 0xad3de14c. +// +// Solidity: function getMap() view returns(int256 data) +func (_NetworkDebugContract *NetworkDebugContractCallerSession) GetMap() (*big.Int, error) { + return _NetworkDebugContract.Contract.GetMap(&_NetworkDebugContract.CallOpts) +} + +// PerformStaticCall is a free data retrieval call binding the contract method 0x3170428e. +// +// Solidity: function performStaticCall() view returns(uint256) +func (_NetworkDebugContract *NetworkDebugContractCaller) PerformStaticCall(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _NetworkDebugContract.contract.Call(opts, &out, "performStaticCall") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// PerformStaticCall is a free data retrieval call binding the contract method 0x3170428e. +// +// Solidity: function performStaticCall() view returns(uint256) +func (_NetworkDebugContract *NetworkDebugContractSession) PerformStaticCall() (*big.Int, error) { + return _NetworkDebugContract.Contract.PerformStaticCall(&_NetworkDebugContract.CallOpts) +} + +// PerformStaticCall is a free data retrieval call binding the contract method 0x3170428e. +// +// Solidity: function performStaticCall() view returns(uint256) +func (_NetworkDebugContract *NetworkDebugContractCallerSession) PerformStaticCall() (*big.Int, error) { + return _NetworkDebugContract.Contract.PerformStaticCall(&_NetworkDebugContract.CallOpts) +} + +// StoredData is a free data retrieval call binding the contract method 0x2a1afcd9. +// +// Solidity: function storedData() view returns(int256) +func (_NetworkDebugContract *NetworkDebugContractCaller) StoredData(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _NetworkDebugContract.contract.Call(opts, &out, "storedData") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// StoredData is a free data retrieval call binding the contract method 0x2a1afcd9. +// +// Solidity: function storedData() view returns(int256) +func (_NetworkDebugContract *NetworkDebugContractSession) StoredData() (*big.Int, error) { + return _NetworkDebugContract.Contract.StoredData(&_NetworkDebugContract.CallOpts) +} + +// StoredData is a free data retrieval call binding the contract method 0x2a1afcd9. +// +// Solidity: function storedData() view returns(int256) +func (_NetworkDebugContract *NetworkDebugContractCallerSession) StoredData() (*big.Int, error) { + return _NetworkDebugContract.Contract.StoredData(&_NetworkDebugContract.CallOpts) +} + +// StoredDataMap is a free data retrieval call binding the contract method 0x48ad9fe8. +// +// Solidity: function storedDataMap(address ) view returns(int256) +func (_NetworkDebugContract *NetworkDebugContractCaller) StoredDataMap(opts *bind.CallOpts, arg0 common.Address) (*big.Int, error) { + var out []interface{} + err := _NetworkDebugContract.contract.Call(opts, &out, "storedDataMap", arg0) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +// StoredDataMap is a free data retrieval call binding the contract method 0x48ad9fe8. +// +// Solidity: function storedDataMap(address ) view returns(int256) +func (_NetworkDebugContract *NetworkDebugContractSession) StoredDataMap(arg0 common.Address) (*big.Int, error) { + return _NetworkDebugContract.Contract.StoredDataMap(&_NetworkDebugContract.CallOpts, arg0) +} + +// StoredDataMap is a free data retrieval call binding the contract method 0x48ad9fe8. +// +// Solidity: function storedDataMap(address ) view returns(int256) +func (_NetworkDebugContract *NetworkDebugContractCallerSession) StoredDataMap(arg0 common.Address) (*big.Int, error) { + return _NetworkDebugContract.Contract.StoredDataMap(&_NetworkDebugContract.CallOpts, arg0) +} + +// SubContract is a free data retrieval call binding the contract method 0xc0d06d89. +// +// Solidity: function subContract() view returns(address) +func (_NetworkDebugContract *NetworkDebugContractCaller) SubContract(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _NetworkDebugContract.contract.Call(opts, &out, "subContract") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +// SubContract is a free data retrieval call binding the contract method 0xc0d06d89. +// +// Solidity: function subContract() view returns(address) +func (_NetworkDebugContract *NetworkDebugContractSession) SubContract() (common.Address, error) { + return _NetworkDebugContract.Contract.SubContract(&_NetworkDebugContract.CallOpts) +} + +// SubContract is a free data retrieval call binding the contract method 0xc0d06d89. +// +// Solidity: function subContract() view returns(address) +func (_NetworkDebugContract *NetworkDebugContractCallerSession) SubContract() (common.Address, error) { + return _NetworkDebugContract.Contract.SubContract(&_NetworkDebugContract.CallOpts) +} + +// AddCounter is a paid mutator transaction binding the contract method 0x23515760. +// +// Solidity: function addCounter(int256 idx, int256 x) returns(int256 value) +func (_NetworkDebugContract *NetworkDebugContractTransactor) AddCounter(opts *bind.TransactOpts, idx *big.Int, x *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "addCounter", idx, x) +} + +// AddCounter is a paid mutator transaction binding the contract method 0x23515760. +// +// Solidity: function addCounter(int256 idx, int256 x) returns(int256 value) +func (_NetworkDebugContract *NetworkDebugContractSession) AddCounter(idx *big.Int, x *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.AddCounter(&_NetworkDebugContract.TransactOpts, idx, x) +} + +// AddCounter is a paid mutator transaction binding the contract method 0x23515760. +// +// Solidity: function addCounter(int256 idx, int256 x) returns(int256 value) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) AddCounter(idx *big.Int, x *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.AddCounter(&_NetworkDebugContract.TransactOpts, idx, x) +} + +// AlwaysRevertsAssert is a paid mutator transaction binding the contract method 0x256560d5. +// +// Solidity: function alwaysRevertsAssert() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) AlwaysRevertsAssert(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "alwaysRevertsAssert") +} + +// AlwaysRevertsAssert is a paid mutator transaction binding the contract method 0x256560d5. +// +// Solidity: function alwaysRevertsAssert() returns() +func (_NetworkDebugContract *NetworkDebugContractSession) AlwaysRevertsAssert() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.AlwaysRevertsAssert(&_NetworkDebugContract.TransactOpts) +} + +// AlwaysRevertsAssert is a paid mutator transaction binding the contract method 0x256560d5. +// +// Solidity: function alwaysRevertsAssert() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) AlwaysRevertsAssert() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.AlwaysRevertsAssert(&_NetworkDebugContract.TransactOpts) +} + +// AlwaysRevertsCustomError is a paid mutator transaction binding the contract method 0x5e9c80d6. +// +// Solidity: function alwaysRevertsCustomError() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) AlwaysRevertsCustomError(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "alwaysRevertsCustomError") +} + +// AlwaysRevertsCustomError is a paid mutator transaction binding the contract method 0x5e9c80d6. +// +// Solidity: function alwaysRevertsCustomError() returns() +func (_NetworkDebugContract *NetworkDebugContractSession) AlwaysRevertsCustomError() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.AlwaysRevertsCustomError(&_NetworkDebugContract.TransactOpts) +} + +// AlwaysRevertsCustomError is a paid mutator transaction binding the contract method 0x5e9c80d6. +// +// Solidity: function alwaysRevertsCustomError() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) AlwaysRevertsCustomError() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.AlwaysRevertsCustomError(&_NetworkDebugContract.TransactOpts) +} + +// AlwaysRevertsCustomErrorNoValues is a paid mutator transaction binding the contract method 0xb600141f. +// +// Solidity: function alwaysRevertsCustomErrorNoValues() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) AlwaysRevertsCustomErrorNoValues(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "alwaysRevertsCustomErrorNoValues") +} + +// AlwaysRevertsCustomErrorNoValues is a paid mutator transaction binding the contract method 0xb600141f. +// +// Solidity: function alwaysRevertsCustomErrorNoValues() returns() +func (_NetworkDebugContract *NetworkDebugContractSession) AlwaysRevertsCustomErrorNoValues() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.AlwaysRevertsCustomErrorNoValues(&_NetworkDebugContract.TransactOpts) +} + +// AlwaysRevertsCustomErrorNoValues is a paid mutator transaction binding the contract method 0xb600141f. +// +// Solidity: function alwaysRevertsCustomErrorNoValues() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) AlwaysRevertsCustomErrorNoValues() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.AlwaysRevertsCustomErrorNoValues(&_NetworkDebugContract.TransactOpts) +} + +// AlwaysRevertsRequire is a paid mutator transaction binding the contract method 0x06595f75. +// +// Solidity: function alwaysRevertsRequire() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) AlwaysRevertsRequire(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "alwaysRevertsRequire") +} + +// AlwaysRevertsRequire is a paid mutator transaction binding the contract method 0x06595f75. +// +// Solidity: function alwaysRevertsRequire() returns() +func (_NetworkDebugContract *NetworkDebugContractSession) AlwaysRevertsRequire() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.AlwaysRevertsRequire(&_NetworkDebugContract.TransactOpts) +} + +// AlwaysRevertsRequire is a paid mutator transaction binding the contract method 0x06595f75. +// +// Solidity: function alwaysRevertsRequire() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) AlwaysRevertsRequire() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.AlwaysRevertsRequire(&_NetworkDebugContract.TransactOpts) +} + +// CallRevertFunctionInSubContract is a paid mutator transaction binding the contract method 0x11b3c478. +// +// Solidity: function callRevertFunctionInSubContract(uint256 x, uint256 y) returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) CallRevertFunctionInSubContract(opts *bind.TransactOpts, x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "callRevertFunctionInSubContract", x, y) +} + +// CallRevertFunctionInSubContract is a paid mutator transaction binding the contract method 0x11b3c478. +// +// Solidity: function callRevertFunctionInSubContract(uint256 x, uint256 y) returns() +func (_NetworkDebugContract *NetworkDebugContractSession) CallRevertFunctionInSubContract(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.CallRevertFunctionInSubContract(&_NetworkDebugContract.TransactOpts, x, y) +} + +// CallRevertFunctionInSubContract is a paid mutator transaction binding the contract method 0x11b3c478. +// +// Solidity: function callRevertFunctionInSubContract(uint256 x, uint256 y) returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) CallRevertFunctionInSubContract(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.CallRevertFunctionInSubContract(&_NetworkDebugContract.TransactOpts, x, y) +} + +// CallRevertFunctionInTheContract is a paid mutator transaction binding the contract method 0x9349d00b. +// +// Solidity: function callRevertFunctionInTheContract() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) CallRevertFunctionInTheContract(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "callRevertFunctionInTheContract") +} + +// CallRevertFunctionInTheContract is a paid mutator transaction binding the contract method 0x9349d00b. +// +// Solidity: function callRevertFunctionInTheContract() returns() +func (_NetworkDebugContract *NetworkDebugContractSession) CallRevertFunctionInTheContract() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.CallRevertFunctionInTheContract(&_NetworkDebugContract.TransactOpts) +} + +// CallRevertFunctionInTheContract is a paid mutator transaction binding the contract method 0x9349d00b. +// +// Solidity: function callRevertFunctionInTheContract() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) CallRevertFunctionInTheContract() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.CallRevertFunctionInTheContract(&_NetworkDebugContract.TransactOpts) +} + +// CallbackMethod is a paid mutator transaction binding the contract method 0xfbcb8d07. +// +// Solidity: function callbackMethod(int256 x) returns(int256) +func (_NetworkDebugContract *NetworkDebugContractTransactor) CallbackMethod(opts *bind.TransactOpts, x *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "callbackMethod", x) +} + +// CallbackMethod is a paid mutator transaction binding the contract method 0xfbcb8d07. +// +// Solidity: function callbackMethod(int256 x) returns(int256) +func (_NetworkDebugContract *NetworkDebugContractSession) CallbackMethod(x *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.CallbackMethod(&_NetworkDebugContract.TransactOpts, x) +} + +// CallbackMethod is a paid mutator transaction binding the contract method 0xfbcb8d07. +// +// Solidity: function callbackMethod(int256 x) returns(int256) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) CallbackMethod(x *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.CallbackMethod(&_NetworkDebugContract.TransactOpts, x) +} + +// EmitAddress is a paid mutator transaction binding the contract method 0xec5c3ede. +// +// Solidity: function emitAddress(address addr) returns(address) +func (_NetworkDebugContract *NetworkDebugContractTransactor) EmitAddress(opts *bind.TransactOpts, addr common.Address) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "emitAddress", addr) +} + +// EmitAddress is a paid mutator transaction binding the contract method 0xec5c3ede. +// +// Solidity: function emitAddress(address addr) returns(address) +func (_NetworkDebugContract *NetworkDebugContractSession) EmitAddress(addr common.Address) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitAddress(&_NetworkDebugContract.TransactOpts, addr) +} + +// EmitAddress is a paid mutator transaction binding the contract method 0xec5c3ede. +// +// Solidity: function emitAddress(address addr) returns(address) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) EmitAddress(addr common.Address) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitAddress(&_NetworkDebugContract.TransactOpts, addr) +} + +// EmitBytes32 is a paid mutator transaction binding the contract method 0x33311ef3. +// +// Solidity: function emitBytes32(bytes32 input) returns(bytes32 output) +func (_NetworkDebugContract *NetworkDebugContractTransactor) EmitBytes32(opts *bind.TransactOpts, input [32]byte) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "emitBytes32", input) +} + +// EmitBytes32 is a paid mutator transaction binding the contract method 0x33311ef3. +// +// Solidity: function emitBytes32(bytes32 input) returns(bytes32 output) +func (_NetworkDebugContract *NetworkDebugContractSession) EmitBytes32(input [32]byte) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitBytes32(&_NetworkDebugContract.TransactOpts, input) +} + +// EmitBytes32 is a paid mutator transaction binding the contract method 0x33311ef3. +// +// Solidity: function emitBytes32(bytes32 input) returns(bytes32 output) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) EmitBytes32(input [32]byte) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitBytes32(&_NetworkDebugContract.TransactOpts, input) +} + +// EmitFourParamMixedEvent is a paid mutator transaction binding the contract method 0xc2124b22. +// +// Solidity: function emitFourParamMixedEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) EmitFourParamMixedEvent(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "emitFourParamMixedEvent") +} + +// EmitFourParamMixedEvent is a paid mutator transaction binding the contract method 0xc2124b22. +// +// Solidity: function emitFourParamMixedEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractSession) EmitFourParamMixedEvent() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitFourParamMixedEvent(&_NetworkDebugContract.TransactOpts) +} + +// EmitFourParamMixedEvent is a paid mutator transaction binding the contract method 0xc2124b22. +// +// Solidity: function emitFourParamMixedEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) EmitFourParamMixedEvent() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitFourParamMixedEvent(&_NetworkDebugContract.TransactOpts) +} + +// EmitInputs is a paid mutator transaction binding the contract method 0x81b375a0. +// +// Solidity: function emitInputs(uint256 inputVal1, string inputVal2) returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) EmitInputs(opts *bind.TransactOpts, inputVal1 *big.Int, inputVal2 string) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "emitInputs", inputVal1, inputVal2) +} + +// EmitInputs is a paid mutator transaction binding the contract method 0x81b375a0. +// +// Solidity: function emitInputs(uint256 inputVal1, string inputVal2) returns() +func (_NetworkDebugContract *NetworkDebugContractSession) EmitInputs(inputVal1 *big.Int, inputVal2 string) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitInputs(&_NetworkDebugContract.TransactOpts, inputVal1, inputVal2) +} + +// EmitInputs is a paid mutator transaction binding the contract method 0x81b375a0. +// +// Solidity: function emitInputs(uint256 inputVal1, string inputVal2) returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) EmitInputs(inputVal1 *big.Int, inputVal2 string) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitInputs(&_NetworkDebugContract.TransactOpts, inputVal1, inputVal2) +} + +// EmitInputsOutputs is a paid mutator transaction binding the contract method 0xd7a80205. +// +// Solidity: function emitInputsOutputs(uint256 inputVal1, string inputVal2) returns(uint256, string) +func (_NetworkDebugContract *NetworkDebugContractTransactor) EmitInputsOutputs(opts *bind.TransactOpts, inputVal1 *big.Int, inputVal2 string) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "emitInputsOutputs", inputVal1, inputVal2) +} + +// EmitInputsOutputs is a paid mutator transaction binding the contract method 0xd7a80205. +// +// Solidity: function emitInputsOutputs(uint256 inputVal1, string inputVal2) returns(uint256, string) +func (_NetworkDebugContract *NetworkDebugContractSession) EmitInputsOutputs(inputVal1 *big.Int, inputVal2 string) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitInputsOutputs(&_NetworkDebugContract.TransactOpts, inputVal1, inputVal2) +} + +// EmitInputsOutputs is a paid mutator transaction binding the contract method 0xd7a80205. +// +// Solidity: function emitInputsOutputs(uint256 inputVal1, string inputVal2) returns(uint256, string) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) EmitInputsOutputs(inputVal1 *big.Int, inputVal2 string) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitInputsOutputs(&_NetworkDebugContract.TransactOpts, inputVal1, inputVal2) +} + +// EmitInts is a paid mutator transaction binding the contract method 0x9e099652. +// +// Solidity: function emitInts(int256 first, int128 second, uint256 third) returns(int256, int128 outputVal1, uint256 outputVal2) +func (_NetworkDebugContract *NetworkDebugContractTransactor) EmitInts(opts *bind.TransactOpts, first *big.Int, second *big.Int, third *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "emitInts", first, second, third) +} + +// EmitInts is a paid mutator transaction binding the contract method 0x9e099652. +// +// Solidity: function emitInts(int256 first, int128 second, uint256 third) returns(int256, int128 outputVal1, uint256 outputVal2) +func (_NetworkDebugContract *NetworkDebugContractSession) EmitInts(first *big.Int, second *big.Int, third *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitInts(&_NetworkDebugContract.TransactOpts, first, second, third) +} + +// EmitInts is a paid mutator transaction binding the contract method 0x9e099652. +// +// Solidity: function emitInts(int256 first, int128 second, uint256 third) returns(int256, int128 outputVal1, uint256 outputVal2) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) EmitInts(first *big.Int, second *big.Int, third *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitInts(&_NetworkDebugContract.TransactOpts, first, second, third) +} + +// EmitNamedInputsOutputs is a paid mutator transaction binding the contract method 0x45f0c9e6. +// +// Solidity: function emitNamedInputsOutputs(uint256 inputVal1, string inputVal2) returns(uint256 outputVal1, string outputVal2) +func (_NetworkDebugContract *NetworkDebugContractTransactor) EmitNamedInputsOutputs(opts *bind.TransactOpts, inputVal1 *big.Int, inputVal2 string) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "emitNamedInputsOutputs", inputVal1, inputVal2) +} + +// EmitNamedInputsOutputs is a paid mutator transaction binding the contract method 0x45f0c9e6. +// +// Solidity: function emitNamedInputsOutputs(uint256 inputVal1, string inputVal2) returns(uint256 outputVal1, string outputVal2) +func (_NetworkDebugContract *NetworkDebugContractSession) EmitNamedInputsOutputs(inputVal1 *big.Int, inputVal2 string) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitNamedInputsOutputs(&_NetworkDebugContract.TransactOpts, inputVal1, inputVal2) +} + +// EmitNamedInputsOutputs is a paid mutator transaction binding the contract method 0x45f0c9e6. +// +// Solidity: function emitNamedInputsOutputs(uint256 inputVal1, string inputVal2) returns(uint256 outputVal1, string outputVal2) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) EmitNamedInputsOutputs(inputVal1 *big.Int, inputVal2 string) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitNamedInputsOutputs(&_NetworkDebugContract.TransactOpts, inputVal1, inputVal2) +} + +// EmitNamedOutputs is a paid mutator transaction binding the contract method 0x7014c81d. +// +// Solidity: function emitNamedOutputs() returns(uint256 outputVal1, string outputVal2) +func (_NetworkDebugContract *NetworkDebugContractTransactor) EmitNamedOutputs(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "emitNamedOutputs") +} + +// EmitNamedOutputs is a paid mutator transaction binding the contract method 0x7014c81d. +// +// Solidity: function emitNamedOutputs() returns(uint256 outputVal1, string outputVal2) +func (_NetworkDebugContract *NetworkDebugContractSession) EmitNamedOutputs() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitNamedOutputs(&_NetworkDebugContract.TransactOpts) +} + +// EmitNamedOutputs is a paid mutator transaction binding the contract method 0x7014c81d. +// +// Solidity: function emitNamedOutputs() returns(uint256 outputVal1, string outputVal2) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) EmitNamedOutputs() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitNamedOutputs(&_NetworkDebugContract.TransactOpts) +} + +// EmitNoIndexEvent is a paid mutator transaction binding the contract method 0x95a81a4c. +// +// Solidity: function emitNoIndexEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) EmitNoIndexEvent(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "emitNoIndexEvent") +} + +// EmitNoIndexEvent is a paid mutator transaction binding the contract method 0x95a81a4c. +// +// Solidity: function emitNoIndexEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractSession) EmitNoIndexEvent() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitNoIndexEvent(&_NetworkDebugContract.TransactOpts) +} + +// EmitNoIndexEvent is a paid mutator transaction binding the contract method 0x95a81a4c. +// +// Solidity: function emitNoIndexEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) EmitNoIndexEvent() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitNoIndexEvent(&_NetworkDebugContract.TransactOpts) +} + +// EmitNoIndexEventString is a paid mutator transaction binding the contract method 0x788c4772. +// +// Solidity: function emitNoIndexEventString() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) EmitNoIndexEventString(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "emitNoIndexEventString") +} + +// EmitNoIndexEventString is a paid mutator transaction binding the contract method 0x788c4772. +// +// Solidity: function emitNoIndexEventString() returns() +func (_NetworkDebugContract *NetworkDebugContractSession) EmitNoIndexEventString() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitNoIndexEventString(&_NetworkDebugContract.TransactOpts) +} + +// EmitNoIndexEventString is a paid mutator transaction binding the contract method 0x788c4772. +// +// Solidity: function emitNoIndexEventString() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) EmitNoIndexEventString() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitNoIndexEventString(&_NetworkDebugContract.TransactOpts) +} + +// EmitNoIndexStructEvent is a paid mutator transaction binding the contract method 0x62c270e1. +// +// Solidity: function emitNoIndexStructEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) EmitNoIndexStructEvent(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "emitNoIndexStructEvent") +} + +// EmitNoIndexStructEvent is a paid mutator transaction binding the contract method 0x62c270e1. +// +// Solidity: function emitNoIndexStructEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractSession) EmitNoIndexStructEvent() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitNoIndexStructEvent(&_NetworkDebugContract.TransactOpts) +} + +// EmitNoIndexStructEvent is a paid mutator transaction binding the contract method 0x62c270e1. +// +// Solidity: function emitNoIndexStructEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) EmitNoIndexStructEvent() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitNoIndexStructEvent(&_NetworkDebugContract.TransactOpts) +} + +// EmitOneIndexEvent is a paid mutator transaction binding the contract method 0x8f856296. +// +// Solidity: function emitOneIndexEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) EmitOneIndexEvent(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "emitOneIndexEvent") +} + +// EmitOneIndexEvent is a paid mutator transaction binding the contract method 0x8f856296. +// +// Solidity: function emitOneIndexEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractSession) EmitOneIndexEvent() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitOneIndexEvent(&_NetworkDebugContract.TransactOpts) +} + +// EmitOneIndexEvent is a paid mutator transaction binding the contract method 0x8f856296. +// +// Solidity: function emitOneIndexEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) EmitOneIndexEvent() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitOneIndexEvent(&_NetworkDebugContract.TransactOpts) +} + +// EmitOutputs is a paid mutator transaction binding the contract method 0x8db611be. +// +// Solidity: function emitOutputs() returns(uint256, string) +func (_NetworkDebugContract *NetworkDebugContractTransactor) EmitOutputs(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "emitOutputs") +} + +// EmitOutputs is a paid mutator transaction binding the contract method 0x8db611be. +// +// Solidity: function emitOutputs() returns(uint256, string) +func (_NetworkDebugContract *NetworkDebugContractSession) EmitOutputs() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitOutputs(&_NetworkDebugContract.TransactOpts) +} + +// EmitOutputs is a paid mutator transaction binding the contract method 0x8db611be. +// +// Solidity: function emitOutputs() returns(uint256, string) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) EmitOutputs() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitOutputs(&_NetworkDebugContract.TransactOpts) +} + +// EmitThreeIndexEvent is a paid mutator transaction binding the contract method 0xaa3fdcf4. +// +// Solidity: function emitThreeIndexEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) EmitThreeIndexEvent(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "emitThreeIndexEvent") +} + +// EmitThreeIndexEvent is a paid mutator transaction binding the contract method 0xaa3fdcf4. +// +// Solidity: function emitThreeIndexEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractSession) EmitThreeIndexEvent() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitThreeIndexEvent(&_NetworkDebugContract.TransactOpts) +} + +// EmitThreeIndexEvent is a paid mutator transaction binding the contract method 0xaa3fdcf4. +// +// Solidity: function emitThreeIndexEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) EmitThreeIndexEvent() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitThreeIndexEvent(&_NetworkDebugContract.TransactOpts) +} + +// EmitTwoIndexEvent is a paid mutator transaction binding the contract method 0x1e31d0a8. +// +// Solidity: function emitTwoIndexEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) EmitTwoIndexEvent(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "emitTwoIndexEvent") +} + +// EmitTwoIndexEvent is a paid mutator transaction binding the contract method 0x1e31d0a8. +// +// Solidity: function emitTwoIndexEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractSession) EmitTwoIndexEvent() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitTwoIndexEvent(&_NetworkDebugContract.TransactOpts) +} + +// EmitTwoIndexEvent is a paid mutator transaction binding the contract method 0x1e31d0a8. +// +// Solidity: function emitTwoIndexEvent() returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) EmitTwoIndexEvent() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.EmitTwoIndexEvent(&_NetworkDebugContract.TransactOpts) +} + +// OnTokenTransfer is a paid mutator transaction binding the contract method 0xa4c0ed36. +// +// Solidity: function onTokenTransfer(address sender, uint256 amount, bytes data) returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) OnTokenTransfer(opts *bind.TransactOpts, sender common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "onTokenTransfer", sender, amount, data) +} + +// OnTokenTransfer is a paid mutator transaction binding the contract method 0xa4c0ed36. +// +// Solidity: function onTokenTransfer(address sender, uint256 amount, bytes data) returns() +func (_NetworkDebugContract *NetworkDebugContractSession) OnTokenTransfer(sender common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.OnTokenTransfer(&_NetworkDebugContract.TransactOpts, sender, amount, data) +} + +// OnTokenTransfer is a paid mutator transaction binding the contract method 0xa4c0ed36. +// +// Solidity: function onTokenTransfer(address sender, uint256 amount, bytes data) returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) OnTokenTransfer(sender common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.OnTokenTransfer(&_NetworkDebugContract.TransactOpts, sender, amount, data) +} + +// Pay is a paid mutator transaction binding the contract method 0x1b9265b8. +// +// Solidity: function pay() payable returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) Pay(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "pay") +} + +// Pay is a paid mutator transaction binding the contract method 0x1b9265b8. +// +// Solidity: function pay() payable returns() +func (_NetworkDebugContract *NetworkDebugContractSession) Pay() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.Pay(&_NetworkDebugContract.TransactOpts) +} + +// Pay is a paid mutator transaction binding the contract method 0x1b9265b8. +// +// Solidity: function pay() payable returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) Pay() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.Pay(&_NetworkDebugContract.TransactOpts) +} + +// ProcessAddressArray is a paid mutator transaction binding the contract method 0xe1111f79. +// +// Solidity: function processAddressArray(address[] input) returns(address[]) +func (_NetworkDebugContract *NetworkDebugContractTransactor) ProcessAddressArray(opts *bind.TransactOpts, input []common.Address) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "processAddressArray", input) +} + +// ProcessAddressArray is a paid mutator transaction binding the contract method 0xe1111f79. +// +// Solidity: function processAddressArray(address[] input) returns(address[]) +func (_NetworkDebugContract *NetworkDebugContractSession) ProcessAddressArray(input []common.Address) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.ProcessAddressArray(&_NetworkDebugContract.TransactOpts, input) +} + +// ProcessAddressArray is a paid mutator transaction binding the contract method 0xe1111f79. +// +// Solidity: function processAddressArray(address[] input) returns(address[]) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) ProcessAddressArray(input []common.Address) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.ProcessAddressArray(&_NetworkDebugContract.TransactOpts, input) +} + +// ProcessDynamicData is a paid mutator transaction binding the contract method 0x7fdc8fe1. +// +// Solidity: function processDynamicData((string,uint256[]) data) returns((string,uint256[])) +func (_NetworkDebugContract *NetworkDebugContractTransactor) ProcessDynamicData(opts *bind.TransactOpts, data NetworkDebugContractData) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "processDynamicData", data) +} + +// ProcessDynamicData is a paid mutator transaction binding the contract method 0x7fdc8fe1. +// +// Solidity: function processDynamicData((string,uint256[]) data) returns((string,uint256[])) +func (_NetworkDebugContract *NetworkDebugContractSession) ProcessDynamicData(data NetworkDebugContractData) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.ProcessDynamicData(&_NetworkDebugContract.TransactOpts, data) +} + +// ProcessDynamicData is a paid mutator transaction binding the contract method 0x7fdc8fe1. +// +// Solidity: function processDynamicData((string,uint256[]) data) returns((string,uint256[])) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) ProcessDynamicData(data NetworkDebugContractData) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.ProcessDynamicData(&_NetworkDebugContract.TransactOpts, data) +} + +// ProcessFixedDataArray is a paid mutator transaction binding the contract method 0x99adad2e. +// +// Solidity: function processFixedDataArray((string,uint256[])[3] data) returns((string,uint256[])[2]) +func (_NetworkDebugContract *NetworkDebugContractTransactor) ProcessFixedDataArray(opts *bind.TransactOpts, data [3]NetworkDebugContractData) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "processFixedDataArray", data) +} + +// ProcessFixedDataArray is a paid mutator transaction binding the contract method 0x99adad2e. +// +// Solidity: function processFixedDataArray((string,uint256[])[3] data) returns((string,uint256[])[2]) +func (_NetworkDebugContract *NetworkDebugContractSession) ProcessFixedDataArray(data [3]NetworkDebugContractData) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.ProcessFixedDataArray(&_NetworkDebugContract.TransactOpts, data) +} + +// ProcessFixedDataArray is a paid mutator transaction binding the contract method 0x99adad2e. +// +// Solidity: function processFixedDataArray((string,uint256[])[3] data) returns((string,uint256[])[2]) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) ProcessFixedDataArray(data [3]NetworkDebugContractData) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.ProcessFixedDataArray(&_NetworkDebugContract.TransactOpts, data) +} + +// ProcessNestedData is a paid mutator transaction binding the contract method 0x7f12881c. +// +// Solidity: function processNestedData(((string,uint256[]),bytes) data) returns(((string,uint256[]),bytes)) +func (_NetworkDebugContract *NetworkDebugContractTransactor) ProcessNestedData(opts *bind.TransactOpts, data NetworkDebugContractNestedData) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "processNestedData", data) +} + +// ProcessNestedData is a paid mutator transaction binding the contract method 0x7f12881c. +// +// Solidity: function processNestedData(((string,uint256[]),bytes) data) returns(((string,uint256[]),bytes)) +func (_NetworkDebugContract *NetworkDebugContractSession) ProcessNestedData(data NetworkDebugContractNestedData) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.ProcessNestedData(&_NetworkDebugContract.TransactOpts, data) +} + +// ProcessNestedData is a paid mutator transaction binding the contract method 0x7f12881c. +// +// Solidity: function processNestedData(((string,uint256[]),bytes) data) returns(((string,uint256[]),bytes)) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) ProcessNestedData(data NetworkDebugContractNestedData) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.ProcessNestedData(&_NetworkDebugContract.TransactOpts, data) +} + +// ProcessNestedData0 is a paid mutator transaction binding the contract method 0xf499af2a. +// +// Solidity: function processNestedData((string,uint256[]) data) returns(((string,uint256[]),bytes)) +func (_NetworkDebugContract *NetworkDebugContractTransactor) ProcessNestedData0(opts *bind.TransactOpts, data NetworkDebugContractData) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "processNestedData0", data) +} + +// ProcessNestedData0 is a paid mutator transaction binding the contract method 0xf499af2a. +// +// Solidity: function processNestedData((string,uint256[]) data) returns(((string,uint256[]),bytes)) +func (_NetworkDebugContract *NetworkDebugContractSession) ProcessNestedData0(data NetworkDebugContractData) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.ProcessNestedData0(&_NetworkDebugContract.TransactOpts, data) +} + +// ProcessNestedData0 is a paid mutator transaction binding the contract method 0xf499af2a. +// +// Solidity: function processNestedData((string,uint256[]) data) returns(((string,uint256[]),bytes)) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) ProcessNestedData0(data NetworkDebugContractData) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.ProcessNestedData0(&_NetworkDebugContract.TransactOpts, data) +} + +// ProcessUintArray is a paid mutator transaction binding the contract method 0x12d91233. +// +// Solidity: function processUintArray(uint256[] input) returns(uint256[]) +func (_NetworkDebugContract *NetworkDebugContractTransactor) ProcessUintArray(opts *bind.TransactOpts, input []*big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "processUintArray", input) +} + +// ProcessUintArray is a paid mutator transaction binding the contract method 0x12d91233. +// +// Solidity: function processUintArray(uint256[] input) returns(uint256[]) +func (_NetworkDebugContract *NetworkDebugContractSession) ProcessUintArray(input []*big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.ProcessUintArray(&_NetworkDebugContract.TransactOpts, input) +} + +// ProcessUintArray is a paid mutator transaction binding the contract method 0x12d91233. +// +// Solidity: function processUintArray(uint256[] input) returns(uint256[]) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) ProcessUintArray(input []*big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.ProcessUintArray(&_NetworkDebugContract.TransactOpts, input) +} + +// ResetCounter is a paid mutator transaction binding the contract method 0xf3396bd9. +// +// Solidity: function resetCounter(int256 idx) returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) ResetCounter(opts *bind.TransactOpts, idx *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "resetCounter", idx) +} + +// ResetCounter is a paid mutator transaction binding the contract method 0xf3396bd9. +// +// Solidity: function resetCounter(int256 idx) returns() +func (_NetworkDebugContract *NetworkDebugContractSession) ResetCounter(idx *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.ResetCounter(&_NetworkDebugContract.TransactOpts, idx) +} + +// ResetCounter is a paid mutator transaction binding the contract method 0xf3396bd9. +// +// Solidity: function resetCounter(int256 idx) returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) ResetCounter(idx *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.ResetCounter(&_NetworkDebugContract.TransactOpts, idx) +} + +// Set is a paid mutator transaction binding the contract method 0xe5c19b2d. +// +// Solidity: function set(int256 x) returns(int256 value) +func (_NetworkDebugContract *NetworkDebugContractTransactor) Set(opts *bind.TransactOpts, x *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "set", x) +} + +// Set is a paid mutator transaction binding the contract method 0xe5c19b2d. +// +// Solidity: function set(int256 x) returns(int256 value) +func (_NetworkDebugContract *NetworkDebugContractSession) Set(x *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.Set(&_NetworkDebugContract.TransactOpts, x) +} + +// Set is a paid mutator transaction binding the contract method 0xe5c19b2d. +// +// Solidity: function set(int256 x) returns(int256 value) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) Set(x *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.Set(&_NetworkDebugContract.TransactOpts, x) +} + +// SetMap is a paid mutator transaction binding the contract method 0xe8116e28. +// +// Solidity: function setMap(int256 x) returns(int256 value) +func (_NetworkDebugContract *NetworkDebugContractTransactor) SetMap(opts *bind.TransactOpts, x *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "setMap", x) +} + +// SetMap is a paid mutator transaction binding the contract method 0xe8116e28. +// +// Solidity: function setMap(int256 x) returns(int256 value) +func (_NetworkDebugContract *NetworkDebugContractSession) SetMap(x *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.SetMap(&_NetworkDebugContract.TransactOpts, x) +} + +// SetMap is a paid mutator transaction binding the contract method 0xe8116e28. +// +// Solidity: function setMap(int256 x) returns(int256 value) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) SetMap(x *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.SetMap(&_NetworkDebugContract.TransactOpts, x) +} + +// SetStatus is a paid mutator transaction binding the contract method 0x2e49d78b. +// +// Solidity: function setStatus(uint8 status) returns(uint8) +func (_NetworkDebugContract *NetworkDebugContractTransactor) SetStatus(opts *bind.TransactOpts, status uint8) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "setStatus", status) +} + +// SetStatus is a paid mutator transaction binding the contract method 0x2e49d78b. +// +// Solidity: function setStatus(uint8 status) returns(uint8) +func (_NetworkDebugContract *NetworkDebugContractSession) SetStatus(status uint8) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.SetStatus(&_NetworkDebugContract.TransactOpts, status) +} + +// SetStatus is a paid mutator transaction binding the contract method 0x2e49d78b. +// +// Solidity: function setStatus(uint8 status) returns(uint8) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) SetStatus(status uint8) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.SetStatus(&_NetworkDebugContract.TransactOpts, status) +} + +// Trace is a paid mutator transaction binding the contract method 0x3e41f135. +// +// Solidity: function trace(int256 x, int256 y) returns(int256) +func (_NetworkDebugContract *NetworkDebugContractTransactor) Trace(opts *bind.TransactOpts, x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "trace", x, y) +} + +// Trace is a paid mutator transaction binding the contract method 0x3e41f135. +// +// Solidity: function trace(int256 x, int256 y) returns(int256) +func (_NetworkDebugContract *NetworkDebugContractSession) Trace(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.Trace(&_NetworkDebugContract.TransactOpts, x, y) +} + +// Trace is a paid mutator transaction binding the contract method 0x3e41f135. +// +// Solidity: function trace(int256 x, int256 y) returns(int256) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) Trace(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.Trace(&_NetworkDebugContract.TransactOpts, x, y) +} + +// TraceDifferent is a paid mutator transaction binding the contract method 0x30985bcc. +// +// Solidity: function traceDifferent(int256 x, int256 y) returns(int256) +func (_NetworkDebugContract *NetworkDebugContractTransactor) TraceDifferent(opts *bind.TransactOpts, x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "traceDifferent", x, y) +} + +// TraceDifferent is a paid mutator transaction binding the contract method 0x30985bcc. +// +// Solidity: function traceDifferent(int256 x, int256 y) returns(int256) +func (_NetworkDebugContract *NetworkDebugContractSession) TraceDifferent(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.TraceDifferent(&_NetworkDebugContract.TransactOpts, x, y) +} + +// TraceDifferent is a paid mutator transaction binding the contract method 0x30985bcc. +// +// Solidity: function traceDifferent(int256 x, int256 y) returns(int256) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) TraceDifferent(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.TraceDifferent(&_NetworkDebugContract.TransactOpts, x, y) +} + +// TraceSubWithCallback is a paid mutator transaction binding the contract method 0x3837a75e. +// +// Solidity: function traceSubWithCallback(int256 x, int256 y) returns(int256) +func (_NetworkDebugContract *NetworkDebugContractTransactor) TraceSubWithCallback(opts *bind.TransactOpts, x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "traceSubWithCallback", x, y) +} + +// TraceSubWithCallback is a paid mutator transaction binding the contract method 0x3837a75e. +// +// Solidity: function traceSubWithCallback(int256 x, int256 y) returns(int256) +func (_NetworkDebugContract *NetworkDebugContractSession) TraceSubWithCallback(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.TraceSubWithCallback(&_NetworkDebugContract.TransactOpts, x, y) +} + +// TraceSubWithCallback is a paid mutator transaction binding the contract method 0x3837a75e. +// +// Solidity: function traceSubWithCallback(int256 x, int256 y) returns(int256) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) TraceSubWithCallback(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.TraceSubWithCallback(&_NetworkDebugContract.TransactOpts, x, y) +} + +// TraceWithValidate is a paid mutator transaction binding the contract method 0xb1ae9d85. +// +// Solidity: function traceWithValidate(int256 x, int256 y) payable returns(int256) +func (_NetworkDebugContract *NetworkDebugContractTransactor) TraceWithValidate(opts *bind.TransactOpts, x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "traceWithValidate", x, y) +} + +// TraceWithValidate is a paid mutator transaction binding the contract method 0xb1ae9d85. +// +// Solidity: function traceWithValidate(int256 x, int256 y) payable returns(int256) +func (_NetworkDebugContract *NetworkDebugContractSession) TraceWithValidate(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.TraceWithValidate(&_NetworkDebugContract.TransactOpts, x, y) +} + +// TraceWithValidate is a paid mutator transaction binding the contract method 0xb1ae9d85. +// +// Solidity: function traceWithValidate(int256 x, int256 y) payable returns(int256) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) TraceWithValidate(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.TraceWithValidate(&_NetworkDebugContract.TransactOpts, x, y) +} + +// TraceYetDifferent is a paid mutator transaction binding the contract method 0x58379d71. +// +// Solidity: function traceYetDifferent(int256 x, int256 y) returns(int256) +func (_NetworkDebugContract *NetworkDebugContractTransactor) TraceYetDifferent(opts *bind.TransactOpts, x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "traceYetDifferent", x, y) +} + +// TraceYetDifferent is a paid mutator transaction binding the contract method 0x58379d71. +// +// Solidity: function traceYetDifferent(int256 x, int256 y) returns(int256) +func (_NetworkDebugContract *NetworkDebugContractSession) TraceYetDifferent(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.TraceYetDifferent(&_NetworkDebugContract.TransactOpts, x, y) +} + +// TraceYetDifferent is a paid mutator transaction binding the contract method 0x58379d71. +// +// Solidity: function traceYetDifferent(int256 x, int256 y) returns(int256) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) TraceYetDifferent(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.TraceYetDifferent(&_NetworkDebugContract.TransactOpts, x, y) +} + +// Validate is a paid mutator transaction binding the contract method 0x04d8215b. +// +// Solidity: function validate(int256 x, int256 y) returns(bool) +func (_NetworkDebugContract *NetworkDebugContractTransactor) Validate(opts *bind.TransactOpts, x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.contract.Transact(opts, "validate", x, y) +} + +// Validate is a paid mutator transaction binding the contract method 0x04d8215b. +// +// Solidity: function validate(int256 x, int256 y) returns(bool) +func (_NetworkDebugContract *NetworkDebugContractSession) Validate(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.Validate(&_NetworkDebugContract.TransactOpts, x, y) +} + +// Validate is a paid mutator transaction binding the contract method 0x04d8215b. +// +// Solidity: function validate(int256 x, int256 y) returns(bool) +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) Validate(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.Validate(&_NetworkDebugContract.TransactOpts, x, y) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() payable returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) Fallback(opts *bind.TransactOpts, calldata []byte) (*types.Transaction, error) { + return _NetworkDebugContract.contract.RawTransact(opts, calldata) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() payable returns() +func (_NetworkDebugContract *NetworkDebugContractSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.Fallback(&_NetworkDebugContract.TransactOpts, calldata) +} + +// Fallback is a paid mutator transaction binding the contract fallback function. +// +// Solidity: fallback() payable returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) Fallback(calldata []byte) (*types.Transaction, error) { + return _NetworkDebugContract.Contract.Fallback(&_NetworkDebugContract.TransactOpts, calldata) +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_NetworkDebugContract *NetworkDebugContractTransactor) Receive(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugContract.contract.RawTransact(opts, nil) // calldata is disallowed for receive function +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_NetworkDebugContract *NetworkDebugContractSession) Receive() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.Receive(&_NetworkDebugContract.TransactOpts) +} + +// Receive is a paid mutator transaction binding the contract receive function. +// +// Solidity: receive() payable returns() +func (_NetworkDebugContract *NetworkDebugContractTransactorSession) Receive() (*types.Transaction, error) { + return _NetworkDebugContract.Contract.Receive(&_NetworkDebugContract.TransactOpts) +} + +// NetworkDebugContractCallDataLengthIterator is returned from FilterCallDataLength and is used to iterate over the raw logs and unpacked data for CallDataLength events raised by the NetworkDebugContract contract. +type NetworkDebugContractCallDataLengthIterator struct { + Event *NetworkDebugContractCallDataLength // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugContractCallDataLengthIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractCallDataLength) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractCallDataLength) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugContractCallDataLengthIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugContractCallDataLengthIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugContractCallDataLength represents a CallDataLength event raised by the NetworkDebugContract contract. +type NetworkDebugContractCallDataLength struct { + Length *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterCallDataLength is a free log retrieval operation binding the contract event 0x962c5df4c8ad201a4f54a88f47715bb2cf291d019e350e2dff50ca6fc0f5d0ed. +// +// Solidity: event CallDataLength(uint256 length) +func (_NetworkDebugContract *NetworkDebugContractFilterer) FilterCallDataLength(opts *bind.FilterOpts) (*NetworkDebugContractCallDataLengthIterator, error) { + + logs, sub, err := _NetworkDebugContract.contract.FilterLogs(opts, "CallDataLength") + if err != nil { + return nil, err + } + return &NetworkDebugContractCallDataLengthIterator{contract: _NetworkDebugContract.contract, event: "CallDataLength", logs: logs, sub: sub}, nil +} + +// WatchCallDataLength is a free log subscription operation binding the contract event 0x962c5df4c8ad201a4f54a88f47715bb2cf291d019e350e2dff50ca6fc0f5d0ed. +// +// Solidity: event CallDataLength(uint256 length) +func (_NetworkDebugContract *NetworkDebugContractFilterer) WatchCallDataLength(opts *bind.WatchOpts, sink chan<- *NetworkDebugContractCallDataLength) (event.Subscription, error) { + + logs, sub, err := _NetworkDebugContract.contract.WatchLogs(opts, "CallDataLength") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugContractCallDataLength) + if err := _NetworkDebugContract.contract.UnpackLog(event, "CallDataLength", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseCallDataLength is a log parse operation binding the contract event 0x962c5df4c8ad201a4f54a88f47715bb2cf291d019e350e2dff50ca6fc0f5d0ed. +// +// Solidity: event CallDataLength(uint256 length) +func (_NetworkDebugContract *NetworkDebugContractFilterer) ParseCallDataLength(log types.Log) (*NetworkDebugContractCallDataLength, error) { + event := new(NetworkDebugContractCallDataLength) + if err := _NetworkDebugContract.contract.UnpackLog(event, "CallDataLength", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugContractCallbackEventIterator is returned from FilterCallbackEvent and is used to iterate over the raw logs and unpacked data for CallbackEvent events raised by the NetworkDebugContract contract. +type NetworkDebugContractCallbackEventIterator struct { + Event *NetworkDebugContractCallbackEvent // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugContractCallbackEventIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractCallbackEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractCallbackEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugContractCallbackEventIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugContractCallbackEventIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugContractCallbackEvent represents a CallbackEvent event raised by the NetworkDebugContract contract. +type NetworkDebugContractCallbackEvent struct { + A *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterCallbackEvent is a free log retrieval operation binding the contract event 0xb16dba9242e1aa07ccc47228094628f72c8cc9699ee45d5bc8d67b84d3038c68. +// +// Solidity: event CallbackEvent(int256 indexed a) +func (_NetworkDebugContract *NetworkDebugContractFilterer) FilterCallbackEvent(opts *bind.FilterOpts, a []*big.Int) (*NetworkDebugContractCallbackEventIterator, error) { + + var aRule []interface{} + for _, aItem := range a { + aRule = append(aRule, aItem) + } + + logs, sub, err := _NetworkDebugContract.contract.FilterLogs(opts, "CallbackEvent", aRule) + if err != nil { + return nil, err + } + return &NetworkDebugContractCallbackEventIterator{contract: _NetworkDebugContract.contract, event: "CallbackEvent", logs: logs, sub: sub}, nil +} + +// WatchCallbackEvent is a free log subscription operation binding the contract event 0xb16dba9242e1aa07ccc47228094628f72c8cc9699ee45d5bc8d67b84d3038c68. +// +// Solidity: event CallbackEvent(int256 indexed a) +func (_NetworkDebugContract *NetworkDebugContractFilterer) WatchCallbackEvent(opts *bind.WatchOpts, sink chan<- *NetworkDebugContractCallbackEvent, a []*big.Int) (event.Subscription, error) { + + var aRule []interface{} + for _, aItem := range a { + aRule = append(aRule, aItem) + } + + logs, sub, err := _NetworkDebugContract.contract.WatchLogs(opts, "CallbackEvent", aRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugContractCallbackEvent) + if err := _NetworkDebugContract.contract.UnpackLog(event, "CallbackEvent", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseCallbackEvent is a log parse operation binding the contract event 0xb16dba9242e1aa07ccc47228094628f72c8cc9699ee45d5bc8d67b84d3038c68. +// +// Solidity: event CallbackEvent(int256 indexed a) +func (_NetworkDebugContract *NetworkDebugContractFilterer) ParseCallbackEvent(log types.Log) (*NetworkDebugContractCallbackEvent, error) { + event := new(NetworkDebugContractCallbackEvent) + if err := _NetworkDebugContract.contract.UnpackLog(event, "CallbackEvent", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugContractCurrentStatusIterator is returned from FilterCurrentStatus and is used to iterate over the raw logs and unpacked data for CurrentStatus events raised by the NetworkDebugContract contract. +type NetworkDebugContractCurrentStatusIterator struct { + Event *NetworkDebugContractCurrentStatus // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugContractCurrentStatusIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractCurrentStatus) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractCurrentStatus) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugContractCurrentStatusIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugContractCurrentStatusIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugContractCurrentStatus represents a CurrentStatus event raised by the NetworkDebugContract contract. +type NetworkDebugContractCurrentStatus struct { + Status uint8 + Raw types.Log // Blockchain specific contextual infos +} + +// FilterCurrentStatus is a free log retrieval operation binding the contract event 0xbea054406fdf249b05d1aef1b5f848d62d902d94389fca702b2d8337677c359a. +// +// Solidity: event CurrentStatus(uint8 indexed status) +func (_NetworkDebugContract *NetworkDebugContractFilterer) FilterCurrentStatus(opts *bind.FilterOpts, status []uint8) (*NetworkDebugContractCurrentStatusIterator, error) { + + var statusRule []interface{} + for _, statusItem := range status { + statusRule = append(statusRule, statusItem) + } + + logs, sub, err := _NetworkDebugContract.contract.FilterLogs(opts, "CurrentStatus", statusRule) + if err != nil { + return nil, err + } + return &NetworkDebugContractCurrentStatusIterator{contract: _NetworkDebugContract.contract, event: "CurrentStatus", logs: logs, sub: sub}, nil +} + +// WatchCurrentStatus is a free log subscription operation binding the contract event 0xbea054406fdf249b05d1aef1b5f848d62d902d94389fca702b2d8337677c359a. +// +// Solidity: event CurrentStatus(uint8 indexed status) +func (_NetworkDebugContract *NetworkDebugContractFilterer) WatchCurrentStatus(opts *bind.WatchOpts, sink chan<- *NetworkDebugContractCurrentStatus, status []uint8) (event.Subscription, error) { + + var statusRule []interface{} + for _, statusItem := range status { + statusRule = append(statusRule, statusItem) + } + + logs, sub, err := _NetworkDebugContract.contract.WatchLogs(opts, "CurrentStatus", statusRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugContractCurrentStatus) + if err := _NetworkDebugContract.contract.UnpackLog(event, "CurrentStatus", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseCurrentStatus is a log parse operation binding the contract event 0xbea054406fdf249b05d1aef1b5f848d62d902d94389fca702b2d8337677c359a. +// +// Solidity: event CurrentStatus(uint8 indexed status) +func (_NetworkDebugContract *NetworkDebugContractFilterer) ParseCurrentStatus(log types.Log) (*NetworkDebugContractCurrentStatus, error) { + event := new(NetworkDebugContractCurrentStatus) + if err := _NetworkDebugContract.contract.UnpackLog(event, "CurrentStatus", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugContractEtherReceivedIterator is returned from FilterEtherReceived and is used to iterate over the raw logs and unpacked data for EtherReceived events raised by the NetworkDebugContract contract. +type NetworkDebugContractEtherReceivedIterator struct { + Event *NetworkDebugContractEtherReceived // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugContractEtherReceivedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractEtherReceived) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractEtherReceived) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugContractEtherReceivedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugContractEtherReceivedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugContractEtherReceived represents a EtherReceived event raised by the NetworkDebugContract contract. +type NetworkDebugContractEtherReceived struct { + Sender common.Address + Amount *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterEtherReceived is a free log retrieval operation binding the contract event 0x1e57e3bb474320be3d2c77138f75b7c3941292d647f5f9634e33a8e94e0e069b. +// +// Solidity: event EtherReceived(address sender, uint256 amount) +func (_NetworkDebugContract *NetworkDebugContractFilterer) FilterEtherReceived(opts *bind.FilterOpts) (*NetworkDebugContractEtherReceivedIterator, error) { + + logs, sub, err := _NetworkDebugContract.contract.FilterLogs(opts, "EtherReceived") + if err != nil { + return nil, err + } + return &NetworkDebugContractEtherReceivedIterator{contract: _NetworkDebugContract.contract, event: "EtherReceived", logs: logs, sub: sub}, nil +} + +// WatchEtherReceived is a free log subscription operation binding the contract event 0x1e57e3bb474320be3d2c77138f75b7c3941292d647f5f9634e33a8e94e0e069b. +// +// Solidity: event EtherReceived(address sender, uint256 amount) +func (_NetworkDebugContract *NetworkDebugContractFilterer) WatchEtherReceived(opts *bind.WatchOpts, sink chan<- *NetworkDebugContractEtherReceived) (event.Subscription, error) { + + logs, sub, err := _NetworkDebugContract.contract.WatchLogs(opts, "EtherReceived") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugContractEtherReceived) + if err := _NetworkDebugContract.contract.UnpackLog(event, "EtherReceived", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseEtherReceived is a log parse operation binding the contract event 0x1e57e3bb474320be3d2c77138f75b7c3941292d647f5f9634e33a8e94e0e069b. +// +// Solidity: event EtherReceived(address sender, uint256 amount) +func (_NetworkDebugContract *NetworkDebugContractFilterer) ParseEtherReceived(log types.Log) (*NetworkDebugContractEtherReceived, error) { + event := new(NetworkDebugContractEtherReceived) + if err := _NetworkDebugContract.contract.UnpackLog(event, "EtherReceived", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugContractIsValidEventIterator is returned from FilterIsValidEvent and is used to iterate over the raw logs and unpacked data for IsValidEvent events raised by the NetworkDebugContract contract. +type NetworkDebugContractIsValidEventIterator struct { + Event *NetworkDebugContractIsValidEvent // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugContractIsValidEventIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractIsValidEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractIsValidEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugContractIsValidEventIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugContractIsValidEventIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugContractIsValidEvent represents a IsValidEvent event raised by the NetworkDebugContract contract. +type NetworkDebugContractIsValidEvent struct { + Success bool + Raw types.Log // Blockchain specific contextual infos +} + +// FilterIsValidEvent is a free log retrieval operation binding the contract event 0xdfac7500004753b91139af55816e7eade36d96faec68b343f77ed66b89912a7b. +// +// Solidity: event IsValidEvent(bool success) +func (_NetworkDebugContract *NetworkDebugContractFilterer) FilterIsValidEvent(opts *bind.FilterOpts) (*NetworkDebugContractIsValidEventIterator, error) { + + logs, sub, err := _NetworkDebugContract.contract.FilterLogs(opts, "IsValidEvent") + if err != nil { + return nil, err + } + return &NetworkDebugContractIsValidEventIterator{contract: _NetworkDebugContract.contract, event: "IsValidEvent", logs: logs, sub: sub}, nil +} + +// WatchIsValidEvent is a free log subscription operation binding the contract event 0xdfac7500004753b91139af55816e7eade36d96faec68b343f77ed66b89912a7b. +// +// Solidity: event IsValidEvent(bool success) +func (_NetworkDebugContract *NetworkDebugContractFilterer) WatchIsValidEvent(opts *bind.WatchOpts, sink chan<- *NetworkDebugContractIsValidEvent) (event.Subscription, error) { + + logs, sub, err := _NetworkDebugContract.contract.WatchLogs(opts, "IsValidEvent") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugContractIsValidEvent) + if err := _NetworkDebugContract.contract.UnpackLog(event, "IsValidEvent", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseIsValidEvent is a log parse operation binding the contract event 0xdfac7500004753b91139af55816e7eade36d96faec68b343f77ed66b89912a7b. +// +// Solidity: event IsValidEvent(bool success) +func (_NetworkDebugContract *NetworkDebugContractFilterer) ParseIsValidEvent(log types.Log) (*NetworkDebugContractIsValidEvent, error) { + event := new(NetworkDebugContractIsValidEvent) + if err := _NetworkDebugContract.contract.UnpackLog(event, "IsValidEvent", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugContractNoIndexEventIterator is returned from FilterNoIndexEvent and is used to iterate over the raw logs and unpacked data for NoIndexEvent events raised by the NetworkDebugContract contract. +type NetworkDebugContractNoIndexEventIterator struct { + Event *NetworkDebugContractNoIndexEvent // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugContractNoIndexEventIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractNoIndexEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractNoIndexEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugContractNoIndexEventIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugContractNoIndexEventIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugContractNoIndexEvent represents a NoIndexEvent event raised by the NetworkDebugContract contract. +type NetworkDebugContractNoIndexEvent struct { + Sender common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterNoIndexEvent is a free log retrieval operation binding the contract event 0x33bc9bae48dbe1e057f264b3fc6a1dacdcceacb3ba28d937231c70e068a02f1c. +// +// Solidity: event NoIndexEvent(address sender) +func (_NetworkDebugContract *NetworkDebugContractFilterer) FilterNoIndexEvent(opts *bind.FilterOpts) (*NetworkDebugContractNoIndexEventIterator, error) { + + logs, sub, err := _NetworkDebugContract.contract.FilterLogs(opts, "NoIndexEvent") + if err != nil { + return nil, err + } + return &NetworkDebugContractNoIndexEventIterator{contract: _NetworkDebugContract.contract, event: "NoIndexEvent", logs: logs, sub: sub}, nil +} + +// WatchNoIndexEvent is a free log subscription operation binding the contract event 0x33bc9bae48dbe1e057f264b3fc6a1dacdcceacb3ba28d937231c70e068a02f1c. +// +// Solidity: event NoIndexEvent(address sender) +func (_NetworkDebugContract *NetworkDebugContractFilterer) WatchNoIndexEvent(opts *bind.WatchOpts, sink chan<- *NetworkDebugContractNoIndexEvent) (event.Subscription, error) { + + logs, sub, err := _NetworkDebugContract.contract.WatchLogs(opts, "NoIndexEvent") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugContractNoIndexEvent) + if err := _NetworkDebugContract.contract.UnpackLog(event, "NoIndexEvent", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseNoIndexEvent is a log parse operation binding the contract event 0x33bc9bae48dbe1e057f264b3fc6a1dacdcceacb3ba28d937231c70e068a02f1c. +// +// Solidity: event NoIndexEvent(address sender) +func (_NetworkDebugContract *NetworkDebugContractFilterer) ParseNoIndexEvent(log types.Log) (*NetworkDebugContractNoIndexEvent, error) { + event := new(NetworkDebugContractNoIndexEvent) + if err := _NetworkDebugContract.contract.UnpackLog(event, "NoIndexEvent", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugContractNoIndexEventStringIterator is returned from FilterNoIndexEventString and is used to iterate over the raw logs and unpacked data for NoIndexEventString events raised by the NetworkDebugContract contract. +type NetworkDebugContractNoIndexEventStringIterator struct { + Event *NetworkDebugContractNoIndexEventString // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugContractNoIndexEventStringIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractNoIndexEventString) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractNoIndexEventString) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugContractNoIndexEventStringIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugContractNoIndexEventStringIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugContractNoIndexEventString represents a NoIndexEventString event raised by the NetworkDebugContract contract. +type NetworkDebugContractNoIndexEventString struct { + Str string + Raw types.Log // Blockchain specific contextual infos +} + +// FilterNoIndexEventString is a free log retrieval operation binding the contract event 0x25b7adba1b046a19379db4bc06aa1f2e71604d7b599a0ee8783d58110f00e16a. +// +// Solidity: event NoIndexEventString(string str) +func (_NetworkDebugContract *NetworkDebugContractFilterer) FilterNoIndexEventString(opts *bind.FilterOpts) (*NetworkDebugContractNoIndexEventStringIterator, error) { + + logs, sub, err := _NetworkDebugContract.contract.FilterLogs(opts, "NoIndexEventString") + if err != nil { + return nil, err + } + return &NetworkDebugContractNoIndexEventStringIterator{contract: _NetworkDebugContract.contract, event: "NoIndexEventString", logs: logs, sub: sub}, nil +} + +// WatchNoIndexEventString is a free log subscription operation binding the contract event 0x25b7adba1b046a19379db4bc06aa1f2e71604d7b599a0ee8783d58110f00e16a. +// +// Solidity: event NoIndexEventString(string str) +func (_NetworkDebugContract *NetworkDebugContractFilterer) WatchNoIndexEventString(opts *bind.WatchOpts, sink chan<- *NetworkDebugContractNoIndexEventString) (event.Subscription, error) { + + logs, sub, err := _NetworkDebugContract.contract.WatchLogs(opts, "NoIndexEventString") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugContractNoIndexEventString) + if err := _NetworkDebugContract.contract.UnpackLog(event, "NoIndexEventString", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseNoIndexEventString is a log parse operation binding the contract event 0x25b7adba1b046a19379db4bc06aa1f2e71604d7b599a0ee8783d58110f00e16a. +// +// Solidity: event NoIndexEventString(string str) +func (_NetworkDebugContract *NetworkDebugContractFilterer) ParseNoIndexEventString(log types.Log) (*NetworkDebugContractNoIndexEventString, error) { + event := new(NetworkDebugContractNoIndexEventString) + if err := _NetworkDebugContract.contract.UnpackLog(event, "NoIndexEventString", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugContractNoIndexStructEventIterator is returned from FilterNoIndexStructEvent and is used to iterate over the raw logs and unpacked data for NoIndexStructEvent events raised by the NetworkDebugContract contract. +type NetworkDebugContractNoIndexStructEventIterator struct { + Event *NetworkDebugContractNoIndexStructEvent // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugContractNoIndexStructEventIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractNoIndexStructEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractNoIndexStructEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugContractNoIndexStructEventIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugContractNoIndexStructEventIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugContractNoIndexStructEvent represents a NoIndexStructEvent event raised by the NetworkDebugContract contract. +type NetworkDebugContractNoIndexStructEvent struct { + A NetworkDebugContractAccount + Raw types.Log // Blockchain specific contextual infos +} + +// FilterNoIndexStructEvent is a free log retrieval operation binding the contract event 0xebe3ff7e2071d351bf2e65b4fccd24e3ae99485f02468f1feecf7d64dc044188. +// +// Solidity: event NoIndexStructEvent((string,uint64,uint256) a) +func (_NetworkDebugContract *NetworkDebugContractFilterer) FilterNoIndexStructEvent(opts *bind.FilterOpts) (*NetworkDebugContractNoIndexStructEventIterator, error) { + + logs, sub, err := _NetworkDebugContract.contract.FilterLogs(opts, "NoIndexStructEvent") + if err != nil { + return nil, err + } + return &NetworkDebugContractNoIndexStructEventIterator{contract: _NetworkDebugContract.contract, event: "NoIndexStructEvent", logs: logs, sub: sub}, nil +} + +// WatchNoIndexStructEvent is a free log subscription operation binding the contract event 0xebe3ff7e2071d351bf2e65b4fccd24e3ae99485f02468f1feecf7d64dc044188. +// +// Solidity: event NoIndexStructEvent((string,uint64,uint256) a) +func (_NetworkDebugContract *NetworkDebugContractFilterer) WatchNoIndexStructEvent(opts *bind.WatchOpts, sink chan<- *NetworkDebugContractNoIndexStructEvent) (event.Subscription, error) { + + logs, sub, err := _NetworkDebugContract.contract.WatchLogs(opts, "NoIndexStructEvent") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugContractNoIndexStructEvent) + if err := _NetworkDebugContract.contract.UnpackLog(event, "NoIndexStructEvent", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseNoIndexStructEvent is a log parse operation binding the contract event 0xebe3ff7e2071d351bf2e65b4fccd24e3ae99485f02468f1feecf7d64dc044188. +// +// Solidity: event NoIndexStructEvent((string,uint64,uint256) a) +func (_NetworkDebugContract *NetworkDebugContractFilterer) ParseNoIndexStructEvent(log types.Log) (*NetworkDebugContractNoIndexStructEvent, error) { + event := new(NetworkDebugContractNoIndexStructEvent) + if err := _NetworkDebugContract.contract.UnpackLog(event, "NoIndexStructEvent", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugContractOneIndexEventIterator is returned from FilterOneIndexEvent and is used to iterate over the raw logs and unpacked data for OneIndexEvent events raised by the NetworkDebugContract contract. +type NetworkDebugContractOneIndexEventIterator struct { + Event *NetworkDebugContractOneIndexEvent // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugContractOneIndexEventIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractOneIndexEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractOneIndexEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugContractOneIndexEventIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugContractOneIndexEventIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugContractOneIndexEvent represents a OneIndexEvent event raised by the NetworkDebugContract contract. +type NetworkDebugContractOneIndexEvent struct { + A *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOneIndexEvent is a free log retrieval operation binding the contract event 0xeace1be0b97ec11f959499c07b9f60f0cc47bf610b28fda8fb0e970339cf3b35. +// +// Solidity: event OneIndexEvent(uint256 indexed a) +func (_NetworkDebugContract *NetworkDebugContractFilterer) FilterOneIndexEvent(opts *bind.FilterOpts, a []*big.Int) (*NetworkDebugContractOneIndexEventIterator, error) { + + var aRule []interface{} + for _, aItem := range a { + aRule = append(aRule, aItem) + } + + logs, sub, err := _NetworkDebugContract.contract.FilterLogs(opts, "OneIndexEvent", aRule) + if err != nil { + return nil, err + } + return &NetworkDebugContractOneIndexEventIterator{contract: _NetworkDebugContract.contract, event: "OneIndexEvent", logs: logs, sub: sub}, nil +} + +// WatchOneIndexEvent is a free log subscription operation binding the contract event 0xeace1be0b97ec11f959499c07b9f60f0cc47bf610b28fda8fb0e970339cf3b35. +// +// Solidity: event OneIndexEvent(uint256 indexed a) +func (_NetworkDebugContract *NetworkDebugContractFilterer) WatchOneIndexEvent(opts *bind.WatchOpts, sink chan<- *NetworkDebugContractOneIndexEvent, a []*big.Int) (event.Subscription, error) { + + var aRule []interface{} + for _, aItem := range a { + aRule = append(aRule, aItem) + } + + logs, sub, err := _NetworkDebugContract.contract.WatchLogs(opts, "OneIndexEvent", aRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugContractOneIndexEvent) + if err := _NetworkDebugContract.contract.UnpackLog(event, "OneIndexEvent", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseOneIndexEvent is a log parse operation binding the contract event 0xeace1be0b97ec11f959499c07b9f60f0cc47bf610b28fda8fb0e970339cf3b35. +// +// Solidity: event OneIndexEvent(uint256 indexed a) +func (_NetworkDebugContract *NetworkDebugContractFilterer) ParseOneIndexEvent(log types.Log) (*NetworkDebugContractOneIndexEvent, error) { + event := new(NetworkDebugContractOneIndexEvent) + if err := _NetworkDebugContract.contract.UnpackLog(event, "OneIndexEvent", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugContractReceivedIterator is returned from FilterReceived and is used to iterate over the raw logs and unpacked data for Received events raised by the NetworkDebugContract contract. +type NetworkDebugContractReceivedIterator struct { + Event *NetworkDebugContractReceived // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugContractReceivedIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractReceived) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractReceived) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugContractReceivedIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugContractReceivedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugContractReceived represents a Received event raised by the NetworkDebugContract contract. +type NetworkDebugContractReceived struct { + Caller common.Address + Amount *big.Int + Message string + Raw types.Log // Blockchain specific contextual infos +} + +// FilterReceived is a free log retrieval operation binding the contract event 0x59e04c3f0d44b7caf6e8ef854b61d9a51cf1960d7a88ff6356cc5e946b4b5832. +// +// Solidity: event Received(address caller, uint256 amount, string message) +func (_NetworkDebugContract *NetworkDebugContractFilterer) FilterReceived(opts *bind.FilterOpts) (*NetworkDebugContractReceivedIterator, error) { + + logs, sub, err := _NetworkDebugContract.contract.FilterLogs(opts, "Received") + if err != nil { + return nil, err + } + return &NetworkDebugContractReceivedIterator{contract: _NetworkDebugContract.contract, event: "Received", logs: logs, sub: sub}, nil +} + +// WatchReceived is a free log subscription operation binding the contract event 0x59e04c3f0d44b7caf6e8ef854b61d9a51cf1960d7a88ff6356cc5e946b4b5832. +// +// Solidity: event Received(address caller, uint256 amount, string message) +func (_NetworkDebugContract *NetworkDebugContractFilterer) WatchReceived(opts *bind.WatchOpts, sink chan<- *NetworkDebugContractReceived) (event.Subscription, error) { + + logs, sub, err := _NetworkDebugContract.contract.WatchLogs(opts, "Received") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugContractReceived) + if err := _NetworkDebugContract.contract.UnpackLog(event, "Received", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseReceived is a log parse operation binding the contract event 0x59e04c3f0d44b7caf6e8ef854b61d9a51cf1960d7a88ff6356cc5e946b4b5832. +// +// Solidity: event Received(address caller, uint256 amount, string message) +func (_NetworkDebugContract *NetworkDebugContractFilterer) ParseReceived(log types.Log) (*NetworkDebugContractReceived, error) { + event := new(NetworkDebugContractReceived) + if err := _NetworkDebugContract.contract.UnpackLog(event, "Received", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugContractThreeIndexAndOneNonIndexedEventIterator is returned from FilterThreeIndexAndOneNonIndexedEvent and is used to iterate over the raw logs and unpacked data for ThreeIndexAndOneNonIndexedEvent events raised by the NetworkDebugContract contract. +type NetworkDebugContractThreeIndexAndOneNonIndexedEventIterator struct { + Event *NetworkDebugContractThreeIndexAndOneNonIndexedEvent // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugContractThreeIndexAndOneNonIndexedEventIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractThreeIndexAndOneNonIndexedEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractThreeIndexAndOneNonIndexedEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugContractThreeIndexAndOneNonIndexedEventIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugContractThreeIndexAndOneNonIndexedEventIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugContractThreeIndexAndOneNonIndexedEvent represents a ThreeIndexAndOneNonIndexedEvent event raised by the NetworkDebugContract contract. +type NetworkDebugContractThreeIndexAndOneNonIndexedEvent struct { + RoundId *big.Int + StartedBy common.Address + StartedAt *big.Int + DataId string + Raw types.Log // Blockchain specific contextual infos +} + +// FilterThreeIndexAndOneNonIndexedEvent is a free log retrieval operation binding the contract event 0x56c2ea44ba516098cee0c181dd9d8db262657368b6e911e83ae0ccfae806c73d. +// +// Solidity: event ThreeIndexAndOneNonIndexedEvent(uint256 indexed roundId, address indexed startedBy, uint256 indexed startedAt, string dataId) +func (_NetworkDebugContract *NetworkDebugContractFilterer) FilterThreeIndexAndOneNonIndexedEvent(opts *bind.FilterOpts, roundId []*big.Int, startedBy []common.Address, startedAt []*big.Int) (*NetworkDebugContractThreeIndexAndOneNonIndexedEventIterator, error) { + + var roundIdRule []interface{} + for _, roundIdItem := range roundId { + roundIdRule = append(roundIdRule, roundIdItem) + } + var startedByRule []interface{} + for _, startedByItem := range startedBy { + startedByRule = append(startedByRule, startedByItem) + } + var startedAtRule []interface{} + for _, startedAtItem := range startedAt { + startedAtRule = append(startedAtRule, startedAtItem) + } + + logs, sub, err := _NetworkDebugContract.contract.FilterLogs(opts, "ThreeIndexAndOneNonIndexedEvent", roundIdRule, startedByRule, startedAtRule) + if err != nil { + return nil, err + } + return &NetworkDebugContractThreeIndexAndOneNonIndexedEventIterator{contract: _NetworkDebugContract.contract, event: "ThreeIndexAndOneNonIndexedEvent", logs: logs, sub: sub}, nil +} + +// WatchThreeIndexAndOneNonIndexedEvent is a free log subscription operation binding the contract event 0x56c2ea44ba516098cee0c181dd9d8db262657368b6e911e83ae0ccfae806c73d. +// +// Solidity: event ThreeIndexAndOneNonIndexedEvent(uint256 indexed roundId, address indexed startedBy, uint256 indexed startedAt, string dataId) +func (_NetworkDebugContract *NetworkDebugContractFilterer) WatchThreeIndexAndOneNonIndexedEvent(opts *bind.WatchOpts, sink chan<- *NetworkDebugContractThreeIndexAndOneNonIndexedEvent, roundId []*big.Int, startedBy []common.Address, startedAt []*big.Int) (event.Subscription, error) { + + var roundIdRule []interface{} + for _, roundIdItem := range roundId { + roundIdRule = append(roundIdRule, roundIdItem) + } + var startedByRule []interface{} + for _, startedByItem := range startedBy { + startedByRule = append(startedByRule, startedByItem) + } + var startedAtRule []interface{} + for _, startedAtItem := range startedAt { + startedAtRule = append(startedAtRule, startedAtItem) + } + + logs, sub, err := _NetworkDebugContract.contract.WatchLogs(opts, "ThreeIndexAndOneNonIndexedEvent", roundIdRule, startedByRule, startedAtRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugContractThreeIndexAndOneNonIndexedEvent) + if err := _NetworkDebugContract.contract.UnpackLog(event, "ThreeIndexAndOneNonIndexedEvent", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseThreeIndexAndOneNonIndexedEvent is a log parse operation binding the contract event 0x56c2ea44ba516098cee0c181dd9d8db262657368b6e911e83ae0ccfae806c73d. +// +// Solidity: event ThreeIndexAndOneNonIndexedEvent(uint256 indexed roundId, address indexed startedBy, uint256 indexed startedAt, string dataId) +func (_NetworkDebugContract *NetworkDebugContractFilterer) ParseThreeIndexAndOneNonIndexedEvent(log types.Log) (*NetworkDebugContractThreeIndexAndOneNonIndexedEvent, error) { + event := new(NetworkDebugContractThreeIndexAndOneNonIndexedEvent) + if err := _NetworkDebugContract.contract.UnpackLog(event, "ThreeIndexAndOneNonIndexedEvent", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugContractThreeIndexEventIterator is returned from FilterThreeIndexEvent and is used to iterate over the raw logs and unpacked data for ThreeIndexEvent events raised by the NetworkDebugContract contract. +type NetworkDebugContractThreeIndexEventIterator struct { + Event *NetworkDebugContractThreeIndexEvent // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugContractThreeIndexEventIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractThreeIndexEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractThreeIndexEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugContractThreeIndexEventIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugContractThreeIndexEventIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugContractThreeIndexEvent represents a ThreeIndexEvent event raised by the NetworkDebugContract contract. +type NetworkDebugContractThreeIndexEvent struct { + RoundId *big.Int + StartedBy common.Address + StartedAt *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterThreeIndexEvent is a free log retrieval operation binding the contract event 0x5660e8f93f0146f45abcd659e026b75995db50053cbbca4d7f365934ade68bf3. +// +// Solidity: event ThreeIndexEvent(uint256 indexed roundId, address indexed startedBy, uint256 indexed startedAt) +func (_NetworkDebugContract *NetworkDebugContractFilterer) FilterThreeIndexEvent(opts *bind.FilterOpts, roundId []*big.Int, startedBy []common.Address, startedAt []*big.Int) (*NetworkDebugContractThreeIndexEventIterator, error) { + + var roundIdRule []interface{} + for _, roundIdItem := range roundId { + roundIdRule = append(roundIdRule, roundIdItem) + } + var startedByRule []interface{} + for _, startedByItem := range startedBy { + startedByRule = append(startedByRule, startedByItem) + } + var startedAtRule []interface{} + for _, startedAtItem := range startedAt { + startedAtRule = append(startedAtRule, startedAtItem) + } + + logs, sub, err := _NetworkDebugContract.contract.FilterLogs(opts, "ThreeIndexEvent", roundIdRule, startedByRule, startedAtRule) + if err != nil { + return nil, err + } + return &NetworkDebugContractThreeIndexEventIterator{contract: _NetworkDebugContract.contract, event: "ThreeIndexEvent", logs: logs, sub: sub}, nil +} + +// WatchThreeIndexEvent is a free log subscription operation binding the contract event 0x5660e8f93f0146f45abcd659e026b75995db50053cbbca4d7f365934ade68bf3. +// +// Solidity: event ThreeIndexEvent(uint256 indexed roundId, address indexed startedBy, uint256 indexed startedAt) +func (_NetworkDebugContract *NetworkDebugContractFilterer) WatchThreeIndexEvent(opts *bind.WatchOpts, sink chan<- *NetworkDebugContractThreeIndexEvent, roundId []*big.Int, startedBy []common.Address, startedAt []*big.Int) (event.Subscription, error) { + + var roundIdRule []interface{} + for _, roundIdItem := range roundId { + roundIdRule = append(roundIdRule, roundIdItem) + } + var startedByRule []interface{} + for _, startedByItem := range startedBy { + startedByRule = append(startedByRule, startedByItem) + } + var startedAtRule []interface{} + for _, startedAtItem := range startedAt { + startedAtRule = append(startedAtRule, startedAtItem) + } + + logs, sub, err := _NetworkDebugContract.contract.WatchLogs(opts, "ThreeIndexEvent", roundIdRule, startedByRule, startedAtRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugContractThreeIndexEvent) + if err := _NetworkDebugContract.contract.UnpackLog(event, "ThreeIndexEvent", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseThreeIndexEvent is a log parse operation binding the contract event 0x5660e8f93f0146f45abcd659e026b75995db50053cbbca4d7f365934ade68bf3. +// +// Solidity: event ThreeIndexEvent(uint256 indexed roundId, address indexed startedBy, uint256 indexed startedAt) +func (_NetworkDebugContract *NetworkDebugContractFilterer) ParseThreeIndexEvent(log types.Log) (*NetworkDebugContractThreeIndexEvent, error) { + event := new(NetworkDebugContractThreeIndexEvent) + if err := _NetworkDebugContract.contract.UnpackLog(event, "ThreeIndexEvent", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugContractTwoIndexEventIterator is returned from FilterTwoIndexEvent and is used to iterate over the raw logs and unpacked data for TwoIndexEvent events raised by the NetworkDebugContract contract. +type NetworkDebugContractTwoIndexEventIterator struct { + Event *NetworkDebugContractTwoIndexEvent // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugContractTwoIndexEventIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractTwoIndexEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugContractTwoIndexEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugContractTwoIndexEventIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugContractTwoIndexEventIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugContractTwoIndexEvent represents a TwoIndexEvent event raised by the NetworkDebugContract contract. +type NetworkDebugContractTwoIndexEvent struct { + RoundId *big.Int + StartedBy common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTwoIndexEvent is a free log retrieval operation binding the contract event 0x33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b5. +// +// Solidity: event TwoIndexEvent(uint256 indexed roundId, address indexed startedBy) +func (_NetworkDebugContract *NetworkDebugContractFilterer) FilterTwoIndexEvent(opts *bind.FilterOpts, roundId []*big.Int, startedBy []common.Address) (*NetworkDebugContractTwoIndexEventIterator, error) { + + var roundIdRule []interface{} + for _, roundIdItem := range roundId { + roundIdRule = append(roundIdRule, roundIdItem) + } + var startedByRule []interface{} + for _, startedByItem := range startedBy { + startedByRule = append(startedByRule, startedByItem) + } + + logs, sub, err := _NetworkDebugContract.contract.FilterLogs(opts, "TwoIndexEvent", roundIdRule, startedByRule) + if err != nil { + return nil, err + } + return &NetworkDebugContractTwoIndexEventIterator{contract: _NetworkDebugContract.contract, event: "TwoIndexEvent", logs: logs, sub: sub}, nil +} + +// WatchTwoIndexEvent is a free log subscription operation binding the contract event 0x33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b5. +// +// Solidity: event TwoIndexEvent(uint256 indexed roundId, address indexed startedBy) +func (_NetworkDebugContract *NetworkDebugContractFilterer) WatchTwoIndexEvent(opts *bind.WatchOpts, sink chan<- *NetworkDebugContractTwoIndexEvent, roundId []*big.Int, startedBy []common.Address) (event.Subscription, error) { + + var roundIdRule []interface{} + for _, roundIdItem := range roundId { + roundIdRule = append(roundIdRule, roundIdItem) + } + var startedByRule []interface{} + for _, startedByItem := range startedBy { + startedByRule = append(startedByRule, startedByItem) + } + + logs, sub, err := _NetworkDebugContract.contract.WatchLogs(opts, "TwoIndexEvent", roundIdRule, startedByRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugContractTwoIndexEvent) + if err := _NetworkDebugContract.contract.UnpackLog(event, "TwoIndexEvent", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseTwoIndexEvent is a log parse operation binding the contract event 0x33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b5. +// +// Solidity: event TwoIndexEvent(uint256 indexed roundId, address indexed startedBy) +func (_NetworkDebugContract *NetworkDebugContractFilterer) ParseTwoIndexEvent(log types.Log) (*NetworkDebugContractTwoIndexEvent, error) { + event := new(NetworkDebugContractTwoIndexEvent) + if err := _NetworkDebugContract.contract.UnpackLog(event, "TwoIndexEvent", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/seth/contracts/bind/link/link_token.go b/seth/contracts/bind/link/link_token.go new file mode 100644 index 000000000..a09d32e21 --- /dev/null +++ b/seth/contracts/bind/link/link_token.go @@ -0,0 +1,2038 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package link_token + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +var LinkTokenMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"supplyAfterMint\",\"type\":\"uint256\"}],\"name\":\"MaxSupplyExceeded\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderNotBurner\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"SenderNotMinter\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"burner\",\"type\":\"address\"}],\"name\":\"BurnAccessGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"burner\",\"type\":\"address\"}],\"name\":\"BurnAccessRevoked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"}],\"name\":\"MintAccessGranted\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"}],\"name\":\"MintAccessRevoked\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferRequested\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"OwnershipTransferred\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[],\"name\":\"acceptOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burn\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"burnFrom\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseApproval\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getBurners\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"getMinters\",\"outputs\":[{\"internalType\":\"address[]\",\"name\":\"\",\"type\":\"address[]\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"burner\",\"type\":\"address\"}],\"name\":\"grantBurnRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"burnAndMinter\",\"type\":\"address\"}],\"name\":\"grantMintAndBurnRoles\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"}],\"name\":\"grantMintRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseApproval\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"burner\",\"type\":\"address\"}],\"name\":\"isBurner\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"}],\"name\":\"isMinter\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"maxSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"mint\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"owner\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"burner\",\"type\":\"address\"}],\"name\":\"revokeBurnRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"minter\",\"type\":\"address\"}],\"name\":\"revokeMintRole\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes4\",\"name\":\"interfaceId\",\"type\":\"bytes4\"}],\"name\":\"supportsInterface\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"},{\"internalType\":\"bytes\",\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"transferAndCall\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"success\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"amount\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"}],\"name\":\"transferOwnership\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x60c06040523480156200001157600080fd5b506040518060400160405280600f81526020016e21b430b4b72634b735902a37b5b2b760891b815250604051806040016040528060048152602001634c494e4b60e01b81525060126b033b2e3c9fd0803ce8000000338060008686818181600390816200007f91906200028c565b5060046200008e82826200028c565b5050506001600160a01b0384169150620000f190505760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600580546001600160a01b0319166001600160a01b0384811691909117909155811615620001245762000124816200013b565b50505060ff90911660805260a05250620003589050565b336001600160a01b03821603620001955760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401620000e8565b600680546001600160a01b0319166001600160a01b03838116918217909255600554604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200021257607f821691505b6020821081036200023357634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200028757600081815260208120601f850160051c81016020861015620002625750805b601f850160051c820191505b8181101562000283578281556001016200026e565b5050505b505050565b81516001600160401b03811115620002a857620002a8620001e7565b620002c081620002b98454620001fd565b8462000239565b602080601f831160018114620002f85760008415620002df5750858301515b600019600386901b1c1916600185901b17855562000283565b600085815260208120601f198616915b82811015620003295788860151825594840194600190910190840162000308565b5085821015620003485787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a051611e4c6200038c60003960008181610447015281816108c301526108ed015260006102710152611e4c6000f3fe608060405234801561001057600080fd5b50600436106101f05760003560e01c806379cc67901161010f578063c2e3273d116100a2578063d73dd62311610071578063d73dd6231461046b578063dd62ed3e1461047e578063f2fde38b146104c4578063f81094f3146104d757600080fd5b8063c2e3273d1461040c578063c630948d1461041f578063c64d0ebc14610432578063d5abeb011461044557600080fd5b80639dc29fac116100de5780639dc29fac146103c0578063a457c2d7146103d3578063a9059cbb146103e6578063aa271e1a146103f957600080fd5b806379cc67901461037557806386fe8b43146103885780638da5cb5b1461039057806395d89b41146103b857600080fd5b806340c10f19116101875780636618846311610156578063661884631461030f5780636b32810b1461032257806370a082311461033757806379ba50971461036d57600080fd5b806340c10f19146102c157806342966c68146102d65780634334614a146102e95780634f5632f8146102fc57600080fd5b806323b872dd116101c357806323b872dd14610257578063313ce5671461026a578063395093511461029b5780634000aea0146102ae57600080fd5b806301ffc9a7146101f557806306fdde031461021d578063095ea7b31461023257806318160ddd14610245575b600080fd5b6102086102033660046119b9565b6104ea565b60405190151581526020015b60405180910390f35b61022561061b565b6040516102149190611a5f565b610208610240366004611a9b565b6106ad565b6002545b604051908152602001610214565b610208610265366004611ac5565b6106c5565b60405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152602001610214565b6102086102a9366004611a9b565b6106e9565b6102086102bc366004611b30565b610735565b6102d46102cf366004611a9b565b610858565b005b6102d46102e4366004611c19565b61097f565b6102086102f7366004611c32565b6109cc565b6102d461030a366004611c32565b6109d9565b61020861031d366004611a9b565b610a35565b61032a610a48565b6040516102149190611c4d565b610249610345366004611c32565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b6102d4610a59565b6102d4610383366004611a9b565b610b5a565b61032a610ba9565b60055460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610214565b610225610bb5565b6102d46103ce366004611a9b565b610bc4565b6102086103e1366004611a9b565b610bce565b6102086103f4366004611a9b565b610c9f565b610208610407366004611c32565b610cad565b6102d461041a366004611c32565b610cba565b6102d461042d366004611c32565b610d16565b6102d4610440366004611c32565b610d24565b7f0000000000000000000000000000000000000000000000000000000000000000610249565b6102d4610479366004611a9b565b610d80565b61024961048c366004611ca7565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b6102d46104d2366004611c32565b610d8a565b6102d46104e5366004611c32565b610d9b565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f36372b0700000000000000000000000000000000000000000000000000000000148061057d57507fffffffff0000000000000000000000000000000000000000000000000000000082167f4000aea000000000000000000000000000000000000000000000000000000000145b806105c957507fffffffff0000000000000000000000000000000000000000000000000000000082167fe6599b4d00000000000000000000000000000000000000000000000000000000145b8061061557507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b60606003805461062a90611cda565b80601f016020809104026020016040519081016040528092919081815260200182805461065690611cda565b80156106a35780601f10610678576101008083540402835291602001916106a3565b820191906000526020600020905b81548152906001019060200180831161068657829003601f168201915b5050505050905090565b6000336106bb818585610df7565b5060019392505050565b6000336106d3858285610e2b565b6106de858585610efc565b506001949350505050565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff871684529091528120549091906106bb9082908690610730908790611d5c565b610df7565b60006107418484610c9f565b508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c1685856040516107a1929190611d6f565b60405180910390a373ffffffffffffffffffffffffffffffffffffffff84163b156106bb576040517fa4c0ed3600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169063a4c0ed369061081c90339087908790600401611d90565b600060405180830381600087803b15801561083657600080fd5b505af115801561084a573d6000803e3d6000fd5b505050505060019392505050565b61086133610cad565b61089e576040517fe2c8c9d50000000000000000000000000000000000000000000000000000000081523360048201526024015b60405180910390fd5b813073ffffffffffffffffffffffffffffffffffffffff8216036108c157600080fd5b7f00000000000000000000000000000000000000000000000000000000000000001580159061092257507f00000000000000000000000000000000000000000000000000000000000000008261091660025490565b6109209190611d5c565b115b15610970578161093160025490565b61093b9190611d5c565b6040517fcbbf111300000000000000000000000000000000000000000000000000000000815260040161089591815260200190565b61097a8383610f2a565b505050565b610988336109cc565b6109c0576040517fc820b10b000000000000000000000000000000000000000000000000000000008152336004820152602401610895565b6109c98161101d565b50565b6000610615600983611027565b6109e1611056565b6109ec6009826110d9565b156109c95760405173ffffffffffffffffffffffffffffffffffffffff8216907f0a675452746933cefe3d74182e78db7afe57ba60eaa4234b5d85e9aa41b0610c90600090a250565b6000610a418383610bce565b9392505050565b6060610a5460076110fb565b905090565b60065473ffffffffffffffffffffffffffffffffffffffff163314610ada576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610895565b600580547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560068054909116905560405173ffffffffffffffffffffffffffffffffffffffff909116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b610b63336109cc565b610b9b576040517fc820b10b000000000000000000000000000000000000000000000000000000008152336004820152602401610895565b610ba58282611108565b5050565b6060610a5460096110fb565b60606004805461062a90611cda565b610ba58282610b5a565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919083811015610c92576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152608401610895565b6106de8286868403610df7565b6000336106bb818585610efc565b6000610615600783611027565b610cc2611056565b610ccd60078261111d565b156109c95760405173ffffffffffffffffffffffffffffffffffffffff8216907fe46fef8bbff1389d9010703cf8ebb363fb3daf5bf56edc27080b67bc8d9251ea90600090a250565b610d1f81610cba565b6109c9815b610d2c611056565b610d3760098261111d565b156109c95760405173ffffffffffffffffffffffffffffffffffffffff8216907f92308bb7573b2a3d17ddb868b39d8ebec433f3194421abc22d084f89658c9bad90600090a250565b61097a82826106e9565b610d92611056565b6109c98161113f565b610da3611056565b610dae6007826110d9565b156109c95760405173ffffffffffffffffffffffffffffffffffffffff8216907fed998b960f6340d045f620c119730f7aa7995e7425c2401d3a5b64ff998a59e990600090a250565b813073ffffffffffffffffffffffffffffffffffffffff821603610e1a57600080fd5b610e25848484611235565b50505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152600160209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610e255781811015610eef576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610895565b610e258484848403610df7565b813073ffffffffffffffffffffffffffffffffffffffff821603610f1f57600080fd5b610e258484846113e8565b73ffffffffffffffffffffffffffffffffffffffff8216610fa7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610895565b8060026000828254610fb99190611d5c565b909155505073ffffffffffffffffffffffffffffffffffffffff8216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b6109c93382611657565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001830160205260408120541515610a41565b60055473ffffffffffffffffffffffffffffffffffffffff1633146110d7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610895565b565b6000610a418373ffffffffffffffffffffffffffffffffffffffff841661181b565b60606000610a418361190e565b611113823383610e2b565b610ba58282611657565b6000610a418373ffffffffffffffffffffffffffffffffffffffff841661196a565b3373ffffffffffffffffffffffffffffffffffffffff8216036111be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610895565b600680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217909255600554604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b73ffffffffffffffffffffffffffffffffffffffff83166112d7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610895565b73ffffffffffffffffffffffffffffffffffffffff821661137a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610895565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff831661148b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610895565b73ffffffffffffffffffffffffffffffffffffffff821661152e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610895565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260208190526040902054818110156115e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610895565b73ffffffffffffffffffffffffffffffffffffffff848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610e25565b73ffffffffffffffffffffffffffffffffffffffff82166116fa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610895565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260208190526040902054818110156117b0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152608401610895565b73ffffffffffffffffffffffffffffffffffffffff83166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b6000818152600183016020526040812054801561190457600061183f600183611dce565b855490915060009061185390600190611dce565b90508181146118b857600086600001828154811061187357611873611de1565b906000526020600020015490508087600001848154811061189657611896611de1565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806118c9576118c9611e10565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610615565b6000915050610615565b60608160000180548060200260200160405190810160405280929190818152602001828054801561195e57602002820191906000526020600020905b81548152602001906001019080831161194a575b50505050509050919050565b60008181526001830160205260408120546119b157508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610615565b506000610615565b6000602082840312156119cb57600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610a4157600080fd5b6000815180845260005b81811015611a2157602081850181015186830182015201611a05565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000610a4160208301846119fb565b803573ffffffffffffffffffffffffffffffffffffffff81168114611a9657600080fd5b919050565b60008060408385031215611aae57600080fd5b611ab783611a72565b946020939093013593505050565b600080600060608486031215611ada57600080fd5b611ae384611a72565b9250611af160208501611a72565b9150604084013590509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080600060608486031215611b4557600080fd5b611b4e84611a72565b925060208401359150604084013567ffffffffffffffff80821115611b7257600080fd5b818601915086601f830112611b8657600080fd5b813581811115611b9857611b98611b01565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715611bde57611bde611b01565b81604052828152896020848701011115611bf757600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b600060208284031215611c2b57600080fd5b5035919050565b600060208284031215611c4457600080fd5b610a4182611a72565b6020808252825182820181905260009190848201906040850190845b81811015611c9b57835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101611c69565b50909695505050505050565b60008060408385031215611cba57600080fd5b611cc383611a72565b9150611cd160208401611a72565b90509250929050565b600181811c90821680611cee57607f821691505b602082108103611d27577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561061557610615611d2d565b828152604060208201526000611d8860408301846119fb565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff84168152826020820152606060408201526000611dc560608301846119fb565b95945050505050565b8181038181111561061557610615611d2d565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea164736f6c6343000813000a", +} + +var LinkTokenABI = LinkTokenMetaData.ABI + +var LinkTokenBin = LinkTokenMetaData.Bin + +func DeployLinkToken(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *LinkToken, error) { + parsed, err := LinkTokenMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(LinkTokenBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &LinkToken{address: address, abi: *parsed, LinkTokenCaller: LinkTokenCaller{contract: contract}, LinkTokenTransactor: LinkTokenTransactor{contract: contract}, LinkTokenFilterer: LinkTokenFilterer{contract: contract}}, nil +} + +type LinkToken struct { + address common.Address + abi abi.ABI + LinkTokenCaller + LinkTokenTransactor + LinkTokenFilterer +} + +type LinkTokenCaller struct { + contract *bind.BoundContract +} + +type LinkTokenTransactor struct { + contract *bind.BoundContract +} + +type LinkTokenFilterer struct { + contract *bind.BoundContract +} + +type LinkTokenSession struct { + Contract *LinkToken + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type LinkTokenCallerSession struct { + Contract *LinkTokenCaller + CallOpts bind.CallOpts +} + +type LinkTokenTransactorSession struct { + Contract *LinkTokenTransactor + TransactOpts bind.TransactOpts +} + +type LinkTokenRaw struct { + Contract *LinkToken +} + +type LinkTokenCallerRaw struct { + Contract *LinkTokenCaller +} + +type LinkTokenTransactorRaw struct { + Contract *LinkTokenTransactor +} + +func NewLinkToken(address common.Address, backend bind.ContractBackend) (*LinkToken, error) { + abi, err := abi.JSON(strings.NewReader(LinkTokenABI)) + if err != nil { + return nil, err + } + contract, err := bindLinkToken(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &LinkToken{address: address, abi: abi, LinkTokenCaller: LinkTokenCaller{contract: contract}, LinkTokenTransactor: LinkTokenTransactor{contract: contract}, LinkTokenFilterer: LinkTokenFilterer{contract: contract}}, nil +} + +func NewLinkTokenCaller(address common.Address, caller bind.ContractCaller) (*LinkTokenCaller, error) { + contract, err := bindLinkToken(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &LinkTokenCaller{contract: contract}, nil +} + +func NewLinkTokenTransactor(address common.Address, transactor bind.ContractTransactor) (*LinkTokenTransactor, error) { + contract, err := bindLinkToken(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &LinkTokenTransactor{contract: contract}, nil +} + +func NewLinkTokenFilterer(address common.Address, filterer bind.ContractFilterer) (*LinkTokenFilterer, error) { + contract, err := bindLinkToken(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &LinkTokenFilterer{contract: contract}, nil +} + +func bindLinkToken(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := LinkTokenMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_LinkToken *LinkTokenRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _LinkToken.Contract.LinkTokenCaller.contract.Call(opts, result, method, params...) +} + +func (_LinkToken *LinkTokenRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _LinkToken.Contract.LinkTokenTransactor.contract.Transfer(opts) +} + +func (_LinkToken *LinkTokenRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _LinkToken.Contract.LinkTokenTransactor.contract.Transact(opts, method, params...) +} + +func (_LinkToken *LinkTokenCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _LinkToken.Contract.contract.Call(opts, result, method, params...) +} + +func (_LinkToken *LinkTokenTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _LinkToken.Contract.contract.Transfer(opts) +} + +func (_LinkToken *LinkTokenTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _LinkToken.Contract.contract.Transact(opts, method, params...) +} + +func (_LinkToken *LinkTokenCaller) Allowance(opts *bind.CallOpts, owner common.Address, spender common.Address) (*big.Int, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "allowance", owner, spender) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) Allowance(owner common.Address, spender common.Address) (*big.Int, error) { + return _LinkToken.Contract.Allowance(&_LinkToken.CallOpts, owner, spender) +} + +func (_LinkToken *LinkTokenCallerSession) Allowance(owner common.Address, spender common.Address) (*big.Int, error) { + return _LinkToken.Contract.Allowance(&_LinkToken.CallOpts, owner, spender) +} + +func (_LinkToken *LinkTokenCaller) BalanceOf(opts *bind.CallOpts, account common.Address) (*big.Int, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "balanceOf", account) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) BalanceOf(account common.Address) (*big.Int, error) { + return _LinkToken.Contract.BalanceOf(&_LinkToken.CallOpts, account) +} + +func (_LinkToken *LinkTokenCallerSession) BalanceOf(account common.Address) (*big.Int, error) { + return _LinkToken.Contract.BalanceOf(&_LinkToken.CallOpts, account) +} + +func (_LinkToken *LinkTokenCaller) Decimals(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "decimals") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) Decimals() (uint8, error) { + return _LinkToken.Contract.Decimals(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCallerSession) Decimals() (uint8, error) { + return _LinkToken.Contract.Decimals(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCaller) GetBurners(opts *bind.CallOpts) ([]common.Address, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "getBurners") + + if err != nil { + return *new([]common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) GetBurners() ([]common.Address, error) { + return _LinkToken.Contract.GetBurners(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCallerSession) GetBurners() ([]common.Address, error) { + return _LinkToken.Contract.GetBurners(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCaller) GetMinters(opts *bind.CallOpts) ([]common.Address, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "getMinters") + + if err != nil { + return *new([]common.Address), err + } + + out0 := *abi.ConvertType(out[0], new([]common.Address)).(*[]common.Address) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) GetMinters() ([]common.Address, error) { + return _LinkToken.Contract.GetMinters(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCallerSession) GetMinters() ([]common.Address, error) { + return _LinkToken.Contract.GetMinters(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCaller) IsBurner(opts *bind.CallOpts, burner common.Address) (bool, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "isBurner", burner) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) IsBurner(burner common.Address) (bool, error) { + return _LinkToken.Contract.IsBurner(&_LinkToken.CallOpts, burner) +} + +func (_LinkToken *LinkTokenCallerSession) IsBurner(burner common.Address) (bool, error) { + return _LinkToken.Contract.IsBurner(&_LinkToken.CallOpts, burner) +} + +func (_LinkToken *LinkTokenCaller) IsMinter(opts *bind.CallOpts, minter common.Address) (bool, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "isMinter", minter) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) IsMinter(minter common.Address) (bool, error) { + return _LinkToken.Contract.IsMinter(&_LinkToken.CallOpts, minter) +} + +func (_LinkToken *LinkTokenCallerSession) IsMinter(minter common.Address) (bool, error) { + return _LinkToken.Contract.IsMinter(&_LinkToken.CallOpts, minter) +} + +func (_LinkToken *LinkTokenCaller) MaxSupply(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "maxSupply") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) MaxSupply() (*big.Int, error) { + return _LinkToken.Contract.MaxSupply(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCallerSession) MaxSupply() (*big.Int, error) { + return _LinkToken.Contract.MaxSupply(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCaller) Name(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "name") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) Name() (string, error) { + return _LinkToken.Contract.Name(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCallerSession) Name() (string, error) { + return _LinkToken.Contract.Name(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCaller) Owner(opts *bind.CallOpts) (common.Address, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "owner") + + if err != nil { + return *new(common.Address), err + } + + out0 := *abi.ConvertType(out[0], new(common.Address)).(*common.Address) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) Owner() (common.Address, error) { + return _LinkToken.Contract.Owner(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCallerSession) Owner() (common.Address, error) { + return _LinkToken.Contract.Owner(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCaller) SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "supportsInterface", interfaceId) + + if err != nil { + return *new(bool), err + } + + out0 := *abi.ConvertType(out[0], new(bool)).(*bool) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _LinkToken.Contract.SupportsInterface(&_LinkToken.CallOpts, interfaceId) +} + +func (_LinkToken *LinkTokenCallerSession) SupportsInterface(interfaceId [4]byte) (bool, error) { + return _LinkToken.Contract.SupportsInterface(&_LinkToken.CallOpts, interfaceId) +} + +func (_LinkToken *LinkTokenCaller) Symbol(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "symbol") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) Symbol() (string, error) { + return _LinkToken.Contract.Symbol(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCallerSession) Symbol() (string, error) { + return _LinkToken.Contract.Symbol(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCaller) TotalSupply(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "totalSupply") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) TotalSupply() (*big.Int, error) { + return _LinkToken.Contract.TotalSupply(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCallerSession) TotalSupply() (*big.Int, error) { + return _LinkToken.Contract.TotalSupply(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenTransactor) AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "acceptOwnership") +} + +func (_LinkToken *LinkTokenSession) AcceptOwnership() (*types.Transaction, error) { + return _LinkToken.Contract.AcceptOwnership(&_LinkToken.TransactOpts) +} + +func (_LinkToken *LinkTokenTransactorSession) AcceptOwnership() (*types.Transaction, error) { + return _LinkToken.Contract.AcceptOwnership(&_LinkToken.TransactOpts) +} + +func (_LinkToken *LinkTokenTransactor) Approve(opts *bind.TransactOpts, spender common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "approve", spender, amount) +} + +func (_LinkToken *LinkTokenSession) Approve(spender common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.Approve(&_LinkToken.TransactOpts, spender, amount) +} + +func (_LinkToken *LinkTokenTransactorSession) Approve(spender common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.Approve(&_LinkToken.TransactOpts, spender, amount) +} + +func (_LinkToken *LinkTokenTransactor) Burn(opts *bind.TransactOpts, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "burn", amount) +} + +func (_LinkToken *LinkTokenSession) Burn(amount *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.Burn(&_LinkToken.TransactOpts, amount) +} + +func (_LinkToken *LinkTokenTransactorSession) Burn(amount *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.Burn(&_LinkToken.TransactOpts, amount) +} + +func (_LinkToken *LinkTokenTransactor) Burn0(opts *bind.TransactOpts, account common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "burn0", account, amount) +} + +func (_LinkToken *LinkTokenSession) Burn0(account common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.Burn0(&_LinkToken.TransactOpts, account, amount) +} + +func (_LinkToken *LinkTokenTransactorSession) Burn0(account common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.Burn0(&_LinkToken.TransactOpts, account, amount) +} + +func (_LinkToken *LinkTokenTransactor) BurnFrom(opts *bind.TransactOpts, account common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "burnFrom", account, amount) +} + +func (_LinkToken *LinkTokenSession) BurnFrom(account common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.BurnFrom(&_LinkToken.TransactOpts, account, amount) +} + +func (_LinkToken *LinkTokenTransactorSession) BurnFrom(account common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.BurnFrom(&_LinkToken.TransactOpts, account, amount) +} + +func (_LinkToken *LinkTokenTransactor) DecreaseAllowance(opts *bind.TransactOpts, spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "decreaseAllowance", spender, subtractedValue) +} + +func (_LinkToken *LinkTokenSession) DecreaseAllowance(spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.DecreaseAllowance(&_LinkToken.TransactOpts, spender, subtractedValue) +} + +func (_LinkToken *LinkTokenTransactorSession) DecreaseAllowance(spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.DecreaseAllowance(&_LinkToken.TransactOpts, spender, subtractedValue) +} + +func (_LinkToken *LinkTokenTransactor) DecreaseApproval(opts *bind.TransactOpts, spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "decreaseApproval", spender, subtractedValue) +} + +func (_LinkToken *LinkTokenSession) DecreaseApproval(spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.DecreaseApproval(&_LinkToken.TransactOpts, spender, subtractedValue) +} + +func (_LinkToken *LinkTokenTransactorSession) DecreaseApproval(spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.DecreaseApproval(&_LinkToken.TransactOpts, spender, subtractedValue) +} + +func (_LinkToken *LinkTokenTransactor) GrantBurnRole(opts *bind.TransactOpts, burner common.Address) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "grantBurnRole", burner) +} + +func (_LinkToken *LinkTokenSession) GrantBurnRole(burner common.Address) (*types.Transaction, error) { + return _LinkToken.Contract.GrantBurnRole(&_LinkToken.TransactOpts, burner) +} + +func (_LinkToken *LinkTokenTransactorSession) GrantBurnRole(burner common.Address) (*types.Transaction, error) { + return _LinkToken.Contract.GrantBurnRole(&_LinkToken.TransactOpts, burner) +} + +func (_LinkToken *LinkTokenTransactor) GrantMintAndBurnRoles(opts *bind.TransactOpts, burnAndMinter common.Address) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "grantMintAndBurnRoles", burnAndMinter) +} + +func (_LinkToken *LinkTokenSession) GrantMintAndBurnRoles(burnAndMinter common.Address) (*types.Transaction, error) { + return _LinkToken.Contract.GrantMintAndBurnRoles(&_LinkToken.TransactOpts, burnAndMinter) +} + +func (_LinkToken *LinkTokenTransactorSession) GrantMintAndBurnRoles(burnAndMinter common.Address) (*types.Transaction, error) { + return _LinkToken.Contract.GrantMintAndBurnRoles(&_LinkToken.TransactOpts, burnAndMinter) +} + +func (_LinkToken *LinkTokenTransactor) GrantMintRole(opts *bind.TransactOpts, minter common.Address) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "grantMintRole", minter) +} + +func (_LinkToken *LinkTokenSession) GrantMintRole(minter common.Address) (*types.Transaction, error) { + return _LinkToken.Contract.GrantMintRole(&_LinkToken.TransactOpts, minter) +} + +func (_LinkToken *LinkTokenTransactorSession) GrantMintRole(minter common.Address) (*types.Transaction, error) { + return _LinkToken.Contract.GrantMintRole(&_LinkToken.TransactOpts, minter) +} + +func (_LinkToken *LinkTokenTransactor) IncreaseAllowance(opts *bind.TransactOpts, spender common.Address, addedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "increaseAllowance", spender, addedValue) +} + +func (_LinkToken *LinkTokenSession) IncreaseAllowance(spender common.Address, addedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.IncreaseAllowance(&_LinkToken.TransactOpts, spender, addedValue) +} + +func (_LinkToken *LinkTokenTransactorSession) IncreaseAllowance(spender common.Address, addedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.IncreaseAllowance(&_LinkToken.TransactOpts, spender, addedValue) +} + +func (_LinkToken *LinkTokenTransactor) IncreaseApproval(opts *bind.TransactOpts, spender common.Address, addedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "increaseApproval", spender, addedValue) +} + +func (_LinkToken *LinkTokenSession) IncreaseApproval(spender common.Address, addedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.IncreaseApproval(&_LinkToken.TransactOpts, spender, addedValue) +} + +func (_LinkToken *LinkTokenTransactorSession) IncreaseApproval(spender common.Address, addedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.IncreaseApproval(&_LinkToken.TransactOpts, spender, addedValue) +} + +func (_LinkToken *LinkTokenTransactor) Mint(opts *bind.TransactOpts, account common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "mint", account, amount) +} + +func (_LinkToken *LinkTokenSession) Mint(account common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.Mint(&_LinkToken.TransactOpts, account, amount) +} + +func (_LinkToken *LinkTokenTransactorSession) Mint(account common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.Mint(&_LinkToken.TransactOpts, account, amount) +} + +func (_LinkToken *LinkTokenTransactor) RevokeBurnRole(opts *bind.TransactOpts, burner common.Address) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "revokeBurnRole", burner) +} + +func (_LinkToken *LinkTokenSession) RevokeBurnRole(burner common.Address) (*types.Transaction, error) { + return _LinkToken.Contract.RevokeBurnRole(&_LinkToken.TransactOpts, burner) +} + +func (_LinkToken *LinkTokenTransactorSession) RevokeBurnRole(burner common.Address) (*types.Transaction, error) { + return _LinkToken.Contract.RevokeBurnRole(&_LinkToken.TransactOpts, burner) +} + +func (_LinkToken *LinkTokenTransactor) RevokeMintRole(opts *bind.TransactOpts, minter common.Address) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "revokeMintRole", minter) +} + +func (_LinkToken *LinkTokenSession) RevokeMintRole(minter common.Address) (*types.Transaction, error) { + return _LinkToken.Contract.RevokeMintRole(&_LinkToken.TransactOpts, minter) +} + +func (_LinkToken *LinkTokenTransactorSession) RevokeMintRole(minter common.Address) (*types.Transaction, error) { + return _LinkToken.Contract.RevokeMintRole(&_LinkToken.TransactOpts, minter) +} + +func (_LinkToken *LinkTokenTransactor) Transfer(opts *bind.TransactOpts, to common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "transfer", to, amount) +} + +func (_LinkToken *LinkTokenSession) Transfer(to common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.Transfer(&_LinkToken.TransactOpts, to, amount) +} + +func (_LinkToken *LinkTokenTransactorSession) Transfer(to common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.Transfer(&_LinkToken.TransactOpts, to, amount) +} + +func (_LinkToken *LinkTokenTransactor) TransferAndCall(opts *bind.TransactOpts, to common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "transferAndCall", to, amount, data) +} + +func (_LinkToken *LinkTokenSession) TransferAndCall(to common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _LinkToken.Contract.TransferAndCall(&_LinkToken.TransactOpts, to, amount, data) +} + +func (_LinkToken *LinkTokenTransactorSession) TransferAndCall(to common.Address, amount *big.Int, data []byte) (*types.Transaction, error) { + return _LinkToken.Contract.TransferAndCall(&_LinkToken.TransactOpts, to, amount, data) +} + +func (_LinkToken *LinkTokenTransactor) TransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "transferFrom", from, to, amount) +} + +func (_LinkToken *LinkTokenSession) TransferFrom(from common.Address, to common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.TransferFrom(&_LinkToken.TransactOpts, from, to, amount) +} + +func (_LinkToken *LinkTokenTransactorSession) TransferFrom(from common.Address, to common.Address, amount *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.TransferFrom(&_LinkToken.TransactOpts, from, to, amount) +} + +func (_LinkToken *LinkTokenTransactor) TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "transferOwnership", to) +} + +func (_LinkToken *LinkTokenSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _LinkToken.Contract.TransferOwnership(&_LinkToken.TransactOpts, to) +} + +func (_LinkToken *LinkTokenTransactorSession) TransferOwnership(to common.Address) (*types.Transaction, error) { + return _LinkToken.Contract.TransferOwnership(&_LinkToken.TransactOpts, to) +} + +type LinkTokenApprovalIterator struct { + Event *LinkTokenApproval + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *LinkTokenApprovalIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(LinkTokenApproval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(LinkTokenApproval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *LinkTokenApprovalIterator) Error() error { + return it.fail +} + +func (it *LinkTokenApprovalIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type LinkTokenApproval struct { + Owner common.Address + Spender common.Address + Value *big.Int + Raw types.Log +} + +func (_LinkToken *LinkTokenFilterer) FilterApproval(opts *bind.FilterOpts, owner []common.Address, spender []common.Address) (*LinkTokenApprovalIterator, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _LinkToken.contract.FilterLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return &LinkTokenApprovalIterator{contract: _LinkToken.contract, event: "Approval", logs: logs, sub: sub}, nil +} + +func (_LinkToken *LinkTokenFilterer) WatchApproval(opts *bind.WatchOpts, sink chan<- *LinkTokenApproval, owner []common.Address, spender []common.Address) (event.Subscription, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _LinkToken.contract.WatchLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(LinkTokenApproval) + if err := _LinkToken.contract.UnpackLog(event, "Approval", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_LinkToken *LinkTokenFilterer) ParseApproval(log types.Log) (*LinkTokenApproval, error) { + event := new(LinkTokenApproval) + if err := _LinkToken.contract.UnpackLog(event, "Approval", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type LinkTokenBurnAccessGrantedIterator struct { + Event *LinkTokenBurnAccessGranted + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *LinkTokenBurnAccessGrantedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(LinkTokenBurnAccessGranted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(LinkTokenBurnAccessGranted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *LinkTokenBurnAccessGrantedIterator) Error() error { + return it.fail +} + +func (it *LinkTokenBurnAccessGrantedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type LinkTokenBurnAccessGranted struct { + Burner common.Address + Raw types.Log +} + +func (_LinkToken *LinkTokenFilterer) FilterBurnAccessGranted(opts *bind.FilterOpts, burner []common.Address) (*LinkTokenBurnAccessGrantedIterator, error) { + + var burnerRule []interface{} + for _, burnerItem := range burner { + burnerRule = append(burnerRule, burnerItem) + } + + logs, sub, err := _LinkToken.contract.FilterLogs(opts, "BurnAccessGranted", burnerRule) + if err != nil { + return nil, err + } + return &LinkTokenBurnAccessGrantedIterator{contract: _LinkToken.contract, event: "BurnAccessGranted", logs: logs, sub: sub}, nil +} + +func (_LinkToken *LinkTokenFilterer) WatchBurnAccessGranted(opts *bind.WatchOpts, sink chan<- *LinkTokenBurnAccessGranted, burner []common.Address) (event.Subscription, error) { + + var burnerRule []interface{} + for _, burnerItem := range burner { + burnerRule = append(burnerRule, burnerItem) + } + + logs, sub, err := _LinkToken.contract.WatchLogs(opts, "BurnAccessGranted", burnerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(LinkTokenBurnAccessGranted) + if err := _LinkToken.contract.UnpackLog(event, "BurnAccessGranted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_LinkToken *LinkTokenFilterer) ParseBurnAccessGranted(log types.Log) (*LinkTokenBurnAccessGranted, error) { + event := new(LinkTokenBurnAccessGranted) + if err := _LinkToken.contract.UnpackLog(event, "BurnAccessGranted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type LinkTokenBurnAccessRevokedIterator struct { + Event *LinkTokenBurnAccessRevoked + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *LinkTokenBurnAccessRevokedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(LinkTokenBurnAccessRevoked) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(LinkTokenBurnAccessRevoked) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *LinkTokenBurnAccessRevokedIterator) Error() error { + return it.fail +} + +func (it *LinkTokenBurnAccessRevokedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type LinkTokenBurnAccessRevoked struct { + Burner common.Address + Raw types.Log +} + +func (_LinkToken *LinkTokenFilterer) FilterBurnAccessRevoked(opts *bind.FilterOpts, burner []common.Address) (*LinkTokenBurnAccessRevokedIterator, error) { + + var burnerRule []interface{} + for _, burnerItem := range burner { + burnerRule = append(burnerRule, burnerItem) + } + + logs, sub, err := _LinkToken.contract.FilterLogs(opts, "BurnAccessRevoked", burnerRule) + if err != nil { + return nil, err + } + return &LinkTokenBurnAccessRevokedIterator{contract: _LinkToken.contract, event: "BurnAccessRevoked", logs: logs, sub: sub}, nil +} + +func (_LinkToken *LinkTokenFilterer) WatchBurnAccessRevoked(opts *bind.WatchOpts, sink chan<- *LinkTokenBurnAccessRevoked, burner []common.Address) (event.Subscription, error) { + + var burnerRule []interface{} + for _, burnerItem := range burner { + burnerRule = append(burnerRule, burnerItem) + } + + logs, sub, err := _LinkToken.contract.WatchLogs(opts, "BurnAccessRevoked", burnerRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(LinkTokenBurnAccessRevoked) + if err := _LinkToken.contract.UnpackLog(event, "BurnAccessRevoked", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_LinkToken *LinkTokenFilterer) ParseBurnAccessRevoked(log types.Log) (*LinkTokenBurnAccessRevoked, error) { + event := new(LinkTokenBurnAccessRevoked) + if err := _LinkToken.contract.UnpackLog(event, "BurnAccessRevoked", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type LinkTokenMintAccessGrantedIterator struct { + Event *LinkTokenMintAccessGranted + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *LinkTokenMintAccessGrantedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(LinkTokenMintAccessGranted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(LinkTokenMintAccessGranted) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *LinkTokenMintAccessGrantedIterator) Error() error { + return it.fail +} + +func (it *LinkTokenMintAccessGrantedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type LinkTokenMintAccessGranted struct { + Minter common.Address + Raw types.Log +} + +func (_LinkToken *LinkTokenFilterer) FilterMintAccessGranted(opts *bind.FilterOpts, minter []common.Address) (*LinkTokenMintAccessGrantedIterator, error) { + + var minterRule []interface{} + for _, minterItem := range minter { + minterRule = append(minterRule, minterItem) + } + + logs, sub, err := _LinkToken.contract.FilterLogs(opts, "MintAccessGranted", minterRule) + if err != nil { + return nil, err + } + return &LinkTokenMintAccessGrantedIterator{contract: _LinkToken.contract, event: "MintAccessGranted", logs: logs, sub: sub}, nil +} + +func (_LinkToken *LinkTokenFilterer) WatchMintAccessGranted(opts *bind.WatchOpts, sink chan<- *LinkTokenMintAccessGranted, minter []common.Address) (event.Subscription, error) { + + var minterRule []interface{} + for _, minterItem := range minter { + minterRule = append(minterRule, minterItem) + } + + logs, sub, err := _LinkToken.contract.WatchLogs(opts, "MintAccessGranted", minterRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(LinkTokenMintAccessGranted) + if err := _LinkToken.contract.UnpackLog(event, "MintAccessGranted", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_LinkToken *LinkTokenFilterer) ParseMintAccessGranted(log types.Log) (*LinkTokenMintAccessGranted, error) { + event := new(LinkTokenMintAccessGranted) + if err := _LinkToken.contract.UnpackLog(event, "MintAccessGranted", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type LinkTokenMintAccessRevokedIterator struct { + Event *LinkTokenMintAccessRevoked + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *LinkTokenMintAccessRevokedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(LinkTokenMintAccessRevoked) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(LinkTokenMintAccessRevoked) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *LinkTokenMintAccessRevokedIterator) Error() error { + return it.fail +} + +func (it *LinkTokenMintAccessRevokedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type LinkTokenMintAccessRevoked struct { + Minter common.Address + Raw types.Log +} + +func (_LinkToken *LinkTokenFilterer) FilterMintAccessRevoked(opts *bind.FilterOpts, minter []common.Address) (*LinkTokenMintAccessRevokedIterator, error) { + + var minterRule []interface{} + for _, minterItem := range minter { + minterRule = append(minterRule, minterItem) + } + + logs, sub, err := _LinkToken.contract.FilterLogs(opts, "MintAccessRevoked", minterRule) + if err != nil { + return nil, err + } + return &LinkTokenMintAccessRevokedIterator{contract: _LinkToken.contract, event: "MintAccessRevoked", logs: logs, sub: sub}, nil +} + +func (_LinkToken *LinkTokenFilterer) WatchMintAccessRevoked(opts *bind.WatchOpts, sink chan<- *LinkTokenMintAccessRevoked, minter []common.Address) (event.Subscription, error) { + + var minterRule []interface{} + for _, minterItem := range minter { + minterRule = append(minterRule, minterItem) + } + + logs, sub, err := _LinkToken.contract.WatchLogs(opts, "MintAccessRevoked", minterRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(LinkTokenMintAccessRevoked) + if err := _LinkToken.contract.UnpackLog(event, "MintAccessRevoked", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_LinkToken *LinkTokenFilterer) ParseMintAccessRevoked(log types.Log) (*LinkTokenMintAccessRevoked, error) { + event := new(LinkTokenMintAccessRevoked) + if err := _LinkToken.contract.UnpackLog(event, "MintAccessRevoked", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type LinkTokenOwnershipTransferRequestedIterator struct { + Event *LinkTokenOwnershipTransferRequested + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *LinkTokenOwnershipTransferRequestedIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(LinkTokenOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(LinkTokenOwnershipTransferRequested) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *LinkTokenOwnershipTransferRequestedIterator) Error() error { + return it.fail +} + +func (it *LinkTokenOwnershipTransferRequestedIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type LinkTokenOwnershipTransferRequested struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_LinkToken *LinkTokenFilterer) FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*LinkTokenOwnershipTransferRequestedIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _LinkToken.contract.FilterLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return &LinkTokenOwnershipTransferRequestedIterator{contract: _LinkToken.contract, event: "OwnershipTransferRequested", logs: logs, sub: sub}, nil +} + +func (_LinkToken *LinkTokenFilterer) WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *LinkTokenOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _LinkToken.contract.WatchLogs(opts, "OwnershipTransferRequested", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(LinkTokenOwnershipTransferRequested) + if err := _LinkToken.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_LinkToken *LinkTokenFilterer) ParseOwnershipTransferRequested(log types.Log) (*LinkTokenOwnershipTransferRequested, error) { + event := new(LinkTokenOwnershipTransferRequested) + if err := _LinkToken.contract.UnpackLog(event, "OwnershipTransferRequested", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type LinkTokenOwnershipTransferredIterator struct { + Event *LinkTokenOwnershipTransferred + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *LinkTokenOwnershipTransferredIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(LinkTokenOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(LinkTokenOwnershipTransferred) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *LinkTokenOwnershipTransferredIterator) Error() error { + return it.fail +} + +func (it *LinkTokenOwnershipTransferredIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type LinkTokenOwnershipTransferred struct { + From common.Address + To common.Address + Raw types.Log +} + +func (_LinkToken *LinkTokenFilterer) FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*LinkTokenOwnershipTransferredIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _LinkToken.contract.FilterLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return &LinkTokenOwnershipTransferredIterator{contract: _LinkToken.contract, event: "OwnershipTransferred", logs: logs, sub: sub}, nil +} + +func (_LinkToken *LinkTokenFilterer) WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *LinkTokenOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _LinkToken.contract.WatchLogs(opts, "OwnershipTransferred", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(LinkTokenOwnershipTransferred) + if err := _LinkToken.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_LinkToken *LinkTokenFilterer) ParseOwnershipTransferred(log types.Log) (*LinkTokenOwnershipTransferred, error) { + event := new(LinkTokenOwnershipTransferred) + if err := _LinkToken.contract.UnpackLog(event, "OwnershipTransferred", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type LinkTokenTransferIterator struct { + Event *LinkTokenTransfer + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *LinkTokenTransferIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(LinkTokenTransfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(LinkTokenTransfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *LinkTokenTransferIterator) Error() error { + return it.fail +} + +func (it *LinkTokenTransferIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type LinkTokenTransfer struct { + From common.Address + To common.Address + Value *big.Int + Raw types.Log +} + +func (_LinkToken *LinkTokenFilterer) FilterTransfer(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*LinkTokenTransferIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _LinkToken.contract.FilterLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return &LinkTokenTransferIterator{contract: _LinkToken.contract, event: "Transfer", logs: logs, sub: sub}, nil +} + +func (_LinkToken *LinkTokenFilterer) WatchTransfer(opts *bind.WatchOpts, sink chan<- *LinkTokenTransfer, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _LinkToken.contract.WatchLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(LinkTokenTransfer) + if err := _LinkToken.contract.UnpackLog(event, "Transfer", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_LinkToken *LinkTokenFilterer) ParseTransfer(log types.Log) (*LinkTokenTransfer, error) { + event := new(LinkTokenTransfer) + if err := _LinkToken.contract.UnpackLog(event, "Transfer", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type LinkTokenTransfer0Iterator struct { + Event *LinkTokenTransfer0 + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *LinkTokenTransfer0Iterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(LinkTokenTransfer0) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(LinkTokenTransfer0) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *LinkTokenTransfer0Iterator) Error() error { + return it.fail +} + +func (it *LinkTokenTransfer0Iterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type LinkTokenTransfer0 struct { + From common.Address + To common.Address + Value *big.Int + Data []byte + Raw types.Log +} + +func (_LinkToken *LinkTokenFilterer) FilterTransfer0(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*LinkTokenTransfer0Iterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _LinkToken.contract.FilterLogs(opts, "Transfer0", fromRule, toRule) + if err != nil { + return nil, err + } + return &LinkTokenTransfer0Iterator{contract: _LinkToken.contract, event: "Transfer0", logs: logs, sub: sub}, nil +} + +func (_LinkToken *LinkTokenFilterer) WatchTransfer0(opts *bind.WatchOpts, sink chan<- *LinkTokenTransfer0, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _LinkToken.contract.WatchLogs(opts, "Transfer0", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(LinkTokenTransfer0) + if err := _LinkToken.contract.UnpackLog(event, "Transfer0", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_LinkToken *LinkTokenFilterer) ParseTransfer0(log types.Log) (*LinkTokenTransfer0, error) { + event := new(LinkTokenTransfer0) + if err := _LinkToken.contract.UnpackLog(event, "Transfer0", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +func (LinkTokenApproval) Topic() common.Hash { + return common.HexToHash("0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925") +} + +func (LinkTokenBurnAccessGranted) Topic() common.Hash { + return common.HexToHash("0x92308bb7573b2a3d17ddb868b39d8ebec433f3194421abc22d084f89658c9bad") +} + +func (LinkTokenBurnAccessRevoked) Topic() common.Hash { + return common.HexToHash("0x0a675452746933cefe3d74182e78db7afe57ba60eaa4234b5d85e9aa41b0610c") +} + +func (LinkTokenMintAccessGranted) Topic() common.Hash { + return common.HexToHash("0xe46fef8bbff1389d9010703cf8ebb363fb3daf5bf56edc27080b67bc8d9251ea") +} + +func (LinkTokenMintAccessRevoked) Topic() common.Hash { + return common.HexToHash("0xed998b960f6340d045f620c119730f7aa7995e7425c2401d3a5b64ff998a59e9") +} + +func (LinkTokenOwnershipTransferRequested) Topic() common.Hash { + return common.HexToHash("0xed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae1278") +} + +func (LinkTokenOwnershipTransferred) Topic() common.Hash { + return common.HexToHash("0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0") +} + +func (LinkTokenTransfer) Topic() common.Hash { + return common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef") +} + +func (LinkTokenTransfer0) Topic() common.Hash { + return common.HexToHash("0xe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c16") +} + +func (_LinkToken *LinkToken) Address() common.Address { + return _LinkToken.address +} + +type LinkTokenInterface interface { + Allowance(opts *bind.CallOpts, owner common.Address, spender common.Address) (*big.Int, error) + + BalanceOf(opts *bind.CallOpts, account common.Address) (*big.Int, error) + + Decimals(opts *bind.CallOpts) (uint8, error) + + GetBurners(opts *bind.CallOpts) ([]common.Address, error) + + GetMinters(opts *bind.CallOpts) ([]common.Address, error) + + IsBurner(opts *bind.CallOpts, burner common.Address) (bool, error) + + IsMinter(opts *bind.CallOpts, minter common.Address) (bool, error) + + MaxSupply(opts *bind.CallOpts) (*big.Int, error) + + Name(opts *bind.CallOpts) (string, error) + + Owner(opts *bind.CallOpts) (common.Address, error) + + SupportsInterface(opts *bind.CallOpts, interfaceId [4]byte) (bool, error) + + Symbol(opts *bind.CallOpts) (string, error) + + TotalSupply(opts *bind.CallOpts) (*big.Int, error) + + AcceptOwnership(opts *bind.TransactOpts) (*types.Transaction, error) + + Approve(opts *bind.TransactOpts, spender common.Address, amount *big.Int) (*types.Transaction, error) + + Burn(opts *bind.TransactOpts, amount *big.Int) (*types.Transaction, error) + + Burn0(opts *bind.TransactOpts, account common.Address, amount *big.Int) (*types.Transaction, error) + + BurnFrom(opts *bind.TransactOpts, account common.Address, amount *big.Int) (*types.Transaction, error) + + DecreaseAllowance(opts *bind.TransactOpts, spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) + + DecreaseApproval(opts *bind.TransactOpts, spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) + + GrantBurnRole(opts *bind.TransactOpts, burner common.Address) (*types.Transaction, error) + + GrantMintAndBurnRoles(opts *bind.TransactOpts, burnAndMinter common.Address) (*types.Transaction, error) + + GrantMintRole(opts *bind.TransactOpts, minter common.Address) (*types.Transaction, error) + + IncreaseAllowance(opts *bind.TransactOpts, spender common.Address, addedValue *big.Int) (*types.Transaction, error) + + IncreaseApproval(opts *bind.TransactOpts, spender common.Address, addedValue *big.Int) (*types.Transaction, error) + + Mint(opts *bind.TransactOpts, account common.Address, amount *big.Int) (*types.Transaction, error) + + RevokeBurnRole(opts *bind.TransactOpts, burner common.Address) (*types.Transaction, error) + + RevokeMintRole(opts *bind.TransactOpts, minter common.Address) (*types.Transaction, error) + + Transfer(opts *bind.TransactOpts, to common.Address, amount *big.Int) (*types.Transaction, error) + + TransferAndCall(opts *bind.TransactOpts, to common.Address, amount *big.Int, data []byte) (*types.Transaction, error) + + TransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, amount *big.Int) (*types.Transaction, error) + + TransferOwnership(opts *bind.TransactOpts, to common.Address) (*types.Transaction, error) + + FilterApproval(opts *bind.FilterOpts, owner []common.Address, spender []common.Address) (*LinkTokenApprovalIterator, error) + + WatchApproval(opts *bind.WatchOpts, sink chan<- *LinkTokenApproval, owner []common.Address, spender []common.Address) (event.Subscription, error) + + ParseApproval(log types.Log) (*LinkTokenApproval, error) + + FilterBurnAccessGranted(opts *bind.FilterOpts, burner []common.Address) (*LinkTokenBurnAccessGrantedIterator, error) + + WatchBurnAccessGranted(opts *bind.WatchOpts, sink chan<- *LinkTokenBurnAccessGranted, burner []common.Address) (event.Subscription, error) + + ParseBurnAccessGranted(log types.Log) (*LinkTokenBurnAccessGranted, error) + + FilterBurnAccessRevoked(opts *bind.FilterOpts, burner []common.Address) (*LinkTokenBurnAccessRevokedIterator, error) + + WatchBurnAccessRevoked(opts *bind.WatchOpts, sink chan<- *LinkTokenBurnAccessRevoked, burner []common.Address) (event.Subscription, error) + + ParseBurnAccessRevoked(log types.Log) (*LinkTokenBurnAccessRevoked, error) + + FilterMintAccessGranted(opts *bind.FilterOpts, minter []common.Address) (*LinkTokenMintAccessGrantedIterator, error) + + WatchMintAccessGranted(opts *bind.WatchOpts, sink chan<- *LinkTokenMintAccessGranted, minter []common.Address) (event.Subscription, error) + + ParseMintAccessGranted(log types.Log) (*LinkTokenMintAccessGranted, error) + + FilterMintAccessRevoked(opts *bind.FilterOpts, minter []common.Address) (*LinkTokenMintAccessRevokedIterator, error) + + WatchMintAccessRevoked(opts *bind.WatchOpts, sink chan<- *LinkTokenMintAccessRevoked, minter []common.Address) (event.Subscription, error) + + ParseMintAccessRevoked(log types.Log) (*LinkTokenMintAccessRevoked, error) + + FilterOwnershipTransferRequested(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*LinkTokenOwnershipTransferRequestedIterator, error) + + WatchOwnershipTransferRequested(opts *bind.WatchOpts, sink chan<- *LinkTokenOwnershipTransferRequested, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferRequested(log types.Log) (*LinkTokenOwnershipTransferRequested, error) + + FilterOwnershipTransferred(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*LinkTokenOwnershipTransferredIterator, error) + + WatchOwnershipTransferred(opts *bind.WatchOpts, sink chan<- *LinkTokenOwnershipTransferred, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseOwnershipTransferred(log types.Log) (*LinkTokenOwnershipTransferred, error) + + FilterTransfer(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*LinkTokenTransferIterator, error) + + WatchTransfer(opts *bind.WatchOpts, sink chan<- *LinkTokenTransfer, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseTransfer(log types.Log) (*LinkTokenTransfer, error) + + FilterTransfer0(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*LinkTokenTransfer0Iterator, error) + + WatchTransfer0(opts *bind.WatchOpts, sink chan<- *LinkTokenTransfer0, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseTransfer0(log types.Log) (*LinkTokenTransfer0, error) + + Address() common.Address +} diff --git a/seth/contracts/bind/link_token_interface/link_token_interface.go b/seth/contracts/bind/link_token_interface/link_token_interface.go new file mode 100644 index 000000000..60022990c --- /dev/null +++ b/seth/contracts/bind/link_token_interface/link_token_interface.go @@ -0,0 +1,701 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package link_token_interface + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +var LinkTokenMetaData = &bind.MetaData{ + ABI: "[{\"constant\":true,\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"name\":\"\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_from\",\"type\":\"address\"},{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"name\":\"\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"name\":\"\",\"type\":\"uint8\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"},{\"name\":\"_data\",\"type\":\"bytes\"}],\"name\":\"transferAndCall\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseApproval\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"name\":\"balance\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"name\":\"\",\"type\":\"string\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_to\",\"type\":\"address\"},{\"name\":\"_value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":false,\"inputs\":[{\"name\":\"_spender\",\"type\":\"address\"},{\"name\":\"_addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseApproval\",\"outputs\":[{\"name\":\"success\",\"type\":\"bool\"}],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"constant\":true,\"inputs\":[{\"name\":\"_owner\",\"type\":\"address\"},{\"name\":\"_spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"name\":\"remaining\",\"type\":\"uint256\"}],\"payable\":false,\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"payable\":false,\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"value\",\"type\":\"uint256\"},{\"indexed\":false,\"name\":\"data\",\"type\":\"bytes\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"}]", + Bin: "0x6060604052341561000f57600080fd5b5b600160a060020a03331660009081526001602052604090206b033b2e3c9fd0803ce800000090555b5b610c51806100486000396000f300606060405236156100b75763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde0381146100bc578063095ea7b31461014757806318160ddd1461017d57806323b872dd146101a2578063313ce567146101de5780634000aea014610207578063661884631461028057806370a08231146102b657806395d89b41146102e7578063a9059cbb14610372578063d73dd623146103a8578063dd62ed3e146103de575b600080fd5b34156100c757600080fd5b6100cf610415565b60405160208082528190810183818151815260200191508051906020019080838360005b8381101561010c5780820151818401525b6020016100f3565b50505050905090810190601f1680156101395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015257600080fd5b610169600160a060020a036004351660243561044c565b604051901515815260200160405180910390f35b341561018857600080fd5b610190610499565b60405190815260200160405180910390f35b34156101ad57600080fd5b610169600160a060020a03600435811690602435166044356104a9565b604051901515815260200160405180910390f35b34156101e957600080fd5b6101f16104f8565b60405160ff909116815260200160405180910390f35b341561021257600080fd5b61016960048035600160a060020a03169060248035919060649060443590810190830135806020601f820181900481020160405190810160405281815292919060208401838380828437509496506104fd95505050505050565b604051901515815260200160405180910390f35b341561028b57600080fd5b610169600160a060020a036004351660243561054c565b604051901515815260200160405180910390f35b34156102c157600080fd5b610190600160a060020a0360043516610648565b60405190815260200160405180910390f35b34156102f257600080fd5b6100cf610667565b60405160208082528190810183818151815260200191508051906020019080838360005b8381101561010c5780820151818401525b6020016100f3565b50505050905090810190601f1680156101395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561037d57600080fd5b610169600160a060020a036004351660243561069e565b604051901515815260200160405180910390f35b34156103b357600080fd5b610169600160a060020a03600435166024356106eb565b604051901515815260200160405180910390f35b34156103e957600080fd5b610190600160a060020a0360043581169060243516610790565b60405190815260200160405180910390f35b60408051908101604052600f81527f436861696e4c696e6b20546f6b656e0000000000000000000000000000000000602082015281565b600082600160a060020a03811615801590610479575030600160a060020a031681600160a060020a031614155b151561048457600080fd5b61048e84846107bd565b91505b5b5092915050565b6b033b2e3c9fd0803ce800000081565b600082600160a060020a038116158015906104d6575030600160a060020a031681600160a060020a031614155b15156104e157600080fd5b6104ec85858561082a565b91505b5b509392505050565b601281565b600083600160a060020a0381161580159061052a575030600160a060020a031681600160a060020a031614155b151561053557600080fd5b6104ec85858561093c565b91505b5b509392505050565b600160a060020a033381166000908152600260209081526040808320938616835292905290812054808311156105a957600160a060020a0333811660009081526002602090815260408083209388168352929052908120556105e0565b6105b9818463ffffffff610a2316565b600160a060020a033381166000908152600260209081526040808320938916835292905220555b600160a060020a0333811660008181526002602090815260408083209489168084529490915290819020547f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925915190815260200160405180910390a3600191505b5092915050565b600160a060020a0381166000908152600160205260409020545b919050565b60408051908101604052600481527f4c494e4b00000000000000000000000000000000000000000000000000000000602082015281565b600082600160a060020a038116158015906106cb575030600160a060020a031681600160a060020a031614155b15156106d657600080fd5b61048e8484610a3a565b91505b5b5092915050565b600160a060020a033381166000908152600260209081526040808320938616835292905290812054610723908363ffffffff610afa16565b600160a060020a0333811660008181526002602090815260408083209489168084529490915290819020849055919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591905190815260200160405180910390a35060015b92915050565b600160a060020a038083166000908152600260209081526040808320938516835292905220545b92915050565b600160a060020a03338116600081815260026020908152604080832094871680845294909152808220859055909291907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259085905190815260200160405180910390a35060015b92915050565b600160a060020a03808416600081815260026020908152604080832033909516835293815283822054928252600190529182205461086e908463ffffffff610a2316565b600160a060020a0380871660009081526001602052604080822093909355908616815220546108a3908463ffffffff610afa16565b600160a060020a0385166000908152600160205260409020556108cc818463ffffffff610a2316565b600160a060020a03808716600081815260026020908152604080832033861684529091529081902093909355908616917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9086905190815260200160405180910390a3600191505b509392505050565b60006109488484610a3a565b5083600160a060020a031633600160a060020a03167fe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c16858560405182815260406020820181815290820183818151815260200191508051906020019080838360005b838110156109c35780820151818401525b6020016109aa565b50505050905090810190601f1680156109f05780820380516001836020036101000a031916815260200191505b50935050505060405180910390a3610a0784610b14565b15610a1757610a17848484610b23565b5b5060015b9392505050565b600082821115610a2f57fe5b508082035b92915050565b600160a060020a033316600090815260016020526040812054610a63908363ffffffff610a2316565b600160a060020a033381166000908152600160205260408082209390935590851681522054610a98908363ffffffff610afa16565b600160a060020a0380851660008181526001602052604090819020939093559133909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9085905190815260200160405180910390a35060015b92915050565b600082820183811015610b0957fe5b8091505b5092915050565b6000813b908111905b50919050565b82600160a060020a03811663a4c0ed363385856040518463ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018084600160a060020a0316600160a060020a0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b83811015610bbd5780820151818401525b602001610ba4565b50505050905090810190601f168015610bea5780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b1515610c0a57600080fd5b6102c65a03f11515610c1b57600080fd5b5050505b505050505600a165627a7a72305820c5f438ff94e5ddaf2058efa0019e246c636c37a622e04bb67827c7374acad8d60029", +} + +var LinkTokenABI = LinkTokenMetaData.ABI + +var LinkTokenBin = LinkTokenMetaData.Bin + +func DeployLinkToken(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *LinkToken, error) { + parsed, err := LinkTokenMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(LinkTokenBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &LinkToken{address: address, abi: *parsed, LinkTokenCaller: LinkTokenCaller{contract: contract}, LinkTokenTransactor: LinkTokenTransactor{contract: contract}, LinkTokenFilterer: LinkTokenFilterer{contract: contract}}, nil +} + +type LinkToken struct { + address common.Address + abi abi.ABI + LinkTokenCaller + LinkTokenTransactor + LinkTokenFilterer +} + +type LinkTokenCaller struct { + contract *bind.BoundContract +} + +type LinkTokenTransactor struct { + contract *bind.BoundContract +} + +type LinkTokenFilterer struct { + contract *bind.BoundContract +} + +type LinkTokenSession struct { + Contract *LinkToken + CallOpts bind.CallOpts + TransactOpts bind.TransactOpts +} + +type LinkTokenCallerSession struct { + Contract *LinkTokenCaller + CallOpts bind.CallOpts +} + +type LinkTokenTransactorSession struct { + Contract *LinkTokenTransactor + TransactOpts bind.TransactOpts +} + +type LinkTokenRaw struct { + Contract *LinkToken +} + +type LinkTokenCallerRaw struct { + Contract *LinkTokenCaller +} + +type LinkTokenTransactorRaw struct { + Contract *LinkTokenTransactor +} + +func NewLinkToken(address common.Address, backend bind.ContractBackend) (*LinkToken, error) { + abi, err := abi.JSON(strings.NewReader(LinkTokenABI)) + if err != nil { + return nil, err + } + contract, err := bindLinkToken(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &LinkToken{address: address, abi: abi, LinkTokenCaller: LinkTokenCaller{contract: contract}, LinkTokenTransactor: LinkTokenTransactor{contract: contract}, LinkTokenFilterer: LinkTokenFilterer{contract: contract}}, nil +} + +func NewLinkTokenCaller(address common.Address, caller bind.ContractCaller) (*LinkTokenCaller, error) { + contract, err := bindLinkToken(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &LinkTokenCaller{contract: contract}, nil +} + +func NewLinkTokenTransactor(address common.Address, transactor bind.ContractTransactor) (*LinkTokenTransactor, error) { + contract, err := bindLinkToken(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &LinkTokenTransactor{contract: contract}, nil +} + +func NewLinkTokenFilterer(address common.Address, filterer bind.ContractFilterer) (*LinkTokenFilterer, error) { + contract, err := bindLinkToken(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &LinkTokenFilterer{contract: contract}, nil +} + +func bindLinkToken(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := LinkTokenMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +func (_LinkToken *LinkTokenRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _LinkToken.Contract.LinkTokenCaller.contract.Call(opts, result, method, params...) +} + +func (_LinkToken *LinkTokenRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _LinkToken.Contract.LinkTokenTransactor.contract.Transfer(opts) +} + +func (_LinkToken *LinkTokenRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _LinkToken.Contract.LinkTokenTransactor.contract.Transact(opts, method, params...) +} + +func (_LinkToken *LinkTokenCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _LinkToken.Contract.contract.Call(opts, result, method, params...) +} + +func (_LinkToken *LinkTokenTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _LinkToken.Contract.contract.Transfer(opts) +} + +func (_LinkToken *LinkTokenTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _LinkToken.Contract.contract.Transact(opts, method, params...) +} + +func (_LinkToken *LinkTokenCaller) Allowance(opts *bind.CallOpts, _owner common.Address, _spender common.Address) (*big.Int, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "allowance", _owner, _spender) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) Allowance(_owner common.Address, _spender common.Address) (*big.Int, error) { + return _LinkToken.Contract.Allowance(&_LinkToken.CallOpts, _owner, _spender) +} + +func (_LinkToken *LinkTokenCallerSession) Allowance(_owner common.Address, _spender common.Address) (*big.Int, error) { + return _LinkToken.Contract.Allowance(&_LinkToken.CallOpts, _owner, _spender) +} + +func (_LinkToken *LinkTokenCaller) BalanceOf(opts *bind.CallOpts, _owner common.Address) (*big.Int, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "balanceOf", _owner) + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) BalanceOf(_owner common.Address) (*big.Int, error) { + return _LinkToken.Contract.BalanceOf(&_LinkToken.CallOpts, _owner) +} + +func (_LinkToken *LinkTokenCallerSession) BalanceOf(_owner common.Address) (*big.Int, error) { + return _LinkToken.Contract.BalanceOf(&_LinkToken.CallOpts, _owner) +} + +func (_LinkToken *LinkTokenCaller) Decimals(opts *bind.CallOpts) (uint8, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "decimals") + + if err != nil { + return *new(uint8), err + } + + out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) Decimals() (uint8, error) { + return _LinkToken.Contract.Decimals(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCallerSession) Decimals() (uint8, error) { + return _LinkToken.Contract.Decimals(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCaller) Name(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "name") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) Name() (string, error) { + return _LinkToken.Contract.Name(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCallerSession) Name() (string, error) { + return _LinkToken.Contract.Name(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCaller) Symbol(opts *bind.CallOpts) (string, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "symbol") + + if err != nil { + return *new(string), err + } + + out0 := *abi.ConvertType(out[0], new(string)).(*string) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) Symbol() (string, error) { + return _LinkToken.Contract.Symbol(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCallerSession) Symbol() (string, error) { + return _LinkToken.Contract.Symbol(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCaller) TotalSupply(opts *bind.CallOpts) (*big.Int, error) { + var out []interface{} + err := _LinkToken.contract.Call(opts, &out, "totalSupply") + + if err != nil { + return *new(*big.Int), err + } + + out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) + + return out0, err + +} + +func (_LinkToken *LinkTokenSession) TotalSupply() (*big.Int, error) { + return _LinkToken.Contract.TotalSupply(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenCallerSession) TotalSupply() (*big.Int, error) { + return _LinkToken.Contract.TotalSupply(&_LinkToken.CallOpts) +} + +func (_LinkToken *LinkTokenTransactor) Approve(opts *bind.TransactOpts, _spender common.Address, _value *big.Int) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "approve", _spender, _value) +} + +func (_LinkToken *LinkTokenSession) Approve(_spender common.Address, _value *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.Approve(&_LinkToken.TransactOpts, _spender, _value) +} + +func (_LinkToken *LinkTokenTransactorSession) Approve(_spender common.Address, _value *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.Approve(&_LinkToken.TransactOpts, _spender, _value) +} + +func (_LinkToken *LinkTokenTransactor) DecreaseApproval(opts *bind.TransactOpts, _spender common.Address, _subtractedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "decreaseApproval", _spender, _subtractedValue) +} + +func (_LinkToken *LinkTokenSession) DecreaseApproval(_spender common.Address, _subtractedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.DecreaseApproval(&_LinkToken.TransactOpts, _spender, _subtractedValue) +} + +func (_LinkToken *LinkTokenTransactorSession) DecreaseApproval(_spender common.Address, _subtractedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.DecreaseApproval(&_LinkToken.TransactOpts, _spender, _subtractedValue) +} + +func (_LinkToken *LinkTokenTransactor) IncreaseApproval(opts *bind.TransactOpts, _spender common.Address, _addedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "increaseApproval", _spender, _addedValue) +} + +func (_LinkToken *LinkTokenSession) IncreaseApproval(_spender common.Address, _addedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.IncreaseApproval(&_LinkToken.TransactOpts, _spender, _addedValue) +} + +func (_LinkToken *LinkTokenTransactorSession) IncreaseApproval(_spender common.Address, _addedValue *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.IncreaseApproval(&_LinkToken.TransactOpts, _spender, _addedValue) +} + +func (_LinkToken *LinkTokenTransactor) Transfer(opts *bind.TransactOpts, _to common.Address, _value *big.Int) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "transfer", _to, _value) +} + +func (_LinkToken *LinkTokenSession) Transfer(_to common.Address, _value *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.Transfer(&_LinkToken.TransactOpts, _to, _value) +} + +func (_LinkToken *LinkTokenTransactorSession) Transfer(_to common.Address, _value *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.Transfer(&_LinkToken.TransactOpts, _to, _value) +} + +func (_LinkToken *LinkTokenTransactor) TransferAndCall(opts *bind.TransactOpts, _to common.Address, _value *big.Int, _data []byte) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "transferAndCall", _to, _value, _data) +} + +func (_LinkToken *LinkTokenSession) TransferAndCall(_to common.Address, _value *big.Int, _data []byte) (*types.Transaction, error) { + return _LinkToken.Contract.TransferAndCall(&_LinkToken.TransactOpts, _to, _value, _data) +} + +func (_LinkToken *LinkTokenTransactorSession) TransferAndCall(_to common.Address, _value *big.Int, _data []byte) (*types.Transaction, error) { + return _LinkToken.Contract.TransferAndCall(&_LinkToken.TransactOpts, _to, _value, _data) +} + +func (_LinkToken *LinkTokenTransactor) TransferFrom(opts *bind.TransactOpts, _from common.Address, _to common.Address, _value *big.Int) (*types.Transaction, error) { + return _LinkToken.contract.Transact(opts, "transferFrom", _from, _to, _value) +} + +func (_LinkToken *LinkTokenSession) TransferFrom(_from common.Address, _to common.Address, _value *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.TransferFrom(&_LinkToken.TransactOpts, _from, _to, _value) +} + +func (_LinkToken *LinkTokenTransactorSession) TransferFrom(_from common.Address, _to common.Address, _value *big.Int) (*types.Transaction, error) { + return _LinkToken.Contract.TransferFrom(&_LinkToken.TransactOpts, _from, _to, _value) +} + +type LinkTokenApprovalIterator struct { + Event *LinkTokenApproval + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *LinkTokenApprovalIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(LinkTokenApproval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(LinkTokenApproval) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *LinkTokenApprovalIterator) Error() error { + return it.fail +} + +func (it *LinkTokenApprovalIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type LinkTokenApproval struct { + Owner common.Address + Spender common.Address + Value *big.Int + Raw types.Log +} + +func (_LinkToken *LinkTokenFilterer) FilterApproval(opts *bind.FilterOpts, owner []common.Address, spender []common.Address) (*LinkTokenApprovalIterator, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _LinkToken.contract.FilterLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return &LinkTokenApprovalIterator{contract: _LinkToken.contract, event: "Approval", logs: logs, sub: sub}, nil +} + +func (_LinkToken *LinkTokenFilterer) WatchApproval(opts *bind.WatchOpts, sink chan<- *LinkTokenApproval, owner []common.Address, spender []common.Address) (event.Subscription, error) { + + var ownerRule []interface{} + for _, ownerItem := range owner { + ownerRule = append(ownerRule, ownerItem) + } + var spenderRule []interface{} + for _, spenderItem := range spender { + spenderRule = append(spenderRule, spenderItem) + } + + logs, sub, err := _LinkToken.contract.WatchLogs(opts, "Approval", ownerRule, spenderRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(LinkTokenApproval) + if err := _LinkToken.contract.UnpackLog(event, "Approval", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_LinkToken *LinkTokenFilterer) ParseApproval(log types.Log) (*LinkTokenApproval, error) { + event := new(LinkTokenApproval) + if err := _LinkToken.contract.UnpackLog(event, "Approval", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +type LinkTokenTransferIterator struct { + Event *LinkTokenTransfer + + contract *bind.BoundContract + event string + + logs chan types.Log + sub ethereum.Subscription + done bool + fail error +} + +func (it *LinkTokenTransferIterator) Next() bool { + + if it.fail != nil { + return false + } + + if it.done { + select { + case log := <-it.logs: + it.Event = new(LinkTokenTransfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + + select { + case log := <-it.logs: + it.Event = new(LinkTokenTransfer) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +func (it *LinkTokenTransferIterator) Error() error { + return it.fail +} + +func (it *LinkTokenTransferIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +type LinkTokenTransfer struct { + From common.Address + To common.Address + Value *big.Int + Data []byte + Raw types.Log +} + +func (_LinkToken *LinkTokenFilterer) FilterTransfer(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*LinkTokenTransferIterator, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _LinkToken.contract.FilterLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return &LinkTokenTransferIterator{contract: _LinkToken.contract, event: "Transfer", logs: logs, sub: sub}, nil +} + +func (_LinkToken *LinkTokenFilterer) WatchTransfer(opts *bind.WatchOpts, sink chan<- *LinkTokenTransfer, from []common.Address, to []common.Address) (event.Subscription, error) { + + var fromRule []interface{} + for _, fromItem := range from { + fromRule = append(fromRule, fromItem) + } + var toRule []interface{} + for _, toItem := range to { + toRule = append(toRule, toItem) + } + + logs, sub, err := _LinkToken.contract.WatchLogs(opts, "Transfer", fromRule, toRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + + event := new(LinkTokenTransfer) + if err := _LinkToken.contract.UnpackLog(event, "Transfer", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +func (_LinkToken *LinkTokenFilterer) ParseTransfer(log types.Log) (*LinkTokenTransfer, error) { + event := new(LinkTokenTransfer) + if err := _LinkToken.contract.UnpackLog(event, "Transfer", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +func (LinkTokenApproval) Topic() common.Hash { + return common.HexToHash("0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925") +} + +func (LinkTokenTransfer) Topic() common.Hash { + return common.HexToHash("0xe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c16") +} + +func (_LinkToken *LinkToken) Address() common.Address { + return _LinkToken.address +} + +type LinkTokenInterface interface { + Allowance(opts *bind.CallOpts, _owner common.Address, _spender common.Address) (*big.Int, error) + + BalanceOf(opts *bind.CallOpts, _owner common.Address) (*big.Int, error) + + Decimals(opts *bind.CallOpts) (uint8, error) + + Name(opts *bind.CallOpts) (string, error) + + Symbol(opts *bind.CallOpts) (string, error) + + TotalSupply(opts *bind.CallOpts) (*big.Int, error) + + Approve(opts *bind.TransactOpts, _spender common.Address, _value *big.Int) (*types.Transaction, error) + + DecreaseApproval(opts *bind.TransactOpts, _spender common.Address, _subtractedValue *big.Int) (*types.Transaction, error) + + IncreaseApproval(opts *bind.TransactOpts, _spender common.Address, _addedValue *big.Int) (*types.Transaction, error) + + Transfer(opts *bind.TransactOpts, _to common.Address, _value *big.Int) (*types.Transaction, error) + + TransferAndCall(opts *bind.TransactOpts, _to common.Address, _value *big.Int, _data []byte) (*types.Transaction, error) + + TransferFrom(opts *bind.TransactOpts, _from common.Address, _to common.Address, _value *big.Int) (*types.Transaction, error) + + FilterApproval(opts *bind.FilterOpts, owner []common.Address, spender []common.Address) (*LinkTokenApprovalIterator, error) + + WatchApproval(opts *bind.WatchOpts, sink chan<- *LinkTokenApproval, owner []common.Address, spender []common.Address) (event.Subscription, error) + + ParseApproval(log types.Log) (*LinkTokenApproval, error) + + FilterTransfer(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*LinkTokenTransferIterator, error) + + WatchTransfer(opts *bind.WatchOpts, sink chan<- *LinkTokenTransfer, from []common.Address, to []common.Address) (event.Subscription, error) + + ParseTransfer(log types.Log) (*LinkTokenTransfer, error) + + Address() common.Address +} diff --git a/seth/contracts/bind/sub/NetworkDebugSubContract.go b/seth/contracts/bind/sub/NetworkDebugSubContract.go new file mode 100644 index 000000000..8babcc2ea --- /dev/null +++ b/seth/contracts/bind/sub/NetworkDebugSubContract.go @@ -0,0 +1,1168 @@ +// Code generated - DO NOT EDIT. +// This file is a generated binding and any manual changes will be lost. + +package network_debug_sub_contract + +import ( + "errors" + "math/big" + "strings" + + ethereum "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/event" +) + +// Reference imports to suppress errors if they are not otherwise used. +var ( + _ = errors.New + _ = big.NewInt + _ = strings.NewReader + _ = ethereum.NotFound + _ = bind.Bind + _ = common.Big1 + _ = types.BloomLookup + _ = event.NewSubscription + _ = abi.ConvertType +) + +// NetworkDebugSubContractAccount is an auto generated low-level Go binding around an user-defined struct. +type NetworkDebugSubContractAccount struct { + Name string + Balance uint64 + DailyLimit *big.Int +} + +// NetworkDebugSubContractMetaData contains all meta data concerning the NetworkDebugSubContract contract. +var NetworkDebugSubContractMetaData = &bind.MetaData{ + ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"available\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"required\",\"type\":\"uint256\"}],\"name\":\"CustomErr\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"NoIndexEvent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":false,\"internalType\":\"string\",\"name\":\"str\",\"type\":\"string\"}],\"name\":\"NoIndexEventString\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"components\":[{\"internalType\":\"string\",\"name\":\"name\",\"type\":\"string\"},{\"internalType\":\"uint64\",\"name\":\"balance\",\"type\":\"uint64\"},{\"internalType\":\"uint256\",\"name\":\"dailyLimit\",\"type\":\"uint256\"}],\"indexed\":false,\"internalType\":\"structNetworkDebugSubContract.Account\",\"name\":\"a\",\"type\":\"tuple\"}],\"name\":\"NoIndexStructEvent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"a\",\"type\":\"uint256\"}],\"name\":\"OneIndexEvent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"roundId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"startedBy\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"startedAt\",\"type\":\"uint256\"}],\"name\":\"ThreeIndexEvent\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"uint256\",\"name\":\"roundId\",\"type\":\"uint256\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"startedBy\",\"type\":\"address\"}],\"name\":\"TwoIndexEvent\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"x\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"y\",\"type\":\"uint256\"}],\"name\":\"alwaysRevertsCustomError\",\"outputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"pay\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"x\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"y\",\"type\":\"int256\"}],\"name\":\"trace\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"x\",\"type\":\"int256\"}],\"name\":\"traceOneInt\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"r\",\"type\":\"int256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"int256\",\"name\":\"x\",\"type\":\"int256\"},{\"internalType\":\"int256\",\"name\":\"y\",\"type\":\"int256\"}],\"name\":\"traceWithCallback\",\"outputs\":[{\"internalType\":\"int256\",\"name\":\"\",\"type\":\"int256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", + Bin: "0x608060405234801561001057600080fd5b506105f4806100206000396000f3fe60806040526004361061004a5760003560e01c8063047c44251461004f57806311abb0021461008c5780631b9265b8146100b55780633e41f135146100bf578063fa8fca7a146100fc575b600080fd5b34801561005b57600080fd5b5061007660048036038101906100719190610368565b610139565b60405161008391906103a4565b60405180910390f35b34801561009857600080fd5b506100b360048036038101906100ae91906103f5565b610186565b005b6100bd6101c5565b005b3480156100cb57600080fd5b506100e660048036038101906100e19190610435565b6101c7565b6040516100f391906103a4565b60405180910390f35b34801561010857600080fd5b50610123600480360381019061011e9190610435565b610230565b60405161013091906103a4565b60405180910390f35b60007f33bc9bae48dbe1e057f264b3fc6a1dacdcceacb3ba28d937231c70e068a02f1c3360405161016a91906104b6565b60405180910390a160038261017f9190610500565b9050919050565b81816040517f4a2eaf7e0000000000000000000000000000000000000000000000000000000081526004016101bc929190610553565b60405180910390fd5b565b60006002826101d69190610500565b91503373ffffffffffffffffffffffffffffffffffffffff16827f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a381836102289190610500565b905092915050565b60003373ffffffffffffffffffffffffffffffffffffffff16827f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a360003373ffffffffffffffffffffffffffffffffffffffff1663fbcb8d07846040518263ffffffff1660e01b81526004016102b191906103a4565b6020604051808303816000875af11580156102d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906102f49190610591565b9050807feace1be0b97ec11f959499c07b9f60f0cc47bf610b28fda8fb0e970339cf3b3560405160405180910390a28091505092915050565b600080fd5b6000819050919050565b61034581610332565b811461035057600080fd5b50565b6000813590506103628161033c565b92915050565b60006020828403121561037e5761037d61032d565b5b600061038c84828501610353565b91505092915050565b61039e81610332565b82525050565b60006020820190506103b96000830184610395565b92915050565b6000819050919050565b6103d2816103bf565b81146103dd57600080fd5b50565b6000813590506103ef816103c9565b92915050565b6000806040838503121561040c5761040b61032d565b5b600061041a858286016103e0565b925050602061042b858286016103e0565b9150509250929050565b6000806040838503121561044c5761044b61032d565b5b600061045a85828601610353565b925050602061046b85828601610353565b9150509250929050565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b60006104a082610475565b9050919050565b6104b081610495565b82525050565b60006020820190506104cb60008301846104a7565b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600061050b82610332565b915061051683610332565b92508282019050828112156000831216838212600084121516171561053e5761053d6104d1565b5b92915050565b61054d816103bf565b82525050565b60006040820190506105686000830185610544565b6105756020830184610544565b9392505050565b60008151905061058b8161033c565b92915050565b6000602082840312156105a7576105a661032d565b5b60006105b58482850161057c565b9150509291505056fea2646970667358221220baf1ca0ebe5c3f70cf36de50eba34b796fa2141e3773a505c3199b335b0023c564736f6c63430008130033", +} + +// NetworkDebugSubContractABI is the input ABI used to generate the binding from. +// Deprecated: Use NetworkDebugSubContractMetaData.ABI instead. +var NetworkDebugSubContractABI = NetworkDebugSubContractMetaData.ABI + +// NetworkDebugSubContractBin is the compiled bytecode used for deploying new contracts. +// Deprecated: Use NetworkDebugSubContractMetaData.Bin instead. +var NetworkDebugSubContractBin = NetworkDebugSubContractMetaData.Bin + +// DeployNetworkDebugSubContract deploys a new Ethereum contract, binding an instance of NetworkDebugSubContract to it. +func DeployNetworkDebugSubContract(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *NetworkDebugSubContract, error) { + parsed, err := NetworkDebugSubContractMetaData.GetAbi() + if err != nil { + return common.Address{}, nil, nil, err + } + if parsed == nil { + return common.Address{}, nil, nil, errors.New("GetABI returned nil") + } + + address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(NetworkDebugSubContractBin), backend) + if err != nil { + return common.Address{}, nil, nil, err + } + return address, tx, &NetworkDebugSubContract{NetworkDebugSubContractCaller: NetworkDebugSubContractCaller{contract: contract}, NetworkDebugSubContractTransactor: NetworkDebugSubContractTransactor{contract: contract}, NetworkDebugSubContractFilterer: NetworkDebugSubContractFilterer{contract: contract}}, nil +} + +// NetworkDebugSubContract is an auto generated Go binding around an Ethereum contract. +type NetworkDebugSubContract struct { + NetworkDebugSubContractCaller // Read-only binding to the contract + NetworkDebugSubContractTransactor // Write-only binding to the contract + NetworkDebugSubContractFilterer // Log filterer for contract events +} + +// NetworkDebugSubContractCaller is an auto generated read-only Go binding around an Ethereum contract. +type NetworkDebugSubContractCaller struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// NetworkDebugSubContractTransactor is an auto generated write-only Go binding around an Ethereum contract. +type NetworkDebugSubContractTransactor struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// NetworkDebugSubContractFilterer is an auto generated log filtering Go binding around an Ethereum contract events. +type NetworkDebugSubContractFilterer struct { + contract *bind.BoundContract // Generic contract wrapper for the low level calls +} + +// NetworkDebugSubContractSession is an auto generated Go binding around an Ethereum contract, +// with pre-set call and transact options. +type NetworkDebugSubContractSession struct { + Contract *NetworkDebugSubContract // Generic contract binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// NetworkDebugSubContractCallerSession is an auto generated read-only Go binding around an Ethereum contract, +// with pre-set call options. +type NetworkDebugSubContractCallerSession struct { + Contract *NetworkDebugSubContractCaller // Generic contract caller binding to set the session for + CallOpts bind.CallOpts // Call options to use throughout this session +} + +// NetworkDebugSubContractTransactorSession is an auto generated write-only Go binding around an Ethereum contract, +// with pre-set transact options. +type NetworkDebugSubContractTransactorSession struct { + Contract *NetworkDebugSubContractTransactor // Generic contract transactor binding to set the session for + TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session +} + +// NetworkDebugSubContractRaw is an auto generated low-level Go binding around an Ethereum contract. +type NetworkDebugSubContractRaw struct { + Contract *NetworkDebugSubContract // Generic contract binding to access the raw methods on +} + +// NetworkDebugSubContractCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. +type NetworkDebugSubContractCallerRaw struct { + Contract *NetworkDebugSubContractCaller // Generic read-only contract binding to access the raw methods on +} + +// NetworkDebugSubContractTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. +type NetworkDebugSubContractTransactorRaw struct { + Contract *NetworkDebugSubContractTransactor // Generic write-only contract binding to access the raw methods on +} + +// NewNetworkDebugSubContract creates a new instance of NetworkDebugSubContract, bound to a specific deployed contract. +func NewNetworkDebugSubContract(address common.Address, backend bind.ContractBackend) (*NetworkDebugSubContract, error) { + contract, err := bindNetworkDebugSubContract(address, backend, backend, backend) + if err != nil { + return nil, err + } + return &NetworkDebugSubContract{NetworkDebugSubContractCaller: NetworkDebugSubContractCaller{contract: contract}, NetworkDebugSubContractTransactor: NetworkDebugSubContractTransactor{contract: contract}, NetworkDebugSubContractFilterer: NetworkDebugSubContractFilterer{contract: contract}}, nil +} + +// NewNetworkDebugSubContractCaller creates a new read-only instance of NetworkDebugSubContract, bound to a specific deployed contract. +func NewNetworkDebugSubContractCaller(address common.Address, caller bind.ContractCaller) (*NetworkDebugSubContractCaller, error) { + contract, err := bindNetworkDebugSubContract(address, caller, nil, nil) + if err != nil { + return nil, err + } + return &NetworkDebugSubContractCaller{contract: contract}, nil +} + +// NewNetworkDebugSubContractTransactor creates a new write-only instance of NetworkDebugSubContract, bound to a specific deployed contract. +func NewNetworkDebugSubContractTransactor(address common.Address, transactor bind.ContractTransactor) (*NetworkDebugSubContractTransactor, error) { + contract, err := bindNetworkDebugSubContract(address, nil, transactor, nil) + if err != nil { + return nil, err + } + return &NetworkDebugSubContractTransactor{contract: contract}, nil +} + +// NewNetworkDebugSubContractFilterer creates a new log filterer instance of NetworkDebugSubContract, bound to a specific deployed contract. +func NewNetworkDebugSubContractFilterer(address common.Address, filterer bind.ContractFilterer) (*NetworkDebugSubContractFilterer, error) { + contract, err := bindNetworkDebugSubContract(address, nil, nil, filterer) + if err != nil { + return nil, err + } + return &NetworkDebugSubContractFilterer{contract: contract}, nil +} + +// bindNetworkDebugSubContract binds a generic wrapper to an already deployed contract. +func bindNetworkDebugSubContract(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { + parsed, err := NetworkDebugSubContractMetaData.GetAbi() + if err != nil { + return nil, err + } + return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_NetworkDebugSubContract *NetworkDebugSubContractRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _NetworkDebugSubContract.Contract.NetworkDebugSubContractCaller.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_NetworkDebugSubContract *NetworkDebugSubContractRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugSubContract.Contract.NetworkDebugSubContractTransactor.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_NetworkDebugSubContract *NetworkDebugSubContractRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _NetworkDebugSubContract.Contract.NetworkDebugSubContractTransactor.contract.Transact(opts, method, params...) +} + +// Call invokes the (constant) contract method with params as input values and +// sets the output to result. The result type might be a single field for simple +// returns, a slice of interfaces for anonymous returns and a struct for named +// returns. +func (_NetworkDebugSubContract *NetworkDebugSubContractCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { + return _NetworkDebugSubContract.Contract.contract.Call(opts, result, method, params...) +} + +// Transfer initiates a plain transaction to move funds to the contract, calling +// its default method if one is available. +func (_NetworkDebugSubContract *NetworkDebugSubContractTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugSubContract.Contract.contract.Transfer(opts) +} + +// Transact invokes the (paid) contract method with params as input values. +func (_NetworkDebugSubContract *NetworkDebugSubContractTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { + return _NetworkDebugSubContract.Contract.contract.Transact(opts, method, params...) +} + +// AlwaysRevertsCustomError is a paid mutator transaction binding the contract method 0x11abb002. +// +// Solidity: function alwaysRevertsCustomError(uint256 x, uint256 y) returns() +func (_NetworkDebugSubContract *NetworkDebugSubContractTransactor) AlwaysRevertsCustomError(opts *bind.TransactOpts, x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugSubContract.contract.Transact(opts, "alwaysRevertsCustomError", x, y) +} + +// AlwaysRevertsCustomError is a paid mutator transaction binding the contract method 0x11abb002. +// +// Solidity: function alwaysRevertsCustomError(uint256 x, uint256 y) returns() +func (_NetworkDebugSubContract *NetworkDebugSubContractSession) AlwaysRevertsCustomError(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugSubContract.Contract.AlwaysRevertsCustomError(&_NetworkDebugSubContract.TransactOpts, x, y) +} + +// AlwaysRevertsCustomError is a paid mutator transaction binding the contract method 0x11abb002. +// +// Solidity: function alwaysRevertsCustomError(uint256 x, uint256 y) returns() +func (_NetworkDebugSubContract *NetworkDebugSubContractTransactorSession) AlwaysRevertsCustomError(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugSubContract.Contract.AlwaysRevertsCustomError(&_NetworkDebugSubContract.TransactOpts, x, y) +} + +// Pay is a paid mutator transaction binding the contract method 0x1b9265b8. +// +// Solidity: function pay() payable returns() +func (_NetworkDebugSubContract *NetworkDebugSubContractTransactor) Pay(opts *bind.TransactOpts) (*types.Transaction, error) { + return _NetworkDebugSubContract.contract.Transact(opts, "pay") +} + +// Pay is a paid mutator transaction binding the contract method 0x1b9265b8. +// +// Solidity: function pay() payable returns() +func (_NetworkDebugSubContract *NetworkDebugSubContractSession) Pay() (*types.Transaction, error) { + return _NetworkDebugSubContract.Contract.Pay(&_NetworkDebugSubContract.TransactOpts) +} + +// Pay is a paid mutator transaction binding the contract method 0x1b9265b8. +// +// Solidity: function pay() payable returns() +func (_NetworkDebugSubContract *NetworkDebugSubContractTransactorSession) Pay() (*types.Transaction, error) { + return _NetworkDebugSubContract.Contract.Pay(&_NetworkDebugSubContract.TransactOpts) +} + +// Trace is a paid mutator transaction binding the contract method 0x3e41f135. +// +// Solidity: function trace(int256 x, int256 y) returns(int256) +func (_NetworkDebugSubContract *NetworkDebugSubContractTransactor) Trace(opts *bind.TransactOpts, x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugSubContract.contract.Transact(opts, "trace", x, y) +} + +// Trace is a paid mutator transaction binding the contract method 0x3e41f135. +// +// Solidity: function trace(int256 x, int256 y) returns(int256) +func (_NetworkDebugSubContract *NetworkDebugSubContractSession) Trace(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugSubContract.Contract.Trace(&_NetworkDebugSubContract.TransactOpts, x, y) +} + +// Trace is a paid mutator transaction binding the contract method 0x3e41f135. +// +// Solidity: function trace(int256 x, int256 y) returns(int256) +func (_NetworkDebugSubContract *NetworkDebugSubContractTransactorSession) Trace(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugSubContract.Contract.Trace(&_NetworkDebugSubContract.TransactOpts, x, y) +} + +// TraceOneInt is a paid mutator transaction binding the contract method 0x047c4425. +// +// Solidity: function traceOneInt(int256 x) returns(int256 r) +func (_NetworkDebugSubContract *NetworkDebugSubContractTransactor) TraceOneInt(opts *bind.TransactOpts, x *big.Int) (*types.Transaction, error) { + return _NetworkDebugSubContract.contract.Transact(opts, "traceOneInt", x) +} + +// TraceOneInt is a paid mutator transaction binding the contract method 0x047c4425. +// +// Solidity: function traceOneInt(int256 x) returns(int256 r) +func (_NetworkDebugSubContract *NetworkDebugSubContractSession) TraceOneInt(x *big.Int) (*types.Transaction, error) { + return _NetworkDebugSubContract.Contract.TraceOneInt(&_NetworkDebugSubContract.TransactOpts, x) +} + +// TraceOneInt is a paid mutator transaction binding the contract method 0x047c4425. +// +// Solidity: function traceOneInt(int256 x) returns(int256 r) +func (_NetworkDebugSubContract *NetworkDebugSubContractTransactorSession) TraceOneInt(x *big.Int) (*types.Transaction, error) { + return _NetworkDebugSubContract.Contract.TraceOneInt(&_NetworkDebugSubContract.TransactOpts, x) +} + +// TraceWithCallback is a paid mutator transaction binding the contract method 0xfa8fca7a. +// +// Solidity: function traceWithCallback(int256 x, int256 y) returns(int256) +func (_NetworkDebugSubContract *NetworkDebugSubContractTransactor) TraceWithCallback(opts *bind.TransactOpts, x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugSubContract.contract.Transact(opts, "traceWithCallback", x, y) +} + +// TraceWithCallback is a paid mutator transaction binding the contract method 0xfa8fca7a. +// +// Solidity: function traceWithCallback(int256 x, int256 y) returns(int256) +func (_NetworkDebugSubContract *NetworkDebugSubContractSession) TraceWithCallback(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugSubContract.Contract.TraceWithCallback(&_NetworkDebugSubContract.TransactOpts, x, y) +} + +// TraceWithCallback is a paid mutator transaction binding the contract method 0xfa8fca7a. +// +// Solidity: function traceWithCallback(int256 x, int256 y) returns(int256) +func (_NetworkDebugSubContract *NetworkDebugSubContractTransactorSession) TraceWithCallback(x *big.Int, y *big.Int) (*types.Transaction, error) { + return _NetworkDebugSubContract.Contract.TraceWithCallback(&_NetworkDebugSubContract.TransactOpts, x, y) +} + +// NetworkDebugSubContractNoIndexEventIterator is returned from FilterNoIndexEvent and is used to iterate over the raw logs and unpacked data for NoIndexEvent events raised by the NetworkDebugSubContract contract. +type NetworkDebugSubContractNoIndexEventIterator struct { + Event *NetworkDebugSubContractNoIndexEvent // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugSubContractNoIndexEventIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugSubContractNoIndexEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugSubContractNoIndexEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugSubContractNoIndexEventIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugSubContractNoIndexEventIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugSubContractNoIndexEvent represents a NoIndexEvent event raised by the NetworkDebugSubContract contract. +type NetworkDebugSubContractNoIndexEvent struct { + Sender common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterNoIndexEvent is a free log retrieval operation binding the contract event 0x33bc9bae48dbe1e057f264b3fc6a1dacdcceacb3ba28d937231c70e068a02f1c. +// +// Solidity: event NoIndexEvent(address sender) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) FilterNoIndexEvent(opts *bind.FilterOpts) (*NetworkDebugSubContractNoIndexEventIterator, error) { + + logs, sub, err := _NetworkDebugSubContract.contract.FilterLogs(opts, "NoIndexEvent") + if err != nil { + return nil, err + } + return &NetworkDebugSubContractNoIndexEventIterator{contract: _NetworkDebugSubContract.contract, event: "NoIndexEvent", logs: logs, sub: sub}, nil +} + +// WatchNoIndexEvent is a free log subscription operation binding the contract event 0x33bc9bae48dbe1e057f264b3fc6a1dacdcceacb3ba28d937231c70e068a02f1c. +// +// Solidity: event NoIndexEvent(address sender) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) WatchNoIndexEvent(opts *bind.WatchOpts, sink chan<- *NetworkDebugSubContractNoIndexEvent) (event.Subscription, error) { + + logs, sub, err := _NetworkDebugSubContract.contract.WatchLogs(opts, "NoIndexEvent") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugSubContractNoIndexEvent) + if err := _NetworkDebugSubContract.contract.UnpackLog(event, "NoIndexEvent", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseNoIndexEvent is a log parse operation binding the contract event 0x33bc9bae48dbe1e057f264b3fc6a1dacdcceacb3ba28d937231c70e068a02f1c. +// +// Solidity: event NoIndexEvent(address sender) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) ParseNoIndexEvent(log types.Log) (*NetworkDebugSubContractNoIndexEvent, error) { + event := new(NetworkDebugSubContractNoIndexEvent) + if err := _NetworkDebugSubContract.contract.UnpackLog(event, "NoIndexEvent", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugSubContractNoIndexEventStringIterator is returned from FilterNoIndexEventString and is used to iterate over the raw logs and unpacked data for NoIndexEventString events raised by the NetworkDebugSubContract contract. +type NetworkDebugSubContractNoIndexEventStringIterator struct { + Event *NetworkDebugSubContractNoIndexEventString // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugSubContractNoIndexEventStringIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugSubContractNoIndexEventString) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugSubContractNoIndexEventString) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugSubContractNoIndexEventStringIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugSubContractNoIndexEventStringIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugSubContractNoIndexEventString represents a NoIndexEventString event raised by the NetworkDebugSubContract contract. +type NetworkDebugSubContractNoIndexEventString struct { + Str string + Raw types.Log // Blockchain specific contextual infos +} + +// FilterNoIndexEventString is a free log retrieval operation binding the contract event 0x25b7adba1b046a19379db4bc06aa1f2e71604d7b599a0ee8783d58110f00e16a. +// +// Solidity: event NoIndexEventString(string str) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) FilterNoIndexEventString(opts *bind.FilterOpts) (*NetworkDebugSubContractNoIndexEventStringIterator, error) { + + logs, sub, err := _NetworkDebugSubContract.contract.FilterLogs(opts, "NoIndexEventString") + if err != nil { + return nil, err + } + return &NetworkDebugSubContractNoIndexEventStringIterator{contract: _NetworkDebugSubContract.contract, event: "NoIndexEventString", logs: logs, sub: sub}, nil +} + +// WatchNoIndexEventString is a free log subscription operation binding the contract event 0x25b7adba1b046a19379db4bc06aa1f2e71604d7b599a0ee8783d58110f00e16a. +// +// Solidity: event NoIndexEventString(string str) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) WatchNoIndexEventString(opts *bind.WatchOpts, sink chan<- *NetworkDebugSubContractNoIndexEventString) (event.Subscription, error) { + + logs, sub, err := _NetworkDebugSubContract.contract.WatchLogs(opts, "NoIndexEventString") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugSubContractNoIndexEventString) + if err := _NetworkDebugSubContract.contract.UnpackLog(event, "NoIndexEventString", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseNoIndexEventString is a log parse operation binding the contract event 0x25b7adba1b046a19379db4bc06aa1f2e71604d7b599a0ee8783d58110f00e16a. +// +// Solidity: event NoIndexEventString(string str) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) ParseNoIndexEventString(log types.Log) (*NetworkDebugSubContractNoIndexEventString, error) { + event := new(NetworkDebugSubContractNoIndexEventString) + if err := _NetworkDebugSubContract.contract.UnpackLog(event, "NoIndexEventString", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugSubContractNoIndexStructEventIterator is returned from FilterNoIndexStructEvent and is used to iterate over the raw logs and unpacked data for NoIndexStructEvent events raised by the NetworkDebugSubContract contract. +type NetworkDebugSubContractNoIndexStructEventIterator struct { + Event *NetworkDebugSubContractNoIndexStructEvent // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugSubContractNoIndexStructEventIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugSubContractNoIndexStructEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugSubContractNoIndexStructEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugSubContractNoIndexStructEventIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugSubContractNoIndexStructEventIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugSubContractNoIndexStructEvent represents a NoIndexStructEvent event raised by the NetworkDebugSubContract contract. +type NetworkDebugSubContractNoIndexStructEvent struct { + A NetworkDebugSubContractAccount + Raw types.Log // Blockchain specific contextual infos +} + +// FilterNoIndexStructEvent is a free log retrieval operation binding the contract event 0xebe3ff7e2071d351bf2e65b4fccd24e3ae99485f02468f1feecf7d64dc044188. +// +// Solidity: event NoIndexStructEvent((string,uint64,uint256) a) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) FilterNoIndexStructEvent(opts *bind.FilterOpts) (*NetworkDebugSubContractNoIndexStructEventIterator, error) { + + logs, sub, err := _NetworkDebugSubContract.contract.FilterLogs(opts, "NoIndexStructEvent") + if err != nil { + return nil, err + } + return &NetworkDebugSubContractNoIndexStructEventIterator{contract: _NetworkDebugSubContract.contract, event: "NoIndexStructEvent", logs: logs, sub: sub}, nil +} + +// WatchNoIndexStructEvent is a free log subscription operation binding the contract event 0xebe3ff7e2071d351bf2e65b4fccd24e3ae99485f02468f1feecf7d64dc044188. +// +// Solidity: event NoIndexStructEvent((string,uint64,uint256) a) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) WatchNoIndexStructEvent(opts *bind.WatchOpts, sink chan<- *NetworkDebugSubContractNoIndexStructEvent) (event.Subscription, error) { + + logs, sub, err := _NetworkDebugSubContract.contract.WatchLogs(opts, "NoIndexStructEvent") + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugSubContractNoIndexStructEvent) + if err := _NetworkDebugSubContract.contract.UnpackLog(event, "NoIndexStructEvent", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseNoIndexStructEvent is a log parse operation binding the contract event 0xebe3ff7e2071d351bf2e65b4fccd24e3ae99485f02468f1feecf7d64dc044188. +// +// Solidity: event NoIndexStructEvent((string,uint64,uint256) a) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) ParseNoIndexStructEvent(log types.Log) (*NetworkDebugSubContractNoIndexStructEvent, error) { + event := new(NetworkDebugSubContractNoIndexStructEvent) + if err := _NetworkDebugSubContract.contract.UnpackLog(event, "NoIndexStructEvent", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugSubContractOneIndexEventIterator is returned from FilterOneIndexEvent and is used to iterate over the raw logs and unpacked data for OneIndexEvent events raised by the NetworkDebugSubContract contract. +type NetworkDebugSubContractOneIndexEventIterator struct { + Event *NetworkDebugSubContractOneIndexEvent // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugSubContractOneIndexEventIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugSubContractOneIndexEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugSubContractOneIndexEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugSubContractOneIndexEventIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugSubContractOneIndexEventIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugSubContractOneIndexEvent represents a OneIndexEvent event raised by the NetworkDebugSubContract contract. +type NetworkDebugSubContractOneIndexEvent struct { + A *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterOneIndexEvent is a free log retrieval operation binding the contract event 0xeace1be0b97ec11f959499c07b9f60f0cc47bf610b28fda8fb0e970339cf3b35. +// +// Solidity: event OneIndexEvent(uint256 indexed a) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) FilterOneIndexEvent(opts *bind.FilterOpts, a []*big.Int) (*NetworkDebugSubContractOneIndexEventIterator, error) { + + var aRule []interface{} + for _, aItem := range a { + aRule = append(aRule, aItem) + } + + logs, sub, err := _NetworkDebugSubContract.contract.FilterLogs(opts, "OneIndexEvent", aRule) + if err != nil { + return nil, err + } + return &NetworkDebugSubContractOneIndexEventIterator{contract: _NetworkDebugSubContract.contract, event: "OneIndexEvent", logs: logs, sub: sub}, nil +} + +// WatchOneIndexEvent is a free log subscription operation binding the contract event 0xeace1be0b97ec11f959499c07b9f60f0cc47bf610b28fda8fb0e970339cf3b35. +// +// Solidity: event OneIndexEvent(uint256 indexed a) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) WatchOneIndexEvent(opts *bind.WatchOpts, sink chan<- *NetworkDebugSubContractOneIndexEvent, a []*big.Int) (event.Subscription, error) { + + var aRule []interface{} + for _, aItem := range a { + aRule = append(aRule, aItem) + } + + logs, sub, err := _NetworkDebugSubContract.contract.WatchLogs(opts, "OneIndexEvent", aRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugSubContractOneIndexEvent) + if err := _NetworkDebugSubContract.contract.UnpackLog(event, "OneIndexEvent", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseOneIndexEvent is a log parse operation binding the contract event 0xeace1be0b97ec11f959499c07b9f60f0cc47bf610b28fda8fb0e970339cf3b35. +// +// Solidity: event OneIndexEvent(uint256 indexed a) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) ParseOneIndexEvent(log types.Log) (*NetworkDebugSubContractOneIndexEvent, error) { + event := new(NetworkDebugSubContractOneIndexEvent) + if err := _NetworkDebugSubContract.contract.UnpackLog(event, "OneIndexEvent", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugSubContractThreeIndexEventIterator is returned from FilterThreeIndexEvent and is used to iterate over the raw logs and unpacked data for ThreeIndexEvent events raised by the NetworkDebugSubContract contract. +type NetworkDebugSubContractThreeIndexEventIterator struct { + Event *NetworkDebugSubContractThreeIndexEvent // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugSubContractThreeIndexEventIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugSubContractThreeIndexEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugSubContractThreeIndexEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugSubContractThreeIndexEventIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugSubContractThreeIndexEventIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugSubContractThreeIndexEvent represents a ThreeIndexEvent event raised by the NetworkDebugSubContract contract. +type NetworkDebugSubContractThreeIndexEvent struct { + RoundId *big.Int + StartedBy common.Address + StartedAt *big.Int + Raw types.Log // Blockchain specific contextual infos +} + +// FilterThreeIndexEvent is a free log retrieval operation binding the contract event 0x5660e8f93f0146f45abcd659e026b75995db50053cbbca4d7f365934ade68bf3. +// +// Solidity: event ThreeIndexEvent(uint256 indexed roundId, address indexed startedBy, uint256 startedAt) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) FilterThreeIndexEvent(opts *bind.FilterOpts, roundId []*big.Int, startedBy []common.Address) (*NetworkDebugSubContractThreeIndexEventIterator, error) { + + var roundIdRule []interface{} + for _, roundIdItem := range roundId { + roundIdRule = append(roundIdRule, roundIdItem) + } + var startedByRule []interface{} + for _, startedByItem := range startedBy { + startedByRule = append(startedByRule, startedByItem) + } + + logs, sub, err := _NetworkDebugSubContract.contract.FilterLogs(opts, "ThreeIndexEvent", roundIdRule, startedByRule) + if err != nil { + return nil, err + } + return &NetworkDebugSubContractThreeIndexEventIterator{contract: _NetworkDebugSubContract.contract, event: "ThreeIndexEvent", logs: logs, sub: sub}, nil +} + +// WatchThreeIndexEvent is a free log subscription operation binding the contract event 0x5660e8f93f0146f45abcd659e026b75995db50053cbbca4d7f365934ade68bf3. +// +// Solidity: event ThreeIndexEvent(uint256 indexed roundId, address indexed startedBy, uint256 startedAt) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) WatchThreeIndexEvent(opts *bind.WatchOpts, sink chan<- *NetworkDebugSubContractThreeIndexEvent, roundId []*big.Int, startedBy []common.Address) (event.Subscription, error) { + + var roundIdRule []interface{} + for _, roundIdItem := range roundId { + roundIdRule = append(roundIdRule, roundIdItem) + } + var startedByRule []interface{} + for _, startedByItem := range startedBy { + startedByRule = append(startedByRule, startedByItem) + } + + logs, sub, err := _NetworkDebugSubContract.contract.WatchLogs(opts, "ThreeIndexEvent", roundIdRule, startedByRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugSubContractThreeIndexEvent) + if err := _NetworkDebugSubContract.contract.UnpackLog(event, "ThreeIndexEvent", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseThreeIndexEvent is a log parse operation binding the contract event 0x5660e8f93f0146f45abcd659e026b75995db50053cbbca4d7f365934ade68bf3. +// +// Solidity: event ThreeIndexEvent(uint256 indexed roundId, address indexed startedBy, uint256 startedAt) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) ParseThreeIndexEvent(log types.Log) (*NetworkDebugSubContractThreeIndexEvent, error) { + event := new(NetworkDebugSubContractThreeIndexEvent) + if err := _NetworkDebugSubContract.contract.UnpackLog(event, "ThreeIndexEvent", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} + +// NetworkDebugSubContractTwoIndexEventIterator is returned from FilterTwoIndexEvent and is used to iterate over the raw logs and unpacked data for TwoIndexEvent events raised by the NetworkDebugSubContract contract. +type NetworkDebugSubContractTwoIndexEventIterator struct { + Event *NetworkDebugSubContractTwoIndexEvent // Event containing the contract specifics and raw log + + contract *bind.BoundContract // Generic contract to use for unpacking event data + event string // Event name to use for unpacking event data + + logs chan types.Log // Log channel receiving the found contract events + sub ethereum.Subscription // Subscription for errors, completion and termination + done bool // Whether the subscription completed delivering logs + fail error // Occurred error to stop iteration +} + +// Next advances the iterator to the subsequent event, returning whether there +// are any more events found. In case of a retrieval or parsing error, false is +// returned and Error() can be queried for the exact failure. +func (it *NetworkDebugSubContractTwoIndexEventIterator) Next() bool { + // If the iterator failed, stop iterating + if it.fail != nil { + return false + } + // If the iterator completed, deliver directly whatever's available + if it.done { + select { + case log := <-it.logs: + it.Event = new(NetworkDebugSubContractTwoIndexEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + default: + return false + } + } + // Iterator still in progress, wait for either a data or an error event + select { + case log := <-it.logs: + it.Event = new(NetworkDebugSubContractTwoIndexEvent) + if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { + it.fail = err + return false + } + it.Event.Raw = log + return true + + case err := <-it.sub.Err(): + it.done = true + it.fail = err + return it.Next() + } +} + +// Error returns any retrieval or parsing error occurred during filtering. +func (it *NetworkDebugSubContractTwoIndexEventIterator) Error() error { + return it.fail +} + +// Close terminates the iteration process, releasing any pending underlying +// resources. +func (it *NetworkDebugSubContractTwoIndexEventIterator) Close() error { + it.sub.Unsubscribe() + return nil +} + +// NetworkDebugSubContractTwoIndexEvent represents a TwoIndexEvent event raised by the NetworkDebugSubContract contract. +type NetworkDebugSubContractTwoIndexEvent struct { + RoundId *big.Int + StartedBy common.Address + Raw types.Log // Blockchain specific contextual infos +} + +// FilterTwoIndexEvent is a free log retrieval operation binding the contract event 0x33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b5. +// +// Solidity: event TwoIndexEvent(uint256 indexed roundId, address indexed startedBy) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) FilterTwoIndexEvent(opts *bind.FilterOpts, roundId []*big.Int, startedBy []common.Address) (*NetworkDebugSubContractTwoIndexEventIterator, error) { + + var roundIdRule []interface{} + for _, roundIdItem := range roundId { + roundIdRule = append(roundIdRule, roundIdItem) + } + var startedByRule []interface{} + for _, startedByItem := range startedBy { + startedByRule = append(startedByRule, startedByItem) + } + + logs, sub, err := _NetworkDebugSubContract.contract.FilterLogs(opts, "TwoIndexEvent", roundIdRule, startedByRule) + if err != nil { + return nil, err + } + return &NetworkDebugSubContractTwoIndexEventIterator{contract: _NetworkDebugSubContract.contract, event: "TwoIndexEvent", logs: logs, sub: sub}, nil +} + +// WatchTwoIndexEvent is a free log subscription operation binding the contract event 0x33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b5. +// +// Solidity: event TwoIndexEvent(uint256 indexed roundId, address indexed startedBy) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) WatchTwoIndexEvent(opts *bind.WatchOpts, sink chan<- *NetworkDebugSubContractTwoIndexEvent, roundId []*big.Int, startedBy []common.Address) (event.Subscription, error) { + + var roundIdRule []interface{} + for _, roundIdItem := range roundId { + roundIdRule = append(roundIdRule, roundIdItem) + } + var startedByRule []interface{} + for _, startedByItem := range startedBy { + startedByRule = append(startedByRule, startedByItem) + } + + logs, sub, err := _NetworkDebugSubContract.contract.WatchLogs(opts, "TwoIndexEvent", roundIdRule, startedByRule) + if err != nil { + return nil, err + } + return event.NewSubscription(func(quit <-chan struct{}) error { + defer sub.Unsubscribe() + for { + select { + case log := <-logs: + // New log arrived, parse the event and forward to the user + event := new(NetworkDebugSubContractTwoIndexEvent) + if err := _NetworkDebugSubContract.contract.UnpackLog(event, "TwoIndexEvent", log); err != nil { + return err + } + event.Raw = log + + select { + case sink <- event: + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + case err := <-sub.Err(): + return err + case <-quit: + return nil + } + } + }), nil +} + +// ParseTwoIndexEvent is a log parse operation binding the contract event 0x33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b5. +// +// Solidity: event TwoIndexEvent(uint256 indexed roundId, address indexed startedBy) +func (_NetworkDebugSubContract *NetworkDebugSubContractFilterer) ParseTwoIndexEvent(log types.Log) (*NetworkDebugSubContractTwoIndexEvent, error) { + event := new(NetworkDebugSubContractTwoIndexEvent) + if err := _NetworkDebugSubContract.contract.UnpackLog(event, "TwoIndexEvent", log); err != nil { + return nil, err + } + event.Raw = log + return event, nil +} diff --git a/seth/contracts/emptyContractDir/.gitkeep b/seth/contracts/emptyContractDir/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/seth/contracts/invalidContractDir/NetworkDebugContract.abi b/seth/contracts/invalidContractDir/NetworkDebugContract.abi new file mode 100644 index 000000000..8d12d0161 --- /dev/null +++ b/seth/contracts/invalidContractDir/NetworkDebugContract.abi @@ -0,0 +1 @@ +["inputs":[],"tateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"available","type":"uint256"},{"internalType":"uint256","name":"required","type":"uint256"}],"name":"CustomErr","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sender","type":"address"}],"name":"NoIndexEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"string","name":"str","type":"string"}],"name":"NoIndexEventString","type":"event"},{"anonymous":false,"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"uint64","name":"balance","type":"uint64"},{"internalType":"uint256","name":"dailyLimit","type":"uint256"}],"indexed":false,"internalType":"struct NetworkDebugContract.Account","name":"a","type":"tuple"}],"name":"NoIndexStructEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"a","type":"uint256"}],"name":"OneIndexEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":true,"internalType":"address","name":"startedBy","type":"address"},{"indexed":false,"internalType":"uint256","name":"startedAt","type":"uint256"}],"name":"ThreeIndexEvent","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"roundId","type":"uint256"},{"indexed":true,"internalType":"address","name":"startedBy","type":"address"}],"name":"TwoIndexEvent","type":"event"},{"inputs":[],"name":"alwaysRevertsAssert","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"alwaysRevertsCustomError","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"alwaysRevertsRequire","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"inputVal1","type":"uint256"},{"internalType":"string","name":"inputVal2","type":"string"}],"name":"emitInputs","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"inputVal1","type":"uint256"},{"internalType":"string","name":"inputVal2","type":"string"}],"name":"emitInputsOutputs","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"inputVal1","type":"uint256"},{"internalType":"string","name":"inputVal2","type":"string"}],"name":"emitNamedInputsOutputs","outputs":[{"internalType":"uint256","name":"outputVal1","type":"uint256"},{"internalType":"string","name":"outputVal2","type":"string"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitNamedOutputs","outputs":[{"internalType":"uint256","name":"outputVal1","type":"uint256"},{"internalType":"string","name":"outputVal2","type":"string"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitNoIndexEvent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitNoIndexEventString","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitNoIndexStructEvent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitOneIndexEvent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitOutputs","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitThreeIndexEvent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"emitTwoIndexEvent","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"get","outputs":[{"internalType":"int256","name":"data","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pay","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"int256","name":"x","type":"int256"}],"name":"set","outputs":[{"internalType":"int256","name":"value","type":"int256"}],"stateMutability":"nonpayable","type":"function"}] diff --git a/seth/decode.go b/seth/decode.go new file mode 100644 index 000000000..c95f872a5 --- /dev/null +++ b/seth/decode.go @@ -0,0 +1,459 @@ +package seth + +import ( + "bytes" + "context" + "encoding/hex" + "fmt" + "math/big" + "strconv" + + "github.com/ethereum/go-ethereum" + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/core/types" + "github.com/ethereum/go-ethereum/rpc" + "github.com/pkg/errors" + "github.com/rs/zerolog" +) + +const ( + ErrDecodeInput = "failed to decode transaction input" + ErrDecodeOutput = "failed to decode transaction output" + ErrDecodeLog = "failed to decode log" + ErrDecodedLogNonIndexed = "failed to decode non-indexed log data" + ErrDecodeILogIndexed = "failed to decode indexed log data" + ErrNoTxData = "no tx data or it's less than 4 bytes" + ErrRPCJSONCastError = "failed to cast CallMsg error as rpc.DataError" + + WarnNoContractStore = "ContractStore is nil, use seth.NewContractStore(...) to decode transactions" +) + +// DecodedTransaction decoded transaction +type DecodedTransaction struct { + CommonData + Index uint `json:"index"` + Hash string `json:"hash,omitempty"` + Protected bool `json:"protected,omitempty"` + Transaction *types.Transaction `json:"transaction,omitempty"` + Receipt *types.Receipt `json:"receipt,omitempty"` + Events []DecodedTransactionLog `json:"events,omitempty"` +} + +type CommonData struct { + CallType string `json:"call_type,omitempty"` + Signature string `json:"signature"` + Method string `json:"method"` + Input map[string]interface{} `json:"input,omitempty"` + Output map[string]interface{} `json:"output,omitempty"` + NestingLevel int `json:"nesting_level,omitempty"` + ParentSignature string `json:"parent_signature,omitempty"` + Error string `json:"error,omitempty"` +} + +// DecodedCall decoded call +type DecodedCall struct { + CommonData + FromAddress string `json:"from_address,omitempty"` + ToAddress string `json:"to_address,omitempty"` + From string `json:"from,omitempty"` + To string `json:"to,omitempty"` + Events []DecodedCommonLog `json:"events,omitempty"` + Comment string `json:"comment,omitempty"` + Value int64 `json:"value,omitempty"` + GasLimit uint64 `json:"gas_limit,omitempty"` + GasUsed uint64 `json:"gas_used,omitempty"` +} + +type DecodedCommonLog struct { + Signature string `json:"signature"` + Address common.Address `json:"address"` + EventData map[string]interface{} `json:"event_data"` + Topics []string `json:"topics,omitempty"` +} + +func getDefaultDecodedCall() *DecodedCall { + return &DecodedCall{ + CommonData: CommonData{ + CallType: UNKNOWN, + Signature: UNKNOWN, + Method: UNKNOWN, + Input: make(map[string]interface{}), + Output: make(map[string]interface{}), + }, + FromAddress: UNKNOWN, + ToAddress: UNKNOWN, + From: UNKNOWN, + To: UNKNOWN, + Events: make([]DecodedCommonLog, 0), + } +} + +func (d *DecodedCommonLog) MergeEventData(newEventData map[string]interface{}) { + if d.EventData == nil { + d.EventData = make(map[string]interface{}) + } + for k, v := range newEventData { + d.EventData[k] = v + } +} + +// DecodedTransactionLog decoded Solidity log(event) +type DecodedTransactionLog struct { + DecodedCommonLog + BlockNumber uint64 `json:"block_number"` + Index uint `json:"index"` + TXHash string `json:"hash"` + TXIndex uint `json:"tx_index"` + Removed bool `json:"removed"` + FileTag string `json:"file_tag,omitempty"` +} + +func (d *DecodedTransactionLog) MergeEventData(newEventData map[string]interface{}) { + if d.EventData == nil { + d.EventData = make(map[string]interface{}) + } + for k, v := range newEventData { + d.EventData[k] = v + } +} + +// decodeTransaction decodes inputs/outputs/logs of a transaction, if tx have logs with topics all topics are decoded +// if `tracing_enabled` flag is set in Client config it will also automatically trace all transaction calls and print the trace +func (m *Client) decodeTransaction(l zerolog.Logger, tx *types.Transaction, receipt *types.Receipt) (*DecodedTransaction, error) { + var txInput map[string]interface{} + var txEvents []DecodedTransactionLog + txData := tx.Data() + defaultTxn := &DecodedTransaction{ + Receipt: receipt, + Transaction: tx, + Protected: tx.Protected(), + Hash: tx.Hash().String(), + } + // if there is no tx data we have no inputs/outputs/logs + if len(txData) == 0 || len(txData) < 4 { + l.Err(errors.New(ErrNoTxData)).Send() + return defaultTxn, nil + } + if m.ContractStore == nil { + L.Warn().Msg(WarnNoContractStore) + return defaultTxn, nil + } + + sig := txData[:4] + if m.ABIFinder == nil { + L.Err(errors.New("ABIFInder is nil")).Msg("ABIFinder is required for transaction decoding") + return defaultTxn, nil + } + + var address string + if tx.To() != nil { + address = tx.To().String() + } else { + address = UNKNOWN + } + + abiResult, err := m.ABIFinder.FindABIByMethod(address, sig) + if err != nil { + return defaultTxn, err + } + + txInput, err = decodeTxInputs(l, txData, abiResult.Method) + if err != nil { + return defaultTxn, errors.Wrap(err, ErrDecodeInput) + } + + var txIndex uint + + if receipt != nil { + l.Trace().Interface("Receipt", receipt).Msg("TX receipt") + logsValues := make([]types.Log, 0) + for _, l := range receipt.Logs { + logsValues = append(logsValues, *l) + } + txEvents, err = m.decodeContractLogs(l, logsValues, abiResult.ABI) + if err != nil { + return defaultTxn, err + } + txIndex = receipt.TransactionIndex + } + ptx := &DecodedTransaction{ + CommonData: CommonData{ + Signature: common.Bytes2Hex(abiResult.Method.ID), + Method: abiResult.Method.Sig, + Input: txInput, + }, + Index: txIndex, + Receipt: receipt, + Transaction: tx, + Protected: tx.Protected(), + Hash: tx.Hash().String(), + Events: txEvents, + } + + return ptx, nil +} + +// printDecodedTXData prints decoded txn data +func (m *Client) printDecodedTXData(l zerolog.Logger, ptx *DecodedTransaction) { + l.Debug().Str("Method signature", ptx.Signature).Send() + l.Debug().Str("Method name", ptx.Method).Send() + if ptx.Input != nil { + l.Debug().Interface("Inputs", ptx.Input).Send() + } + if ptx.Output != nil { + l.Debug().Interface("Outputs", ptx.Output).Send() + } + for _, e := range ptx.Events { + l.Debug(). + Str("Signature", e.Signature). + Interface("Log", e.EventData).Send() + } +} + +// DecodeCustomABIErr decodes typed Solidity errors +func (m *Client) DecodeCustomABIErr(txErr error) (string, error) { + //nolint + cerr, ok := txErr.(rpc.DataError) + if !ok { + return "", errors.New(ErrRPCJSONCastError) + } + if m.ContractStore == nil { + L.Warn().Msg(WarnNoContractStore) + return "", nil + } + if cerr.ErrorData() != nil { + L.Trace().Msg("Decoding custom ABI error from tx") + for _, a := range m.ContractStore.ABIs { + for k, abiError := range a.Errors { + data, err := hex.DecodeString(cerr.ErrorData().(string)[2:]) + if err != nil { + return "", err + } + if len(data) < 4 { + return "", err + } + if bytes.Equal(data[:4], abiError.ID.Bytes()[:4]) { + // Found a matching error + v, err := abiError.Unpack(data) + if err != nil { + return "", err + } + L.Trace().Interface("Error", k).Interface("Args", v).Msg("Revert Reason") + return fmt.Sprintf("error type: %s, error values: %v", k, v), nil + } + } + } + } else { + L.Warn().Msg("No error data in tx") + } + return "", nil +} + +// CallMsgFromTx creates ethereum.CallMsg from tx, used in simulated calls +func (m *Client) CallMsgFromTx(tx *types.Transaction) (ethereum.CallMsg, error) { + signer := types.LatestSignerForChainID(tx.ChainId()) + sender, err := types.Sender(signer, tx) + if err != nil { + L.Warn().Err(err).Msg("Failed to get sender from tx") + return ethereum.CallMsg{}, err + } + + if tx.Type() == types.LegacyTxType { + return ethereum.CallMsg{ + From: sender, + To: tx.To(), + Gas: tx.Gas(), + GasPrice: tx.GasPrice(), + Value: tx.Value(), + Data: tx.Data(), + }, nil + } + return ethereum.CallMsg{ + From: sender, + To: tx.To(), + Gas: tx.Gas(), + GasFeeCap: tx.GasFeeCap(), + GasTipCap: tx.GasTipCap(), + Value: tx.Value(), + Data: tx.Data(), + }, nil +} + +func (m *Client) DownloadContractAndGetPragma(address common.Address, block *big.Int) (Pragma, error) { + bytecode, err := m.Client.CodeAt(context.Background(), address, block) + if err != nil { + return Pragma{}, errors.Wrap(err, "failed to get contract code") + } + + pragma, err := DecodePragmaVersion(common.Bytes2Hex(bytecode)) + if err != nil { + return Pragma{}, err + } + + return pragma, nil +} + +// callAndGetRevertReason executes transaction locally and gets revert reason +func (m *Client) callAndGetRevertReason(tx *types.Transaction, rc *types.Receipt) error { + L.Trace().Msg("Decoding revert error") + // bind should support custom errors decoding soon, not yet merged + // https://github.com/ethereum/go-ethereum/issues/26823 + // there are 2 types of possible errors, plain old assert/revert string + // or new ABI encoded errors, first we try to find ABI one + // if there is no match we print the error from CallMsg call + msg, err := m.CallMsgFromTx(tx) + if err != nil { + L.Warn().Err(err).Msg("Failed to get call msg from tx. We won't be able to decode revert reason.") + return nil + } + _, plainStringErr := m.Client.CallContract(context.Background(), msg, rc.BlockNumber) + + decodedABIErrString, err := m.DecodeCustomABIErr(plainStringErr) + if err != nil { + return err + } + if decodedABIErrString != "" { + return errors.New(decodedABIErrString) + } + + if plainStringErr != nil { + L.Warn().Msg("Failed to decode revert reason") + + if plainStringErr.Error() == "execution reverted" && tx != nil && rc != nil { + if tx.To() != nil { + pragma, err := m.DownloadContractAndGetPragma(*tx.To(), rc.BlockNumber) + if err == nil { + if DoesPragmaSupportCustomRevert(pragma) { + L.Warn().Str("Pragma", fmt.Sprint(pragma)).Msg("Custom revert reason is supported by pragma, but we could not decode it. This might be a bug in Seth. Please contact the Test Tooling team.") + } else { + L.Info().Str("Pragma", fmt.Sprint(pragma)).Msg("Custom revert reason is not supported by pragma version (must be >= 0.8.4). There's nothing more we can do to get custom revert reason.") + } + } else { + L.Warn().Err(err).Msg("Failed to decode pragma version. Contract either uses very old version or was compiled without metadata. We won't be able to decode revert reason.") + } + } else { + L.Warn().Msg("Transaction has no recipient address. Most likely it's a contract creation transaction. We don't support decoding revert reasons for contract creation transactions yet.") + } + } + + return plainStringErr + } + return nil +} + +// decodeTxInputs decoded tx inputs +func decodeTxInputs(l zerolog.Logger, txData []byte, method *abi.Method) (map[string]interface{}, error) { + l.Trace().Msg("Parsing tx inputs") + if (len(txData)) < 4 { + return nil, errors.New(ErrNoTxData) + } + + inputMap := make(map[string]interface{}) + payload := txData[4:] + if len(payload) == 0 || len(method.Inputs) == 0 { + return nil, nil + } + err := method.Inputs.UnpackIntoMap(inputMap, payload) + if err != nil { + return nil, err + } + l.Trace().Interface("Inputs", inputMap).Msg("Transaction inputs") + return inputMap, nil +} + +// decodeTxOutputs decoded tx outputs +func decodeTxOutputs(l zerolog.Logger, payload []byte, method *abi.Method) (map[string]interface{}, error) { + l.Trace().Msg("Parsing tx outputs") + outputMap := make(map[string]interface{}) + // unpack method outputs + if len(payload) == 0 { + return nil, nil + } + // TODO: is it possible to have both anonymous and non-anonymous fields in solidity? + if len(method.Outputs) > 0 && method.Outputs[0].Name == "" { + vals, err := method.Outputs.UnpackValues(payload) + if err != nil { + return nil, err + } + for i, v := range vals { + outputMap[strconv.Itoa(i)] = v + } + } else { + err := method.Outputs.UnpackIntoMap(outputMap, payload) + if err != nil { + return nil, errors.Wrap(err, ErrDecodeOutput) + } + } + l.Trace().Interface("Outputs", outputMap).Msg("Transaction outputs") + return outputMap, nil +} + +type DecodableLog interface { + GetTopics() []common.Hash + GetData() []byte +} + +// decodeEventFromLog parses log body and additional topic fields +func decodeEventFromLog( + l zerolog.Logger, + a abi.ABI, + eventABISpec abi.Event, + lo DecodableLog, +) (map[string]interface{}, map[string]interface{}, error) { + eventsMap := make(map[string]interface{}) + topicsMap := make(map[string]interface{}) + // if no data event has only indexed fields + if len(lo.GetData()) != 0 { + err := a.UnpackIntoMap(eventsMap, eventABISpec.Name, lo.GetData()) + if err != nil { + return nil, nil, errors.Wrap(err, ErrDecodedLogNonIndexed) + } + l.Trace().Interface("Non-indexed", eventsMap).Send() + } + // might have up to 3 additional indexed fields + if len(lo.GetTopics()) > 1 { + topics := lo.GetTopics()[1:] + var indexed []abi.Argument + indexedTopics := make([]common.Hash, 0) + for idx, topic := range topics { + arg := eventABISpec.Inputs[idx] + if arg.Indexed { + indexed = append(indexed, arg) + indexedTopics = append(indexedTopics, topic) + } + } + l.Trace().Int("Topics", len(lo.GetTopics()[1:])).Int("Arguments", len(indexed)).Send() + l.Trace().Interface("AllTopics", lo.GetTopics()).Send() + l.Trace().Interface("HashOfName", eventABISpec.ID.Hex()).Send() + l.Trace().Interface("FirstTopic", lo.GetTopics()[0]).Send() + l.Trace().Interface("Topics", lo.GetTopics()[1:]).Send() + l.Trace().Interface("Arguments", eventABISpec.Inputs).Send() + l.Trace().Interface("Indexed", indexed).Send() + err := abi.ParseTopicsIntoMap(topicsMap, indexed, indexedTopics) + if err != nil { + return nil, nil, errors.Wrap(err, ErrDecodeILogIndexed) + } + l.Trace().Interface("Indexed", topicsMap).Send() + } + return eventsMap, topicsMap, nil +} + +type LogWithEventData interface { + MergeEventData(map[string]interface{}) +} + +// decodedLogFromMaps creates DecodedLog from events and topics maps +func decodedLogFromMaps(log LogWithEventData, eventsMap map[string]interface{}, topicsMap map[string]interface{}) LogWithEventData { + newMap := map[string]interface{}{} + for k, v := range eventsMap { + newMap[k] = v + } + for k, v := range topicsMap { + newMap[k] = v + } + + log.MergeEventData(newMap) + + return log +} diff --git a/seth/docs/abi_finder_contract_map.md b/seth/docs/abi_finder_contract_map.md new file mode 100644 index 000000000..2f95a8f84 --- /dev/null +++ b/seth/docs/abi_finder_contract_map.md @@ -0,0 +1,33 @@ +# ABI Finder and Contract map + +In order to be able to decode and trace transactions and calls between smart contracts we need their ABIs. Unfortunately it might happen that two or more contracts have methods with the same signatures, which might result in incorrect tracing. To make that problem less severe we have decided to add a single point of entry for contract deployment in Seth as that way we always know what contract is deployed at which address and thus avoid incorrect tracing due to potentially ambiguous method signatures. + +## ABI Finder + +1. We don’t know what contract (ABI) is located at a given address. Should be the case, when the contract either wasn’t uploaded via Seth or we haven’t supplied Seth with a contract map as part of its configuration (more on that later). + + a. We sequentially iterate over all known ABIs (map: `name -> ABI_name`) checking whether it has a method with a given signature. Once we get a first match we will upsert that (`address -> ABI_name`) data into the contract map and return the ABI. + + The caveat here is that if the method we are searching for belongs is present in more than one ABI we might associate the address with an incorrect address (we will use the first match). + + b. If no match is found we will return an error. + +2. We know what ABI is located at a given address. It should be the case, when we have either uploaded the contract via Seth, provided Seth with a contract map or already traced a transaction to that address and found an ABI with matching method signature. + + a. We fetch the corresponding ABIand check if it indeed contains the method we are looking for (as mentioned earlier in some cases it might not be the case). + + b. If it does, we return the ABI. + + c. If it doesn’t we iterate over all known ABIs, in the same way as in 1a. If we find a match we update the (`address -> ABI_name`) association in the contract map and return the ABI. + + It is possible that this will happen multiple times in case we have multiple contracts with multiple identical methods, but given a sufficiently diverse set of methods that were called we should eventually arrive at a fully correct contract map. + + d. If no match is found we will return an error. + +## Contract map + +We support in-memory contract map and a TOML file contract map that keeps the association of (`address -> ABI_name`). The latter map is only used for non-simulated networks. Every time we deploy a contract we save (`address -> ABI_name`) entry in the in-memory map.If the network is not a simulated one we also save it in a file. That file can later be pointed to in Seth configuration and we will load the contract map from it (**currently without validating whether we have all the ABIs mentioned in the file**). + +When saving contract deployment information we will either generate filename for you (if you didn’t configure Seth to use a particular file) using the pattern of `deployed_contracts_${network_name}_${timestamp}.toml` or use the filename provided in Seth TOML configuration file. + +It has to be noted that the file contract map is currently updated only, when new contracts are deployed. There’s no mechanism for updating it if we found the mapping invalid (which might be the case if you manually created the entry in the file). diff --git a/seth/docs/contract_store.md b/seth/docs/contract_store.md new file mode 100644 index 000000000..b1ffe2b4a --- /dev/null +++ b/seth/docs/contract_store.md @@ -0,0 +1,7 @@ +# Contract Store + +Seth can be used with the contract store, but it would have very limited usage as transaction decoding and tracing cannot work with ABIs. Thus, when you initialise Seth with contract store it is necessary that it can load at least one ABI. + +Another use of Contract Store is simplified contract deployment. For that we also need the contract's bytecode. The contract store can be used to store the bytecode of the contract and then deploy it using the `DeployContractFromContractStore(auth *bind.TransactOpts, name string, backend bind.ContractBackend, params ...interface{})` method. When Seth is intialisied with the contract store and no bytecode files (`*.bin`) are provided, it will log a warning, but initialise successfully nonetheless. + +If bytecode file wasn't provided you need to use `DeployContract(auth *bind.TransactOpts, name string, abi abi.ABI, bytecode []byte, backend bind.ContractBackend, params ...interface{})` method, which expects you to provide contract name (best if equal to the name of the ABI file), bytecode and the ABI. diff --git a/seth/docs/tracing_example.png b/seth/docs/tracing_example.png new file mode 100644 index 0000000000000000000000000000000000000000..e95a9e0a0ef6b8adacb809ba30deccf2be6785bc GIT binary patch literal 259998 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

jdVt68UjnU@RU~ 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

j7T|$34{_>52a#K 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

#uCuxPg`)u3+Trci@aH(`k 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 aKmg_6xXw{75?ok?sk~!b_*O|sY}&t|!b@rZ literal 0 HcmV?d00001 diff --git a/seth/dot_graph.go b/seth/dot_graph.go new file mode 100644 index 000000000..defecc718 --- /dev/null +++ b/seth/dot_graph.go @@ -0,0 +1,395 @@ +package seth + +import ( + //we use it only to generate hash that's used to identify a node in graph, so we don't care about this function being weak + //nolint + "crypto/sha1" + "encoding/hex" + "encoding/json" + "fmt" + "os" + "path/filepath" + "strconv" + "strings" + + "github.com/awalterschulze/gographviz" +) + +func findShortestPath(calls []*DecodedCall) []string { + callMap := make(map[string]*DecodedCall) + for _, call := range calls { + callMap[call.CommonData.Signature] = call + } + + var root *DecodedCall + for _, call := range calls { + if call.CommonData.ParentSignature == "" { + root = call + break + } + } + + if root == nil { + return nil // No root found + } + + var end *DecodedCall + for i := len(calls) - 1; i >= 0; i-- { + if calls[i].CommonData.Error != "" { + end = calls[i] + break + } + } + if end == nil { + end = calls[len(calls)-1] + } + + type node struct { + call *DecodedCall + path []string + } + + queue := []node{{call: root, path: []string{root.CommonData.Signature}}} + visited := make(map[string]bool) + + for len(queue) > 0 { + currentNode := queue[0] + queue = queue[1:] + + currentCall := currentNode.call + currentPath := currentNode.path + + if currentCall.CommonData.Signature == end.CommonData.Signature { + return currentPath + } + + visited[currentCall.CommonData.Signature] = true + + for _, call := range calls { + if call.CommonData.ParentSignature == currentCall.CommonData.Signature && !visited[call.CommonData.Signature] { + newPath := append([]string{}, currentPath...) + newPath = append(newPath, call.CommonData.Signature) + queue = append(queue, node{call: call, path: newPath}) + } + } + } + + return nil // No path found +} + +var defaultTruncateTo = 20 + +func (t *Tracer) generateDotGraph(txHash string, calls []*DecodedCall, revertErr error) error { + if !t.Cfg.hasOutput(TraceOutput_DOT) { + return nil + } + + shortestPath := findShortestPath(calls) + + callHashToID := make(map[string]int) + nextID := 1 + + g := gographviz.NewGraph() + if err := g.SetName("G"); err != nil { + return fmt.Errorf("failed to set graph name: %w", err) + } + if err := g.SetDir(true); err != nil { + return fmt.Errorf("failed to set graph direction: %w", err) + } + + nodesAtLevel := make(map[int][]string) + revertedCallIdx := -1 + + if len(calls) > 0 { + for i, dc := range calls { + if dc.Error != "" { + revertedCallIdx = i + } + } + } + + if err := g.AddNode("G", "start", map[string]string{"label": "\"Start\n\"", "shape": "circle", "style": "filled", "fillcolor": "darkseagreen3", "color": "darkslategray", "fontcolor": "darkslategray"}); err != nil { + return fmt.Errorf("failed to add start node: %w", err) + } + + for idx, call := range calls { + hash := hashCall(call) + + var callID int + _, exists := callHashToID[hash] + if !exists { + callID = nextID + nextID++ + callHashToID[hash] = callID + + basicNodeID := "node" + strconv.Itoa(callID) + "_basic" + extraNodeID := "node" + strconv.Itoa(callID) + "_extra" + + var from, to string + if call.From != "" && call.From != UNKNOWN { + from = call.From + } else { + from = call.FromAddress + } + + if call.To != "" && call.To != UNKNOWN { + to = call.To + } else { + to = call.ToAddress + } + + basicLabel := fmt.Sprintf("\"%s -> %s\n %s\"", from, to, call.CommonData.Method) + extraLabel := fmt.Sprintf("\"Inputs: %s\nOutputs: %s\"", formatMapForLabel(call.CommonData.Input, defaultTruncateTo), formatMapForLabel(call.CommonData.Output, defaultTruncateTo)) + + isMajorNode := false + for _, path := range shortestPath { + if path == call.Signature { + isMajorNode = true + break + } + } + + style := "filled" + nodeColor := "darkslategray" + fontSize := "14.0" + if !isMajorNode { + style = "dashed" + fontSize = "9.0" + nodeColor = "lightslategray" + } + + var subgraphAttrs map[string]string + subgraphAttrs = map[string]string{"color": "darkslategray"} + if call.Error != "" { + subgraphAttrs = map[string]string{"color": "lightcoral"} + nodeColor = "lightcoral" + } + + if err := g.AddNode("G", basicNodeID, map[string]string{"label": basicLabel, "shape": "box", "style": style, "fillcolor": "ivory", "color": nodeColor, "fontcolor": "darkslategray", "fontsize": fontSize, "tooltip": formatTooltip(call)}); err != nil { + return fmt.Errorf("failed to add node: %w", err) + } + if err := g.AddNode("G", extraNodeID, map[string]string{"label": extraLabel, "shape": "box", "style": style, "fillcolor": "gainsboro", "color": nodeColor, "fontcolor": "darkslategray", "fontsize": fontSize}); err != nil { + return fmt.Errorf("failed to add node: %w", err) + } + + subGraphName := "cluster_" + strconv.Itoa(callID) + if err := g.AddSubGraph("G", subGraphName, subgraphAttrs); err != nil { + return fmt.Errorf("failed to add node: %w", err) + } + + if err := g.AddNode(subGraphName, basicNodeID, nil); err != nil { + return fmt.Errorf("failed to add node: %w", err) + } + if err := g.AddNode(subGraphName, extraNodeID, map[string]string{"rank": "same"}); err != nil { + return fmt.Errorf("failed to add node: %w", err) + } + + if idx == 0 { + if err := g.AddEdge("start", basicNodeID, true, nil); err != nil { + return fmt.Errorf("failed to add edge: %w", err) + } + } + + if call.CommonData.ParentSignature != "" { + for _, parentCall := range calls { + if parentCall.CommonData.Signature == call.CommonData.ParentSignature { + parentHash := hashCall(parentCall) + parentID := callHashToID[parentHash] + parentBasicNodeID := "node" + strconv.Itoa(parentID) + "_basic" + attrs := map[string]string{"fontsize": fontSize, "label": fmt.Sprintf(" \"(%s)\"", ordinalNumber(idx))} + if call.Error != "" { + attrs["color"] = "lightcoral" + attrs["fontcolor"] = "lightcoral" + } else { + attrs["color"] = "darkslategray" + attrs["fontcolor"] = "darkslategray" + } + + if err := g.AddEdge(parentBasicNodeID, basicNodeID, true, attrs); err != nil { + return fmt.Errorf("failed to add edge: %w", err) + } + break + } + } + } + } else { + // This could be also valid if the same call is present twice in the trace, but in typical scenarios that should not happen + L.Warn().Msg("The same call was present twice. This should not happen and might indicate a bug in the tracer. Check debug log for details") + marshalled, err := json.Marshal(call) + if err == nil { + L.Debug().Msgf("Call: %v", marshalled) + } + continue + } + } + + // Create dummy nodes to adjust vertical positions + for level, nodes := range nodesAtLevel { + for i, node := range nodes { + if i > 0 { + dummyNode := fmt.Sprintf("dummy_%d_%d", level, i) + if err := g.AddNode("G", dummyNode, map[string]string{"label": "\"\"", "shape": "none", "height": "0.1", "width": "0.1"}); err != nil { + return fmt.Errorf("failed to add node: %w", err) + } + if err := g.AddEdge(nodes[i-1], dummyNode, true, map[string]string{"style": "invis"}); err != nil { + return fmt.Errorf("failed to add node: %w", err) + } + if err := g.AddEdge(dummyNode, node, true, map[string]string{"style": "invis"}); err != nil { + return fmt.Errorf("failed to add node: %w", err) + } + } + } + } + + if revertErr != nil { + revertNode := fmt.Sprintf("revert_node_%d", nextID-1) + + if err := g.AddNode("G", revertNode, map[string]string{"label": fmt.Sprintf("\"%s\"", revertErr.Error()), "shape": "rectangle", "style": "filled", "color": "lightcoral", "fillcolor": "lightcoral", "fontcolor": "darkslategray"}); err != nil { + return fmt.Errorf("failed to add node: %w", err) + } + + hash := hashCall(calls[revertedCallIdx]) + revertParentNodeId, ok := callHashToID[hash] + if !ok { + return fmt.Errorf("failed to find parent node for revert node. This should never happen and likely indicates a bug in code") + } + + parentBasicNodeID := "node" + strconv.Itoa(revertParentNodeId) + "_basic" + + if err := g.AddEdge(revertNode, parentBasicNodeID, true, map[string]string{"style": "filled", "fillcolor": "lightcoral", "color": "lightcoral", "fontcolor": "darkslategray"}); err != nil { + return fmt.Errorf("failed to add node: %w", err) + } + } else { + if err := g.AddNode("G", "end", map[string]string{"label": "\"End\n\"", "shape": "circle", "style": "filled", "fillcolor": "darkseagreen3", "color": "darkslategray", "fontcolor": "darkslategray"}); err != nil { + return fmt.Errorf("failed to add end node: %w", err) + } + + hash := hashCall(calls[len(calls)-1]) + lastNodeId, ok := callHashToID[hash] + if !ok { + return fmt.Errorf("failed to find parent node for revert node. This should never happen and likely indicates a bug in code") + } + + parentBasicNodeID := "node" + strconv.Itoa(lastNodeId) + "_basic" + if err := g.AddEdge(parentBasicNodeID, "end", true, nil); err != nil { + return fmt.Errorf("failed to add edge: %w", err) + } + } + + dirPath := filepath.Join(t.Cfg.ArtifactsDir, "dot_graphs") + err := os.MkdirAll(dirPath, os.ModePerm) + if err != nil { + return fmt.Errorf("failed to create directory: %w", err) + } + + filePath := filepath.Join(dirPath, fmt.Sprintf("%s.dot", txHash)) + + f, err := os.Create(filePath) + if err != nil { + return fmt.Errorf("error creating file: %v", err) + } + defer func() { _ = f.Close() }() + + if _, err := f.WriteString(g.String()); err != nil { + return fmt.Errorf("error writing to file: %v", err) + } + + L.Debug().Msgf("DOT graph saved to %s", filePath) + L.Debug().Msgf("To view run: xdot %s", filePath) + + return nil +} + +func formatTooltip(call *DecodedCall) string { + basicTooltip := fmt.Sprintf("\"BASIC\nFrom: %s\nTo: %s\nType: %s\nGas Used/Limit: %s\nValue: %d\n\nINPUTS%s\n\nOUTPUTS%s\n\nEVENTS%s\n\"", + call.FromAddress, call.ToAddress, call.CommonData.CallType, fmt.Sprintf("%d/%d", call.GasUsed, call.GasLimit), call.Value, formatMapForTooltip(call.CommonData.Input), formatMapForTooltip(call.CommonData.Output), formatEvent(call.Events)) + + if call.Comment == "" { + return basicTooltip + } + + return fmt.Sprintf("%s\nCOMMENT\n%s", basicTooltip, call.Comment) +} + +func formatEvent(events []DecodedCommonLog) string { + if len(events) == 0 { + return "\n{}" + } + parts := make([]string, 0, len(events)) + for _, event := range events { + parts = append(parts, fmt.Sprintf("\n%s %s", event.Signature, formatMapForTooltip(event.EventData))) + } + return strings.Join(parts, "\n") +} + +func prepareMapParts(m map[string]interface{}, truncateTo int) []string { + if len(m) == 0 { + return []string{} + } + parts := make([]string, 0, len(m)) + for k, v := range m { + value := fmt.Sprint(v) + if truncateTo != -1 && len(value) > truncateTo { + value = value[:truncateTo] + "..." + } + parts = append(parts, fmt.Sprintf("%s: %v", k, value)) + } + + return parts +} + +func formatMapForTooltip(m map[string]interface{}) string { + if len(m) == 0 { + return "\n{}" + } + parts := prepareMapParts(m, -1) + return "\n" + strings.Join(parts, "\n") +} + +func formatMapForLabel(m map[string]interface{}, truncateTo int) string { + if len(m) == 0 { + return "{}" + } + parts := prepareMapParts(m, truncateTo) + return "\n" + strings.Join(parts, "\\l") + "\\l" +} + +func hashCall(call *DecodedCall) string { + //we use it only to generate hash that's used to identify a node in graph, so we don't care about this function being weak + //nolint + h := sha1.New() + h.Write([]byte(fmt.Sprintf("%v", call))) + return hex.EncodeToString(h.Sum(nil)) +} + +func ordinalNumber(n int) string { + if n <= 0 { + return strconv.Itoa(n) + } + + var suffix string + switch n % 10 { + case 1: + if n%100 == 11 { + suffix = "th" + } else { + suffix = "st" + } + case 2: + if n%100 == 12 { + suffix = "th" + } else { + suffix = "nd" + } + case 3: + if n%100 == 13 { + suffix = "th" + } else { + suffix = "rd" + } + default: + suffix = "th" + } + + return strconv.Itoa(n) + suffix +} diff --git a/seth/examples/example_deployment_test.go b/seth/examples/example_deployment_test.go new file mode 100644 index 000000000..4605f636e --- /dev/null +++ b/seth/examples/example_deployment_test.go @@ -0,0 +1,84 @@ +package seth_test + +import ( + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" + network_debug_contract "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/debug" + link_token "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/link" + network_debug_sub_contract "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/sub" +) + +// Shows how to deploy a contract with parameterless constructor and bind it to it's Geth wrapper +func TestDeploymentParameterlessConstructorExample(t *testing.T) { + commonEnvVars(t) + + c, err := seth.NewClient() + require.NoError(t, err, "failed to initialise seth") + contractData, err := c.DeployContractFromContractStore(c.NewTXOpts(), "NetworkDebugSubContract.abi") + require.NoError(t, err, "failed to deploy sub-debug contract") + + contract, err := network_debug_sub_contract.NewNetworkDebugSubContract(contractData.Address, c.Client) + require.NoError(t, err, "failed to create debug contract instance") + + _, err = c.Decode(contract.TraceOneInt(c.NewTXOpts(), big.NewInt(1))) + require.NoError(t, err, "failed to decode transaction") +} + +// Shows how to deploy a contract with constructor with parameters and bind it to it's Geth wrapper +func TestDeploymentConstructorWithParametersExample(t *testing.T) { + commonEnvVars(t) + + c, err := seth.NewClient() + require.NoError(t, err, "failed to initialise seth") + contractData, err := c.DeployContractFromContractStore(c.NewTXOpts(), "NetworkDebugSubContract.abi", common.Address{}) + require.NoError(t, err, "failed to deploy debug contract") + + contract, err := network_debug_contract.NewNetworkDebugContract(contractData.Address, c.Client) + require.NoError(t, err, "failed to create debug contract instance") + + _, err = c.Decode(contract.ProcessUintArray(c.NewTXOpts(), []*big.Int{big.NewInt(1)})) + require.NoError(t, err, "failed to decode transaction") +} + +// Shows how to deploy a contract with parameterless constructor that takes ABI and BIN from Geth wrapper +// and bind it to that wrapper +func TestDeploymentFromGethWrapperExample(t *testing.T) { + commonEnvVars(t) + + c, err := seth.NewClient() + require.NoError(t, err, "failed to initialise seth") + abi, err := network_debug_contract.NetworkDebugContractMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + contractData, err := c.DeployContract(c.NewTXOpts(), "NetworkDebugSubContract", *abi, common.FromHex(network_debug_contract.NetworkDebugContractBin)) + require.NoError(t, err, "failed to deploy sub-debug contract from wrapper's ABI/BIN") + + contract, err := network_debug_sub_contract.NewNetworkDebugSubContract(contractData.Address, c.Client) + require.NoError(t, err, "failed to create debug contract instance") + + _, err = c.Decode(contract.TraceOneInt(c.NewTXOpts(), big.NewInt(1))) + require.NoError(t, err, "failed to decode transaction") +} + +func TestDeploymentLinkTokenFromGethWrapperExample(t *testing.T) { + commonEnvVars(t) + + c, err := seth.NewClient() + require.NoError(t, err, "failed to initialise seth") + abi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + c.ContractStore.ABIs["LinkToken.abi"] = *abi + require.NoError(t, err, "failed to get ABI") + contractData, err := c.DeployContract(c.NewTXOpts(), "LinkToken", *abi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + require.NoError(t, err, "failed to deploy link token contract from wrapper's ABI/BIN") + + contract, err := link_token.NewLinkToken(contractData.Address, c.Client) + require.NoError(t, err, "failed to create debug contract instance") + + _, err = c.Decode(contract.Mint(c.NewTXOpts(), c.Addresses[0], big.NewInt(1))) + require.Error(t, err, "did not fail to mint tokens") +} diff --git a/seth/examples/example_test.go b/seth/examples/example_test.go new file mode 100644 index 000000000..551a6c3ea --- /dev/null +++ b/seth/examples/example_test.go @@ -0,0 +1,220 @@ +package seth_test + +import ( + "context" + "fmt" + "math/big" + "os" + "strings" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" + "github.com/smartcontractkit/chainlink-testing-framework/seth/test_utils" + network_debug_contract "github.com/smartcontractkit/seth/contracts/bind/debug" +) + +func commonEnvVars(t *testing.T) { + t.Setenv(seth.NETWORK_ENV_VAR, seth.GETH) + t.Setenv(seth.ROOT_PRIVATE_KEY_ENV_VAR, "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80") + t.Setenv(seth.CONFIG_FILE_ENV_VAR, "../seth.toml") +} + +func deployDebugContracts(t *testing.T) *network_debug_contract.NetworkDebugContract { + c, err := seth.NewClient() + require.NoError(t, err, "failed to initialise seth") + nonce := c.NonceManager.NextNonce(c.Addresses[0]) + require.NoError(t, err, "failed to initialise seth") + subData, err := c.DeployContractFromContractStore(c.NewTXOpts(), "NetworkDebugSubContract.abi") + require.NoError(t, err, "failed to deploy sub-debug contract") + data, err := c.DeployContractFromContractStore(c.NewTXOpts(), "NetworkDebugContract.abi", subData.Address) + require.NoError(t, err, "failed to deploy debug contract") + contract, err := network_debug_contract.NewNetworkDebugContract(data.Address, c.Client) + require.NoError(t, err, "failed to create debug contract instance") + // these ^ are internal methods, so we need to update nonces manually + err = c.NonceManager.UpdateNonces() + require.NoError(t, err) + nonce2 := c.NonceManager.NextNonce(c.Addresses[0]) + require.Equal(t, big.NewInt(0).Add(nonce, big.NewInt(2)).String(), nonce2.String(), "nonces should be updated after contract deployment") + + return contract +} + +func setup(t *testing.T) *network_debug_contract.NetworkDebugContract { + commonEnvVars(t) + return deployDebugContracts(t) +} + +func TestSmokeExampleWait(t *testing.T) { + contract := setup(t) + c, err := seth.NewClient() + require.NoError(t, err) + + // receive decoded transaction or decoded err in case of revert + dec, err := c.Decode( + contract.Set(c.NewTXOpts(), big.NewInt(1)), + ) + require.NoError(t, err) + // original data + _ = dec.Transaction + _ = dec.Receipt + // decoded data + _ = dec.Input + _ = dec.Output + _ = dec.Events + res, err := contract.Get(c.NewCallOpts()) + require.NoError(t, err) + fmt.Printf("Result: %d", res.Int64()) +} + +func TestSmokeExampleMultiKey(t *testing.T) { + // example of using client with multiple keys that are provided in the config + // in this example we just generate and fund them inside NewClientWithAddresses() function + // to simulate a case, when they were provided as part of the network config, instead of being + // generated as ephemeral keys by Seth + contract := setup(t) + c := test_utils.NewClientWithAddresses(t, 10, big.NewInt(2)) + t.Cleanup(func() { + err := seth.ReturnFunds(c, c.Addresses[0].Hex()) + require.NoError(t, err) + }) + + // you can use multiple keys to really execute transactions in parallel + tx1, err1 := c.Decode(contract.Set( + c.NewTXKeyOpts(1), + big.NewInt(1), + )) + require.NoError(t, err1) + tx2, err2 := c.Decode(contract.Set( + c.NewTXKeyOpts(2), + big.NewInt(1), + )) + require.NoError(t, err2) + // original data + _ = tx1.Transaction + _ = tx1.Receipt + // decoded data + _ = tx1.Input + _ = tx1.Output + _ = tx1.Events + // original data + _ = tx2.Transaction + _ = tx2.Receipt + // decoded data + _ = tx2.Input + _ = tx2.Output + _ = tx2.Events + res, err := contract.Get(c.NewCallOpts()) + require.NoError(t, err) + fmt.Printf("Result: %d", res.Int64()) +} + +func TestSmokeExampleMultiKeyFromEnv(t *testing.T) { + // example of creating client with multiple keys that are read from environment variable called `SETH_KEYS` + // we assume here that you will be using `Geth` network + contract := setup(t) + cfg, err := seth.ReadConfig() + require.NoError(t, err) + + _, pk1, err := seth.NewAddress() + require.NoError(t, err, "failed to generate new address") + + _, pk2, err := seth.NewAddress() + require.NoError(t, err, "failed to generate new address") + + err = os.Setenv("SETH_KEYS", pk1+","+pk2) + require.NoError(t, err, "failed to set env var") + + keys := os.Getenv("SETH_KEYS") + require.NotEmpty(t, keys, "SETH_KEYS env var is empty") + var pks []string + pks = append(pks, strings.Split(keys, ",")...) + require.GreaterOrEqual(t, len(pks), 1, "SETH_KEYS env var should contain at least 1 key") + + updated := cfg.AppendPksToNetwork(pks, "Geth") + require.True(t, updated, "network should have been updated") + + c, err := seth.NewClientWithConfig(cfg) + require.NoError(t, err) + + // required as our keys have no funds + err = c.TransferETHFromKey(context.Background(), 0, c.Addresses[1].Hex(), big.NewInt(10_000_000_000_000_000), big.NewInt(100_000_000)) + require.NoError(t, err, "failed to transfer funds to pk1") + + err = c.TransferETHFromKey(context.Background(), 0, c.Addresses[2].Hex(), big.NewInt(10_000_000_000_000_000), big.NewInt(100_000_000)) + require.NoError(t, err, "failed to transfer funds to pk2") + + t.Cleanup(func() { + err = c.NonceManager.UpdateNonces() + require.NoError(t, err) + err = seth.ReturnFunds(c, c.Addresses[0].Hex()) + require.NoError(t, err) + }) + + // you can use multiple keys to really execute transactions in parallel + tx1, err1 := c.Decode(contract.Set( + c.NewTXKeyOpts(1), + big.NewInt(1), + )) + require.NoError(t, err1) + tx2, err2 := c.Decode(contract.Set( + c.NewTXKeyOpts(2), + big.NewInt(1), + )) + require.NoError(t, err2) + // original data + _ = tx1.Transaction + _ = tx1.Receipt + // decoded data + _ = tx1.Input + _ = tx1.Output + _ = tx1.Events + // original data + _ = tx2.Transaction + _ = tx2.Receipt + // decoded data + _ = tx2.Input + _ = tx2.Output + _ = tx2.Events + res, err := contract.Get(c.NewCallOpts()) + require.NoError(t, err) + fmt.Printf("Result: %d", res.Int64()) +} + +func TestSmokeExampleMultiKeyEphemeral(t *testing.T) { + // example of using ephemeral keys + // suitable for testing ephemeral networks where network is created every time + contract := setup(t) + c, err := seth.NewClient() + require.NoError(t, err) + + // you can use multiple keys to really execute transactions in parallel + tx1, err1 := c.Decode(contract.Set( + c.NewTXKeyOpts(1), + big.NewInt(1), + )) + require.NoError(t, err1) + tx2, err2 := c.Decode(contract.Set( + c.NewTXKeyOpts(2), + big.NewInt(1), + )) + require.NoError(t, err2) + // original data + _ = tx1.Transaction + _ = tx1.Receipt + // decoded data + _ = tx1.Input + _ = tx1.Output + _ = tx1.Events + // original data + _ = tx2.Transaction + _ = tx2.Receipt + // decoded data + _ = tx2.Input + _ = tx2.Output + _ = tx2.Events + res, err := contract.Get(c.NewCallOpts()) + require.NoError(t, err) + fmt.Printf("Result: %d", res.Int64()) +} diff --git a/seth/examples/example_tracing_test.go b/seth/examples/example_tracing_test.go new file mode 100644 index 000000000..587234ecf --- /dev/null +++ b/seth/examples/example_tracing_test.go @@ -0,0 +1,23 @@ +package seth_test + +import ( + "math/big" + "testing" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" + "github.com/stretchr/testify/require" +) + +// All you need to do is enable automated tracing and then wrap you contract call with `c.Decode`. +// This will automatically trace the transaction and decode it for you +func TestDecodeExample(t *testing.T) { + contract := setup(t) + c, err := seth.NewClient() + require.NoError(t, err, "failed to initialise seth") + + // when this level is set we don't need to call TraceGethTX, because it's called automatically + c.Cfg.TracingLevel = seth.TracingLevel_All + + _, err = c.Decode(contract.TraceDifferent(c.NewTXOpts(), big.NewInt(1), big.NewInt(2))) + require.NoError(t, err, "failed to decode transaction") +} diff --git a/seth/examples_wasp/README.md b/seth/examples_wasp/README.md new file mode 100644 index 000000000..3061aa504 --- /dev/null +++ b/seth/examples_wasp/README.md @@ -0,0 +1,109 @@ +## Running multi-key load test with Seth and WASP + +To effectively simulate transaction workloads from multiple keys, you can utilize a "rotating wallet." Refer to the [example](client_wasp_test.go) code provided for guidance. + +There are 2 modes: Ephemeral and a static private keys mode. + +### Ephemeral mode + +We generate 60 ephemeral keys and run the test, set `ephemeral_addresses_number` in `seth.toml` + +This mode **should never be used on testnets or mainnets** in order not to lose funds. Please use it to test with simulated networks, like private `Geth` or `Anvil` + +```toml +ephemeral_addresses_number = 60 +``` + +Then start the Geth and run the test + +``` +nix develop +make GethSync + +// another terminal, from examples_wasp dir +export SETH_LOG_LEVEL=debug +export SETH_CONFIG_PATH=seth.toml +export SETH_NETWORK=Geth +export SETH_ROOT_PRIVATE_KEY=ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80 +export LOKI_TENANT_ID=promtail +export LOKI_URL=... + +go test -v -run TestWithWasp +``` + +See both [generator](client_wasp_test.go) and [test](client_wasp_test.go) implementation example + +Check your results [here](https://grafana.ops.prod.cldev.sh/d/WaspDebug/waspdebug?orgId=1&from=now-5m&to=now) + +If you see `key sync timeout`, just increase `ephemeral_addresses_number` to have more load + +You can also change default `key_sync` values + +```toml +[nonce_manager] +# 20 req/s limit for key syncing +key_sync_rate_limit_per_sec = 20 +# key synchronization timeout, if it's more than N sec you'll see an error, raise amount of keys or increase the timeout +key_sync_timeout = "30s" +# key sync retry delay, each N seconds we'll updage each key nonce +key_sync_retry_delay = "1s" +# total number of retries until we throw an error +key_sync_retries = 30 +``` + +### Static private keys mode + +In that mode you should pass static keys that you already have as part of `Network` configuration. It's strongly recommended to do that programmatically, not via config file, since accidentally committing private keys to the repository will compromise the funds. +It would be better to read the TOML configuration first: + +```go +cfg, err := seth.ReadCfg() +if err != nil { + log.Fatal(err) +} +``` + +Then read the private keys in a safe manner. For example from a secure vault or environment variables: + +```go +var privateKeys []string +var err error +privateKeys, err = some_utils.ReadPrivateKeysFromEnv() +if err != nil { +log.Fatal(err) +} +``` + +and then add them to the `Network` you plan to use. Let's assume it's called `Sepolia`: + +```go +for i, network := range cfg.Networks { + if network.Name == "Sepolia" { + cfg.Networks[i].PrivateKeys = privateKeys + } +} +``` + +Or if you aren't using `[[Networks]]` in your TOML config and have just a single `Network`: + +```go +cfg.Network.PrivateKeys = privateKeys +``` + +Or... you can use the convenience function `AppendPksToNetwork()` to have them added to both the `Network` and `Networks` slice: + +```go +added := cfg.AppendPksToNetwork(privateKeys, "Sepolia") +if !added { + log.Fatal("Network Sepolia not found in the config") +} +``` + +Finally, proceed to create a new Seth instance: + +```go +seth, err := seth.NewClientWithConfig(cfg) +if err != nil { + log.Fatal(err) +} +``` diff --git a/seth/examples_wasp/client_main_test.go b/seth/examples_wasp/client_main_test.go new file mode 100644 index 000000000..2838523ff --- /dev/null +++ b/seth/examples_wasp/client_main_test.go @@ -0,0 +1,130 @@ +package examples_wasp + +import ( + "os" + "testing" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/pkg/errors" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" + network_debug_contract "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/debug" + link_token "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/link" + network_sub_contract "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/sub" +) + +func init() { + _ = os.Setenv("SETH_CONFIG_PATH", "seth.toml") +} + +var ( + TestEnv TestEnvironment +) + +type TestEnvironment struct { + Client *seth.Client + DebugContract *network_debug_contract.NetworkDebugContract + DebugSubContract *network_sub_contract.NetworkDebugSubContract + LinkTokenContract *link_token.LinkToken + DebugContractAddress common.Address + DebugSubContractAddress common.Address + DebugContractRaw *bind.BoundContract + ContractMap seth.ContractMap +} + +func NewDebugContractSetup() ( + *seth.Client, + *network_debug_contract.NetworkDebugContract, + common.Address, + common.Address, + *bind.BoundContract, + error, +) { + cfg, err := seth.ReadConfig() + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, err + } + cs, err := seth.NewContractStore(cfg.ABIDir, cfg.BINDir) + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, err + } + addrs, pkeys, err := cfg.ParseKeys() + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, err + } + contractMap := seth.NewEmptyContractMap() + + abiFinder := seth.NewABIFinder(contractMap, cs) + tracer, err := seth.NewTracer(cs, &abiFinder, cfg, contractMap, addrs) + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, err + } + + nm, err := seth.NewNonceManager(cfg, addrs, pkeys) + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, errors.Wrap(err, seth.ErrCreateNonceManager) + } + + c, err := seth.NewClientRaw(cfg, addrs, pkeys, seth.WithContractStore(cs), seth.WithTracer(tracer), seth.WithNonceManager(nm)) + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, err + } + subData, err := c.DeployContractFromContractStore(c.NewTXOpts(), "NetworkDebugSubContract.abi") + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, err + } + data, err := c.DeployContractFromContractStore(c.NewTXOpts(), "NetworkDebugContract.abi", subData.Address) + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, err + } + contract, err := network_debug_contract.NewNetworkDebugContract(data.Address, c.Client) + if err != nil { + return nil, nil, common.Address{}, common.Address{}, nil, err + } + return c, contract, data.Address, subData.Address, data.BoundContract, nil +} + +func TestMain(m *testing.M) { + var err error + client, debugContract, debugContractAddress, debugSubContractAddress, debugContractRaw, err := NewDebugContractSetup() + if err != nil { + panic(err) + } + + linkTokenAbi, err := link_token.LinkTokenMetaData.GetAbi() + if err != nil { + panic(err) + } + linkDeploymentData, err := client.DeployContract(client.NewTXOpts(), "LinkToken", *linkTokenAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + if err != nil { + panic(err) + } + linkToken, err := link_token.NewLinkToken(linkDeploymentData.Address, client.Client) + if err != nil { + panic(err) + } + linkAbi, err := link_token.LinkTokenMetaData.GetAbi() + if err != nil { + panic(err) + } + client.ContractStore.AddABI("LinkToken", *linkAbi) + + contractMap := seth.NewEmptyContractMap() + for k, v := range client.ContractAddressToNameMap.GetContractMap() { + contractMap.AddContract(k, v) + } + + TestEnv = TestEnvironment{ + Client: client, + DebugContract: debugContract, + LinkTokenContract: linkToken, + DebugContractAddress: debugContractAddress, + DebugSubContractAddress: debugSubContractAddress, + DebugContractRaw: debugContractRaw, + ContractMap: contractMap, + } + + exitVal := m.Run() + os.Exit(exitVal) +} diff --git a/seth/examples_wasp/client_wasp_test.go b/seth/examples_wasp/client_wasp_test.go new file mode 100644 index 000000000..f71bca8d9 --- /dev/null +++ b/seth/examples_wasp/client_wasp_test.go @@ -0,0 +1,64 @@ +package examples_wasp + +import ( + "errors" + "math/big" + "testing" + "time" + + "github.com/smartcontractkit/wasp" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" +) + +type ExampleGun struct { + client *seth.Client + Data []string +} + +func NewExampleHTTPGun(client *seth.Client) *ExampleGun { + return &ExampleGun{ + client: client, + Data: make([]string, 0), + } +} + +func (m *ExampleGun) Call(l *wasp.Generator) *wasp.Response { + _, err := m.client.Decode( + TestEnv.DebugContract.AddCounter(m.client.NewTXKeyOpts(m.client.AnySyncedKey()), big.NewInt(0), big.NewInt(1)), + ) + if err != nil { + return &wasp.Response{Error: errors.Join(err).Error()} + } + return &wasp.Response{} +} + +func TestWithWasp(t *testing.T) { + t.Setenv(seth.ROOT_PRIVATE_KEY_ENV_VAR, "ac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80") + t.Setenv(seth.CONFIG_FILE_ENV_VAR, "seth.toml") + cfg, err := seth.ReadConfig() + require.NoError(t, err, "failed to read config") + c, err := seth.NewClientWithConfig(cfg) + require.NoError(t, err, "failed to initialise seth") + labels := map[string]string{ + "go_test_name": "TestWithWasp", + "gen_name": "TestWithWasp", + "branch": "TestWithWasp", + "commit": "TestWithWasp", + } + gen, err := wasp.NewGenerator(&wasp.Config{ + LoadType: wasp.RPS, + Schedule: wasp.CombineAndRepeat( + 2, + wasp.Plain(2, 30*time.Second), + wasp.Plain(10, 30*time.Second), + wasp.Plain(2, 30*time.Second), + ), + Gun: NewExampleHTTPGun(c), + Labels: labels, + LokiConfig: wasp.NewEnvLokiConfig(), + }) + require.NoError(t, err) + gen.Run(true) +} diff --git a/seth/examples_wasp/go.mod b/seth/examples_wasp/go.mod new file mode 100644 index 000000000..17e5e267c --- /dev/null +++ b/seth/examples_wasp/go.mod @@ -0,0 +1,228 @@ +module github.com/smartcontractkit/seth-wasp-test + +go 1.22.5 + +toolchain go1.22.6 + +require ( + github.com/ethereum/go-ethereum v1.13.8 + github.com/pkg/errors v0.9.1 + github.com/smartcontractkit/chainlink-testing-framework/seth v1.2.0 + github.com/smartcontractkit/wasp v0.4.7-0.20240328221214-00cd8313cfd4 + github.com/stretchr/testify v1.9.0 +) + +require ( + github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 // indirect + github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect + github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect + github.com/Masterminds/goutils v1.1.1 // indirect + github.com/Masterminds/semver/v3 v3.2.1 // indirect + github.com/Masterminds/sprig/v3 v3.2.3 // indirect + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/armon/go-metrics v0.4.1 // indirect + github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/avast/retry-go v3.0.0+incompatible // indirect + github.com/awalterschulze/gographviz v2.0.3+incompatible // indirect + github.com/aws/aws-sdk-go v1.45.25 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect + github.com/buger/jsonparser v1.1.1 // indirect + github.com/bytedance/sonic v1.9.1 // indirect + github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b // indirect + github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 // indirect + github.com/cespare/xxhash v1.1.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/coreos/go-semver v0.3.0 // indirect + github.com/coreos/go-systemd/v22 v22.5.0 // indirect + github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/deckarep/golang-set/v2 v2.1.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/dennwc/varint v1.0.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/edsrzf/mmap-go v1.1.0 // indirect + github.com/emicklei/go-restful/v3 v3.10.2 // indirect + github.com/ethereum/c-kzg-4844 v0.4.0 // indirect + github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb // indirect + github.com/fatih/color v1.15.0 // indirect + github.com/felixge/httpsnoop v1.0.3 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/gabriel-vasile/mimetype v1.4.2 // indirect + github.com/gin-contrib/sse v0.1.0 // indirect + github.com/gin-gonic/gin v1.9.1 // indirect + github.com/go-kit/log v0.2.1 // indirect + github.com/go-logfmt/logfmt v0.6.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/go-openapi/analysis v0.21.4 // indirect + github.com/go-openapi/errors v0.20.4 // indirect + github.com/go-openapi/jsonpointer v0.20.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/loads v0.21.2 // indirect + github.com/go-openapi/spec v0.20.9 // indirect + github.com/go-openapi/strfmt v0.21.7 // indirect + github.com/go-openapi/swag v0.22.4 // indirect + github.com/go-openapi/validate v0.22.1 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.14.0 // indirect + github.com/go-redis/redis/v8 v8.11.5 // indirect + github.com/go-resty/resty/v2 v2.14.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/gogo/googleapis v1.4.1 // indirect + github.com/gogo/protobuf v1.3.3 // indirect + github.com/gogo/status v1.1.1 // indirect + github.com/golang-jwt/jwt/v4 v4.5.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb // indirect + github.com/google/btree v1.1.2 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/go-cmp v0.6.0 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/grafana/dskit v0.0.0-20231120170505-765e343eda4f // indirect + github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586 // indirect + github.com/grafana/grafana-foundation-sdk/go v0.0.0-20240326122733-6f96a993222b // indirect + github.com/grafana/loki v1.6.2-0.20231215164305-b51b7d7b5503 // indirect + github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608 // indirect + github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect + github.com/hashicorp/consul/api v1.25.1 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-cleanhttp v0.5.2 // indirect + github.com/hashicorp/go-hclog v1.5.0 // indirect + github.com/hashicorp/go-immutable-radix v1.3.1 // indirect + github.com/hashicorp/go-msgpack v0.5.5 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-rootcerts v1.0.2 // indirect + github.com/hashicorp/go-sockaddr v1.0.2 // indirect + github.com/hashicorp/golang-lru v0.6.0 // indirect + github.com/hashicorp/memberlist v0.5.0 // indirect + github.com/hashicorp/serf v0.10.1 // indirect + github.com/holiman/uint256 v1.2.4 // indirect + github.com/huandu/xstrings v1.3.3 // indirect + github.com/imdario/mergo v0.3.16 // indirect + github.com/jmespath/go-jmespath v0.4.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/jpillora/backoff v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/julienschmidt/httprouter v1.3.0 // indirect + github.com/klauspost/compress v1.17.1 // indirect + github.com/klauspost/cpuid/v2 v2.2.5 // indirect + github.com/kylelemons/godebug v1.1.0 // indirect + github.com/leodido/go-urn v1.2.4 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/miekg/dns v1.1.56 // indirect + github.com/mitchellh/copystructure v1.0.0 // indirect + github.com/mitchellh/go-homedir v1.1.0 // indirect + github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/reflectwalk v1.0.1 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/montanaflynn/stats v0.7.1 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f // indirect + github.com/oklog/ulid v1.3.1 // indirect + github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e // indirect + github.com/opentracing-contrib/go-stdlib v1.0.0 // indirect + github.com/opentracing/opentracing-go v1.2.0 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect + github.com/pelletier/go-toml/v2 v2.2.2 // indirect + github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/alertmanager v0.26.0 // indirect + github.com/prometheus/client_golang v1.17.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/common/sigv4 v0.1.0 // indirect + github.com/prometheus/exporter-toolkit v0.10.1-0.20230714054209-2f4150c63f97 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/prometheus/prometheus v0.47.2-0.20231010075449-4b9c19fe5510 // indirect + github.com/rs/zerolog v1.30.0 // indirect + github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect + github.com/sercand/kuberesolver/v5 v5.1.1 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/shopspring/decimal v1.2.0 // indirect + github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449 // indirect + github.com/soheilhy/cmux v0.1.5 // indirect + github.com/sony/gobreaker v0.5.0 // indirect + github.com/spf13/cast v1.3.1 // indirect + github.com/spf13/pflag v1.0.5 // indirect + github.com/stretchr/objx v0.5.2 // indirect + github.com/supranational/blst v0.3.11 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/uber/jaeger-client-go v2.30.0+incompatible // indirect + github.com/uber/jaeger-lib v2.4.1+incompatible // indirect + github.com/ugorji/go/codec v1.2.11 // indirect + go.etcd.io/etcd/api/v3 v3.5.7 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.7 // indirect + go.etcd.io/etcd/client/v3 v3.5.7 // indirect + go.mongodb.org/mongo-driver v1.12.0 // indirect + go.opentelemetry.io/collector/pdata v1.0.0-rcv0015 // indirect + go.opentelemetry.io/collector/semconv v0.81.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 // indirect + go.opentelemetry.io/otel v1.19.0 // indirect + go.opentelemetry.io/otel/metric v1.19.0 // indirect + go.opentelemetry.io/otel/trace v1.19.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/goleak v1.2.1 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/ratelimit v0.3.0 // indirect + go.uber.org/zap v1.26.0 // indirect + go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect + golang.org/x/arch v0.4.0 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/net v0.27.0 // indirect + golang.org/x/oauth2 v0.15.0 // indirect + golang.org/x/sync v0.7.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/term v0.22.0 // indirect + golang.org/x/text v0.16.0 // indirect + golang.org/x/time v0.6.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c // indirect + google.golang.org/grpc v1.59.0 // indirect + google.golang.org/protobuf v1.32.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.28.2 // indirect + k8s.io/apimachinery v0.28.2 // indirect + k8s.io/client-go v0.28.2 // indirect + k8s.io/klog/v2 v2.100.1 // indirect + k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 // indirect + k8s.io/utils v0.0.0-20230711102312-30195339c3c7 // indirect + nhooyr.io/websocket v1.8.7 // indirect + rsc.io/tmplfunc v0.0.3 // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect +) + +replace ( + github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 + github.com/smartcontractkit/chainlink-testing-framework/seth => ../ +) diff --git a/seth/examples_wasp/go.sum b/seth/examples_wasp/go.sum new file mode 100644 index 000000000..3d2771992 --- /dev/null +++ b/seth/examples_wasp/go.sum @@ -0,0 +1,1349 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v65.0.0+incompatible h1:HzKLt3kIwMm4KeJYTdx9EbjRYTySD/t8i1Ee/W5EGXw= +github.com/Azure/azure-sdk-for-go v65.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0 h1:8q4SaHjFsClSvuVne0ID/5Ka8u3fcIHyqkLjcFpNRHQ= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.29 h1:I4+HL/JDvErx2LjyzaVxllw2lRDB5/BT2Bm4g20iqYw= +github.com/Azure/go-autorest/autorest v0.11.29/go.mod h1:ZtEzC4Jy2JDrZLxvWs8LrBWEBycl1hbT1eknI8MtfAs= +github.com/Azure/go-autorest/autorest/adal v0.9.23 h1:Yepx8CvFxwNKpH6ja7RZ+sKX+DWYNldbLiALMC3BTz8= +github.com/Azure/go-autorest/autorest/adal v0.9.23/go.mod h1:5pcMqFkdPhviJdlEy3kC/v1ZLnQl0MH6XA5YCcMhy4c= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= +github.com/Azure/go-autorest/autorest/to v0.4.0/go.mod h1:fE8iZBn7LQR7zH/9XU2NcPR4o9jEImooCeWJcYV/zLE= +github.com/Azure/go-autorest/autorest/validation v0.3.1 h1:AgyqjAd94fwNAoTjl/WQXg4VvFeRFpO+UhNyRXqF1ac= +github.com/Azure/go-autorest/autorest/validation v0.3.1/go.mod h1:yhLgjC0Wda5DYXl6JAsWyUe4KVNffhoDhG0zVzUMo3E= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY= +github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ= +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= +github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= +github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= +github.com/Masterminds/goutils v1.1.1/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= +github.com/Masterminds/semver/v3 v3.2.0/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0= +github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ= +github.com/Masterminds/sprig/v3 v3.2.3 h1:eL2fZNezLomi0uOLqjQoN6BfsDD+fyLtgbJMAj9n6YA= +github.com/Masterminds/sprig/v3 v3.2.3/go.mod h1:rXcFaZ2zZbLRJv/xSysmlgIM1u11eBaRMhvYXJNkGuM= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/OneOfOne/xxhash v1.2.6 h1:U68crOE3y3MPttCMQGywZOLrTeF5HHJ3/vDBCJn9/bA= +github.com/OneOfOne/xxhash v1.2.6/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= +github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= +github.com/Workiva/go-datastructures v1.1.0 h1:hu20UpgZneBhQ3ZvwiOGlqJSKIosin2Rd5wAKUHEO/k= +github.com/Workiva/go-datastructures v1.1.0/go.mod h1:1yZL+zfsztete+ePzZz/Zb1/t5BnDuE2Ya2MMGhzP6A= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZpUGpz5+4FfNmIU+FmZg2P3Xaj2v2bfNWmk= +github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc= +github.com/alicebob/miniredis v2.5.0+incompatible h1:yBHoLpsyjupjz3NL3MhKMVkR41j82Yjf3KFv7ApYzUI= +github.com/alicebob/miniredis/v2 v2.30.4 h1:8S4/o1/KoUArAGbGwPxcwf0krlzceva2XVOSchFS7Eo= +github.com/alicebob/miniredis/v2 v2.30.4/go.mod h1:b25qWj4fCEsBeAAR2mlb0ufImGC6uH3VlUfb/HS5zKg= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= +github.com/armon/go-metrics v0.4.1 h1:hR91U9KYmb6bLBYLQjyM+3j+rcd/UhE+G78SFnF8gJA= +github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= +github.com/asaskevich/govalidator v0.0.0-20200907205600-7a23bdc65eef/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= +github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= +github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= +github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E= +github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= +github.com/aws/aws-sdk-go v1.38.35/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro= +github.com/aws/aws-sdk-go v1.45.25 h1:c4fLlh5sLdK2DCRTY1z0hyuJZU4ygxX8m1FswL6/nF4= +github.com/aws/aws-sdk-go v1.45.25/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI= +github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLoBaFpOOds6QyY1L8AX7uoY+Ln3BHc22W40X0= +github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df/go.mod h1:hiVxq5OP2bUGBRNS3Z/bt/reCLFNbdcST6gISi1fiOM= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= +github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= +github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b h1:6+ZFm0flnudZzdSE0JxlhR2hKnGPcNB35BjQf4RYQDY= +github.com/c2h5oh/datasize v0.0.0-20220606134207-859f65c6625b/go.mod h1:S/7n9copUssQ56c7aAgHqftWO4LTf4xY6CGWt8Bc+3M= +github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8 h1:SjZ2GvvOononHOpK84APFuMvxqsk3tEIaKH/z4Rpu3g= +github.com/c9s/goprocinfo v0.0.0-20210130143923-c95fcf8c64a8/go.mod h1:uEyr4WpAH4hio6LFriaPkL938XnrvLpNPmQHBdrmbIE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag= +github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= +github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= +github.com/cockroachdb/redact v1.0.8 h1:8QG/764wK+vmEYoOlfobpe12EQcS81ukx/a4hdVMxNw= +github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 h1:IKgmqgMQlVJIZj19CdocBeSfSaiCbEBZGKODaixqtHM= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= +github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/dennwc/varint v1.0.0 h1:kGNFFSSw8ToIy3obO/kKr8U9GZYUAxQEVuix4zfDWzE= +github.com/dennwc/varint v1.0.0/go.mod h1:hnItb35rvZvJrbTALZtY/iQfDs48JKRG1RPpgziApxA= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/digitalocean/godo v1.99.0 h1:gUHO7n9bDaZFWvbzOum4bXE0/09ZuYA9yA8idQHX57E= +github.com/digitalocean/godo v1.99.0/go.mod h1:SsS2oXo2rznfM/nORlZ/6JaUJZFhmKTib1YhopUc8NA= +github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM= +github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/edsrzf/mmap-go v1.1.0 h1:6EUwBLQ/Mcr1EYLE4Tn1VdW1A4ckqCQWZBw8Hr0kjpQ= +github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q= +github.com/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE= +github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.11.1 h1:wSUXTlLfiAQRWs2F+p+EKOY9rUyis1MyGqJ2DIk5HpM= +github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQLaTEw+YhGluxZkrTmD0g= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= +github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.13.8 h1:1od+thJel3tM52ZUNQwvpYOeRHlbkVFZ5S8fhi0Lgsg= +github.com/ethereum/go-ethereum v1.13.8/go.mod h1:sc48XYQxCzH3fG9BcrXCOOgQk2JfZzNAmIKnceogzsA= +github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb h1:IT4JYU7k4ikYg1SCxNI1/Tieq/NFvh6dzLdgi7eu0tM= +github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW64qjjJq8M2u4dxNaBiDfKK+z/3eGDpXEQhc= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU= +github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= +github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= +github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/felixge/httpsnoop v1.0.3 h1:s/nj+GCswXYzN5v2DpNMuMQYe+0DDwt5WVCU6CWBdXk= +github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= +github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= +github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= +github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= +github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= +github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= +github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.1 h1:MRVx0/zhvdseW+Gza6N9rVzU/IVzaeE1SFI4raAhmBU= +github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.6.0 h1:wGYYu3uicYdqXVgoYbvnkrPVXkuLM1p1ifugDMEdRi4= +github.com/go-logfmt/logfmt v0.6.0/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/go-openapi/analysis v0.21.2/go.mod h1:HZwRk4RRisyG8vx2Oe6aqeSQcoxRp47Xkp3+K6q+LdY= +github.com/go-openapi/analysis v0.21.4 h1:ZDFLvSNxpDaomuCueM0BlSXxpANBlFYiBvr+GXrvIHc= +github.com/go-openapi/analysis v0.21.4/go.mod h1:4zQ35W4neeZTqh3ol0rv/O8JBbka9QyAgQRPp9y3pfo= +github.com/go-openapi/errors v0.19.8/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.19.9/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.2/go.mod h1:cM//ZKUKyO06HSwqAelJ5NsEMMcpa6VpXe8DOa1Mi1M= +github.com/go-openapi/errors v0.20.4 h1:unTcVm6PispJsMECE3zWgvG4xTiKda1LIR5rCRWLG6M= +github.com/go-openapi/errors v0.20.4/go.mod h1:Z3FlZ4I8jEGxjUK+bugx3on2mIAk4txuAOhlsB1FSgk= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.20.0 h1:ESKJdU9ASRfaPNOPRx12IUyA1vn3R9GiE3KYD14BXdQ= +github.com/go-openapi/jsonpointer v0.20.0/go.mod h1:6PGzBjjIIumbLYysB73Klnms1mwnU4G3YHOECG3CedA= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/loads v0.21.1/go.mod h1:/DtAMXXneXFjbQMGEtbamCZb+4x7eGwkvZCvBmwUG+g= +github.com/go-openapi/loads v0.21.2 h1:r2a/xFIYeZ4Qd2TnGpWDIQNcP80dIaZgf704za8enro= +github.com/go-openapi/loads v0.21.2/go.mod h1:Jq58Os6SSGz0rzh62ptiu8Z31I+OTHqmULx5e/gJbNw= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/spec v0.20.6/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/spec v0.20.9 h1:xnlYNQAwKd2VQRRfwTEI0DcK+2cbuvI/0c7jx3gA8/8= +github.com/go-openapi/spec v0.20.9/go.mod h1:2OpW+JddWPrpXSCIX8eOx7lZ5iyuWj3RYR6VaaBKcWA= +github.com/go-openapi/strfmt v0.21.0/go.mod h1:ZRQ409bWMj+SOgXofQAGTIo2Ebu72Gs+WaRADcS5iNg= +github.com/go-openapi/strfmt v0.21.1/go.mod h1:I/XVKeLc5+MM5oPNN7P6urMOpuLXEcNrCX/rPGuWb0k= +github.com/go-openapi/strfmt v0.21.3/go.mod h1:k+RzNO0Da+k3FrrynSNN8F7n/peCmQQqbbXjtDfvmGg= +github.com/go-openapi/strfmt v0.21.7 h1:rspiXgNWgeUzhjo1YU01do6qsahtJNByjLVbPLNHb8k= +github.com/go-openapi/strfmt v0.21.7/go.mod h1:adeGTkxE44sPyLk0JV235VQAO/ZXUr8KAzYjclFs3ew= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.22.4 h1:QLMzNJnMGPRNDCbySlcj1x01tzU8/9LTTL9hZZZogBU= +github.com/go-openapi/swag v0.22.4/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/validate v0.22.1 h1:G+c2ub6q47kfX1sOBLwIQwzBVt8qmOAARyo/9Fqs9NU= +github.com/go-openapi/validate v0.22.1/go.mod h1:rjnrwK57VJ7A8xqfpAOEKRH8yQSGUriMu5/zuPSQ1hg= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= +github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js= +github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= +github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= +github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= +github.com/go-resty/resty/v2 v2.14.0 h1:/rhkzsAqGQkozwfKS5aFAbb6TyKd3zyFRWcdRXLPCAU= +github.com/go-resty/resty/v2 v2.14.0/go.mod h1:IW6mekUOsElt9C7oWr0XRt9BNSD6D5rr9mhk6NjmNHg= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg= +github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw= +github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0= +github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY= +github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg= +github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI= +github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs= +github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI= +github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk= +github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28= +github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo= +github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk= +github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw= +github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360= +github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg= +github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE= +github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8= +github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc= +github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4= +github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ= +github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0= +github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= +github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= +github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= +github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.0.2 h1:CoAavW/wd/kulfZmSIBt6p24n4j7tHgNVCjsfHVNUbo= +github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= +github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= +github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= +github.com/gogo/status v1.1.1 h1:DuHXlSFHNKqTQ+/ACf5Vs6r4X/dH2EgIzR9Vr+H65kg= +github.com/gogo/status v1.1.1/go.mod h1:jpG3dM5QPcqu19Hg8lkUhBFBa3TcLs1DG7+2Jqci7oU= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= +github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8 h1:n6vlPhxsA+BW/XsS5+uqi7GyzaLa5MH7qlSLBZtRdiA= +github.com/google/pprof v0.0.0-20230705174524-200ffdc848b8/go.mod h1:Jh3hGz2jkYak8qXPD19ryItVnUgpgeqzdkY/D0EaeuA= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gophercloud/gophercloud v1.5.0 h1:cDN6XFCLKiiqvYpjQLq9AiM7RDRbIC9450WpPH+yvXo= +github.com/gophercloud/gophercloud v1.5.0/go.mod h1:aAVqcocTSXh2vYFZ1JTvx4EQmfgzxRcNupUfxZbBNDM= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/grafana/dskit v0.0.0-20231120170505-765e343eda4f h1:gyojr97YeWZ70pKNakWv5/tKwBHuLy3icnIeCo9gQr4= +github.com/grafana/dskit v0.0.0-20231120170505-765e343eda4f/go.mod h1:8dsy5tQOkeNQyjXpm5mQsbCu3H5uzeBD35MzRQFznKU= +github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586 h1:/of8Z8taCPftShATouOrBVy6GaTTjgQd/VfNiZp/VXQ= +github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586/go.mod h1:PGk3RjYHpxMM8HFPhKKo+vve3DdlPUELZLSDEFehPuU= +github.com/grafana/grafana-foundation-sdk/go v0.0.0-20240326122733-6f96a993222b h1:Msqs1nc2qWMxTriDCITKl58Td+7Md/RURmUmH7RXKns= +github.com/grafana/grafana-foundation-sdk/go v0.0.0-20240326122733-6f96a993222b/go.mod h1:WtWosval1KCZP9BGa42b8aVoJmVXSg0EvQXi9LDSVZQ= +github.com/grafana/loki v1.6.2-0.20231215164305-b51b7d7b5503 h1:gdrsYbmk8822v6qvPwZO5DC6QjnAW7uKJ9YXnoUmV8c= +github.com/grafana/loki v1.6.2-0.20231215164305-b51b7d7b5503/go.mod h1:d8seWXCEXkL42mhuIJYcGi6DxfehzoIpLrMQWJojvOo= +github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608 h1:ZYk42718kSXOiIKdjZKljWLgBpzL5z1yutKABksQCMg= +github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608/go.mod h1:f3JSoxBTPXX5ec4FxxeC19nTBSxoTz+cBgS3cYLMcr0= +github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD0O/HjhShYuM6XE0i/lbE6J94kww= +github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= +github.com/grpc-ecosystem/grpc-opentracing v0.0.0-20180507213350-8e809c8a8645/go.mod h1:6iZfnjpejD4L/4DwD7NryNaJyCQdzwWwH2MWhCA90Kw= +github.com/hashicorp/consul/api v1.25.1 h1:CqrdhYzc8XZuPnhIYZWH45toM0LB9ZeYr/gvpLVI3PE= +github.com/hashicorp/consul/api v1.25.1/go.mod h1:iiLVwR/htV7mas/sy0O+XSuEnrdBUUydemjxcUrAt4g= +github.com/hashicorp/consul/sdk v0.14.1 h1:ZiwE2bKb+zro68sWzZ1SgHF3kRMBZ94TwOCFRF4ylPs= +github.com/hashicorp/consul/sdk v0.14.1/go.mod h1:vFt03juSzocLRFo59NkeQHHmQa6+g7oU0pfzdI1mUhg= +github.com/hashicorp/cronexpr v1.1.2 h1:wG/ZYIKT+RT3QkOdgYc+xsKWVRgnxJ1OJtjjy84fJ9A= +github.com/hashicorp/cronexpr v1.1.2/go.mod h1:P4wA0KBl9C5q2hABiMO7cp6jcIg96CDh1Efb3g1PWA4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= +github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= +github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-hclog v1.5.0 h1:bI2ocEMgcVlz55Oj1xZNBsVi900c7II+fWDyV9o+13c= +github.com/hashicorp/go-hclog v1.5.0/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= +github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-immutable-radix v1.3.1 h1:DKHmCUm2hRBK510BaiZlwvpD40f8bJFeZnpfm2KLowc= +github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= +github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-msgpack v0.5.5 h1:i9R9JSrqIz0QVLz3sz+i3YJdT7TTSLcfLLzJi9aZTuI= +github.com/hashicorp/go-msgpack v0.5.5/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= +github.com/hashicorp/go-multierror v1.1.0/go.mod h1:spPvp8C1qA32ftKqdAHm4hHTbPw+vmowP0z+KUhOZdA= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs= +github.com/hashicorp/go-retryablehttp v0.7.4 h1:ZQgVdpTdAL7WpMIwLzCfbalOcSUdkDZnpUv3/+BxzFA= +github.com/hashicorp/go-retryablehttp v0.7.4/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8= +github.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc= +github.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8= +github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= +github.com/hashicorp/go-sockaddr v1.0.2 h1:ztczhD1jLxIRjVejw8gFomI1BQZOe2WoVOu0SyteCQc= +github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A= +github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= +github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= +github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.6.0 h1:uL2shRDx7RTrOrTCUZEGP/wJUFiUI8QT6E7z5o8jga4= +github.com/hashicorp/golang-lru v0.6.0/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= +github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= +github.com/hashicorp/mdns v1.0.4/go.mod h1:mtBihi+LeNXGtG8L9dX59gAEa12BDtBQSp4v/YAJqrc= +github.com/hashicorp/memberlist v0.5.0 h1:EtYPN8DpAURiapus508I4n9CzHs2W+8NZGbmmR/prTM= +github.com/hashicorp/memberlist v0.5.0/go.mod h1:yvyXLpo0QaGE59Y7hDTsTzDD25JYBZ4mHgHUZ8lrOI0= +github.com/hashicorp/nomad/api v0.0.0-20230718173136-3a687930bd3e h1:sr4lujmn9heD030xx/Pd4B/JSmvRhFzuotNXaaV0WLs= +github.com/hashicorp/nomad/api v0.0.0-20230718173136-3a687930bd3e/go.mod h1:O23qLAZuCx4htdY9zBaO4cJPXgleSFEdq6D/sezGgYE= +github.com/hashicorp/serf v0.10.1 h1:Z1H2J60yRKvfDYAOZLd2MU0ND4AH/WDz7xYHDWQsIPY= +github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfEvMqbG+4= +github.com/hetznercloud/hcloud-go/v2 v2.0.0 h1:Sg1DJ+MAKvbYAqaBaq9tPbwXBS2ckPIaMtVdUjKu+4g= +github.com/hetznercloud/hcloud-go/v2 v2.0.0/go.mod h1:4iUG2NG8b61IAwNx6UsMWQ6IfIf/i1RsG0BbsKAyR5Q= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/huandu/xstrings v1.3.3 h1:/Gcsuc1x8JVbJ9/rlye4xZnVAbEkGauT8lbebqcQws4= +github.com/huandu/xstrings v1.3.3/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/ionos-cloud/sdk-go/v6 v6.1.8 h1:493wE/BkZxJf7x79UCE0cYGPZoqQcPiEBALvt7uVGY0= +github.com/ionos-cloud/sdk-go/v6 v6.1.8/go.mod h1:EzEgRIDxBELvfoa/uBN0kOQaqovLjUWEB7iW4/Q+t4k= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U= +github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4= +github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.17.1 h1:NE3C767s2ak2bweCZo3+rdP4U/HoyVXLv/X9f2gPS5g= +github.com/klauspost/compress v1.17.1/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= +github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= +github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b h1:udzkj9S/zlT5X367kqJis0QP7YMxobob6zhzq6Yre00= +github.com/kolo/xmlrpc v0.0.0-20220921171641-a4b6fa1dd06b/go.mod h1:pcaDhQK0/NJZEvtCO0qQPPropqV0sJOJ6YW7X+9kRwM= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= +github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q= +github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= +github.com/linode/linodego v1.19.0 h1:n4WJrcr9+30e9JGZ6DI0nZbm5SdAj1kSwvvt/998YUw= +github.com/linode/linodego v1.19.0/go.mod h1:XZFR+yJ9mm2kwf6itZ6SCpu+6w3KnIevV0Uu5HNWJgQ= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE= +github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/miekg/dns v1.1.26/go.mod h1:bPDLeHnStXmXAq1m/Ch/hvfNHr14JKNPMBo3VZKjuso= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/dns v1.1.56 h1:5imZaSeoRNvpM9SzWNhEcP9QliKiz20/dA2QabIGVnE= +github.com/miekg/dns v1.1.56/go.mod h1:cRm6Oo2C8TY9ZS/TqsSrseAcncm74lfK5G+ikN2SWWY= +github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= +github.com/mitchellh/cli v1.1.0/go.mod h1:xcISNoH86gajksDmfB23e/pu+B+GeFRMYmoHXxx3xhI= +github.com/mitchellh/copystructure v1.0.0 h1:Laisrj+bAB6b/yJwB5Bt3ITZhGJdqmxquMKeZ+mmkFQ= +github.com/mitchellh/copystructure v1.0.0/go.mod h1:SNtv71yrdKgLRyLFxmLdkAbkKEFWgYaq1OVrnRcwhnw= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo= +github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.3.3/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mitchellh/reflectwalk v1.0.1 h1:FVzMWA5RllMAKIdUSC8mdWo3XtwoecrH79BY70sEEpE= +github.com/mitchellh/reflectwalk v1.0.1/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.9.7 h1:06xGQy5www2oN160RtEZoTvnP2sPhEfePYmCDc2szss= +github.com/onsi/ginkgo/v2 v2.9.7/go.mod h1:cxrmXWykAwTwhQsJOPfdIDiJ+l2RYq7U8hFU+M/1uw0= +github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= +github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM= +github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e h1:4cPxUYdgaGzZIT5/j0IfqOrrXmq6bG8AwvwisMXpdrg= +github.com/opentracing-contrib/go-grpc v0.0.0-20210225150812-73cb765af46e/go.mod h1:DYR5Eij8rJl8h7gblRrOZ8g0kW1umSpKqYIBTgeDtLo= +github.com/opentracing-contrib/go-stdlib v1.0.0 h1:TBS7YuVotp8myLon4Pv7BtCBzOTo1DeZCld0Z63mW2w= +github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= +github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs= +github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc= +github.com/ovh/go-ovh v1.4.1 h1:VBGa5wMyQtTP7Zb+w97zRCh9sLtM/2YKRyy+MEJmWaM= +github.com/ovh/go-ovh v1.4.1/go.mod h1:6bL6pPyUT7tBfI0pqOegJgRjgjuO+mOo+MyXd1EEC0M= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= +github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= +github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pierrec/lz4/v4 v4.1.18 h1:xaKrnTkyoqfh1YItXl56+6KJNVYWlEEPuAQW9xsplYQ= +github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= +github.com/posener/complete v1.2.3/go.mod h1:WZIdtGGp+qx0sLrYKtIRAruyNpv6hFCicSgv7Sy7s/s= +github.com/prometheus/alertmanager v0.26.0 h1:uOMJWfIwJguc3NaM3appWNbbrh6G/OjvaHMk22aBBYc= +github.com/prometheus/alertmanager v0.26.0/go.mod h1:rVcnARltVjavgVaNnmevxK7kOn7IZavyf0KNgHkbEpU= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/common/sigv4 v0.1.0 h1:qoVebwtwwEhS85Czm2dSROY5fTo2PAPEVdDeppTwGX4= +github.com/prometheus/common/sigv4 v0.1.0/go.mod h1:2Jkxxk9yYvCkE5G1sQT7GuEXm57JrvHu9k5YwTjsNtI= +github.com/prometheus/exporter-toolkit v0.10.1-0.20230714054209-2f4150c63f97 h1:oHcfzdJnM/SFppy2aUlvomk37GI33x9vgJULihE5Dt8= +github.com/prometheus/exporter-toolkit v0.10.1-0.20230714054209-2f4150c63f97/go.mod h1:LoBCZeRh+5hX+fSULNyFnagYlQG/gBsyA/deNzROkq8= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/prometheus/prometheus v0.47.2-0.20231010075449-4b9c19fe5510 h1:6ksZ7t1hNOzGPPs8DK7SvXQf6UfWzi+W5Z7PCBl8gx4= +github.com/prometheus/prometheus v0.47.2-0.20231010075449-4b9c19fe5510/go.mod h1:UC0TwJiF90m2T3iYPQBKnGu8gv3s55dF/EgpTq8gyvo= +github.com/pyroscope-io/client v0.7.1 h1:yFRhj3vbgjBxehvxQmedmUWJQ4CAfCHhn+itPsuWsHw= +github.com/pyroscope-io/client v0.7.1/go.mod h1:4h21iOU4pUOq0prKyDlvYRL+SCKsBc5wKiEtV+rJGqU= +github.com/pyroscope-io/godeltaprof v0.1.2 h1:MdlEmYELd5w+lvIzmZvXGNMVzW2Qc9jDMuJaPOR75g4= +github.com/pyroscope-io/godeltaprof v0.1.2/go.mod h1:psMITXp90+8pFenXkKIpNhrfmI9saQnPbba27VIaiQE= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= +github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= +github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.20 h1:a9hSJdJcd16e0HoMsnFvaHvxB3pxSD+SC7+CISp7xY0= +github.com/scaleway/scaleway-sdk-go v1.0.0-beta.20/go.mod h1:fCa7OJZ/9DRTnOKmxvT6pn+LPWUptQAmHF/SBJUGEcg= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= +github.com/sercand/kuberesolver/v5 v5.1.1 h1:CYH+d67G0sGBj7q5wLK61yzqJJ8gLLC8aeprPTHb6yY= +github.com/sercand/kuberesolver/v5 v5.1.1/go.mod h1:Fs1KbKhVRnB2aDWN12NjKCB+RgYMWZJ294T3BtmVCpQ= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449 h1:fX/xmGm1GBsD1ZZnooNT+eWA0hiTAqFlHzOC5CY4dy8= +github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449/go.mod h1:DC8sQMyTlI/44UCTL8QWFwb0bYNoXCfjwCv2hMivYZU= +github.com/smartcontractkit/wasp v0.4.7-0.20240328221214-00cd8313cfd4 h1:JO2hGwIa5dBrd/ffj047/Iqms73JMH7ht3cgMkt1PZk= +github.com/smartcontractkit/wasp v0.4.7-0.20240328221214-00cd8313cfd4/go.mod h1:jeabvyXikb2aNoLQwcZGqaz17efrR8NJhpq4seAmdgs= +github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= +github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= +github.com/sony/gobreaker v0.5.0 h1:dRCvqm0P490vZPmy7ppEk2qCnCieBooFJ+YoXGYB+yg= +github.com/sony/gobreaker v0.5.0/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/uber/jaeger-client-go v2.30.0+incompatible h1:D6wyKGCecFaSRUpo8lCVbaOOb6ThwMmTEbhRwtKR97o= +github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk= +github.com/uber/jaeger-lib v2.4.1+incompatible h1:td4jdvLcExb4cBISKIpHuGoVXh+dVKhn2Um6rjCsSsg= +github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U= +github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= +github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= +github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU= +github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= +github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= +github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs= +github.com/vultr/govultr/v2 v2.17.2/go.mod h1:ZFOKGWmgjytfyjeyAdhQlSWwTjh2ig+X49cAp50dzXI= +github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI= +github.com/xdg-go/scram v1.0.2/go.mod h1:1WAq6h33pAW+iRreB34OORO2Nf7qel3VV3fjBj+hCSs= +github.com/xdg-go/scram v1.1.1/go.mod h1:RaEWvsqvNKKvBPvcKeFjrG2cJqOkHTiyTpzz23ni57g= +github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4= +github.com/xdg-go/stringprep v1.0.2/go.mod h1:8F9zXuvzgwmyT5DUm4GUfZGDdT3W+LCvS6+da4O5kxM= +github.com/xdg-go/stringprep v1.0.3/go.mod h1:W3f5j4i+9rC0kuIEJL0ky1VpHXQU3ocBgklLGvcBnW8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yuin/gopher-lua v1.1.0 h1:BojcDhfyDWgU2f2TOzYK/g5p2gxMrku8oupLDqlnSqE= +github.com/yuin/gopher-lua v1.1.0/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw= +go.etcd.io/etcd/api/v3 v3.5.7 h1:sbcmosSVesNrWOJ58ZQFitHMdncusIifYcrBfwrlJSY= +go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA= +go.etcd.io/etcd/client/pkg/v3 v3.5.7 h1:y3kf5Gbp4e4q7egZdn5T7W9TSHUvkClN6u+Rq9mEOmg= +go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY= +go.etcd.io/etcd/client/v3 v3.5.7 h1:u/OhpiuCgYY8awOHlhIhmGIGpxfBU/GZBUP3m/3/Iz4= +go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw= +go.mongodb.org/mongo-driver v1.7.3/go.mod h1:NqaYOwnXWr5Pm7AOpO5QFxKJ503nbMse/R79oO62zWg= +go.mongodb.org/mongo-driver v1.7.5/go.mod h1:VXEWRZ6URJIkUq2SCAyapmhH0ZLRBP+FT4xhp5Zvxng= +go.mongodb.org/mongo-driver v1.10.0/go.mod h1:wsihk0Kdgv8Kqu1Anit4sfK+22vSFbUrAVEYRhCXrA8= +go.mongodb.org/mongo-driver v1.12.0 h1:aPx33jmn/rQuJXPQLZQ8NtfPQG8CaqgLThFtqRb0PiE= +go.mongodb.org/mongo-driver v1.12.0/go.mod h1:AZkxhPnFJUoH7kZlFkVKucV20K387miPfm7oimrSmK0= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opentelemetry.io/collector/pdata v1.0.0-rcv0015 h1:8PzrQFk3oKiT1Sd5EmNEcagdMyt1KcBy5/OyF5He5gY= +go.opentelemetry.io/collector/pdata v1.0.0-rcv0015/go.mod h1:I1PqyHJlsXjANC73tp43nDId7/jiv82NoZZ6uS0xdwM= +go.opentelemetry.io/collector/semconv v0.81.0 h1:lCYNNo3powDvFIaTPP2jDKIrBiV1T92NK4QgL/aHYXw= +go.opentelemetry.io/collector/semconv v0.81.0/go.mod h1:TlYPtzvsXyHOgr5eATi43qEMqwSmIziivJB2uctKswo= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZx8cOF0+Kkazoc7lwUNMGy0LrzEMxTm4BbTxg= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q= +go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= +go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= +go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= +go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= +go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= +go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw= +go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go4.org/netipx v0.0.0-20230125063823-8449b0a6169f h1:ketMxHg+vWm3yccyYiq+uK8D3fRmna2Fcj+awpQp84s= +go4.org/netipx v0.0.0-20230125063823-8449b0a6169f/go.mod h1:tgPU4N2u9RByaTN3NC2p9xOzyFpte4jYwsIIRF7XlSc= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.4.0 h1:A8WCeEWhLwPBKNbFi5Wv5UTCBx5zzubnXDlMOFAzFMc= +golang.org/x/arch v0.4.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190923035154-9ee001bba392/go.mod h1:/lpIB1dKB+9EgE3H3cr1v9wB50oz8l4C4h62xy7jSTY= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190921015927-1a5e07d1ff72/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190923162816-aa69164e4478/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210410081132-afb366fc7cd1/go.mod h1:9tjilg8BloeKEkVJvy7fQ90B1CfIiPueXVOjqfkSzI8= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= +golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys= +golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190922100055-0a153f010e69/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190907020128-2ca718005c18/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200324203455-a04cca1dde73/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 h1:SeZZZx0cP0fqUyA+oRzP9k7cSwJlvDFiROO72uwD6i0= +google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97/go.mod h1:t1VqOqqvce95G3hIDCT5FeO3YUc6Q4Oe24L/+rNMxRk= +google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a h1:myvhA4is3vrit1a6NZCWBIwN0kNEnX21DJOJX/NvIfI= +google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a/go.mod h1:SUBoKXbI1Efip18FClrQVGjWcyd0QZd8KkvdP34t7ww= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c h1:jHkCUWkseRf+W+edG5hMzr/Uh1xkDREY4caybAq4dpY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c/go.mod h1:4cYg8o5yUbm77w8ZX00LhMVNl/YVBFJRYWDc0uYWMs0= +google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200605160147-a5ece683394c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.28.2 h1:9mpl5mOb6vXZvqbQmankOfPIGiudghwCoLl1EYfUZbw= +k8s.io/api v0.28.2/go.mod h1:RVnJBsjU8tcMq7C3iaRSGMeaKt2TWEUXcpIt/90fjEg= +k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ= +k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU= +k8s.io/client-go v0.28.2 h1:DNoYI1vGq0slMBN/SWKMZMw0Rq+0EQW6/AK4v9+3VeY= +k8s.io/client-go v0.28.2/go.mod h1:sMkApowspLuc7omj1FOSUxSoqjr+d5Q0Yc0LOFnYFJY= +k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg= +k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 h1:LyMgNKD2P8Wn1iAwQU5OhxCKlKJy0sHc+PcDwFB24dQ= +k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9/go.mod h1:wZK2AVp1uHCp4VamDVgBP2COHZjqD1T68Rf0CM3YjSM= +k8s.io/utils v0.0.0-20230711102312-30195339c3c7 h1:ZgnF1KZsYxWIifwSNZFZgNtWE89WI5yiP5WwlfDoIyc= +k8s.io/utils v0.0.0-20230711102312-30195339c3c7/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +nhooyr.io/websocket v1.8.7 h1:usjR2uOr/zjjkVMy0lW+PPohFok7PCow5sDjLgX4P4g= +nhooyr.io/websocket v1.8.7/go.mod h1:B70DZP8IakI65RVQ51MsWP/8jndNma26DVA/nFSCgW0= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk= +sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= diff --git a/seth/examples_wasp/seth.toml b/seth/examples_wasp/seth.toml new file mode 100644 index 000000000..998b29367 --- /dev/null +++ b/seth/examples_wasp/seth.toml @@ -0,0 +1,23 @@ +abi_dir = "../contracts/abi" +bin_dir = "../contracts/bin" +tracing_enabled = false +trace_to_json = false +ephemeral_addresses_number = 60 +pending_nonce_protection_enabled = false + +[[networks]] +name = "Geth" +chain_id = "1337" +transaction_timeout = "30s" +urls_secret = ["ws://localhost:8546"] +transfer_gas_fee = 21_000 +gas_limit = 8_000_000 +gas_price = 1_000_000_000 +gas_fee_cap = 10_000_000_000 +gas_tip_cap = 3_000_000_000 + +[nonce_manager] +key_sync_rate_limit_per_sec = 20 +key_sync_timeout = "30s" +key_sync_retry_delay = "1s" +key_sync_retries = 30 diff --git a/seth/flake.lock b/seth/flake.lock new file mode 100644 index 000000000..cb6c21e1f --- /dev/null +++ b/seth/flake.lock @@ -0,0 +1,111 @@ +{ + "nodes": { + "flake-utils": { + "inputs": { + "systems": "systems" + }, + "locked": { + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "flake-utils_2": { + "locked": { + "lastModified": 1644229661, + "narHash": "sha256-1YdnJAsNy69bpcjuoKdOYQX0YxZBiCYZo4Twxerqv7k=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "3cecb5b042f7f209c56ffd8371b2711a290ec797", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "foundry": { + "inputs": { + "flake-utils": "flake-utils_2", + "nixpkgs": "nixpkgs" + }, + "locked": { + "lastModified": 1707037862, + "narHash": "sha256-jCNrmFDx+neh7Uz0Q2kmqz19Yyz8OxnGoZpzd2w3SME=", + "owner": "shazow", + "repo": "foundry.nix", + "rev": "03b8af1efb00c51dceaac92462dc77b1b57683e0", + "type": "github" + }, + "original": { + "owner": "shazow", + "ref": "monthly", + "repo": "foundry.nix", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1666753130, + "narHash": "sha256-Wff1dGPFSneXJLI2c0kkdWTgxnQ416KE6X4KnFkgPYQ=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "f540aeda6f677354f1e7144ab04352f61aaa0118", + "type": "github" + }, + "original": { + "id": "nixpkgs", + "type": "indirect" + } + }, + "nixpkgs_2": { + "locked": { + "lastModified": 1706913249, + "narHash": "sha256-x3M7iV++CsvRXI1fpyFPduGELUckZEhSv0XWnUopAG8=", + "owner": "nixos", + "repo": "nixpkgs", + "rev": "e92b6015881907e698782c77641aa49298330223", + "type": "github" + }, + "original": { + "owner": "nixos", + "ref": "nixos-unstable", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "foundry": "foundry", + "nixpkgs": "nixpkgs_2" + } + }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/seth/flake.nix b/seth/flake.nix new file mode 100644 index 000000000..9b5e0a1d4 --- /dev/null +++ b/seth/flake.nix @@ -0,0 +1,16 @@ +{ + description = "seth"; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-utils.url = "github:numtide/flake-utils"; + foundry.url = "github:shazow/foundry.nix/monthly"; # Use monthly branch for permanent releases + }; + + outputs = inputs@{ self, nixpkgs, flake-utils, foundry, ... }: + flake-utils.lib.eachDefaultSystem (system: + let pkgs = import nixpkgs { inherit system; overlays = [ foundry.overlay ]; }; + in rec { + devShell = pkgs.callPackage ./shell.nix {}; + }); +} diff --git a/seth/gas.go b/seth/gas.go new file mode 100644 index 000000000..10307ecf7 --- /dev/null +++ b/seth/gas.go @@ -0,0 +1,125 @@ +package seth + +import ( + "context" + "math/big" + + "github.com/montanaflynn/stats" +) + +// GasEstimator estimates gas prices +type GasEstimator struct { + Client *Client + BlockGasLimits []uint64 + TransactionGasPrice []uint64 +} + +// NewGasEstimator creates a new gas estimator +func NewGasEstimator(c *Client) *GasEstimator { + return &GasEstimator{Client: c} +} + +// Stats prints gas stats +func (m *GasEstimator) Stats(fromNumber uint64, priorityPerc float64) (GasSuggestions, error) { + bn, err := m.Client.Client.BlockNumber(context.Background()) + if err != nil { + return GasSuggestions{}, err + } + hist, err := m.Client.Client.FeeHistory(context.Background(), fromNumber, big.NewInt(int64(bn)), []float64{priorityPerc}) + if err != nil { + return GasSuggestions{}, err + } + baseFees := make([]float64, 0) + for _, bf := range hist.BaseFee { + if bf == nil { + bf = big.NewInt(0) + } + f := new(big.Float).SetInt(bf) + ff, _ := f.Float64() + baseFees = append(baseFees, ff) + } + gasPercs, err := quantilesFromFloatArray(baseFees) + if err != nil { + return GasSuggestions{}, err + } + tips := make([]float64, 0) + for _, bf := range hist.Reward { + if len(bf) == 0 { + continue + } + if bf[0] == nil { + bf[0] = big.NewInt(0) + } + f := new(big.Float).SetInt(bf[0]) + ff, _ := f.Float64() + tips = append(tips, ff) + } + tipPercs, err := quantilesFromFloatArray(tips) + if err != nil { + return GasSuggestions{}, err + } + suggestedGasPrice, err := m.Client.Client.SuggestGasPrice(context.Background()) + if err != nil { + return GasSuggestions{}, err + } + suggestedGasTipCap, err := m.Client.Client.SuggestGasTipCap(context.Background()) + if err != nil { + return GasSuggestions{}, err + } + L.Trace(). + Interface("History", hist). + Msg("Fee history") + return GasSuggestions{ + GasPrice: gasPercs, + TipCap: tipPercs, + SuggestedGasPrice: suggestedGasPrice, + SuggestedGasTipCap: suggestedGasTipCap, + }, nil +} + +// GasPercentiles contains gas percentiles +type GasPercentiles struct { + Max float64 + Perc99 float64 + Perc75 float64 + Perc50 float64 + Perc25 float64 +} + +type GasSuggestions struct { + GasPrice *GasPercentiles + TipCap *GasPercentiles + SuggestedGasPrice *big.Int + SuggestedGasTipCap *big.Int +} + +// quantilesFromFloatArray calculates quantiles from a float array +func quantilesFromFloatArray(fa []float64) (*GasPercentiles, error) { + perMax, err := stats.Max(fa) + if err != nil { + return nil, err + } + perc99, err := stats.Percentile(fa, 99) + if err != nil { + return nil, err + } + perc75, err := stats.Percentile(fa, 75) + if err != nil { + return nil, err + } + perc50, err := stats.Percentile(fa, 50) + if err != nil { + return nil, err + } + perc25, err := stats.Percentile(fa, 25) + if err != nil { + return nil, err + } + return &GasPercentiles{ + Max: perMax, + Perc99: perc99, + Perc75: perc75, + Perc50: perc50, + Perc25: perc25, + }, nil +} diff --git a/seth/gas_adjuster.go b/seth/gas_adjuster.go new file mode 100644 index 000000000..1e6a6cbe9 --- /dev/null +++ b/seth/gas_adjuster.go @@ -0,0 +1,542 @@ +package seth + +import ( + "context" + "fmt" + "math" + "math/big" + "slices" + "strings" + "sync" + "time" + + "github.com/ethereum/go-ethereum/core/types" + "github.com/pkg/errors" +) + +const ( + Priority_Degen = "degen" //this is undocumented option, which we left for cases, when we need to set the highest gas price + Priority_Fast = "fast" + Priority_Standard = "standard" + Priority_Slow = "slow" + + Congestion_Low = "low" + Congestion_Medium = "medium" + Congestion_High = "high" + Congestion_VeryHigh = "extreme" +) + +const ( + // each block has the same weight in the computation + CongestionStrategy_Simple = "simple" + // newer blocks have more weight in the computation + CongestionStrategy_NewestFirst = "newest_first" +) + +var ( + ZeroGasSuggestedErr = "either base fee or suggested tip is 0" + BlockFetchingErr = "failed to fetch enough block headers for congestion calculation" +) + +// CalculateNetworkCongestionMetric calculates a simple congestion metric based on the last N blocks +// according to selected strategy. +func (m *Client) CalculateNetworkCongestionMetric(blocksNumber uint64, strategy string) (float64, error) { + if m.HeaderCache == nil { + return 0, fmt.Errorf("header cache is nil") + } + var getHeaderData = func(bn *big.Int) (*types.Header, error) { + if bn == nil { + return nil, fmt.Errorf("block number is nil") + } + cachedHeader, ok := m.HeaderCache.Get(bn.Int64()) + if ok { + return cachedHeader, nil + } + + timeout := blocksNumber / 100 + if timeout < 3 { + timeout = 3 + } else if timeout > 6 { + timeout = 6 + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second) + defer cancel() + header, err := m.Client.HeaderByNumber(ctx, bn) + if err != nil { + return nil, err + } + // ignore the error here as at this point it is very improbable that block is nil and there's no error + _ = m.HeaderCache.Set(header) + return header, nil + } + + ctx, cancel := context.WithTimeout(context.Background(), time.Duration(2*time.Second)) + defer cancel() + lastBlockNumber, err := m.Client.BlockNumber(ctx) + if err != nil { + return 0, err + } + + L.Trace().Msgf("Block range for gas calculation: %d - %d", lastBlockNumber-blocksNumber, lastBlockNumber) + + lastBlock, err := getHeaderData(big.NewInt(int64(lastBlockNumber))) + if err != nil { + return 0, err + } + + var headers []*types.Header + headers = append(headers, lastBlock) + + var wg sync.WaitGroup + dataCh := make(chan *types.Header) + + go func() { + for header := range dataCh { + headers = append(headers, header) + // placed here, because we want to wait for all headers to be received and added to slice before continuing + wg.Done() + } + }() + + startTime := time.Now() + for i := lastBlockNumber; i > lastBlockNumber-blocksNumber; i-- { + // better safe than sorry (might happen for brand-new chains) + if i <= 1 { + break + } + + wg.Add(1) + go func(bn *big.Int) { + header, err := getHeaderData(bn) + if err != nil { + L.Error().Err(err).Msgf("Failed to get block %d header", bn.Int64()) + return + } + dataCh <- header + }(big.NewInt(int64(i))) + } + + wg.Wait() + close(dataCh) + + endTime := time.Now() + L.Debug().Msgf("Time to fetch %d block headers: %v", blocksNumber, endTime.Sub(startTime)) + + minBlockCount := int(float64(blocksNumber) * 0.8) + if len(headers) < minBlockCount { + return 0, fmt.Errorf("%s. Wanted at least %d, got %d", BlockFetchingErr, minBlockCount, len(headers)) + } + + switch strategy { + case CongestionStrategy_Simple: + return calculateSimpleNetworkCongestionMetric(headers), nil + case CongestionStrategy_NewestFirst: + return calculateNewestFirstNetworkCongestionMetric(headers), nil + default: + return 0, fmt.Errorf("unknown congestion strategy: %s", strategy) + } +} + +// average gas used ratio for a basic congestion metric +func calculateSimpleNetworkCongestionMetric(headers []*types.Header) float64 { + return calculateGasUsedRatio(headers) +} + +// calculates a congestion metric using a logarithmic function that gives more weight to most recent block headers +func calculateNewestFirstNetworkCongestionMetric(headers []*types.Header) float64 { + // sort blocks so that we are sure they are in ascending order + slices.SortFunc(headers, func(i, j *types.Header) int { + return int(i.Number.Uint64() - j.Number.Uint64()) + }) + + var weightedSum, totalWeight float64 + // Determines how quickly the weight decreases. The lower the number, the higher the weight of newer blocks. + scaleFactor := 10.0 + + // Calculate weights starting from the older to most recent block header. + for i, header := range headers { + congestion := float64(header.GasUsed) / float64(header.GasLimit) + + distance := float64(len(headers) - 1 - i) + weight := 1.0 / math.Log10(distance+scaleFactor) + + weightedSum += congestion * weight + totalWeight += weight + } + + if totalWeight == 0 { + return 0 + } + return weightedSum / totalWeight +} + +// GetSuggestedEIP1559Fees returns suggested tip/fee cap calculated based on historical data, current congestion, and priority. +func (m *Client) GetSuggestedEIP1559Fees(ctx context.Context, priority string) (maxFeeCap *big.Int, adjustedTipCap *big.Int, err error) { + L.Info().Msg("Calculating suggested EIP-1559 fees") + var suggestedGasTip *big.Int + suggestedGasTip, err = m.Client.SuggestGasTipCap(ctx) + if err != nil { + return + } + + L.Debug(). + Str("CurrentGasTip", fmt.Sprintf("%s wei / %s ether", suggestedGasTip.String(), WeiToEther(suggestedGasTip).Text('f', -1))). + Msg("Current suggested gas tip") + + // Fetch the baseline historical base fee and tip for the selected priority + var baseFee64, historicalSuggestedTip64 float64 + //nolint + baseFee64, historicalSuggestedTip64, err = m.HistoricalFeeData(priority) + if err != nil { + return + } + + L.Debug(). + Str("HistoricalBaseFee", fmt.Sprintf("%.0f wei / %s ether", baseFee64, WeiToEther(big.NewInt(int64(baseFee64))).Text('f', -1))). + Str("HistoricalSuggestedTip", fmt.Sprintf("%.0f wei / %s ether", historicalSuggestedTip64, WeiToEther(big.NewInt(int64(historicalSuggestedTip64))).Text('f', -1))). + Str("Priority", priority). + Msg("Historical fee data") + + _, tipMagnitudeDiffText := calculateMagnitudeDifference(big.NewFloat(historicalSuggestedTip64), new(big.Float).SetInt(suggestedGasTip)) + + L.Debug(). + Msgf("Historical tip is %s than suggested tip", tipMagnitudeDiffText) + + currentGasTip := suggestedGasTip + if big.NewInt(int64(historicalSuggestedTip64)).Cmp(currentGasTip) > 0 { + L.Debug().Msg("Historical suggested tip is higher than current suggested tip. Will use it instead.") + currentGasTip = big.NewInt(int64(historicalSuggestedTip64)) + } else { + L.Debug().Msg("Suggested tip is higher than historical tip. Will use suggested tip.") + } + + if m.Cfg.IsExperimentEnabled(Experiment_Eip1559FeeEqualier) { + L.Debug().Msg("FeeEqualier experiment is enabled. Will adjust base fee and tip to be of the same order of magnitude.") + baseFeeTipMagnitudeDiff, _ := calculateMagnitudeDifference(big.NewFloat(baseFee64), new(big.Float).SetInt(currentGasTip)) + + //one of values is 0, infinite order of magnitude smaller or larger + if baseFeeTipMagnitudeDiff == -0 { + if baseFee64 == 0.0 { + L.Debug().Msg("Historical base fee is 0.0. Will use suggested tip as base fee.") + baseFee64 = float64(currentGasTip.Int64()) + } else { + L.Debug().Msg("Suggested tip is 0.0. Will use historical base fee as tip.") + currentGasTip = big.NewInt(int64(baseFee64)) + } + } else if baseFeeTipMagnitudeDiff < 3 { + L.Debug().Msg("Historical base fee is 3 orders of magnitude lower than suggested tip. Will use suggested tip as base fee.") + baseFee64 = float64(currentGasTip.Int64()) + } else if baseFeeTipMagnitudeDiff > 3 { + L.Debug().Msg("Suggested tip is 3 orders of magnitude lower than historical base fee. Will use historical base fee as tip.") + currentGasTip = big.NewInt(int64(baseFee64)) + } + } + + if baseFee64 == 0.0 { + err = errors.New(ZeroGasSuggestedErr) + + L.Error(). + Err(err). + Float64("BaseFee", baseFee64). + Int64("SuggestedTip", currentGasTip.Int64()). + Msg("Incorrect gas data received from node. Skipping automation gas estimation") + return + } + + if currentGasTip.Int64() == 0 { + L.Warn(). + Msg("Suggested tip is 0.0. Although not strictly incorrect, it is unusual. Transaction might take much longer to confirm.") + } + + // between 0.8 and 1.5 + var adjustmentFactor float64 + adjustmentFactor, err = getAdjustmentFactor(priority) + if err != nil { + return + } + + // Calculate adjusted tip based on priority + adjustedTipCapFloat := new(big.Float).Mul(big.NewFloat(adjustmentFactor), new(big.Float).SetFloat64(float64(currentGasTip.Int64()))) + adjustedTipCap, _ = adjustedTipCapFloat.Int(nil) + + adjustedBaseFeeFloat := new(big.Float).Mul(big.NewFloat(adjustmentFactor), new(big.Float).SetFloat64(baseFee64)) + adjustedBaseFee, _ := adjustedBaseFeeFloat.Int(nil) + + initialFeeCap := new(big.Int).Add(big.NewInt(int64(baseFee64)), currentGasTip) + + // between 0 and 1 (empty blocks - full blocks) + var congestionMetric float64 + //nolint + congestionMetric, err = m.CalculateNetworkCongestionMetric(m.Cfg.Network.GasPriceEstimationBlocks, CongestionStrategy_NewestFirst) + if err == nil { + congestionClassification := classifyCongestion(congestionMetric) + + L.Debug(). + Str("CongestionMetric", fmt.Sprintf("%.4f", congestionMetric)). + Str("CongestionClassification", congestionClassification). + Float64("AdjustmentFactor", adjustmentFactor). + Str("Priority", priority). + Msg("Adjustment factors") + + // between 1.1 and 1.4 + var bufferAdjustment float64 + bufferAdjustment, err = getCongestionFactor(congestionClassification) + if err != nil { + return + } + + // Calculate base fee buffer + bufferedBaseFeeFloat := new(big.Float).Mul(new(big.Float).SetInt(adjustedBaseFee), big.NewFloat(bufferAdjustment)) + adjustedBaseFee, _ = bufferedBaseFeeFloat.Int(nil) + + // Apply buffer also to the tip + bufferedTipCapFloat := new(big.Float).Mul(new(big.Float).SetInt(adjustedTipCap), big.NewFloat(bufferAdjustment)) + adjustedTipCap, _ = bufferedTipCapFloat.Int(nil) + } else if !strings.Contains(err.Error(), BlockFetchingErr) { + return + } else { + L.Warn(). + Err(err). + Msg("Failed to calculate congestion metric. Skipping congestion buffer adjustment") + + // set error to nil, as we can still calculate the fees, but without congestion buffer + // we don't want to return an error in this case + err = nil + } + + maxFeeCap = new(big.Int).Add(adjustedBaseFee, adjustedTipCap) + + baseFeeDiff := big.NewInt(0).Sub(adjustedBaseFee, big.NewInt(int64(baseFee64))) + gasTipDiff := big.NewInt(0).Sub(adjustedTipCap, currentGasTip) + gasCapDiff := big.NewInt(0).Sub(maxFeeCap, initialFeeCap) + + L.Debug(). + Str("Diff (Wei/Ether)", fmt.Sprintf("%s wei / %s ether", gasTipDiff.String(), WeiToEther(gasTipDiff).Text('f', -1))). + Str("Initial Tip", fmt.Sprintf("%s wei / %s ether", currentGasTip.String(), WeiToEther(currentGasTip).Text('f', -1))). + Str("Final Tip", fmt.Sprintf("%s wei / %s ether", adjustedTipCap.String(), WeiToEther(adjustedTipCap).Text('f', -1))). + Msg("Tip adjustment") + + L.Debug(). + Str("Diff (Wei/Ether)", fmt.Sprintf("%s wei / %s ether", baseFeeDiff.String(), WeiToEther(baseFeeDiff).Text('f', -1))). + Str("Initial Base Fee", fmt.Sprintf("%s wei / %s ether", big.NewInt(int64(baseFee64)).String(), WeiToEther(big.NewInt(int64(baseFee64))).Text('f', -1))). + Str("Final Base Fee", fmt.Sprintf("%s wei / %s ether", adjustedBaseFee.String(), WeiToEther(adjustedBaseFee).Text('f', -1))). + Msg("Base Fee adjustment") + + L.Debug(). + Str("Diff (Wei/Ether)", fmt.Sprintf("%s wei / %s ether", gasCapDiff.String(), WeiToEther(gasCapDiff).Text('f', -1))). + Str("Initial Fee Cap", fmt.Sprintf("%s wei / %s ether", initialFeeCap.String(), WeiToEther(initialFeeCap).Text('f', -1))). + Str("Final Fee Cap", fmt.Sprintf("%s wei / %s ether", maxFeeCap.String(), WeiToEther(maxFeeCap).Text('f', -1))). + Msg("Fee Cap adjustment") + + L.Info(). + Str("GasTipCap", fmt.Sprintf("%s wei / %s ether", adjustedTipCap.String(), WeiToEther(adjustedTipCap).Text('f', -1))). + Str("GasFeeCap", fmt.Sprintf("%s wei / %s ether", maxFeeCap.String(), WeiToEther(maxFeeCap).Text('f', -1))). + Msg("Calculated suggested EIP-1559 fees") + + return +} + +// GetSuggestedLegacyFees calculates the suggested gas price based on historical data, current congestion, and priority. +func (m *Client) GetSuggestedLegacyFees(ctx context.Context, priority string) (adjustedGasPrice *big.Int, err error) { + L.Info(). + Msg("Calculating suggested Legacy fees") + + var suggestedGasPrice *big.Int + suggestedGasPrice, err = m.Client.SuggestGasPrice(ctx) + if err != nil { + return + } + + if suggestedGasPrice.Int64() == 0 { + err = fmt.Errorf("suggested gas price is 0") + L.Error(). + Err(err). + Msg("Incorrect gas data received from node. Skipping automation gas estimation") + return + } + + var adjustmentFactor float64 + adjustmentFactor, err = getAdjustmentFactor(priority) + if err != nil { + return + } + + // Calculate adjusted tip based on congestion and priority + adjustedGasPriceFloat := new(big.Float).Mul(big.NewFloat(adjustmentFactor), new(big.Float).SetFloat64(float64(suggestedGasPrice.Int64()))) + adjustedGasPrice, _ = adjustedGasPriceFloat.Int(nil) + + // between 0 and 1 (empty blocks - full blocks) + var congestionMetric float64 + //nolint + congestionMetric, err = m.CalculateNetworkCongestionMetric(m.Cfg.Network.GasPriceEstimationBlocks, CongestionStrategy_NewestFirst) + if err == nil { + congestionClassification := classifyCongestion(congestionMetric) + + L.Debug(). + Str("CongestionMetric", fmt.Sprintf("%.4f", congestionMetric)). + Str("CongestionClassification", congestionClassification). + Float64("AdjustmentFactor", adjustmentFactor). + Str("Priority", priority). + Msg("Suggested Legacy fees") + + // between 1.1 and 1.4 + var bufferAdjustment float64 + bufferAdjustment, err = getCongestionFactor(congestionClassification) + if err != nil { + return + } + + // Calculate and apply the buffer. + bufferedGasPriceFloat := new(big.Float).Mul(new(big.Float).SetInt(adjustedGasPrice), big.NewFloat(bufferAdjustment)) + adjustedGasPrice, _ = bufferedGasPriceFloat.Int(nil) + } else if !strings.Contains(err.Error(), BlockFetchingErr) { + return + } else { + L.Warn(). + Err(err). + Msg("Failed to calculate congestion metric. Skipping congestion buffer adjustment") + + // set error to nil, as we can still calculate the fees, but without congestion buffer + // we don't want to return an error in this case + err = nil + } + + L.Debug(). + Str("Diff (Wei/Ether)", fmt.Sprintf("%s/%s", big.NewInt(0).Sub(adjustedGasPrice, suggestedGasPrice).String(), WeiToEther(big.NewInt(0).Sub(adjustedGasPrice, suggestedGasPrice)).Text('f', -1))). + Str("Initial GasPrice (Wei/Ether)", fmt.Sprintf("%s/%s", suggestedGasPrice.String(), WeiToEther(suggestedGasPrice).Text('f', -1))). + Str("Final GasPrice (Wei/Ether)", fmt.Sprintf("%s/%s", adjustedGasPrice.String(), WeiToEther(adjustedGasPrice).Text('f', -1))). + Msg("Suggested Legacy fees") + + L.Info(). + Str("GasPrice", fmt.Sprintf("%s wei / %s ether", adjustedGasPrice.String(), WeiToEther(adjustedGasPrice).Text('f', -1))). + Msg("Calculated suggested Legacy fees") + + return +} + +func getAdjustmentFactor(priority string) (float64, error) { + switch priority { + case Priority_Degen: + return 1.5, nil + case Priority_Fast: + return 1.2, nil + case Priority_Standard: + return 1.0, nil + case Priority_Slow: + return 0.8, nil + default: + return 0, fmt.Errorf("unknown priority: %s", priority) + } +} + +func getCongestionFactor(congestionClassification string) (float64, error) { + switch congestionClassification { + case Congestion_Low: + return 1.10, nil + case Congestion_Medium: + return 1.20, nil + case Congestion_High: + return 1.30, nil + case Congestion_VeryHigh: + return 1.40, nil + default: + return 0, fmt.Errorf("unknown congestion classification: %s", congestionClassification) + } +} + +func classifyCongestion(congestionMetric float64) string { + switch { + case congestionMetric < 0.33: + return Congestion_Low + case congestionMetric <= 0.66: + return Congestion_Medium + case congestionMetric <= 0.75: + return Congestion_High + default: + return Congestion_VeryHigh + } +} + +func (m *Client) HistoricalFeeData(priority string) (baseFee float64, historicalGasTipCap float64, err error) { + estimator := NewGasEstimator(m) + stats, err := estimator.Stats(m.Cfg.Network.GasPriceEstimationBlocks, 99) + if err != nil { + L.Error(). + Err(err). + Msg("Failed to get fee history. Skipping automation gas estimation") + + return + } + + switch priority { + case Priority_Degen: + baseFee = stats.GasPrice.Max + historicalGasTipCap = stats.TipCap.Max + case Priority_Fast: + baseFee = stats.GasPrice.Perc99 + historicalGasTipCap = stats.TipCap.Perc99 + case Priority_Standard: + baseFee = stats.GasPrice.Perc50 + historicalGasTipCap = stats.TipCap.Perc50 + case Priority_Slow: + baseFee = stats.GasPrice.Perc25 + historicalGasTipCap = stats.TipCap.Perc25 + default: + err = fmt.Errorf("unknown priority: %s", priority) + L.Error(). + Str("Priority", priority). + Msg("Unknown priority. Skipping automation gas estimation") + + return + + } + + return baseFee, historicalGasTipCap, err +} + +// calculateGasUsedRatio averages the gas used ratio for a sense of how full blocks are +func calculateGasUsedRatio(headers []*types.Header) float64 { + if len(headers) == 0 { + return 0 + } + + var totalRatio float64 + for _, header := range headers { + if header.GasLimit == 0 { + continue + } + ratio := float64(header.GasUsed) / float64(header.GasLimit) + totalRatio += ratio + } + averageRatio := totalRatio / float64(len(headers)) + return averageRatio +} + +func calculateMagnitudeDifference(first, second *big.Float) (int, string) { + firstFloat, _ := first.Float64() + secondFloat, _ := second.Float64() + + if firstFloat == 0.0 { + return -0, "infinite orders of magnitude smaller" + } + + if secondFloat == 0.0 { + return -0, "infinite orders of magnitude larger" + } + + firstOrderOfMagnitude := math.Log10(firstFloat) + secondOrderOfMagnitude := math.Log10(secondFloat) + + diff := firstOrderOfMagnitude - secondOrderOfMagnitude + + if diff < 0 { + intDiff := math.Floor(diff) + return int(intDiff), fmt.Sprintf("%d orders of magnitude smaller", int(math.Abs(intDiff))) + } else if diff > 0 && diff <= 1 { + return 0, "the same order of magnitude" + } + + intDiff := int(math.Ceil(diff)) + return intDiff, fmt.Sprintf("%d orders of magnitude larger", intDiff) +} diff --git a/seth/gas_bump_test.go b/seth/gas_bump_test.go new file mode 100644 index 000000000..b0c88d984 --- /dev/null +++ b/seth/gas_bump_test.go @@ -0,0 +1,624 @@ +package seth_test + +import ( + "math/big" + "testing" + "time" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" + link_token "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/link" + "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/link_token_interface" + "github.com/smartcontractkit/chainlink-testing-framework/seth/test_utils" +) + +var oneEth = big.NewInt(1000000000000000000) +var zero int64 + +func TestGasBumping_Contract_Deployment_Legacy_SufficientBumping(t *testing.T) { + c := newClient(t) + newPk := test_utils.NewPrivateKeyWithFunds(t, c, oneEth) + + configCopy, err := test_utils.CopyConfig(c.Cfg) + require.NoError(t, err, "failed to copy config") + + gasBumps := 0 + + // Set a low gas price and a short timeout + configCopy.Network.PrivateKeys = []string{newPk} + configCopy.Network.GasPrice = 1 + configCopy.Network.TxnTimeout = seth.MustMakeDuration(10 * time.Second) + configCopy.GasBump = &seth.GasBumpConfig{ + Retries: 10, + MaxGasPrice: 100000000, + StrategyFn: func(gasPrice *big.Int) *big.Int { + gasBumps++ + return new(big.Int).Mul(gasPrice, big.NewInt(100)) + }, + } + + client, err := seth.NewClientWithConfig(configCopy) + require.NoError(t, err) + + t.Cleanup(func() { + configCopy.Network.GasPrice = 1_000_000_000 + err = test_utils.TransferAllFundsBetweenKeyAndAddress(client, 0, c.Addresses[0]) + require.NoError(t, err, "failed to transfer funds back to original root key") + }) + + contractAbi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + + // Send a transaction with low gas price + data, err := client.DeployContract(client.NewTXOpts(), "LinkToken", *contractAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + require.NoError(t, err, "contract wasn't deployed") + require.GreaterOrEqual(t, gasBumps, 1, "expected at least one gas bump") + require.Greater(t, data.Transaction.GasPrice().Int64(), int64(1), "expected gas price to be bumped") +} + +func TestGasBumping_Contract_Deployment_Legacy_InsufficientBumping(t *testing.T) { + c := newClient(t) + newPk := test_utils.NewPrivateKeyWithFunds(t, c, oneEth) + + configCopy, err := test_utils.CopyConfig(c.Cfg) + require.NoError(t, err, "failed to copy config") + + gasBumps := 0 + + // Set a low gas price and a short timeout + configCopy.Network.PrivateKeys = []string{newPk} + configCopy.Network.GasPrice = 1 + configCopy.Network.TxnTimeout = seth.MustMakeDuration(10 * time.Second) + configCopy.GasBump = &seth.GasBumpConfig{ + Retries: 2, + StrategyFn: func(gasPrice *big.Int) *big.Int { + gasBumps++ + return new(big.Int).Add(gasPrice, big.NewInt(1)) + }, + } + + client, err := seth.NewClientWithConfig(configCopy) + require.NoError(t, err) + + t.Cleanup(func() { + configCopy.Network.GasPrice = 1_000_000_000 + err = test_utils.TransferAllFundsBetweenKeyAndAddress(client, 0, c.Addresses[0]) + require.NoError(t, err, "failed to transfer funds back to original root key") + }) + + contractAbi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + + // Send a transaction with a low gas price + _, err = client.DeployContract(client.NewTXOpts(), "LinkToken", *contractAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + + require.Error(t, err, "contract was deployed, but gas bumping shouldn't be sufficient to deploy it") + require.GreaterOrEqual(t, gasBumps, 1, "expected at least one gas bump") +} + +func TestGasBumping_Contract_Deployment_Legacy_FailedBumping(t *testing.T) { + c := newClient(t) + newPk := test_utils.NewPrivateKeyWithFunds(t, c, oneEth) + + configCopy, err := test_utils.CopyConfig(c.Cfg) + require.NoError(t, err, "failed to copy config") + + gasBumps := 0 + + // Set a low gas price and a short timeout + configCopy.Network.PrivateKeys = []string{newPk} + configCopy.Network.GasPrice = 1 + configCopy.Network.TxnTimeout = seth.MustMakeDuration(10 * time.Second) + configCopy.GasBump = &seth.GasBumpConfig{ + Retries: 2, + StrategyFn: func(gasPrice *big.Int) *big.Int { + gasBumps++ + return new(big.Int).Mul(gasPrice, big.NewInt(1000000000000)) + }, + } + + client, err := seth.NewClientWithConfig(configCopy) + require.NoError(t, err) + + t.Cleanup(func() { + configCopy.Network.GasPrice = 1_000_000_000 + err = test_utils.TransferAllFundsBetweenKeyAndAddress(client, 0, c.Addresses[0]) + require.NoError(t, err, "failed to transfer funds back to original root key") + }) + + contractAbi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + + // Send a transaction with a low gas price and then bump it too high to be accepted + _, err = client.DeployContract(client.NewTXOpts(), "LinkToken", *contractAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + require.Error(t, err, "contract was deployed, but gas bumping should be failing") + require.GreaterOrEqual(t, gasBumps, 1, "expected at least one gas bump") +} + +func TestGasBumping_Contract_Deployment_Legacy_BumpingDisabled(t *testing.T) { + c := newClient(t) + newPk := test_utils.NewPrivateKeyWithFunds(t, c, oneEth) + + configCopy, err := test_utils.CopyConfig(c.Cfg) + require.NoError(t, err, "failed to copy config") + + gasBumps := 0 + + // Set a low gas price and a short timeout, but disable gas bumping + configCopy.Network.PrivateKeys = []string{newPk} + configCopy.Network.GasPrice = 1 + configCopy.Network.TxnTimeout = seth.MustMakeDuration(10 * time.Second) + configCopy.GasBump = &seth.GasBumpConfig{ + StrategyFn: func(gasPrice *big.Int) *big.Int { + gasBumps++ + return gasPrice + }, + } + + client, err := seth.NewClientWithConfig(configCopy) + require.NoError(t, err) + + t.Cleanup(func() { + configCopy.Network.GasPrice = 1_000_000_000 + err = test_utils.TransferAllFundsBetweenKeyAndAddress(client, 0, c.Addresses[0]) + require.NoError(t, err, "failed to transfer funds back to original root key") + }) + + contractAbi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + + // Send a transaction with a low gas price + _, err = client.DeployContract(client.NewTXOpts(), "LinkToken", *contractAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + require.Error(t, err, "contract was deployed, but gas bumping is disabled") + require.GreaterOrEqual(t, gasBumps, 0, "expected no gas bumps") +} + +func TestGasBumping_Contract_Deployment_Legacy_CustomBumpingFunction(t *testing.T) { + c := newClient(t) + newPk := test_utils.NewPrivateKeyWithFunds(t, c, oneEth) + + customGasBumps := 0 + + client, err := seth.NewClientBuilder(). + WithRpcUrl(c.Cfg.Network.URLs[0]). + WithPrivateKeys([]string{newPk}). + WithGasPriceEstimations(false, 0, ""). + WithEIP1559DynamicFees(false). + WithLegacyGasPrice(1). + WithTransactionTimeout(10*time.Second). + WithProtections(false, false). + WithGasBumping(5, 0, func(gasPrice *big.Int) *big.Int { + customGasBumps++ + return new(big.Int).Mul(gasPrice, big.NewInt(512)) + }). + Build() + require.NoError(t, err) + + t.Cleanup(func() { + client.Cfg.Network.GasPrice = 1_000_000_000 + err = test_utils.TransferAllFundsBetweenKeyAndAddress(client, 0, c.Addresses[0]) + }) + + // we don't want to expose it in builder, but setting it to 0 (automatic gas limit estimation) doesn't work well with gas price of 1 wei + client.Cfg.Network.GasLimit = 8_000_000 + + contractAbi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + + _, err = client.DeployContract(client.NewTXOpts(), "LinkToken", *contractAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + require.NoError(t, err, "contract was not deployed") + require.GreaterOrEqual(t, customGasBumps, 1, "expected at least one custom gas bump") +} + +func TestGasBumping_Contract_Interaction_Legacy_MaxGas(t *testing.T) { + spammer := test_utils.NewClientWithAddresses(t, 5, oneEth) + + configCopy, err := test_utils.CopyConfig(spammer.Cfg) + require.NoError(t, err, "failed to copy config") + + newPk := test_utils.NewPrivateKeyWithFunds(t, spammer, oneEth) + configCopy.Network.PrivateKeys = []string{newPk} + configCopy.EphemeralAddrs = &zero + + client, err := seth.NewClientWithConfig(configCopy) + require.NoError(t, err, "failed to create client") + + contractAbi, err := link_token_interface.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + + data, err := client.DeployContract(client.NewTXOpts(), "LinkToken", *contractAbi, common.FromHex(link_token_interface.LinkTokenMetaData.Bin)) + require.NoError(t, err, "contract wasn't deployed") + + linkContract, err := link_token.NewLinkToken(data.Address, client.Client) + require.NoError(t, err, "failed to instantiate contract") + + var gasPrices []*big.Int + + // Update config and set a low gas price and a short timeout + client.Cfg.Network.GasPrice = 1 + client.Cfg.Network.TxnTimeout = seth.MustMakeDuration(10 * time.Second) + client.Cfg.GasBump = &seth.GasBumpConfig{ + Retries: 5, + MaxGasPrice: 5, //after 2 retries gas price will be 5 + StrategyFn: func(gasPrice *big.Int) *big.Int { + gasPrices = append(gasPrices, gasPrice) + return new(big.Int).Add(gasPrice, big.NewInt(2)) + }, + } + + // introduce some traffic, so that bumping is necessary to mine the transaction + go func() { + for i := 0; i < 5; i++ { + _, _ = spammer.DeployContract(spammer.NewTXKeyOpts(spammer.AnySyncedKey()), "LinkToken", *contractAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + } + }() + + // Send a transaction with a low gas price + _, _ = client.Decode(linkContract.Transfer(client.NewTXOpts(), client.Addresses[0], big.NewInt(1000000000000000000))) + require.NoError(t, err, "failed to transfer tokens") + require.GreaterOrEqual(t, len(gasPrices), 3, "expected 2 gas bumps") + require.True(t, func() bool { + for _, gasPrice := range gasPrices { + if gasPrice.Cmp(big.NewInt(client.Cfg.GasBump.MaxGasPrice)) > 0 { + return false + } + } + return true + }(), "at least one gas bump was too high") +} + +func TestGasBumping_Contract_Interaction_EIP1559_MaxGas(t *testing.T) { + spammer := test_utils.NewClientWithAddresses(t, 5, oneEth) + + configCopy, err := test_utils.CopyConfig(spammer.Cfg) + require.NoError(t, err, "failed to copy config") + + newPk := test_utils.NewPrivateKeyWithFunds(t, spammer, oneEth) + configCopy.Network.PrivateKeys = []string{newPk} + configCopy.EphemeralAddrs = &zero + + client, err := seth.NewClientWithConfig(configCopy) + require.NoError(t, err, "failed to create client") + + contractAbi, err := link_token_interface.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + + data, err := client.DeployContract(client.NewTXOpts(), "LinkToken", *contractAbi, common.FromHex(link_token_interface.LinkTokenMetaData.Bin)) + require.NoError(t, err, "contract wasn't deployed") + + linkContract, err := link_token.NewLinkToken(data.Address, client.Client) + require.NoError(t, err, "failed to instantiate contract") + + var gasPrices []*big.Int + + // Update config and set a low gas price and a short timeout + client.Cfg.Network.GasFeeCap = 1 + client.Cfg.Network.GasTipCap = 1 + client.Cfg.Network.EIP1559DynamicFees = true + client.Cfg.Network.TxnTimeout = seth.MustMakeDuration(10 * time.Second) + client.Cfg.GasBump = &seth.GasBumpConfig{ + Retries: 4, + MaxGasPrice: 5, // for both fee and tip, which means that after a single bump, the gas price will be 5 and no more bumps should ever occur + StrategyFn: func(gasPrice *big.Int) *big.Int { + gasPrices = append(gasPrices, gasPrice) + return new(big.Int).Add(gasPrice, big.NewInt(2)) + }, + } + + // introduce some traffic, so that bumping is necessary to mine the transaction + go func() { + for i := 0; i < 5; i++ { + _, _ = spammer.DeployContract(spammer.NewTXKeyOpts(spammer.AnySyncedKey()), "LinkToken", *contractAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + } + }() + + // Send a transaction with a low gas price + _, _ = client.Decode(linkContract.Transfer(client.NewTXOpts(), client.Addresses[0], big.NewInt(1000000000000000000))) + require.NoError(t, err, "failed to transfer tokens") + require.GreaterOrEqual(t, len(gasPrices), 3, "expected at least 3 gas bumps") + require.True(t, func() bool { + for _, gasPrice := range gasPrices { + // any other price higher than 2 would result in cumulated gas price (fee + cap) > 5 + if gasPrice.Cmp(big.NewInt(2)) > 0 { + return false + } + } + return true + }(), "at least one gas bump was too high") +} + +func TestGasBumping_Contract_Deployment_EIP_1559_SufficientBumping(t *testing.T) { + c := newClient(t) + newPk := test_utils.NewPrivateKeyWithFunds(t, c, oneEth) + + configCopy, err := test_utils.CopyConfig(c.Cfg) + require.NoError(t, err, "failed to copy config") + + gasBumps := 0 + + // Set a low gas fee and tip cap and a short timeout + configCopy.Network.PrivateKeys = []string{newPk} + configCopy.Network.GasTipCap = 1 + configCopy.Network.GasFeeCap = 1 + configCopy.Network.EIP1559DynamicFees = true + configCopy.Network.TxnTimeout = seth.MustMakeDuration(10 * time.Second) + configCopy.GasBump = &seth.GasBumpConfig{ + Retries: 10, + MaxGasPrice: 10000000, + StrategyFn: func(gasPrice *big.Int) *big.Int { + gasBumps++ + return new(big.Int).Mul(gasPrice, big.NewInt(100)) + }, + } + + client, err := seth.NewClientWithConfig(configCopy) + require.NoError(t, err) + + t.Cleanup(func() { + client.Cfg.Network.GasTipCap = 50_000_000_000 + client.Cfg.Network.GasFeeCap = 100_000_000_000 + err = test_utils.TransferAllFundsBetweenKeyAndAddress(client, 0, c.Addresses[0]) + require.NoError(t, err, "failed to transfer funds back to original root key") + }) + + contractAbi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + + // Send a transaction with a low gas price + data, err := client.DeployContract(client.NewTXOpts(), "LinkToken", *contractAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + require.NoError(t, err, "contract wasn't deployed") + require.GreaterOrEqual(t, gasBumps, 1, "expected at least one gas bump") + require.Greater(t, data.Transaction.GasTipCap().Int64(), int64(1), "expected gas tip cap to be bumped") + require.Greater(t, data.Transaction.GasFeeCap().Int64(), int64(1), "expected gas fee cap to be bumped") +} + +func TestGasBumping_Contract_Deployment_EIP_1559_NonRootKey(t *testing.T) { + c := newClient(t) + newPk := test_utils.NewPrivateKeyWithFunds(t, c, big.NewInt(0).Mul(oneEth, big.NewInt(10))) + + configCopy, err := test_utils.CopyConfig(c.Cfg) + require.NoError(t, err, "failed to copy config") + + gasBumps := 0 + var one int64 = 1 + + // Set a low gas fee and tip cap and a short timeout + configCopy.EphemeralAddrs = &one + configCopy.RootKeyFundsBuffer = &one + configCopy.Network.PrivateKeys = []string{newPk} + configCopy.Network.GasTipCap = 1 + configCopy.Network.GasFeeCap = 1 + configCopy.Network.EIP1559DynamicFees = true + configCopy.Network.TxnTimeout = seth.MustMakeDuration(10 * time.Second) + configCopy.GasBump = &seth.GasBumpConfig{ + Retries: 10, + MaxGasPrice: 10000000, + StrategyFn: func(gasPrice *big.Int) *big.Int { + gasBumps++ + return new(big.Int).Mul(gasPrice, big.NewInt(100)) + }, + } + + client, err := seth.NewClientWithConfig(configCopy) + require.NoError(t, err) + + t.Cleanup(func() { + err := client.NonceManager.UpdateNonces() + require.NoError(t, err, "failed to update nonces") + err = seth.ReturnFunds(client, client.Addresses[0].Hex()) + require.NoError(t, err, "failed to return funds") + err = test_utils.TransferAllFundsBetweenKeyAndAddress(client, 0, c.Addresses[0]) + require.NoError(t, err, "failed to transfer funds back to original root key") + }) + + contractAbi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + + // Send a transaction with a low gas price + data, err := client.DeployContract(client.NewTXKeyOpts(1), "LinkToken", *contractAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + require.NoError(t, err, "contract wasn't deployed from key 1") + require.GreaterOrEqual(t, gasBumps, 1, "expected at least one gas bump") + require.Greater(t, data.Transaction.GasTipCap().Int64(), int64(1), "expected gas tip cap to be bumped") + require.Greater(t, data.Transaction.GasFeeCap().Int64(), int64(1), "expected gas fee cap to be bumped") +} + +func TestGasBumping_Contract_Deployment_EIP_1559_UnknownKey(t *testing.T) { + c := newClient(t) + newPk := test_utils.NewPrivateKeyWithFunds(t, c, big.NewInt(0).Mul(oneEth, big.NewInt(10))) + + configCopy, err := test_utils.CopyConfig(c.Cfg) + require.NoError(t, err, "failed to copy config") + + var one int64 = 1 + + // Set a low gas fee and tip cap and a short timeout + configCopy.EphemeralAddrs = &one + configCopy.RootKeyFundsBuffer = &one + configCopy.Network.PrivateKeys = []string{newPk} + configCopy.Network.GasTipCap = 1 + configCopy.Network.GasFeeCap = 1 + configCopy.Network.EIP1559DynamicFees = true + configCopy.Network.TxnTimeout = seth.MustMakeDuration(10 * time.Second) + configCopy.GasBump = &seth.GasBumpConfig{ + Retries: 2, + } + + client, err := seth.NewClientWithConfig(configCopy) + require.NoError(t, err) + + removedAddress := client.Addresses[1] + + gasBumps := 0 + + client.Cfg.GasBump.StrategyFn = func(gasPrice *big.Int) *big.Int { + // remove address from client to simulate an unlikely situation, where we try to bump a transaction with having sender's private key + client.Addresses = client.Addresses[:1] + gasBumps++ + return gasPrice + } + + t.Cleanup(func() { + client.Addresses = append(client.Addresses, removedAddress) + err := client.NonceManager.UpdateNonces() + require.NoError(t, err, "failed to update nonces") + err = seth.ReturnFunds(client, client.Addresses[0].Hex()) + require.NoError(t, err, "failed to return funds") + err = test_utils.TransferAllFundsBetweenKeyAndAddress(client, 0, c.Addresses[0]) + require.NoError(t, err, "failed to transfer funds back to original root key") + }) + + contractAbi, err := link_token.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + + _, err = client.DeployContract(client.NewTXKeyOpts(1), "LinkToken", *contractAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + require.Error(t, err, "contract was deployed from unknown key") + require.GreaterOrEqual(t, gasBumps, 1, "expected at least one gas bump attempt") +} + +func TestGasBumping_Contract_Interaction_Legacy_SufficientBumping(t *testing.T) { + spammer := test_utils.NewClientWithAddresses(t, 5, oneEth) + + configCopy, err := test_utils.CopyConfig(spammer.Cfg) + require.NoError(t, err, "failed to copy config") + + newPk := test_utils.NewPrivateKeyWithFunds(t, spammer, oneEth) + configCopy.Network.PrivateKeys = []string{newPk} + configCopy.EphemeralAddrs = &zero + + client, err := seth.NewClientWithConfig(configCopy) + require.NoError(t, err, "failed to create client") + + contractAbi, err := link_token_interface.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + + data, err := client.DeployContract(client.NewTXOpts(), "LinkToken", *contractAbi, common.FromHex(link_token_interface.LinkTokenMetaData.Bin)) + require.NoError(t, err, "contract wasn't deployed") + + linkContract, err := link_token.NewLinkToken(data.Address, client.Client) + require.NoError(t, err, "failed to instantiate contract") + + gasBumps := 0 + + // Update config and set a low gas price and a short timeout + client.Cfg.Network.GasPrice = 1 + client.Cfg.Network.TxnTimeout = seth.MustMakeDuration(10 * time.Second) + client.Cfg.GasBump = &seth.GasBumpConfig{ + Retries: 10, + MaxGasPrice: 10000000, + StrategyFn: func(gasPrice *big.Int) *big.Int { + gasBumps++ + return new(big.Int).Mul(gasPrice, big.NewInt(100)) + }, + } + + // introduce some traffic, so that bumping is necessary to mine the transaction + go func() { + for i := 0; i < 5; i++ { + _, _ = spammer.DeployContract(spammer.NewTXKeyOpts(spammer.AnySyncedKey()), "LinkToken", *contractAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + } + }() + + // Send a transaction with a low gas price + _, err = client.Decode(linkContract.Transfer(client.NewTXOpts(), client.Addresses[0], big.NewInt(1000000000000000000))) + require.NoError(t, err, "failed to mint tokens") + require.GreaterOrEqual(t, gasBumps, 1, "expected at least one transaction gas bump") +} + +func TestGasBumping_Contract_Interaction_Legacy_BumpingDisabled(t *testing.T) { + spammer := test_utils.NewClientWithAddresses(t, 5, oneEth) + + configCopy, err := test_utils.CopyConfig(spammer.Cfg) + require.NoError(t, err, "failed to copy config") + + newPk := test_utils.NewPrivateKeyWithFunds(t, spammer, oneEth) + configCopy.Network.PrivateKeys = []string{newPk} + configCopy.EphemeralAddrs = &zero + + client, err := seth.NewClientWithConfig(configCopy) + require.NoError(t, err, "failed to create client") + + contractAbi, err := link_token_interface.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + + data, err := client.DeployContract(client.NewTXOpts(), "LinkToken", *contractAbi, common.FromHex(link_token_interface.LinkTokenMetaData.Bin)) + require.NoError(t, err, "contract wasn't deployed") + + linkContract, err := link_token.NewLinkToken(data.Address, client.Client) + require.NoError(t, err, "failed to instantiate contract") + + gasBumps := 0 + + // Update config and set a low gas price and a short timeout + client.Cfg.Network.GasPrice = 1 + client.Cfg.Network.TxnTimeout = seth.MustMakeDuration(10 * time.Second) + client.Cfg.GasBump = &seth.GasBumpConfig{ + StrategyFn: func(gasPrice *big.Int) *big.Int { + gasBumps++ + // do not bump anything + return gasPrice + }, + } + + // introduce some traffic, so that bumping is necessary to mine the transaction + go func() { + for i := 0; i < 5; i++ { + _, _ = spammer.DeployContract(spammer.NewTXKeyOpts(spammer.AnySyncedKey()), "LinkToken", *contractAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + } + }() + + // Send a transaction with a low gas price + _, err = client.Decode(linkContract.Transfer(client.NewTXOpts(), client.Addresses[0], big.NewInt(1000000000000000000))) + require.Error(t, err, "did not fail to transfer tokens, even though gas bumping is disabled") + require.Equal(t, gasBumps, 0, "expected no gas bumps") +} + +func TestGasBumping_Contract_Interaction_Legacy_FailedBumping(t *testing.T) { + spammer := test_utils.NewClientWithAddresses(t, 5, oneEth) + + configCopy, err := test_utils.CopyConfig(spammer.Cfg) + require.NoError(t, err, "failed to copy config") + + newPk := test_utils.NewPrivateKeyWithFunds(t, spammer, oneEth) + configCopy.Network.PrivateKeys = []string{newPk} + configCopy.EphemeralAddrs = &zero + + client, err := seth.NewClientWithConfig(configCopy) + require.NoError(t, err, "failed to create client") + + contractAbi, err := link_token_interface.LinkTokenMetaData.GetAbi() + require.NoError(t, err, "failed to get ABI") + + data, err := client.DeployContract(client.NewTXOpts(), "LinkToken", *contractAbi, common.FromHex(link_token_interface.LinkTokenMetaData.Bin)) + require.NoError(t, err, "contract wasn't deployed") + + linkContract, err := link_token.NewLinkToken(data.Address, client.Client) + require.NoError(t, err, "failed to instantiate contract") + + gasBumps := 0 + + // Update config and set a low gas price and a short timeout + client.Cfg.Network.GasPrice = 1 + client.Cfg.Network.TxnTimeout = seth.MustMakeDuration(10 * time.Second) + client.Cfg.GasBump = &seth.GasBumpConfig{ + Retries: 3, + StrategyFn: func(gasPrice *big.Int) *big.Int { + gasBumps++ + // this results in a gas bump that is too high to be accepted + return new(big.Int).Mul(gasPrice, big.NewInt(1000000000000)) + }, + } + + // introduce some traffic, so that bumping is necessary to mine the transaction + go func() { + for i := 0; i < 5; i++ { + _, _ = spammer.DeployContract(spammer.NewTXKeyOpts(spammer.AnySyncedKey()), "LinkToken", *contractAbi, common.FromHex(link_token.LinkTokenMetaData.Bin)) + } + }() + + // Send a transaction with a low gas price + _, err = client.Decode(linkContract.Transfer(client.NewTXOpts(), client.Addresses[0], big.NewInt(1000000000000000000))) + require.Error(t, err, "did not fail to transfer tokens, even though gas bumping is disabled") + require.Equal(t, 3, gasBumps, "expected 2 gas bumps") +} diff --git a/seth/gas_test.go b/seth/gas_test.go new file mode 100644 index 000000000..f0d49197f --- /dev/null +++ b/seth/gas_test.go @@ -0,0 +1,38 @@ +package seth_test + +import ( + "context" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" +) + +func TestGasEstimator(t *testing.T) { + c := newClient(t) + bn, err := c.Client.BlockNumber(context.Background()) + require.NoError(t, err, "BlockNumber should not error") + for i := 0; i < 10; i++ { + _, err := c.DeployContractFromContractStore(c.NewTXOpts(), "NetworkDebugSubContract") + require.NoError(t, err, "Deploying contract should not error") + } + estimator := seth.NewGasEstimator(c) + + suggestions, err := estimator.Stats(bn, 25) + require.NoError(t, err, "Gas estimator should not err") + require.NotNil(t, suggestions.GasPrice, "Suggested gas price should not be nil") + require.NotNil(t, suggestions.TipCap, "Suggested tip cap should not be nil") + + require.Greater(t, suggestions.GasPrice.Perc25, float64(0), "Suggested 25th percentile gas price should be greater than 0") + require.GreaterOrEqual(t, suggestions.GasPrice.Perc50, suggestions.GasPrice.Perc25, "Suggested 50th percentile gas price should be greater than or equal to 25th percentile") + require.GreaterOrEqual(t, suggestions.GasPrice.Perc75, suggestions.GasPrice.Perc50, "Suggested 75th percentile gas price should be greater than or equal to 50th percentile") + require.GreaterOrEqual(t, suggestions.GasPrice.Perc99, suggestions.GasPrice.Perc75, "Suggested 99th percentile gas price should be greater than or equal to 75th percentile") + require.GreaterOrEqual(t, suggestions.GasPrice.Max, suggestions.GasPrice.Perc99, "Suggested max gas price should be greater than or equal to 99th percentile") + + require.GreaterOrEqual(t, suggestions.TipCap.Perc25, float64(0), "Suggested 25th percentile tip cap should be greater than or equal to 0") + require.GreaterOrEqual(t, suggestions.TipCap.Perc50, suggestions.TipCap.Perc25, "Suggested 50th percentile tip cap should be greater than or equal to 25th percentile") + require.GreaterOrEqual(t, suggestions.TipCap.Perc75, suggestions.TipCap.Perc50, "Suggested 75th percentile tip cap should be greater than or equal to 50th percentile") + require.GreaterOrEqual(t, suggestions.TipCap.Perc99, suggestions.TipCap.Perc75, "Suggested 99th percentile tip cap should be greater than or equal to 75th percentile") + require.GreaterOrEqual(t, suggestions.TipCap.Max, suggestions.TipCap.Perc99, "Suggested max tip cap should be greater than or equal to 99th percentile") +} diff --git a/seth/geth_data/clique_genesis.json b/seth/geth_data/clique_genesis.json new file mode 100644 index 000000000..21dfe8a74 --- /dev/null +++ b/seth/geth_data/clique_genesis.json @@ -0,0 +1,33 @@ +{ + "config": { + "chainId": 1337, + "homesteadBlock": 0, + "daoForkBlock": 0, + "daoForkSupport": true, + "eip150Block": 0, + "eip155Block": 0, + "eip158Block": 0, + "byzantiumBlock": 0, + "constantinopleBlock": 0, + "petersburgBlock": 0, + "istanbulBlock": 0, + "muirGlacierBlock": 0, + "berlinBlock": 0, + "londonBlock": 0, + "arrowGlacierBlock": 0, + "grayGlacierBlock": 0, + "shanghaiTime": 0, + "clique": { + "period": 1, + "epoch": 30000 + } + }, + "difficulty": "1", + "gasLimit": "800000000", + "extradata": "0x0000000000000000000000000000000000000000000000000000000000000000f39Fd6e51aad88F6F4ce6aB8827279cffFb922660000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "alloc": { + "f39Fd6e51aad88F6F4ce6aB8827279cffFb92266": { + "balance": "20000000000000000000000" + } + } +} diff --git a/seth/geth_data/keystore/key1 b/seth/geth_data/keystore/key1 new file mode 100644 index 000000000..56c804234 --- /dev/null +++ b/seth/geth_data/keystore/key1 @@ -0,0 +1 @@ +{"address":"f39fd6e51aad88f6f4ce6ab8827279cfffb92266","crypto":{"cipher":"aes-128-ctr","ciphertext":"c36afd6e60b82d6844530bd6ab44dbc3b85a53e826c3a7f6fc6a75ce38c1e4c6","cipherparams":{"iv":"f69d2bb8cd0cb6274535656553b61806"},"kdf":"scrypt","kdfparams":{"dklen":32,"n":262144,"p":1,"r":8,"salt":"80d5f5e38ba175b6b89acfc8ea62a6f163970504af301292377ff7baafedab53"},"mac":"f2ecec2c4d05aacc10eba5235354c2fcc3776824f81ec6de98022f704efbf065"},"id":"e5c124e9-e280-4b10-a27b-d7f3e516b408","version":3} diff --git a/seth/geth_data/password.txt b/seth/geth_data/password.txt new file mode 100644 index 000000000..e69de29bb diff --git a/seth/go.mod b/seth/go.mod new file mode 100644 index 000000000..e1b626776 --- /dev/null +++ b/seth/go.mod @@ -0,0 +1,60 @@ +module github.com/smartcontractkit/chainlink-testing-framework/seth + +go 1.22.5 + +require ( + github.com/avast/retry-go v3.0.0+incompatible + github.com/awalterschulze/gographviz v2.0.3+incompatible + github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df + github.com/ethereum/go-ethereum v1.13.8 + github.com/holiman/uint256 v1.2.4 + github.com/montanaflynn/stats v0.7.1 + github.com/pelletier/go-toml/v2 v2.2.2 + github.com/pkg/errors v0.9.1 + github.com/rs/zerolog v1.30.0 + github.com/smartcontractkit/seth v1.2.0 + github.com/stretchr/testify v1.9.0 + github.com/urfave/cli/v2 v2.25.7 + go.uber.org/ratelimit v0.3.0 + golang.org/x/sync v0.7.0 +) + +require ( + github.com/Microsoft/go-winio v0.6.1 // indirect + github.com/StackExchange/wmi v1.2.1 // indirect + github.com/benbjohnson/clock v1.3.5 // indirect + github.com/bits-and-blooms/bitset v1.10.0 // indirect + github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect + github.com/consensys/bavard v0.1.13 // indirect + github.com/consensys/gnark-crypto v0.12.1 // indirect + github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect + github.com/crate-crypto/go-kzg-4844 v0.7.0 // indirect + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/deckarep/golang-set/v2 v2.1.0 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect + github.com/ethereum/c-kzg-4844 v0.4.0 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect + github.com/go-ole/go-ole v1.2.5 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.19 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/mmcloughlin/addchain v0.4.0 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible // indirect + github.com/supranational/blst v0.3.11 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect + golang.org/x/mod v0.17.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + rsc.io/tmplfunc v0.0.3 // indirect +) + +replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 diff --git a/seth/go.sum b/seth/go.sum new file mode 100644 index 000000000..7ae4a8c18 --- /dev/null +++ b/seth/go.sum @@ -0,0 +1,232 @@ +github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= +github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= +github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow= +github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM= +github.com/StackExchange/wmi v1.2.1 h1:VIkavFPXSjcnS+O8yTq7NI32k0R5Aj+v39y29VYDOSA= +github.com/StackExchange/wmi v1.2.1/go.mod h1:rcmrprowKIVzvc+NUiLncP2uuArMWLCbu9SBzvHz7e8= +github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= +github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= +github.com/avast/retry-go v3.0.0+incompatible h1:4SOWQ7Qs+oroOTQOYnAHqelpCO0biHSxpiH9JdtuBj0= +github.com/avast/retry-go v3.0.0+incompatible/go.mod h1:XtSnn+n/sHqQIpZ10K1qAevBhOOCWBLXXy3hyiqqBrY= +github.com/awalterschulze/gographviz v2.0.3+incompatible h1:9sVEXJBJLwGX7EQVhLm2elIKCm7P2YHFC8v6096G09E= +github.com/awalterschulze/gographviz v2.0.3+incompatible/go.mod h1:GEV5wmg4YquNw7v1kkyoX9etIk8yVmXj+AkDHuuETHs= +github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df h1:GSoSVRLoBaFpOOds6QyY1L8AX7uoY+Ln3BHc22W40X0= +github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df/go.mod h1:hiVxq5OP2bUGBRNS3Z/bt/reCLFNbdcST6gISi1fiOM= +github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= +github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bits-and-blooms/bitset v1.10.0 h1:ePXTeiPEazB5+opbv5fr8umg2R/1NlzgDsyepwsSr88= +github.com/bits-and-blooms/bitset v1.10.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= +github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k= +github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= +github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= +github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= +github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= +github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= +github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= +github.com/cockroachdb/redact v1.0.8 h1:8QG/764wK+vmEYoOlfobpe12EQcS81ukx/a4hdVMxNw= +github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 h1:IKgmqgMQlVJIZj19CdocBeSfSaiCbEBZGKODaixqtHM= +github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= +github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= +github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= +github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= +github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= +github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= +github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= +github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= +github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= +github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI= +github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs= +github.com/ethereum/c-kzg-4844 v0.4.0 h1:3MS1s4JtA868KpJxroZoepdV0ZKBp3u/O5HcZ7R3nlY= +github.com/ethereum/c-kzg-4844 v0.4.0/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= +github.com/ethereum/go-ethereum v1.13.8 h1:1od+thJel3tM52ZUNQwvpYOeRHlbkVFZ5S8fhi0Lgsg= +github.com/ethereum/go-ethereum v1.13.8/go.mod h1:sc48XYQxCzH3fG9BcrXCOOgQk2JfZzNAmIKnceogzsA= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5 h1:FtmdgXiUlNeRsoNMFlKLDt+S+6hbjVMEW6RGQ7aUf7c= +github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= +github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= +github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= +github.com/go-ole/go-ole v1.2.5 h1:t4MGB5xEDZvXI+0rMjjsfBsD7yAgp/s9ZDkL1JndXwY= +github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= +github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= +github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= +github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= +github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7 h1:3JQNjnMRil1yD0IfZKHF9GxxWKDJGj8I0IqOUol//sw= +github.com/holiman/billy v0.0.0-20230718173358-1c7e68d277a7/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= +github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= +github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= +github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= +github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= +github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= +github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/klauspost/compress v1.17.1 h1:NE3C767s2ak2bweCZo3+rdP4U/HoyVXLv/X9f2gPS5g= +github.com/klauspost/compress v1.17.1/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= +github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= +github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= +github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= +github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= +github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= +github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= +github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= +github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE= +github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= +github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= +github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.17.0 h1:rl2sfwZMtSthVU752MqfjQozy7blglC+1SOtjMAMh+Q= +github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= +github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= +github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= +github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rs/cors v1.9.0 h1:l9HGsTsHJcvW14Nk7J9KFz8bzeAWXn3CG6bgt7LsrAE= +github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= +github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c= +github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= +github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= +github.com/smartcontractkit/seth v1.2.0 h1:2BzO5siO7Ehuqsig/SB4szx+/2hZpOjZNFU9vFPmSM8= +github.com/smartcontractkit/seth v1.2.0/go.mod h1:+h2QvWqOQFBacL+w0KBMyJmVYbmXXTBkPbnR45JCfYQ= +github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= +github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= +github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= +github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= +github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= +github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= +github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= +github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/ratelimit v0.3.0 h1:IdZd9wqvFXnvLvSEBo0KPcGfkoBGNkpTHlrE3Rcjkjw= +go.uber.org/ratelimit v0.3.0/go.mod h1:So5LG7CV1zWpY1sHe+DXTJqQvOx+FFPFaAs2SnoyBaI= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ= +golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= +google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= +rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= diff --git a/seth/header_cache.go b/seth/header_cache.go new file mode 100644 index 000000000..c1752c9d3 --- /dev/null +++ b/seth/header_cache.go @@ -0,0 +1,85 @@ +package seth + +import ( + "fmt" + "sync" + + "github.com/ethereum/go-ethereum/core/types" +) + +type cacheItem struct { + header *types.Header + frequency int +} + +// LFUHeaderCache is a Least Frequently Used header cache +type LFUHeaderCache struct { + capacity uint64 + mu *sync.RWMutex + cache map[int64]*cacheItem //key is block number +} + +// NewLFUBlockCache creates a new LFU cache with the given capacity. +func NewLFUBlockCache(capacity uint64) *LFUHeaderCache { + return &LFUHeaderCache{ + capacity: capacity, + cache: make(map[int64]*cacheItem), + mu: &sync.RWMutex{}, + } +} + +// Get retrieves a header from the cache. +func (c *LFUHeaderCache) Get(blockNumber int64) (*types.Header, bool) { + c.mu.Lock() + defer c.mu.Unlock() + + if item, found := c.cache[blockNumber]; found { + item.frequency++ + L.Trace().Msgf("Found header %d in cache", blockNumber) + return item.header, true + } + return nil, false +} + +// Set adds or updates a header in the cache. +func (c *LFUHeaderCache) Set(header *types.Header) error { + if header == nil { + return fmt.Errorf("header is nil") + } + c.mu.Lock() + defer c.mu.Unlock() + + if oldHeader, found := c.cache[header.Number.Int64()]; found { + L.Trace().Msgf("Setting header %d in cache", header.Number.Int64()) + c.cache[int64(header.Number.Int64())] = &cacheItem{header: header, frequency: oldHeader.frequency + 1} + return nil + } + + if uint64(len(c.cache)) >= c.capacity { + c.evict() + } + L.Trace().Msgf("Setting header %d in cache", header.Number.Int64()) + c.cache[int64(header.Number.Int64())] = &cacheItem{header: header, frequency: 1} + + return nil +} + +// evict removes the least frequently used item from the cache. If more than one item has the same frequency, the oldest is evicted. +func (c *LFUHeaderCache) evict() { + var leastFreq = int(^uint(0) >> 1) + var evictKey int64 + oldestBlockNumber := ^uint64(0) + for key, item := range c.cache { + if item.frequency < leastFreq { + evictKey = key + leastFreq = item.frequency + oldestBlockNumber = item.header.Number.Uint64() + } else if item.frequency == leastFreq && item.header.Number.Uint64() < oldestBlockNumber { + // If frequencies are the same, evict the oldest based on block number + evictKey = key + oldestBlockNumber = item.header.Number.Uint64() + } + } + L.Trace().Msgf("Evicted header %d from cache", evictKey) + delete(c.cache, evictKey) +} diff --git a/seth/http_logging_transport.go b/seth/http_logging_transport.go new file mode 100644 index 000000000..fd0ba66d9 --- /dev/null +++ b/seth/http_logging_transport.go @@ -0,0 +1,66 @@ +package seth + +import ( + "crypto/tls" + "fmt" + "net/http" + "net/http/httputil" + "os" + "time" +) + +// LoggingTransport is a custom transport to log requests and responses +type LoggingTransport struct { + Transport http.RoundTripper +} + +// RoundTrip implements the RoundTripper interface +func (t *LoggingTransport) RoundTrip(req *http.Request) (*http.Response, error) { + start := time.Now() + + reqDump, err := httputil.DumpRequestOut(req, true) + if err != nil { + L.Error().Err(err).Msg("Error dumping request") + } else { + fmt.Printf("Request:\n%s\n", string(reqDump)) + } + + transport := t.Transport + if transport == nil { + transport = http.DefaultTransport + } + + resp, err := transport.RoundTrip(req) + if err != nil { + return nil, err + } + + respDump, err := httputil.DumpResponse(resp, true) + if err != nil { + L.Error().Err(err).Msg("Error dumping response") + } else { + fmt.Printf("Response:\n%s\n", string(respDump)) + } + + fmt.Printf("Request took %s\n", time.Since(start)) + return resp, nil +} + +// NewLoggingTransport creates a new logging transport for GAP or default transport +// controlled by SETH_LOG_LEVEL +func NewLoggingTransport() http.RoundTripper { + if os.Getenv(LogLevelEnvVar) == "debug" { + return &LoggingTransport{ + // TODO: GAP, add proper certificates + Transport: &http.Transport{ + //nolint + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + } + } + + return &http.Transport{ + //nolint + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + } +} diff --git a/seth/keyfile.go b/seth/keyfile.go new file mode 100644 index 000000000..e0241b63d --- /dev/null +++ b/seth/keyfile.go @@ -0,0 +1,106 @@ +package seth + +import ( + "context" + "crypto/ecdsa" + "math/big" + + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/crypto" + "github.com/pkg/errors" + "golang.org/x/sync/errgroup" +) + +// NewAddress creates a new address +func NewAddress() (string, string, error) { + privateKey, err := crypto.GenerateKey() + if err != nil { + return "", "", err + } + privateKeyBytes := crypto.FromECDSA(privateKey) + publicKey := privateKey.Public() + publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) + if !ok { + return "", "", errors.New("error casting public key to ECDSA") + } + address := crypto.PubkeyToAddress(*publicKeyECDSA).Hex() + L.Info(). + Str("Addr", address). + Msg("New address created") + return address, hexutil.Encode(privateKeyBytes)[2:], nil +} + +// ReturnFunds returns funds to the root key from all other keys +func ReturnFunds(c *Client, toAddr string) error { + if toAddr == "" { + toAddr = c.Addresses[0].Hex() + } + + gasPrice, err := c.GetSuggestedLegacyFees(context.Background(), Priority_Standard) + if err != nil { + gasPrice = big.NewInt(c.Cfg.Network.GasPrice) + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + eg, egCtx := errgroup.WithContext(ctx) + + if len(c.Addresses) == 1 { + return errors.New("No addresses to return funds from. Have you passed correct key file?") + } + + for i := 1; i < len(c.Addresses); i++ { + idx := i + eg.Go(func() error { + ctx, balanceCancel := context.WithTimeout(egCtx, c.Cfg.Network.TxnTimeout.Duration()) + balance, err := c.Client.BalanceAt(ctx, c.Addresses[idx], nil) + balanceCancel() + if err != nil { + L.Error().Err(err).Msg("Error getting balance") + return err + } + + var gasLimit int64 + //nolint + gasLimitRaw, err := c.EstimateGasLimitForFundTransfer(c.Addresses[idx], common.HexToAddress(toAddr), balance) + if err != nil { + gasLimit = c.Cfg.Network.TransferGasFee + } else { + gasLimit = int64(gasLimitRaw) + } + + networkTransferFee := gasPrice.Int64() * gasLimit + fundsToReturn := new(big.Int).Sub(balance, big.NewInt(networkTransferFee)) + + if fundsToReturn.Cmp(big.NewInt(0)) == -1 { + L.Warn(). + Str("Key", c.Addresses[idx].Hex()). + Interface("Balance", balance). + Interface("NetworkFee", networkTransferFee). + Interface("FundsToReturn", fundsToReturn). + Msg("Insufficient funds to return. Skipping.") + return nil + } + + L.Info(). + Str("Key", c.Addresses[idx].Hex()). + Interface("Balance", balance). + Interface("NetworkFee", c.Cfg.Network.GasPrice*gasLimit). + Interface("GasLimit", gasLimit). + Interface("GasPrice", gasPrice). + Interface("FundsToReturn", fundsToReturn). + Msg("Returning funds from address") + + return c.TransferETHFromKey( + egCtx, + idx, + toAddr, + fundsToReturn, + gasPrice, + ) + }) + } + + return eg.Wait() +} diff --git a/seth/log.go b/seth/log.go new file mode 100644 index 000000000..792ebae85 --- /dev/null +++ b/seth/log.go @@ -0,0 +1,32 @@ +package seth + +import ( + "os" + + "github.com/rs/zerolog" + "github.com/rs/zerolog/log" +) + +const ( + LogLevelEnvVar = "SETH_LOG_LEVEL" +) + +var ( + L zerolog.Logger +) + +func init() { + initDefaultLogging() +} + +func initDefaultLogging() { + lvlStr := os.Getenv(LogLevelEnvVar) + if lvlStr == "" { + lvlStr = "info" + } + lvl, err := zerolog.ParseLevel(lvlStr) + if err != nil { + panic(err) + } + L = log.Output(zerolog.ConsoleWriter{Out: os.Stderr}).Level(lvl) +} diff --git a/seth/nonce.go b/seth/nonce.go new file mode 100644 index 000000000..6c2e2131d --- /dev/null +++ b/seth/nonce.go @@ -0,0 +1,152 @@ +package seth + +import ( + "context" + "crypto/ecdsa" + "errors" + "time" + + "math/big" + "sync" + + "github.com/avast/retry-go" + "github.com/ethereum/go-ethereum/common" + "go.uber.org/ratelimit" +) + +const ( + ErrKeySyncTimeout = "key sync timeout, consider increasing key_sync_timeout in seth.toml, or increasing the number of keys" + ErrKeySync = "failed to sync the key" + ErrNonce = "failed to get nonce" + TimeoutKeyNum = -80001 +) + +// NonceManager tracks nonce for each address +type NonceManager struct { + *sync.Mutex + cfg *NonceManagerCfg + rl ratelimit.Limiter + Client *Client + SyncTimeout time.Duration + SyncedKeys chan *KeyNonce + Addresses []common.Address + PrivateKeys []*ecdsa.PrivateKey + Nonces map[common.Address]int64 +} + +type KeyNonce struct { + KeyNum int + Nonce uint64 +} + +// NewNonceManager creates a new nonce manager that tracks nonce for each address +func NewNonceManager(cfg *Config, addrs []common.Address, privKeys []*ecdsa.PrivateKey) (*NonceManager, error) { + nonces := make(map[common.Address]int64) + for _, addr := range addrs { + nonces[addr] = 0 + } + return &NonceManager{ + Mutex: &sync.Mutex{}, + cfg: cfg.NonceManager, + rl: ratelimit.New(cfg.NonceManager.KeySyncRateLimitSec, ratelimit.WithoutSlack), + Nonces: nonces, + Addresses: addrs, + PrivateKeys: privKeys, + SyncedKeys: make(chan *KeyNonce, len(addrs)), + }, nil +} + +// UpdateNonces syncs nonces for addresses +func (m *NonceManager) UpdateNonces() error { + L.Debug().Interface("Addrs", m.Addresses).Msg("Updating nonces for addresses") + m.Lock() + defer m.Unlock() + for addr := range m.Nonces { + nonce, err := m.Client.Client.NonceAt(context.Background(), addr, nil) + if err != nil { + return err + } + m.Nonces[addr] = int64(nonce) + } + L.Debug().Interface("Nonces", m.Nonces).Msg("Updated nonces for addresses") + m.SyncedKeys = make(chan *KeyNonce, len(m.Addresses)) + for keyNum, addr := range m.Addresses[1:] { + m.SyncedKeys <- &KeyNonce{ + KeyNum: keyNum + 1, + Nonce: uint64(m.Nonces[addr]), + } + } + return nil +} + +// NextNonce returns new nonce for addr +// this method is external for module testing, but you should not use it +// since handling nonces on the client is unpredictable +func (m *NonceManager) NextNonce(addr common.Address) *big.Int { + m.Lock() + defer m.Unlock() + nextNonce := big.NewInt(m.Nonces[addr]) + m.Nonces[addr]++ + return nextNonce +} + +func (m *NonceManager) anySyncedKey() int { + ctx, cancel := context.WithTimeout(context.Background(), m.cfg.KeySyncTimeout.Duration()) + defer cancel() + select { + case <-ctx.Done(): + m.Lock() + defer m.Unlock() + L.Error().Msg(ErrKeySyncTimeout) + m.Client.Errors = append(m.Client.Errors, errors.New(ErrKeySync)) + return TimeoutKeyNum //so that it's pretty unique number of invalid key + case keyData := <-m.SyncedKeys: + L.Trace(). + Interface("KeyNum", keyData.KeyNum). + Uint64("Nonce", keyData.Nonce). + Interface("Address", m.Addresses[keyData.KeyNum]). + Msg("Key selected") + go func() { + err := retry.Do( + func() error { + m.rl.Take() + L.Trace(). + Interface("KeyNum", keyData.KeyNum). + Interface("Address", m.Addresses[keyData.KeyNum]). + Msg("Key is syncing") + nonce, err := m.Client.Client.NonceAt(context.Background(), m.Addresses[keyData.KeyNum], nil) + if err != nil { + return errors.New(ErrNonce) + } + if nonce == keyData.Nonce+1 { + L.Trace(). + Interface("KeyNum", keyData.KeyNum). + Uint64("Nonce", nonce). + Interface("Address", m.Addresses[keyData.KeyNum]). + Msg("Key synced") + m.SyncedKeys <- &KeyNonce{ + KeyNum: keyData.KeyNum, + Nonce: nonce, + } + return nil + } + + L.Trace(). + Interface("KeyNum", keyData.KeyNum). + Uint64("Nonce", nonce). + Int("Expected nonce", int(keyData.Nonce+1)). + Interface("Address", m.Addresses[keyData.KeyNum]). + Msg("Key NOT synced") + + return errors.New(ErrKeySync) + }, + retry.Attempts(m.cfg.KeySyncRetries), + retry.Delay(m.cfg.KeySyncRetryDelay.Duration()), + ) + if err != nil { + m.Client.Errors = append(m.Client.Errors, errors.New(ErrKeySync)) + } + }() + return keyData.KeyNum + } +} diff --git a/seth/retry.go b/seth/retry.go new file mode 100644 index 000000000..5c1db37bd --- /dev/null +++ b/seth/retry.go @@ -0,0 +1,249 @@ +package seth + +import ( + "context" + "fmt" + "math/big" + "strings" + "time" + + "github.com/holiman/uint256" + + "github.com/avast/retry-go" + "github.com/ethereum/go-ethereum/core/types" + "github.com/pkg/errors" +) + +/* these are the common errors of RPCs */ + +const ( + ErrRPCConnectionRefused = "connection refused" +) + +const ( + ErrRetryTimeout = "retry timeout" +) + +// RetryTxAndDecode executes transaction several times, retries if connection is lost and decodes all the data +func (m *Client) RetryTxAndDecode(f func() (*types.Transaction, error)) (*DecodedTransaction, error) { + var tx *types.Transaction + err := retry.Do( + func() error { + var err error + tx, err = f() + return err + }, retry.OnRetry(func(i uint, _ error) { + L.Debug().Uint("Attempt", i).Msg("Retrying transaction...") + }), + retry.DelayType(retry.FixedDelay), + retry.Attempts(10), retry.Delay(time.Duration(1)*time.Second), retry.RetryIf(func(err error) bool { + return strings.Contains(err.Error(), ErrRPCConnectionRefused) + }), + ) + + if err != nil { + return &DecodedTransaction{}, errors.New(ErrRetryTimeout) + } + + dt, err := m.Decode(tx, nil) + if err != nil { + return &DecodedTransaction{}, errors.Wrap(err, "error decoding transaction") + } + + return dt, nil +} + +// GasBumpStrategyFn is a function that returns a new gas price based on the previous one +type GasBumpStrategyFn = func(previousGasPrice *big.Int) *big.Int + +// NoOpGasBumpStrategyFn is a default gas bump strategy that does nothing +var NoOpGasBumpStrategyFn = func(previousGasPrice *big.Int) *big.Int { + return previousGasPrice +} + +// PriorityBasedGasBumpingStrategyFn is a function that returns a gas bump strategy based on the priority. +// For Fast priority it bumps gas price by 30%, for Standard by 15%, for Slow by 5% and for the rest it does nothing. +var PriorityBasedGasBumpingStrategyFn = func(priority string) GasBumpStrategyFn { + switch priority { + case Priority_Degen: + // +100% + return func(gasPrice *big.Int) *big.Int { + return gasPrice.Mul(gasPrice, big.NewInt(2)) + } + case Priority_Fast: + // +30% + return func(gasPrice *big.Int) *big.Int { + gasPriceFloat, _ := gasPrice.Float64() + newGasPriceFloat := big.NewFloat(0.0).Mul(big.NewFloat(gasPriceFloat), big.NewFloat(1.3)) + newGasPrice, _ := newGasPriceFloat.Int64() + return big.NewInt(newGasPrice) + } + case Priority_Standard: + // 15% + return func(gasPrice *big.Int) *big.Int { + gasPriceFloat, _ := gasPrice.Float64() + newGasPriceFloat := big.NewFloat(0.0).Mul(big.NewFloat(gasPriceFloat), big.NewFloat(1.15)) + newGasPrice, _ := newGasPriceFloat.Int64() + return big.NewInt(newGasPrice) + } + case Priority_Slow: + // 5% + return func(gasPrice *big.Int) *big.Int { + gasPriceFloat, _ := gasPrice.Float64() + newGasPriceFloat := big.NewFloat(0.0).Mul(big.NewFloat(gasPriceFloat), big.NewFloat(1.05)) + newGasPrice, _ := newGasPriceFloat.Int64() + return big.NewInt(newGasPrice) + } + default: + return func(gasPrice *big.Int) *big.Int { + return gasPrice + } + } +} + +// prepareReplacementTransaction bumps gas price of the transaction if it wasn't confirmed in time. It returns a signed replacement transaction. +// Errors might be returned, because transaction was no longer pending, max gas price was reached or there was an error sending the transaction (e.g. nonce too low, meaning that original transaction was mined). +var prepareReplacementTransaction = func(client *Client, tx *types.Transaction) (*types.Transaction, error) { + L.Warn().Msgf("Transaction wasn't confirmed in %s. Bumping gas", client.Cfg.Network.TxnTimeout.String()) + + ctxPending, cancelPending := context.WithTimeout(context.Background(), client.Cfg.Network.TxnTimeout.Duration()) + _, isPending, err := client.Client.TransactionByHash(ctxPending, tx.Hash()) + defer cancelPending() + if err != nil { + return nil, err + } + + if !isPending { + L.Debug().Str("Tx hash", tx.Hash().Hex()).Msg("Transaction was confirmed before bumping gas") + return nil, errors.New("transaction was confirmed before bumping gas") + } + + signer := types.LatestSignerForChainID(tx.ChainId()) + sender, err := types.Sender(signer, tx) + if err != nil { + return nil, err + } + + senderPkIdx := -1 + for j, maybeSender := range client.Addresses { + if maybeSender == sender { + senderPkIdx = j + break + } + } + + if senderPkIdx == -1 { + return nil, fmt.Errorf("sender address '%s' not found in loaded private keys", sender) + } + + maxGasPrice := big.NewInt(client.Cfg.GasBump.MaxGasPrice) + privateKey := client.PrivateKeys[senderPkIdx] + var replacementTx *types.Transaction + + var checkMaxPrice = func(gasPrice, maxGasPrice *big.Int) error { + if !client.Cfg.HasMaxBumpGasPrice() { + L.Debug().Msg("Max gas price for gas bump is not set, skipping check") + return nil + } + + if gasPrice.Cmp(maxGasPrice) > 0 { + return fmt.Errorf("bumped gas price %s is higher than max gas price %s", gasPrice.String(), big.NewInt(client.Cfg.GasBump.MaxGasPrice).String()) + } + + return nil + } + + switch tx.Type() { + case types.LegacyTxType: + gasPrice := client.Cfg.GasBump.StrategyFn(tx.GasPrice()) + if err := checkMaxPrice(gasPrice, maxGasPrice); err != nil { + return nil, err + } + L.Warn().Interface("Old gas price", tx.GasPrice()).Interface("New gas price", gasPrice).Msg("Bumping gas price for legacy transaction") + txData := &types.LegacyTx{ + Nonce: tx.Nonce(), + To: tx.To(), + Value: tx.Value(), + Gas: tx.Gas(), + GasPrice: gasPrice, + Data: tx.Data(), + } + replacementTx, err = types.SignNewTx(privateKey, signer, txData) + case types.DynamicFeeTxType: + gasFeeCap := client.Cfg.GasBump.StrategyFn(tx.GasFeeCap()) + gasTipCap := client.Cfg.GasBump.StrategyFn(tx.GasTipCap()) + if err := checkMaxPrice(big.NewInt(0).Add(gasFeeCap, gasTipCap), maxGasPrice); err != nil { + return nil, err + } + L.Warn().Interface("Old gas fee cap", tx.GasFeeCap()).Interface("New gas fee cap", gasFeeCap).Interface("Old gas tip cap", tx.GasTipCap()).Interface("New gas tip cap", gasTipCap).Msg("Bumping gas fee cap and tip cap for EIP-1559 transaction") + txData := &types.DynamicFeeTx{ + Nonce: tx.Nonce(), + To: tx.To(), + Value: tx.Value(), + Gas: tx.Gas(), + GasFeeCap: gasFeeCap, + GasTipCap: gasTipCap, + Data: tx.Data(), + } + + replacementTx, err = types.SignNewTx(privateKey, signer, txData) + case types.BlobTxType: + if tx.To() == nil { + return nil, fmt.Errorf("blob tx with nil recipient is not supported") + } + gasFeeCap := client.Cfg.GasBump.StrategyFn(tx.GasFeeCap()) + gasTipCap := client.Cfg.GasBump.StrategyFn(tx.GasTipCap()) + blobFeeCap := client.Cfg.GasBump.StrategyFn(tx.BlobGasFeeCap()) + if err := checkMaxPrice(big.NewInt(0).Add(gasFeeCap, big.NewInt(0).Add(gasTipCap, blobFeeCap)), maxGasPrice); err != nil { + return nil, err + } + + L.Warn().Interface("Old gas fee cap", tx.GasFeeCap()).Interface("Old max fee per blob", tx.BlobGasFeeCap()).Interface("New max fee per blob", blobFeeCap).Interface("New gas fee cap", gasFeeCap).Interface("Old gas tip cap", tx.GasTipCap()).Interface("New gas tip cap", gasTipCap).Msg("Bumping gas fee cap and tip cap for Blob transaction") + txData := &types.BlobTx{ + Nonce: tx.Nonce(), + To: *tx.To(), + Value: uint256.NewInt(tx.Value().Uint64()), + Gas: tx.Gas(), + GasFeeCap: uint256.NewInt(gasFeeCap.Uint64()), + GasTipCap: uint256.NewInt(gasTipCap.Uint64()), + BlobFeeCap: uint256.NewInt(blobFeeCap.Uint64()), + BlobHashes: tx.BlobHashes(), + Data: tx.Data(), + } + + replacementTx, err = types.SignNewTx(privateKey, signer, txData) + case types.AccessListTxType: + gasPrice := client.Cfg.GasBump.StrategyFn(tx.GasPrice()) + if err := checkMaxPrice(gasPrice, maxGasPrice); err != nil { + return nil, err + } + L.Warn().Interface("Old gas price", tx.GasPrice()).Interface("New gas price", gasPrice).Msg("Bumping gas price for access list transaction") + + txData := &types.AccessListTx{ + Nonce: tx.Nonce(), + To: tx.To(), + Value: tx.Value(), + Gas: tx.Gas(), + Data: tx.Data(), + AccessList: tx.AccessList(), + } + + replacementTx, err = types.SignNewTx(privateKey, signer, txData) + + default: + return nil, fmt.Errorf("unsupported tx type %d", tx.Type()) + } + + if err != nil { + return nil, err + } + + ctx, cancel := context.WithTimeout(context.Background(), client.Cfg.Network.TxnTimeout.Duration()) + defer cancel() + err = client.Client.SendTransaction(ctx, replacementTx) + if err != nil { + return nil, err + } + + return replacementTx, nil +} diff --git a/seth/seth.toml b/seth/seth.toml new file mode 100644 index 000000000..d995d40d3 --- /dev/null +++ b/seth/seth.toml @@ -0,0 +1,228 @@ +# if there are no ABIs Seth will fail to initialise with Contract Store +abi_dir = "contracts/abi" +# contract bytecodes are optional, but necessary if we want to deploy them via Contract Store +bin_dir = "contracts/bin" + +# Uncomment if you want to load (address -> ABI_name) mapping from a file +# It will also save any new contract deployment (address -> ABI_name) mapping there. +# This functionality is not used for simulated networks. +#contract_map_file = "deployed_contracts_mumbai.toml" + +# controls which transactions are decoded/traced. Supported values are: none, all, reverted (default). +# if transaction level doesn't match, then calling Decode() does nothing. It's advised to keep it set +# to 'reverted' to limit noise. If you combine it with 'trace_to_json' it will save all possible data +# in JSON files for reverted transactions. +tracing_level = "reverted" + +# saves each decoding/tracing results to JSON files; what exactly is saved depends on what we +# were able te decode, we try to save maximum information possible. It can either be: +# just tx hash, decoded transaction or call trace. Which transactions traces are saved depends +# on 'tracing_level'. +# following outputs are possible: dot, json, console +# dot creates DOT graphs for each transaction, json saves decoded transactions and traces to JSON files +trace_outputs = ["console"] + +# where to place all artifacts that are generated by Seth, like transaction traces (assuming tracing is enabled and set to files) +artifacts_dir = "artifacts" + +# number of addresses to be generated and runtime, if set to 0, no addresses will be generated +# each generated address will receive a proportion of native tokens from root private key's balance +# with the value equal to (root_balance / ephemeral_addresses_number) - transfer_fee * ephemeral_addresses_number +ephemeral_addresses_number = 0 + +# If enabled we will panic when getting transaction options if current key/address has a pending transaction +# That's because the one we are about to send would get queued, possibly for a very long time. It's best to disable +# it when running load tests. +pending_nonce_protection_enabled = false + +# Amount to be left on root key/address, when we are using ephemeral addresses. It's the amount that will not +# be divided into ephemeral keys. +root_key_funds_buffer = 10 # 10 ether + +# feature-flagged expriments; first one sets funds return priority to 'slow' (core only!), second one +# sets the tip/base fee to the higher value in case there's 3+ orders of magnitude difference between them +experiments_enabled = ["slow_funds_return", "eip_1559_fee_equalizer"] + +# when enabled when creating a new Seth client we will send 10k wei from root address to root address +# to make sure transaction can be submitted and mined +check_rpc_health_on_start = false + +[gas_bumps] +# when > 0 then we will bump gas price for transactions that are stuck in the mempool +# by default the bump step is controlled by gas_price_estimation_tx_priority (check readme.md for more details) +# we bump both contract deployment transactions and any other transaction as long as it's passed to Decode() function +retries = 0 +# when > 0 then this will cap the gas price for bumped transactions. Once the cap is reached Seth will stop bumping +# the gas price and will wait for the transaction to be mined. +max_gas_price = 0 + +[nonce_manager] +key_sync_rate_limit_per_sec = 10 +key_sync_timeout = "20s" +key_sync_retry_delay = "1s" +key_sync_retries = 10 + +[[networks]] +name = "Anvil" +dial_timeout="1m" +transaction_timeout = "30s" +urls_secret = ["ws://localhost:8545"] +transfer_gas_fee = 21_000 +gas_limit = 10_000_000 +# legacy transactions +gas_price = 1_000_000_000 +# EIP-1559 transactions +#eip_1559_dynamic_fees = true +gas_fee_cap = 1_000_000_000 +gas_tip_cap = 1_000_000_000 + +[[networks]] +name = "Geth" +dial_timeout="1m" +transaction_timeout = "30s" +urls_secret = ["ws://localhost:8546"] +transfer_gas_fee = 21_000 +gas_limit = 8_000_000 +# legacy transactions +gas_price = 1_000_000_000 +# EIP-1559 transactions +#eip_1559_dynamic_fees = true +gas_fee_cap = 10_000_000_000 +gas_tip_cap = 3_000_000_000 + +[[networks]] +name = "Default" +dial_timeout="1m" +transaction_timeout = "30s" +# enable EIP-1559 transactions, because Seth will disable them if they are not supported +eip_1559_dynamic_fees = true +# enable automated gas estimation, because Seth will auto-disable it if any of the required JSON RPC methods are missing +gas_price_estimation_enabled = true +gas_price_estimation_blocks = 100 +gas_price_estimation_tx_priority = "standard" + +# fallback values +transfer_gas_fee = 21_000 +gas_price = 150_000_000_000 #150 gwei +gas_fee_cap = 150_000_000_000 #150 gwei +gas_tip_cap = 50_000_000_000 #50 gwei + +[[networks]] +name = "Fuji" +dial_timeout="1m" +transaction_timeout = "30s" +eip_1559_dynamic_fees = true + +# automated gas estimation +#gas_price_estimation_enabled = true +#gas_price_estimation_blocks = 100 +#gas_price_estimation_tx_priority = "standard" + +# gas limits +#transfer_gas_fee = 21_000 +# gas limit should be explicitly set only if you are connecting to a node that's incapable of estimating gas limit itself (should only happen for very old versions) +# gas_limit = 8_000_000 + +# manual settings, used when gas_price_estimation_enabled is false or when it fails +# legacy transactions +#gas_price = 30_000_000_000 +# EIP-1559 transactions +#gas_fee_cap = 30_000_000_000 +#gas_tip_cap = 1_800_000_000 + +[[networks]] +name = "Sepolia" +dial_timeout="1m" +transaction_timeout = "30s" +eip_1559_dynamic_fees = true + +# automated gas estimation +gas_price_estimation_enabled = true +gas_price_estimation_blocks = 100 +gas_price_estimation_tx_priority = "standard" + +# gas limits +transfer_gas_fee = 21_000 +# gas limit should be explicitly set only if you are connecting to a node that's incapable of estimating gas limit itself (should only happen for very old versions) +# gas_limit = 14_000_000 + +# manual settings, used when gas_price_estimation_enabled is false or when it fails +# legacy transactions +gas_price = 1_000_000_000 +# EIP-1559 transactions +gas_fee_cap = 25_000_000_000 +gas_tip_cap = 5_000_000_000 + + +[[networks]] +name = "Mumbai" +dial_timeout="1m" +transaction_timeout = "30s" +eip_1559_dynamic_fees = true + +# automated gas estimation for live networks +# if set to true we will dynamically estimate gas for every transaction (based on suggested values, priority and congestion rate for last X blocks) +gas_price_estimation_enabled = true +# number of blocks to use for congestion rate estimation (it will determine buffer added on top of suggested values) +gas_price_estimation_blocks = 100 +# transaction priority, which determines adjustment factor multiplier applied to suggested values (fast - 1.2x, standard - 1x, slow - 0.8x) +gas_price_estimation_tx_priority = "standard" + +# gas limits +transfer_gas_fee = 21_000 +# gas limit should be explicitly set only if you are connecting to a node that's incapable of estimating gas limit itself (should only happen for very old versions) +# gas_limit = 7_000_000 + +# manual settings, used when gas_price_estimation_enabled is false or when it fails +# # legacy transactions +gas_price = 30460480821 +# # EIP-1559 transactions +gas_fee_cap = 1_800_000_000 +gas_tip_cap = 30460480806 + + +[[networks]] +name = "zkEVM" +dial_timeout="1m" +transaction_timeout = "30s" +eip_1559_dynamic_fees = false + +# automated gas estimation +gas_price_estimation_enabled = true +gas_price_estimation_blocks = 100 +gas_price_estimation_tx_priority = "standard" + +# gas limits +transfer_gas_fee = 21_000 +# gas limit should be explicitly set only if you are connecting to a node that's incapable of estimating gas limit itself (should only happen for very old versions) +# gas_limit = 3_000_000 + +# manual settings, used when gas_price_estimation_enabled is false or when it fails +# legacy transactions +gas_price = 50_000_000 +# EIP-1559 transactions + +gas_fee_cap = 1_800_000_000 +gas_tip_cap = 1_800_000_000 + +[[networks]] +name = "ARBITRUM_SEPOLIA" +dial_timeout="1m" +transaction_timeout = "10m" +transfer_gas_fee = 50_000 +# gas_limit = 15_000_000 +# legacy transactions +gas_price = 200_000_000 +# EIP-1559 transactions +eip_1559_dynamic_fees = true +gas_fee_cap = 1009_694 +gas_tip_cap = 300_000 +# if set to true we will estimate gas for every transaction +gas_price_estimation_enabled = true +# how many last blocks to use, when estimating gas for a transaction +gas_price_estimation_blocks = 100 +# priority of the transaction, can be "fast", "standard" or "slow" (the higher the priority, the higher adjustment factor will be used for gas estimation) [default: "standard"] +gas_price_estimation_tx_priority = "standard" + +[block_stats] +rpc_requests_per_second_limit = 15 diff --git a/seth/shell.nix b/seth/shell.nix new file mode 100644 index 000000000..f484a8708 --- /dev/null +++ b/seth/shell.nix @@ -0,0 +1,21 @@ +{ stdenv, pkgs, lib }: + +pkgs.mkShell { + buildInputs = with pkgs; [ + foundry-bin + go-ethereum + solc-select + go + gopls + delve + golangci-lint + gotools + jq + ]; + GOROOT="${pkgs.go}/share/go"; + + shellHook = '' + solc-select install 0.8.19 + solc-select use 0.8.19 + ''; +} diff --git a/seth/test_utils/client.go b/seth/test_utils/client.go new file mode 100644 index 000000000..ef905679f --- /dev/null +++ b/seth/test_utils/client.go @@ -0,0 +1,170 @@ +package test_utils + +import ( + "context" + "math/big" + "testing" + + "github.com/ethereum/go-ethereum/common" + "github.com/stretchr/testify/require" + "golang.org/x/sync/errgroup" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" +) + +var zero int64 + +// +//// NewClientWithEphemeralAddresses creates a new Seth client with the given number of addresses. Each address is funded with the +//// calculated with the amount of ETH calculated by dividing the total balance of root key by the number of addresses (minus root key buffer amount). +//func NewClientWithEphemeralAddresses(t *testing.T, addressCount int) *seth.Client { +// cfg, err := seth.ReadConfig() +// require.NoError(t, err, "failed to read config") +// +// cfg.EphemeralAddrs = &zero +// +// c, err := seth.NewClientWithConfig(cfg) +// require.NoError(t, err, "failed to initialize seth") +// +// var privateKeys []string +// var addresses []string +// for i := 0; i < addressCount; i++ { +// addr, pk, err := seth.NewAddress() +// require.NoError(t, err, "failed to generate new address") +// +// privateKeys = append(privateKeys, pk) +// addresses = append(addresses, addr) +// } +// +// gasPrice, err := c.GetSuggestedLegacyFees(context.Background(), seth.Priority_Standard) +// if err != nil { +// gasPrice = big.NewInt(c.Cfg.Network.GasPrice) +// } +// +// bd, err := c.CalculateSubKeyFunding(int64(addressCount), gasPrice.Int64(), *cfg.RootKeyFundsBuffer) +// require.NoError(t, err, "failed to calculate subkey funding") +// +// ctx, cancel := context.WithCancel(context.Background()) +// defer cancel() +// eg, egCtx := errgroup.WithContext(ctx) +// // root key is element 0 in ephemeral +// for _, addr := range addresses { +// addr := addr +// eg.Go(func() error { +// return c.TransferETHFromKey(egCtx, 0, addr, bd.AddrFunding, gasPrice) +// }) +// } +// err = eg.Wait() +// require.NoError(t, err, "failed to transfer funds to subkeys") +// +// // Add root private key to the list of private keys +// pksToUse := []string{cfg.Network.PrivateKeys[0]} +// pksToUse = append(pksToUse, privateKeys...) +// // Set funded private keys in config and create a new Seth client to simulate a situation, in which PKs were passed in config to a new client +// cfg.Network.PrivateKeys = pksToUse +// +// newClient, err := seth.NewClientWithConfig(cfg) +// require.NoError(t, err, "failed to initialize new Seth with private keys") +// +// return newClient +//} + +// NewClientWithAddresses creates a new Seth client with the given number of addresses. Each address is funded with the given amount of native tokens. +// If funding is `nil` then each address is funded with the calculated with the amount of ETH calculated by dividing the total balance of root key by the number of addresses (minus root key buffer amount). +func NewClientWithAddresses(t *testing.T, addressCount int, funding *big.Int) *seth.Client { + cfg, err := seth.ReadConfig() + require.NoError(t, err, "failed to read config") + + cfg.EphemeralAddrs = &zero + + c, err := seth.NewClientWithConfig(cfg) + require.NoError(t, err, "failed to initialize seth") + + var privateKeys []string + var addresses []string + for i := 0; i < addressCount; i++ { + addr, pk, err := seth.NewAddress() + require.NoError(t, err, "failed to generate new address") + + privateKeys = append(privateKeys, pk) + addresses = append(addresses, addr) + } + + gasPrice, err := c.GetSuggestedLegacyFees(context.Background(), seth.Priority_Standard) + if err != nil { + gasPrice = big.NewInt(c.Cfg.Network.GasPrice) + } + + if funding == nil { + bd, err := c.CalculateSubKeyFunding(int64(addressCount), gasPrice.Int64(), *cfg.RootKeyFundsBuffer) + require.NoError(t, err, "failed to calculate subkey funding") + + funding = bd.AddrFunding + } + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + eg, egCtx := errgroup.WithContext(ctx) + // root key is element 0 in ephemeral + for _, addr := range addresses { + addr := addr + eg.Go(func() error { + return c.TransferETHFromKey(egCtx, 0, addr, funding, gasPrice) + }) + } + err = eg.Wait() + require.NoError(t, err, "failed to transfer funds to subkeys") + + // Add root private key to the list of private keys + pksToUse := []string{cfg.Network.PrivateKeys[0]} + pksToUse = append(pksToUse, privateKeys...) + // Set funded private keys in config and create a new Seth client to simulate a situation, in which PKs were passed in config to a new client + cfg.Network.PrivateKeys = pksToUse + + newClient, err := seth.NewClientWithConfig(cfg) + require.NoError(t, err, "failed to initialize new Seth with private keys") + + return newClient +} + +// NewPrivateKeyWithFunds generates a new private key and funds it with the given amount of native tokens. +func NewPrivateKeyWithFunds(t *testing.T, c *seth.Client, funds *big.Int) string { + addr, pk, err := seth.NewAddress() + require.NoError(t, err, "failed to generate new address") + + gasPrice, err := c.GetSuggestedLegacyFees(context.Background(), seth.Priority_Standard) + if err != nil { + gasPrice = big.NewInt(c.Cfg.Network.GasPrice) + } + + ctx, cancel := context.WithTimeout(context.Background(), c.Cfg.Network.TxnTimeout.Duration()) + err = c.TransferETHFromKey(ctx, 0, addr, funds, gasPrice) + defer cancel() + require.NoError(t, err, "failed to transfer funds to subkeys") + + return pk +} + +// TransferAllFundsBetweenKeyAndAddress transfers all funds key at specified index has to the given address. +func TransferAllFundsBetweenKeyAndAddress(client *seth.Client, keyNum int, toAddress common.Address) error { + err := client.NonceManager.UpdateNonces() + if err != nil { + return err + } + + gasPrice, err := client.GetSuggestedLegacyFees(context.Background(), seth.Priority_Standard) + if err != nil { + gasPrice = big.NewInt(client.Cfg.Network.GasPrice) + } + + balance, err := client.Client.BalanceAt(context.Background(), client.Addresses[0], nil) + if err != nil { + return err + } + + toTransfer := new(big.Int).Sub(balance, big.NewInt(0).Mul(gasPrice, big.NewInt(client.Cfg.Network.TransferGasFee))) + + ctx, cancel := context.WithTimeout(context.Background(), client.Cfg.Network.TxnTimeout.Duration()) + defer cancel() + return client.TransferETHFromKey(ctx, keyNum, toAddress.Hex(), toTransfer, gasPrice) +} diff --git a/seth/test_utils/config.go b/seth/test_utils/config.go new file mode 100644 index 000000000..bc4e4cea0 --- /dev/null +++ b/seth/test_utils/config.go @@ -0,0 +1,23 @@ +package test_utils + +import ( + "github.com/pelletier/go-toml/v2" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" +) + +// CopyConfig creates a deep copy of Seth config +func CopyConfig(config *seth.Config) (*seth.Config, error) { + marshalled, err := toml.Marshal(config) + if err != nil { + return nil, err + } + + var configCopy seth.Config + err = toml.Unmarshal(marshalled, &configCopy) + if err != nil { + return nil, err + } + + return &configCopy, nil +} diff --git a/seth/tracing.go b/seth/tracing.go new file mode 100644 index 000000000..69f96fb05 --- /dev/null +++ b/seth/tracing.go @@ -0,0 +1,718 @@ +package seth + +import ( + "context" + "fmt" + "strconv" + "strings" + "sync" + + "github.com/ethereum/go-ethereum/accounts/abi" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/common/hexutil" + "github.com/ethereum/go-ethereum/rpc" + "github.com/pkg/errors" + "github.com/rs/zerolog" +) + +const ( + ErrNoTrace = "no trace found" + ErrNoABIMethod = "no ABI method found" + ErrNoAbiFound = "no ABI found in Contract Store" + ErrNoFourByteFound = "no method signatures found in tracing data" + ErrInvalidMethodSignature = "no method signature found or it's not 4 bytes long" + WrnMissingCallTrace = "This call was missing from call trace, but it's signature was present in 4bytes trace. Most data is missing; Call order remains unknown" + + FAILED_TO_DECODE = "failed to decode" + UNKNOWN = "unknown" + NO_DATA = "no data" + + CommentMissingABI = "Call not decoded due to missing ABI instance" +) + +type Tracer struct { + Cfg *Config + rpcClient *rpc.Client + traces map[string]*Trace + Addresses []common.Address + ContractStore *ContractStore + ContractAddressToNameMap ContractMap + decodedCalls map[string][]*DecodedCall + ABIFinder *ABIFinder + tracesMutex *sync.RWMutex + decodedMutex *sync.RWMutex +} + +func (t *Tracer) getTrace(txHash string) *Trace { + t.tracesMutex.Lock() + defer t.tracesMutex.Unlock() + return t.traces[txHash] +} + +func (t *Tracer) addTrace(txHash string, trace *Trace) { + t.tracesMutex.Lock() + defer t.tracesMutex.Unlock() + t.traces[txHash] = trace +} + +func (t *Tracer) GetDecodedCalls(txHash string) []*DecodedCall { + t.decodedMutex.Lock() + defer t.decodedMutex.Unlock() + return t.decodedCalls[txHash] +} + +func (t *Tracer) GetAllDecodedCalls() map[string][]*DecodedCall { + t.decodedMutex.Lock() + defer t.decodedMutex.Unlock() + return t.decodedCalls +} + +func (t *Tracer) AddDecodedCalls(txHash string, calls []*DecodedCall) { + t.decodedMutex.Lock() + defer t.decodedMutex.Unlock() + t.decodedCalls[txHash] = calls +} + +type Trace struct { + TxHash string + FourByte map[string]*TXFourByteMetadataOutput + CallTrace *TXCallTraceOutput + OpCodesTrace map[string]interface{} +} + +type TXFourByteMetadataOutput struct { + CallSize int + Times int +} + +type TXCallTraceOutput struct { + Call + Calls []Call `json:"calls"` +} + +func (t *TXCallTraceOutput) AsCall() Call { + return t.Call +} + +type TraceLog struct { + Address string `json:"address"` + Data string `json:"data"` + Topics []string `json:"topics"` +} + +func (t TraceLog) GetTopics() []common.Hash { + var h []common.Hash + for _, v := range t.Topics { + h = append(h, common.HexToHash(v)) + } + return h +} + +func (t TraceLog) GetData() []byte { + return common.Hex2Bytes(strings.TrimPrefix(t.Data, "0x")) +} + +type Call struct { + From string `json:"from"` + Gas string `json:"gas"` + GasUsed string `json:"gasUsed"` + Input string `json:"input"` + Logs []TraceLog `json:"logs"` + Output string `json:"output"` + To string `json:"to"` + Type string `json:"type"` + Value string `json:"value"` + Error string `json:"error"` + Calls []Call `json:"calls"` +} + +func NewTracer(cs *ContractStore, abiFinder *ABIFinder, cfg *Config, contractAddressToNameMap ContractMap, addresses []common.Address) (*Tracer, error) { + ctx, cancel := context.WithTimeout(context.Background(), cfg.Network.DialTimeout.Duration()) + defer cancel() + c, err := rpc.DialOptions(ctx, cfg.FirstNetworkURL(), rpc.WithHeaders(cfg.RPCHeaders)) + if err != nil { + return nil, fmt.Errorf("failed to connect to '%s' due to: %w", cfg.FirstNetworkURL(), err) + } + return &Tracer{ + Cfg: cfg, + rpcClient: c, + traces: make(map[string]*Trace), + Addresses: addresses, + ContractStore: cs, + ContractAddressToNameMap: contractAddressToNameMap, + decodedCalls: make(map[string][]*DecodedCall), + ABIFinder: abiFinder, + tracesMutex: &sync.RWMutex{}, + decodedMutex: &sync.RWMutex{}, + }, nil +} + +func (t *Tracer) TraceGethTX(txHash string, revertErr error) error { + fourByte, err := t.trace4Byte(txHash) + if err != nil { + L.Debug().Err(err).Msg("Failed to trace 4byte signatures. Some tracing data might be missing") + } + opCodesTrace, err := t.traceOpCodesTracer(txHash) + if err != nil { + L.Debug().Err(err).Msg("Failed to trace opcodes. Some tracing data will be missing") + } + + callTrace, err := t.traceCallTracer(txHash) + if err != nil { + return err + } + + t.addTrace(txHash, &Trace{ + TxHash: txHash, + FourByte: fourByte, + CallTrace: callTrace, + OpCodesTrace: opCodesTrace, + }) + + decodedCalls, err := t.DecodeTrace(L, *t.getTrace(txHash)) + if err != nil { + return err + } + + if len(decodedCalls) != 0 { + t.printDecodedCallData(L, decodedCalls, revertErr) + + err = t.generateDotGraph(txHash, decodedCalls, revertErr) + if err != nil { + return err + } + } + + return t.PrintTXTrace(txHash) +} + +func (t *Tracer) PrintTXTrace(txHash string) error { + trace := t.getTrace(txHash) + if trace == nil { + return errors.New(ErrNoTrace) + } + l := L.With().Str("Transaction", txHash).Logger() + l.Trace().Interface("4Byte", trace.FourByte).Msg("Calls function signatures (names)") + l.Trace().Interface("CallTrace", trace.CallTrace).Msg("Full call trace with logs") + return nil +} + +func (t *Tracer) trace4Byte(txHash string) (map[string]*TXFourByteMetadataOutput, error) { + var trace map[string]int + if err := t.rpcClient.Call(&trace, "debug_traceTransaction", txHash, map[string]interface{}{"tracer": "4byteTracer"}); err != nil { + return nil, err + } + out := make(map[string]*TXFourByteMetadataOutput) + for k, v := range trace { + d := strings.Split(k, "-") + callParamsSize, err := strconv.Atoi(d[1]) + if err != nil { + return nil, err + } + out[d[0]] = &TXFourByteMetadataOutput{Times: v, CallSize: callParamsSize} + } + return out, nil +} + +func (t *Tracer) traceCallTracer(txHash string) (*TXCallTraceOutput, error) { + var trace *TXCallTraceOutput + if err := t.rpcClient.Call( + &trace, + "debug_traceTransaction", + txHash, + map[string]interface{}{ + "tracer": "callTracer", + "tracerConfig": map[string]interface{}{ + "withLog": true, + }, + }); err != nil { + return nil, err + } + return trace, nil +} + +func (t *Tracer) traceOpCodesTracer(txHash string) (map[string]interface{}, error) { + var trace map[string]interface{} + if err := t.rpcClient.Call(&trace, "debug_traceTransaction", txHash); err != nil { + return nil, err + } + return trace, nil +} + +// DecodeTrace decodes the trace of a transaction including all subcalls. It returns a list of decoded calls. +// Depending on the config it also saves the decoded calls as JSON files. +func (t *Tracer) DecodeTrace(l zerolog.Logger, trace Trace) ([]*DecodedCall, error) { + var decodedCalls []*DecodedCall + + if t.ContractStore == nil { + L.Warn().Msg(WarnNoContractStore) + return []*DecodedCall{}, nil + } + + // we can still decode the calls without 4byte signatures + if len(trace.FourByte) == 0 { + L.Debug().Msg(ErrNoFourByteFound) + } + + methods := make([]string, 0, len(trace.CallTrace.Calls)+1) + + var getSignature = func(input string) (string, error) { + if len(input) < 10 { + err := errors.New(ErrInvalidMethodSignature) + l.Err(err). + Str("Input", input). + Send() + return "", errors.New(ErrInvalidMethodSignature) + } + + return input[2:10], nil + } + + mainSig, err := getSignature(trace.CallTrace.Input) + if err != nil { + return nil, err + } + methods = append(methods, mainSig) + + var gatherAllMethodsFn func(calls []Call) error + gatherAllMethodsFn = func(calls []Call) error { + for _, call := range calls { + sig, err := getSignature(call.Input) + if err != nil { + return err + } + + methods = append(methods, sig) + + if len(call.Calls) > 0 { + if err := gatherAllMethodsFn(call.Calls); err != nil { + return err + } + } + } + return nil + } + + err = gatherAllMethodsFn(trace.CallTrace.Calls) + if err != nil { + return nil, err + } + + decodedMainCall, err := t.decodeCall(common.Hex2Bytes(methods[0]), trace.CallTrace.AsCall()) + if err != nil { + l.Debug(). + Err(err). + Str("From", decodedMainCall.FromAddress). + Str("To", decodedMainCall.ToAddress). + Msg("Failed to decode main call") + + return nil, err + } + + decodedCalls = append(decodedCalls, decodedMainCall) + + methodCounter := 0 + nestingLevel := 1 + var processCallsFn func(calls []Call, parentSignature string) error + processCallsFn = func(calls []Call, parentSignature string) error { + for _, call := range calls { + methodCounter++ + if methodCounter >= len(methods) { + return errors.New("method counter exceeds the number of methods. This indicates there's a logical error in tracing. Please reach out to Test Tooling team") + } + + methodHex := methods[methodCounter] + methodByte := common.Hex2Bytes(methodHex) + decodedSubCall, err := t.decodeCall(methodByte, call) + if err != nil { + l.Debug(). + Err(err). + Str("From", call.From). + Str("To", call.To). + Msg("Failed to decode sub call") + decodedCalls = append(decodedCalls, &DecodedCall{ + CommonData: CommonData{Method: FAILED_TO_DECODE, + Input: map[string]interface{}{"error": FAILED_TO_DECODE}, + Output: map[string]interface{}{"error": FAILED_TO_DECODE}, + }, + FromAddress: call.From, + ToAddress: call.To, + }) + continue + } + decodedSubCall.NestingLevel = nestingLevel + decodedSubCall.ParentSignature = parentSignature + decodedCalls = append(decodedCalls, decodedSubCall) + + if len(call.Calls) > 0 { + nestingLevel++ + if err := processCallsFn(call.Calls, methodHex); err != nil { + return err + } + nestingLevel-- + } + } + return nil + } + + err = processCallsFn(trace.CallTrace.Calls, mainSig) + if err != nil { + return nil, err + } + + missingCalls := t.checkForMissingCalls(trace) + decodedCalls = append(decodedCalls, missingCalls...) + + t.AddDecodedCalls(trace.TxHash, decodedCalls) + return decodedCalls, nil +} + +func (t *Tracer) decodeCall(byteSignature []byte, rawCall Call) (*DecodedCall, error) { + var txInput map[string]interface{} + var txOutput map[string]interface{} + var txEvents []DecodedCommonLog + + var generateDuplicatesComment = func(abiResult ABIFinderResult) string { + var comment string + if abiResult.DuplicateCount > 0 { + comment = fmt.Sprintf("potentially inaccurate - method present in %d other contracts", abiResult.DuplicateCount) + } + + return comment + } + + defaultCall := getDefaultDecodedCall() + + abiResult, err := t.ABIFinder.FindABIByMethod(rawCall.To, byteSignature) + + defaultCall.CommonData.Signature = common.Bytes2Hex(byteSignature) + defaultCall.FromAddress = rawCall.From + defaultCall.ToAddress = rawCall.To + defaultCall.From = t.getHumanReadableAddressName(rawCall.From) + defaultCall.To = t.getHumanReadableAddressName(rawCall.To) //somehow mark it with "*" + defaultCall.Comment = generateDuplicatesComment(abiResult) + + defaultCall.CallType = rawCall.Type + defaultCall.Error = rawCall.Error + + if rawCall.Value != "" && rawCall.Value != "0x0" { + decimalValue, err := strconv.ParseInt(strings.TrimPrefix(rawCall.Value, "0x"), 16, 64) + if err != nil { + L.Debug(). + Err(err). + Str("Value", rawCall.Value). + Msg("Failed to parse value") + } else { + defaultCall.Value = decimalValue + } + } + + if rawCall.Gas != "" && rawCall.Gas != "0x0" { + decimalValue, err := strconv.ParseInt(strings.TrimPrefix(rawCall.Gas, "0x"), 16, 64) + if err != nil { + L.Debug(). + Err(err). + Str("Gas", rawCall.Gas). + Msg("Failed to parse value") + } else { + defaultCall.GasLimit = uint64(decimalValue) + } + } + + if rawCall.GasUsed != "" && rawCall.GasUsed != "0x0" { + decimalValue, err := strconv.ParseInt(strings.TrimPrefix(rawCall.GasUsed, "0x"), 16, 64) + if err != nil { + L.Debug(). + Err(err). + Str("GasUsed", rawCall.GasUsed). + Msg("Failed to parse value") + } else { + defaultCall.GasUsed = uint64(decimalValue) + } + } + + if err != nil { + if defaultCall.Comment != "" { + defaultCall.Comment = fmt.Sprintf("%s; %s", defaultCall.Comment, CommentMissingABI) + } else { + defaultCall.Comment = CommentMissingABI + } + L.Warn(). + Err(err). + Str("Method signature", common.Bytes2Hex(byteSignature)). + Str("Contract", rawCall.To). + Msg("Method not found in any ABI instance. Unable to provide full tracing information") + + // let's not return the error, as we can still provide some information + return defaultCall, nil + } + + defaultCall.Method = abiResult.Method.Sig + defaultCall.Signature = common.Bytes2Hex(abiResult.Method.ID) + + txInput, err = decodeTxInputs(L, common.Hex2Bytes(strings.TrimPrefix(rawCall.Input, "0x")), abiResult.Method) + if err != nil { + L.Debug().Err(err).Msg("Failed to decode inputs") + } else { + defaultCall.Input = txInput + } + + if rawCall.Output != "" { + output, err := hexutil.Decode(rawCall.Output) + if err != nil { + return defaultCall, errors.Wrap(err, ErrDecodeOutput) + } + txOutput, err = decodeTxOutputs(L, output, abiResult.Method) + if err != nil { + L.Debug().Err(err).Msg("Failed to decode outputs") + } else { + defaultCall.Output = txOutput + } + + } + + txEvents, err = t.decodeContractLogs(L, rawCall.Logs, abiResult.ABI) + if err != nil { + L.Debug().Err(err).Msg("Failed to decode logs") + } else { + defaultCall.Events = txEvents + } + + return defaultCall, nil +} + +func (t *Tracer) isOwnAddress(addr string) bool { + for _, a := range t.Addresses { + if strings.ToLower(a.Hex()) == addr { + return true + } + } + + return false +} + +func (t *Tracer) checkForMissingCalls(trace Trace) []*DecodedCall { + expected := 0 + for _, v := range trace.FourByte { + expected += v.Times + } + + var countAllTracedCallsFn func(calls []Call, previous int) int + countAllTracedCallsFn = func(call []Call, previous int) int { + for _, c := range call { + previous++ + previous = countAllTracedCallsFn(c.Calls, previous) + } + + return previous + } + + actual := countAllTracedCallsFn(trace.CallTrace.Calls, 1) // +1 for the main call + + diff := expected - actual + if diff != 0 { + L.Debug(). + Int("Debugged calls", actual). + Int("4byte signatures", len(trace.FourByte)). + Msgf("Number of calls and signatures does not match. There were %d more call that were't debugged", diff) + + unknownCall := &DecodedCall{ + CommonData: CommonData{Method: NO_DATA, + Input: map[string]interface{}{"warning": NO_DATA}, + Output: map[string]interface{}{"warning": NO_DATA}, + }, + FromAddress: UNKNOWN, + ToAddress: UNKNOWN, + Events: []DecodedCommonLog{ + {Signature: NO_DATA, EventData: map[string]interface{}{"warning": NO_DATA}}, + }, + } + + var missingSignatures []string + var findSignatureFn func(fourByteSign string, calls []Call) bool + findSignatureFn = func(fourByteSign string, calls []Call) bool { + for _, c := range calls { + if strings.Contains(c.Input, fourByteSign) { + return true + } + + if findSignatureFn(fourByteSign, c.Calls) { + return true + } + } + + return false + } + for k := range trace.FourByte { + if strings.Contains(trace.CallTrace.Input, k) { + continue + } + + found := findSignatureFn(k, trace.CallTrace.Calls) + + if !found { + missingSignatures = append(missingSignatures, k) + } + } + + missedCalls := make([]*DecodedCall, 0, len(missingSignatures)) + + for _, missingSig := range missingSignatures { + byteSignature := common.Hex2Bytes(strings.TrimPrefix(missingSig, "0x")) + humanName := missingSig + + abiResult, err := t.ABIFinder.FindABIByMethod(UNKNOWN, byteSignature) + if err != nil { + L.Info(). + Str("Signature", humanName). + Msg("Method not found in any ABI instance. Unable to provide any more tracing information") + + missedCalls = append(missedCalls, unknownCall) + continue + } + + toAddress := t.ContractAddressToNameMap.GetContractAddress(abiResult.ContractName()) + comment := WrnMissingCallTrace + if abiResult.DuplicateCount > 0 { + comment = fmt.Sprintf("%s; Potentially inaccurate - method present in %d other contracts", comment, abiResult.DuplicateCount) + } + + missedCalls = append(missedCalls, &DecodedCall{ + CommonData: CommonData{ + Signature: humanName, + Method: abiResult.Method.Name, + Input: map[string]interface{}{"warning": NO_DATA}, + Output: map[string]interface{}{"warning": NO_DATA}, + }, + FromAddress: UNKNOWN, + ToAddress: toAddress, + To: abiResult.ContractName(), + From: UNKNOWN, + Comment: comment, + Events: []DecodedCommonLog{ + {Signature: NO_DATA, EventData: map[string]interface{}{"warning": NO_DATA}}, + }, + }) + } + + return missedCalls + } + + return []*DecodedCall{} +} + +func (t *Tracer) SaveDecodedCallsAsJson(dirname string) error { + for txHash, calls := range t.GetAllDecodedCalls() { + _, err := saveAsJson(calls, dirname, txHash) + if err != nil { + return err + } + } + + return nil +} + +func (t *Tracer) decodeContractLogs(l zerolog.Logger, logs []TraceLog, a abi.ABI) ([]DecodedCommonLog, error) { + l.Trace().Msg("Decoding events") + var eventsParsed []DecodedCommonLog + for _, lo := range logs { + for _, evSpec := range a.Events { + if evSpec.ID.Hex() == lo.Topics[0] { + l.Trace().Str("Name", evSpec.RawName).Str("Signature", evSpec.Sig).Msg("Unpacking event") + eventsMap, topicsMap, err := decodeEventFromLog(l, a, evSpec, lo) + if err != nil { + return nil, errors.Wrap(err, ErrDecodeLog) + } + parsedEvent := decodedLogFromMaps(&DecodedCommonLog{}, eventsMap, topicsMap) + if decodedLog, ok := parsedEvent.(*DecodedCommonLog); ok { + decodedLog.Signature = evSpec.Sig + t.mergeLogMeta(decodedLog, lo) + eventsParsed = append(eventsParsed, *decodedLog) + l.Trace().Interface("Log", parsedEvent).Msg("Transaction log") + } else { + l.Trace(). + Str("Actual type", fmt.Sprintf("%T", decodedLog)). + Msg("Failed to cast decoded event to DecodedCommonLog") + } + } + } + } + return eventsParsed, nil +} + +// mergeLogMeta add metadata from log +func (t *Tracer) mergeLogMeta(pe *DecodedCommonLog, l TraceLog) { + pe.Address = common.HexToAddress(l.Address) + pe.Topics = l.Topics +} + +func (t *Tracer) getHumanReadableAddressName(address string) string { + if t.ContractAddressToNameMap.IsKnownAddress(address) { + address = t.ContractAddressToNameMap.GetContractName(address) + } else if t.isOwnAddress(address) { + address = "you" + } else { + address = "unknown" + } + + return address +} + +// printDecodedCallData prints decoded txn data +func (t *Tracer) printDecodedCallData(l zerolog.Logger, calls []*DecodedCall, revertErr error) { + if !t.Cfg.hasOutput(TraceOutput_Console) { + return + } + var getIndentation = func(dc *DecodedCall) string { + var indentation string + for i := 0; i < dc.NestingLevel; i++ { + indentation += " " + } + return indentation + } + + L.Debug(). + Msg("----------- Decoding transaction trace started -----------") + + for i, dc := range calls { + indentation := getIndentation(dc) + + l.Debug().Str(fmt.Sprintf("%s- Call", indentation), fmt.Sprintf("%s -> %s", dc.FromAddress, dc.ToAddress)).Send() + l.Debug().Str(fmt.Sprintf("%s- From -> To", indentation), fmt.Sprintf("%s -> %s", dc.From, dc.To)).Send() + l.Debug().Str(fmt.Sprintf("%s- Call Type", indentation), dc.CallType).Send() + + l.Debug().Str(fmt.Sprintf("%s- Method signature", indentation), dc.Signature).Send() + l.Debug().Str(fmt.Sprintf("%s- Method name", indentation), dc.Method).Send() + l.Debug().Str(fmt.Sprintf("%s- Gas used/limit", indentation), fmt.Sprintf("%d/%d", dc.GasUsed, dc.GasLimit)).Send() + l.Debug().Str(fmt.Sprintf("%s- Gas left", indentation), fmt.Sprintf("%d", dc.GasLimit-dc.GasUsed)).Send() + if dc.Comment != "" { + l.Debug().Str(fmt.Sprintf("%s- Comment", indentation), dc.Comment).Send() + } + if dc.Input != nil { + l.Debug().Interface(fmt.Sprintf("%s- Inputs", indentation), dc.Input).Send() + } + if dc.Output != nil { + l.Debug().Interface(fmt.Sprintf("%s- Outputs", indentation), dc.Output).Send() + } + for _, e := range dc.Events { + l.Debug(). + Str("Signature", e.Signature). + Interface(fmt.Sprintf("%s- Log", indentation), e.EventData).Send() + } + + if revertErr != nil && dc.Error != "" { + l.Error().Str(fmt.Sprintf("%s- Revert", indentation), revertErr.Error()).Send() + } + + if i < len(calls)-1 { + l.Debug().Msg("") + } + } + + L.Debug(). + Msg("----------- Decoding transaction trace started -----------") + + if revertErr != nil { + L.Error().Err(revertErr).Msg("Transaction reverted") + } +} diff --git a/seth/tracing_cli_test.go b/seth/tracing_cli_test.go new file mode 100644 index 000000000..aef377397 --- /dev/null +++ b/seth/tracing_cli_test.go @@ -0,0 +1,33 @@ +package seth_test + +import ( + "context" + "os" + "testing" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" + sethcmd "github.com/smartcontractkit/chainlink-testing-framework/seth/cmd" +) + +func TestCLITracing(t *testing.T) { + c := newClientWithContractMapFromEnv(t) + SkipAnvil(t, c) + + file, err := os.CreateTemp("", "reverted_transactions.json") + require.NoError(t, err, "should have created temp file") + + tx, txErr := TestEnv.DebugContract.AlwaysRevertsCustomError(c.NewTXOpts()) + require.NoError(t, txErr, "transaction should have reverted") + + _, err = c.WaitMined(context.Background(), seth.L, c.Client, tx) + require.NoError(t, err, "should have waited for transaction to be mined") + + err = seth.CreateOrAppendToJsonArray(file.Name(), tx.Hash().Hex()) + require.NoError(t, err, "should have written to file") + + _ = os.Setenv(seth.CONFIG_FILE_ENV_VAR, "seth.toml") + err = sethcmd.RunCLI([]string{"seth", "-n", "Geth", "trace", "-f", file.Name()}) + require.NoError(t, err, "should have traced transactions") +} diff --git a/seth/util.go b/seth/util.go new file mode 100644 index 000000000..0ef0a44f5 --- /dev/null +++ b/seth/util.go @@ -0,0 +1,455 @@ +package seth + +import ( + "context" + "database/sql/driver" + "encoding/hex" + "encoding/json" + "fmt" + "io" + "math/big" + "os" + "path/filepath" + "strconv" + "strings" + "time" + + "github.com/ethereum/go-ethereum/accounts/abi/bind" + "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/params" + "github.com/pkg/errors" + + network_debug_contract "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/debug" + network_sub_debug_contract "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/sub" +) + +const ( + ErrInsufficientRootKeyBalance = "insufficient root key balance: %s" +) + +// FundingDetails funding details about shares we put into test keys +type FundingDetails struct { + RootBalance *big.Int + TotalFee *big.Int + FreeBalance *big.Int + AddrFunding *big.Int + NetworkTransferFee int64 +} + +// NewEphemeralKeys creates desired number of ephemeral keys, should be used only with ephemeral networks. Remember that they are not persisted anywhere, so you shouldn't use that option with live networks. +func NewEphemeralKeys(addrs int64) ([]string, error) { + privKeys := make([]string, 0) + for i := 0; i < int(addrs); i++ { + _, pKey, err := NewAddress() + if err != nil { + return nil, err + } + privKeys = append(privKeys, pKey) + } + return privKeys, nil +} + +// CalculateSubKeyFunding calculates all required params to split funds from the root key to N test keys +func (m *Client) CalculateSubKeyFunding(addrs, gasPrice, rooKeyBuffer int64) (*FundingDetails, error) { + balance, err := m.Client.BalanceAt(context.Background(), m.Addresses[0], nil) + if err != nil { + return nil, err + } + + gasLimit := m.Cfg.Network.TransferGasFee + newAddress, _, err := NewAddress() + if err == nil { + gasLimitRaw, err := m.EstimateGasLimitForFundTransfer(m.Addresses[0], common.HexToAddress(newAddress), big.NewInt(0).Quo(balance, big.NewInt(addrs))) + if err == nil { + gasLimit = int64(gasLimitRaw) + } + } + + networkTransferFee := gasPrice * gasLimit + totalFee := new(big.Int).Mul(big.NewInt(networkTransferFee), big.NewInt(addrs)) + rootKeyBuffer := new(big.Int).Mul(big.NewInt(rooKeyBuffer), big.NewInt(1_000_000_000_000_000_000)) + freeBalance := new(big.Int).Sub(balance, big.NewInt(0).Add(totalFee, rootKeyBuffer)) + + L.Info(). + Str("Balance (wei/ether)", fmt.Sprintf("%s/%s", balance.String(), WeiToEther(balance).Text('f', -1))). + Str("Total fee (wei/ether)", fmt.Sprintf("%s/%s", totalFee.String(), WeiToEther(totalFee).Text('f', -1))). + Str("Free Balance (wei/ether)", fmt.Sprintf("%s/%s", freeBalance.String(), WeiToEther(freeBalance).Text('f', -1))). + Str("Buffer (wei/ether)", fmt.Sprintf("%s/%s", rootKeyBuffer.String(), WeiToEther(rootKeyBuffer).Text('f', -1))). + Msg("Root key balance") + + if freeBalance.Cmp(big.NewInt(0)) < 0 { + return nil, fmt.Errorf(ErrInsufficientRootKeyBalance, freeBalance.String()) + } + + addrFunding := new(big.Int).Div(freeBalance, big.NewInt(addrs)) + requiredBalance := big.NewInt(0).Mul(addrFunding, big.NewInt(addrs)) + + L.Debug(). + Str("Funding per ephemeral key (wei/ether)", fmt.Sprintf("%s/%s", addrFunding.String(), WeiToEther(addrFunding).Text('f', -1))). + Str("Available balance (wei/ether)", fmt.Sprintf("%s/%s", freeBalance.String(), WeiToEther(freeBalance).Text('f', -1))). + Interface("Required balance (wei/ether)", fmt.Sprintf("%s/%s", requiredBalance.String(), WeiToEther(requiredBalance).Text('f', -1))). + Msg("Using hardcoded ephemeral funding") + + if freeBalance.Cmp(requiredBalance) < 0 { + return nil, fmt.Errorf(ErrInsufficientRootKeyBalance, freeBalance.String()) + } + + bd := &FundingDetails{ + RootBalance: balance, + TotalFee: totalFee, + FreeBalance: freeBalance, + AddrFunding: addrFunding, + NetworkTransferFee: networkTransferFee, + } + L.Info(). + Interface("RootBalance", bd.RootBalance.String()). + Interface("RootKeyBuffer", rootKeyBuffer.String()). + Interface("TransferFeesTotal", bd.TotalFee.String()). + Interface("NetworkTransferFee", bd.NetworkTransferFee). + Interface("FreeBalance", bd.FreeBalance.String()). + Interface("EachAddrGets", bd.AddrFunding.String()). + Msg("Splitting funds from the root account") + + return bd, nil +} + +func (m *Client) DeployDebugSubContract() (*network_sub_debug_contract.NetworkDebugSubContract, common.Address, error) { + address, tx, instance, err := network_sub_debug_contract.DeployNetworkDebugSubContract(m.NewTXOpts(), m.Client) + if err != nil { + return nil, common.Address{}, err + } + L.Info(). + Str("Address", address.Hex()). + Str("TXHash", tx.Hash().Hex()). + Msg("Deploying sub-debug contract") + if _, err := bind.WaitDeployed(context.Background(), m.Client, tx); err != nil { + return nil, common.Address{}, err + } + L.Info(). + Str("Address", address.Hex()). + Str("TXHash", tx.Hash().Hex()). + Msg("Sub-debug contract deployed") + return instance, address, nil +} + +func (m *Client) DeployDebugContract(subDbgAddr common.Address) (*network_debug_contract.NetworkDebugContract, common.Address, error) { + address, tx, instance, err := network_debug_contract.DeployNetworkDebugContract(m.NewTXOpts(), m.Client, subDbgAddr) + if err != nil { + return nil, common.Address{}, err + } + L.Info(). + Str("Address", address.Hex()). + Str("TXHash", tx.Hash().Hex()). + Msg("Deploying debug contract") + if _, err := bind.WaitDeployed(context.Background(), m.Client, tx); err != nil { + return nil, common.Address{}, err + } + L.Info(). + Str("Address", address.Hex()). + Str("TXHash", tx.Hash().Hex()). + Msg("Debug contract deployed") + return instance, address, nil +} + +// Duration is a non-negative time duration. +type Duration struct{ D time.Duration } + +func MakeDuration(d time.Duration) (Duration, error) { + if d < time.Duration(0) { + return Duration{}, fmt.Errorf("cannot make negative time duration: %s", d) + } + return Duration{D: d}, nil +} + +func ParseDuration(s string) (Duration, error) { + d, err := time.ParseDuration(s) + if err != nil { + return Duration{}, err + } + + return MakeDuration(d) +} + +func MustMakeDuration(d time.Duration) *Duration { + rv, err := MakeDuration(d) + if err != nil { + panic(err) + } + return &rv +} + +// Duration returns the value as the standard time.Duration value. +func (d Duration) Duration() time.Duration { + return d.D +} + +// Before returns the time d units before time t +func (d Duration) Before(t time.Time) time.Time { + return t.Add(-d.Duration()) +} + +// Shorter returns true if and only if d is shorter than od. +func (d Duration) Shorter(od Duration) bool { return d.D < od.D } + +// IsInstant is true if and only if d is of duration 0 +func (d Duration) IsInstant() bool { return d.D == 0 } + +// String returns a string representing the duration in the form "72h3m0.5s". +// Leading zero units are omitted. As a special case, durations less than one +// second format use a smaller unit (milli-, micro-, or nanoseconds) to ensure +// that the leading digit is non-zero. The zero duration formats as 0s. +func (d Duration) String() string { + return d.Duration().String() +} + +// MarshalJSON implements the json.Marshaler interface. +func (d Duration) MarshalJSON() ([]byte, error) { + return json.Marshal(d.String()) +} + +// UnmarshalJSON implements the json.Unmarshaler interface. +func (d *Duration) UnmarshalJSON(input []byte) error { + var txt string + err := json.Unmarshal(input, &txt) + if err != nil { + return err + } + v, err := time.ParseDuration(string(txt)) + if err != nil { + return err + } + *d, err = MakeDuration(v) + if err != nil { + return err + } + return nil +} + +func (d *Duration) Scan(v interface{}) (err error) { + switch tv := v.(type) { + case int64: + *d, err = MakeDuration(time.Duration(tv)) + return err + default: + return errors.Errorf(`don't know how to parse "%s" of type %T as a `+ + `models.Duration`, tv, tv) + } +} + +func (d Duration) Value() (driver.Value, error) { + return int64(d.D), nil +} + +// MarshalText implements the text.Marshaler interface. +func (d Duration) MarshalText() ([]byte, error) { + return []byte(d.D.String()), nil +} + +// UnmarshalText implements the text.Unmarshaler interface. +func (d *Duration) UnmarshalText(input []byte) error { + v, err := time.ParseDuration(string(input)) + if err != nil { + return err + } + pd, err := MakeDuration(v) + if err != nil { + return err + } + *d = pd + return nil +} + +func saveAsJson(v any, dirName, name string) (string, error) { + pwd, err := os.Getwd() + if err != nil { + return "", err + } + dir := filepath.Join(pwd, dirName) + if _, err := os.Stat(dir); errors.Is(err, os.ErrNotExist) { + err := os.MkdirAll(dir, os.ModePerm) + if err != nil { + return "", err + } + } + confPath := filepath.Join(dir, fmt.Sprintf("%s.json", name)) + f, _ := json.MarshalIndent(v, "", " ") + err = os.WriteFile(confPath, f, 0600) + + return confPath, err +} + +func OpenJsonFileAsStruct(path string, v any) error { + jsonFile, err := os.Open(path) + if err != nil { + return err + } + defer jsonFile.Close() + b, _ := io.ReadAll(jsonFile) + err = json.Unmarshal(b, v) + if err != nil { + return err + } + return nil +} + +// CreateOrAppendToJsonArray appends to a JSON array in a file or creates a new JSON array if the file is empty or doesn't exist +func CreateOrAppendToJsonArray(filePath string, newItem any) error { + f, err := os.OpenFile(filePath, os.O_RDWR|os.O_CREATE, 0644) + if err != nil { + return err + } + defer f.Close() + + size, err := f.Seek(0, io.SeekEnd) + if err != nil { + return err + } + + jsonBytes, err := json.Marshal(newItem) + if err != nil { + return err + } + jsonValue := string(jsonBytes) + + if size == 0 { + _, err = f.WriteString(fmt.Sprintf("[%s]", jsonValue)) + } else { + // Move cursor back by one character, so we can append data just before array end. + _, err = f.Seek(-1, io.SeekEnd) + if err != nil { + return err + } + _, err = f.WriteString(fmt.Sprintf(",\n%s]", jsonValue)) + } + return err +} + +// EtherToWei converts an ETH float amount to wei +func EtherToWei(eth *big.Float) *big.Int { + truncInt, _ := eth.Int(nil) + truncInt = new(big.Int).Mul(truncInt, big.NewInt(params.Ether)) + fracStr := strings.Split(fmt.Sprintf("%.18f", eth), ".")[1] + fracStr += strings.Repeat("0", 18-len(fracStr)) + fracInt, _ := new(big.Int).SetString(fracStr, 10) + wei := new(big.Int).Add(truncInt, fracInt) + return wei +} + +// WeiToEther converts a wei amount to eth float +func WeiToEther(wei *big.Int) *big.Float { + f := new(big.Float) + f.SetPrec(236) // IEEE 754 octuple-precision binary floating-point format: binary256 + f.SetMode(big.ToNearestEven) + fWei := new(big.Float) + fWei.SetPrec(236) // IEEE 754 octuple-precision binary floating-point format: binary256 + fWei.SetMode(big.ToNearestEven) + return f.Quo(fWei.SetInt(wei), big.NewFloat(params.Ether)) +} + +const ( + MetadataNotFoundErr = "metadata section not found" + InvalidMetadataLengthErr = "invalid metadata length" + FailedToDecodeMetadataErr = "failed to decode metadata" + NotCompiledWithSolcErr = "not compiled with solc" +) + +// Pragma represents the version of the Solidity compiler used to compile the contract +type Pragma struct { + Minor uint64 + Major uint64 + Patch uint64 +} + +// String returns the string representation of the Pragma +func (p Pragma) String() string { + return fmt.Sprintf("%d.%d.%d", p.Major, p.Minor, p.Patch) +} + +// DecodePragmaVersion extracts the pragma version from the bytecode or returns an error if it's not found or can't be decoded. +// Based on https://www.rareskills.io/post/solidity-metadata +func DecodePragmaVersion(bytecode string) (Pragma, error) { + metadataEndIndex := len(bytecode) - 4 + metadataLengthHex := bytecode[metadataEndIndex:] + metadataLengthByte, err := hex.DecodeString(metadataLengthHex) + + if err != nil { + return Pragma{}, fmt.Errorf("failed to decode metadata length: %v", err) + } + + metadataByteLengthUint, err := strconv.ParseUint(hex.EncodeToString(metadataLengthByte), 16, 16) + if err != nil { + return Pragma{}, fmt.Errorf("failed to convert metadata length to int: %v", err) + } + + // each byte is represented by 2 characters in hex + metadataLengthInt := int(metadataByteLengthUint) * 2 + + // if we get nonsensical metadata length, it means that metadata section is not present and last 2 bytes do not represent metadata length + if metadataLengthInt > len(bytecode) { + return Pragma{}, errors.New(MetadataNotFoundErr) + } + + metadataStarIndex := metadataEndIndex - metadataLengthInt + maybeMetadata := bytecode[metadataStarIndex:metadataEndIndex] + + if len(maybeMetadata) != metadataLengthInt { + return Pragma{}, fmt.Errorf("%s. expected: %d, actual: %d", InvalidMetadataLengthErr, metadataLengthInt, len(maybeMetadata)) + } + + // INVALID opcode is used as a marker for the start of the metadata section + metadataMarker := "fe" + maybeMarker := bytecode[metadataStarIndex-2 : metadataStarIndex] + + if maybeMarker != metadataMarker { + return Pragma{}, errors.New(MetadataNotFoundErr) + } + + // this is byte-encoded version of the string "solc" + solcMarker := "736f6c63" + if !strings.Contains(maybeMetadata, solcMarker) { + return Pragma{}, errors.New(NotCompiledWithSolcErr) + } + + // now that we know that last section indeed contains metadata let's grab the version + maybePragma := bytecode[metadataEndIndex-6 : metadataEndIndex] + majorHex := maybePragma[0:2] + minorHex := maybePragma[2:4] + patchHex := maybePragma[4:6] + + major, err := strconv.ParseUint(majorHex, 16, 16) + if err != nil { + return Pragma{}, fmt.Errorf("%s: %v", FailedToDecodeMetadataErr, err) + } + + minor, err := strconv.ParseUint(minorHex, 16, 16) + if err != nil { + return Pragma{}, fmt.Errorf("%s: %v", FailedToDecodeMetadataErr, err) + } + + patch, err := strconv.ParseUint(patchHex, 16, 16) + if err != nil { + return Pragma{}, fmt.Errorf("%s: %v", FailedToDecodeMetadataErr, err) + } + + return Pragma{Major: major, Minor: minor, Patch: patch}, nil +} + +// DoesPragmaSupportCustomRevert checks if the pragma version supports custom revert messages (must be >= 0.8.4) +func DoesPragmaSupportCustomRevert(pragma Pragma) bool { + return pragma.Minor > 8 || (pragma.Minor == 8 && pragma.Patch >= 4) || pragma.Major > 0 +} + +func wrapErrInMessageWithASuggestion(err error) error { + message := ` + +This error could be caused by several issues. Please try these steps to resolve it: + +1. Make sure the address you are using has sufficient funds. +2. Use a different RPC node. The current one might be out of sync or malfunctioning. +3. Review the logs to see if automatic gas estimations were unsuccessful. If they were, check that the fallback gas prices are set correctly. +4. If a gas limit was manually set, try commenting it out to let the node estimate it instead and see if that resolves the issue. +5. Conversely, if a gas limit was set manually, try increasing it to a higher value. This adjustment is especially crucial for some Layer 2 solutions that have variable gas limits. + +Original error:` + return fmt.Errorf("%s\n%s", message, err.Error()) +} diff --git a/seth/util_test.go b/seth/util_test.go new file mode 100644 index 000000000..5b48e6ae3 --- /dev/null +++ b/seth/util_test.go @@ -0,0 +1,231 @@ +package seth_test + +import ( + "context" + "errors" + "math/big" + "sync" + "testing" + "time" + + "github.com/stretchr/testify/require" + + "github.com/smartcontractkit/chainlink-testing-framework/seth" + network_sub_contract "github.com/smartcontractkit/chainlink-testing-framework/seth/contracts/bind/sub" +) + +func TestUtilDecodePragmaVersion(t *testing.T) { + tests := []struct { + name string + bytecode string + expectedResult seth.Pragma + expectedError error + }{ + { + name: "Valid Bytecode [NetworkDebugContract]", + bytecode: "60806040523480156200001157600080fd5b50604051620034c2380380620034c28339818101604052810190620000379190620000e9565b80600360006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff160217905550506200011b565b600080fd5b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b6000620000b18262000084565b9050919050565b620000c381620000a4565b8114620000cf57600080fd5b50565b600081519050620000e381620000b8565b92915050565b6000602082840312156200010257620001016200007f565b5b60006200011284828501620000d2565b91505092915050565b613397806200012b6000396000f3fe60806040526004361061028c5760003560e01c80637f12881c1161015a578063b600141f116100c1578063e8116e281161007a578063e8116e2814610a2f578063ec5c3ede14610a6c578063ef8a923514610aa9578063f3396bd914610ad4578063f499af2a14610afd578063fbcb8d0714610b3a576102cc565b8063b600141f1461091e578063c0d06d8914610935578063c2124b2214610960578063d7a8020514610977578063e1111f79146109b5578063e5c19b2d146109f2576102cc565b806395a81a4c1161011357806395a81a4c1461082057806399adad2e146108375780639e09965214610874578063a4c0ed36146108b3578063aa3fdcf4146108dc578063ad3de14c146108f3576102cc565b80637f12881c146107235780637fdc8fe11461076057806381b375a01461079d5780638db611be146107c65780638f856296146107f25780639349d00b14610809576102cc565b80633837a75e116101fe5780635e9c80d6116101b75780635e9c80d61461064a5780636284117d1461066157806362c270e11461069e5780636d4ce63c146106b55780637014c81d146106e0578063788c47721461070c576102cc565b80633837a75e146104db5780633e41f1351461051857806345f0c9e61461055557806348ad9fe81461059357806358379d71146105d05780635921483f1461060d576102cc565b8063235157601161025057806323515760146103a5578063256560d5146103e25780632a1afcd9146103f95780632e49d78b1461042457806330985bcc1461046157806333311ef31461049e576102cc565b806306595f751461030757806311b3c4781461031e57806312d91233146103475780631b9265b8146103845780631e31d0a81461038e576102cc565b366102cc577f59e04c3f0d44b7caf6e8ef854b61d9a51cf1960d7a88ff6356cc5e946b4b583233346040516102c2929190611a20565b60405180910390a1005b7f1e57e3bb474320be3d2c77138f75b7c3941292d647f5f9634e33a8e94e0e069b33346040516102fd929190611a5c565b60405180910390a1005b34801561031357600080fd5b5061031c610b77565b005b34801561032a57600080fd5b5061034560048036038101906103409190611ac5565b610bba565b005b34801561035357600080fd5b5061036e60048036038101906103699190611c5e565b610c4d565b60405161037b9190611d65565b60405180910390f35b61038c610d0c565b005b34801561039a57600080fd5b506103a3610d0e565b005b3480156103b157600080fd5b506103cc60048036038101906103c79190611dbd565b610d55565b6040516103d99190611e0c565b60405180910390f35b3480156103ee57600080fd5b506103f7610d8a565b005b34801561040557600080fd5b5061040e610d9b565b60405161041b9190611e0c565b60405180910390f35b34801561043057600080fd5b5061044b60048036038101906104469190611e4c565b610da1565b6040516104589190611ef0565b60405180910390f35b34801561046d57600080fd5b5061048860048036038101906104839190611dbd565b610e23565b6040516104959190611e0c565b60405180910390f35b3480156104aa57600080fd5b506104c560048036038101906104c09190611f41565b610f05565b6040516104d29190611f7d565b60405180910390f35b3480156104e757600080fd5b5061050260048036038101906104fd9190611dbd565b610f0f565b60405161050f9190611e0c565b60405180910390f35b34801561052457600080fd5b5061053f600480360381019061053a9190611dbd565b61101a565b60405161054c9190611e0c565b60405180910390f35b34801561056157600080fd5b5061057c6004803603810190610577919061204d565b611115565b60405161058a929190612117565b60405180910390f35b34801561059f57600080fd5b506105ba60048036038101906105b59190612173565b611126565b6040516105c79190611e0c565b60405180910390f35b3480156105dc57600080fd5b506105f760048036038101906105f29190611dbd565b61113e565b6040516106049190611e0c565b60405180910390f35b34801561061957600080fd5b50610634600480360381019061062f91906121a0565b611239565b6040516106419190611e0c565b60405180910390f35b34801561065657600080fd5b5061065f611256565b005b34801561066d57600080fd5b50610688600480360381019061068391906121a0565b611297565b6040516106959190611e0c565b60405180910390f35b3480156106aa57600080fd5b506106b36112af565b005b3480156106c157600080fd5b506106ca611343565b6040516106d79190611e0c565b60405180910390f35b3480156106ec57600080fd5b506106f561134c565b604051610703929190612117565b60405180910390f35b34801561071857600080fd5b50610721611391565b005b34801561072f57600080fd5b5061074a600480360381019061074591906121f1565b6113c8565b60405161075791906123d0565b60405180910390f35b34801561076c57600080fd5b5061078760048036038101906107829190612411565b6113e1565b604051610794919061261e565b60405180910390f35b3480156107a957600080fd5b506107c460048036038101906107bf919061204d565b6113ea565b005b3480156107d257600080fd5b506107db6113ee565b6040516107e9929190612117565b60405180910390f35b3480156107fe57600080fd5b50610807611433565b005b34801561081557600080fd5b5061081e611463565b005b34801561082c57600080fd5b5061083561146d565b005b34801561084357600080fd5b5061085e60048036038101906108599190612662565b6114a6565b60405161086b9190612761565b60405180910390f35b34801561088057600080fd5b5061089b600480360381019061089691906127bc565b611551565b6040516108aa9392919061281e565b60405180910390f35b3480156108bf57600080fd5b506108da60048036038101906108d591906128b0565b611568565b005b3480156108e857600080fd5b506108f16115a9565b005b3480156108ff57600080fd5b506109086115f2565b6040516109159190611e0c565b60405180910390f35b34801561092a57600080fd5b50610933611639565b005b34801561094157600080fd5b5061094a61166b565b6040516109579190612983565b60405180910390f35b34801561096c57600080fd5b50610975611691565b005b34801561098357600080fd5b5061099e6004803603810190610999919061204d565b6116e3565b6040516109ac929190612117565b60405180910390f35b3480156109c157600080fd5b506109dc60048036038101906109d79190612a61565b6116f4565b6040516109e99190612b68565b60405180910390f35b3480156109fe57600080fd5b50610a196004803603810190610a1491906121a0565b6116fe565b604051610a269190611e0c565b60405180910390f35b348015610a3b57600080fd5b50610a566004803603810190610a5191906121a0565b61170f565b604051610a639190611e0c565b60405180910390f35b348015610a7857600080fd5b50610a936004803603810190610a8e9190612173565b61175d565b604051610aa09190612b8a565b60405180910390f35b348015610ab557600080fd5b50610abe611767565b604051610acb9190611ef0565b60405180910390f35b348015610ae057600080fd5b50610afb6004803603810190610af691906121a0565b61177a565b005b348015610b0957600080fd5b50610b246004803603810190610b1f9190612411565b611796565b604051610b3191906123d0565b60405180910390f35b348015610b4657600080fd5b50610b616004803603810190610b5c91906121a0565b6118cb565b604051610b6e9190611e0c565b60405180910390f35b6000610bb8576040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401610baf90612bf1565b60405180910390fd5b565b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff166311abb00283836040518363ffffffff1660e01b8152600401610c17929190612c11565b600060405180830381600087803b158015610c3157600080fd5b505af1158015610c45573d6000803e3d6000fd5b505050505050565b60606000825167ffffffffffffffff811115610c6c57610c6b611b1b565b5b604051908082528060200260200182016040528015610c9a5781602001602082028036833780820191505090505b50905060005b8351811015610d02576001848281518110610cbe57610cbd612c3a565b5b6020026020010151610cd09190612c98565b828281518110610ce357610ce2612c3a565b5b6020026020010181815250508080610cfa90612ccc565b915050610ca0565b5080915050919050565b565b3373ffffffffffffffffffffffffffffffffffffffff1660017f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a3565b600081600260008581526020019081526020016000206000828254610d7a9190612d14565b9250508190555081905092915050565b6000610d9957610d98612d58565b5b565b60005481565b600081600360146101000a81548160ff02191690836003811115610dc857610dc7611e79565b5b0217905550600360149054906101000a900460ff166003811115610def57610dee611e79565b5b7fbea054406fdf249b05d1aef1b5f848d62d902d94389fca702b2d8337677c359a60405160405180910390a2819050919050565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663047c4425836040518263ffffffff1660e01b8152600401610e809190611e0c565b6020604051808303816000875af1158015610e9f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610ec39190612d9c565b50827feace1be0b97ec11f959499c07b9f60f0cc47bf610b28fda8fb0e970339cf3b3560405160405180910390a28183610efd9190612d14565b905092915050565b6000819050919050565b6000600282610f1e9190612d14565b9150600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1663fa8fca7a84846040518363ffffffff1660e01b8152600401610f7d929190612dc9565b6020604051808303816000875af1158015610f9c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fc09190612d9c565b503373ffffffffffffffffffffffffffffffffffffffff1660017f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a381836110129190612d14565b905092915050565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633e41f13584846040518363ffffffff1660e01b8152600401611079929190612dc9565b6020604051808303816000875af1158015611098573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906110bc9190612d9c565b503373ffffffffffffffffffffffffffffffffffffffff16827f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a3818361110d9190612d14565b905092915050565b600060608383915091509250929050565b60016020528060005260406000206000915090505481565b6000600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16633e41f13584846040518363ffffffff1660e01b815260040161119d929190612dc9565b6020604051808303816000875af11580156111bc573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111e09190612d9c565b503373ffffffffffffffffffffffffffffffffffffffff16827f33b47a1cd66813164ec00800d74296f57415217c22505ee380594a712936a0b560405160405180910390a381836112319190612d14565b905092915050565b600060026000838152602001908152602001600020549050919050565b600c60156040517f4a2eaf7e00000000000000000000000000000000000000000000000000000000815260040161128e929190612e68565b60405180910390fd5b60026020528060005260406000206000915090505481565b7febe3ff7e2071d351bf2e65b4fccd24e3ae99485f02468f1feecf7d64dc04418860405180606001604052806040518060400160405280600481526020017f4a6f686e000000000000000000000000000000000000000000000000000000008152508152602001600567ffffffffffffffff168152602001600a8152506040516113399190612f04565b60405180910390a1565b60008054905090565b60006060617a696040518060400160405280600a81526020017f6f757470757456616c3100000000000000000000000000000000000000000000815250915091509091565b7f25b7adba1b046a19379db4bc06aa1f2e71604d7b599a0ee8783d58110f00e16a6040516113be90612f72565b60405180910390a1565b6113d0611902565b816113da9061314d565b9050919050565b36819050919050565b5050565b60006060617a696040518060400160405280600a81526020017f6f757470757456616c3100000000000000000000000000000000000000000000815250915091509091565b60537feace1be0b97ec11f959499c07b9f60f0cc47bf610b28fda8fb0e970339cf3b3560405160405180910390a2565b61146b611256565b565b7f33bc9bae48dbe1e057f264b3fc6a1dacdcceacb3ba28d937231c70e068a02f1c3360405161149c9190612b8a565b60405180910390a1565b6114ae611922565b6114b6611922565b826000600381106114ca576114c9612c3a565b5b6020028101906114da919061316f565b6114e390613197565b816000600281106114f7576114f6612c3a565b5b60200201819052508260016003811061151357611512612c3a565b5b602002810190611523919061316f565b61152c90613197565b816001600281106115405761153f612c3a565b5b602002018190525080915050919050565b600080600085858592509250925093509350939050565b606460656040517f4a2eaf7e0000000000000000000000000000000000000000000000000000000081526004016115a0929190613220565b60405180910390fd5b60033373ffffffffffffffffffffffffffffffffffffffff1660017f5660e8f93f0146f45abcd659e026b75995db50053cbbca4d7f365934ade68bf360405160405180910390a4565b6000600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905090565b6040517fa0c2d2db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600360009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60033373ffffffffffffffffffffffffffffffffffffffff1660027f56c2ea44ba516098cee0c181dd9d8db262657368b6e911e83ae0ccfae806c73d6040516116d990613295565b60405180910390a4565b600060608383915091509250929050565b6060819050919050565b600081600081905550819050919050565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002081905550819050919050565b6000819050919050565b600360149054906101000a900460ff1681565b6000600260008381526020019081526020016000208190555050565b61179e611902565b60008280600001906117b091906132b5565b6040516020016117c1929190613348565b6040516020818303038152906040528051906020012090506000602067ffffffffffffffff8111156117f6576117f5611b1b565b5b6040519080825280601f01601f1916602001820160405280156118285781602001600182028036833780820191505090505b50905060005b60208110156118a35782816020811061184a57611849612c3a565b5b1a60f81b82828151811061186157611860612c3a565b5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350808061189b90612ccc565b91505061182e565b506040518060400160405280856118b990613197565b81526020018281525092505050919050565b6000817fb16dba9242e1aa07ccc47228094628f72c8cc9699ee45d5bc8d67b84d3038c6860405160405180910390a2819050919050565b604051806040016040528061191561194f565b8152602001606081525090565b60405180604001604052806002905b61193961194f565b8152602001906001900390816119315790505090565b604051806040016040528060608152602001606081525090565b600073ffffffffffffffffffffffffffffffffffffffff82169050919050565b600061199482611969565b9050919050565b6119a481611989565b82525050565b6000819050919050565b6119bd816119aa565b82525050565b600082825260208201905092915050565b7f5265636569766564204574686572000000000000000000000000000000000000600082015250565b6000611a0a600e836119c3565b9150611a15826119d4565b602082019050919050565b6000606082019050611a35600083018561199b565b611a4260208301846119b4565b8181036040830152611a53816119fd565b90509392505050565b6000604082019050611a71600083018561199b565b611a7e60208301846119b4565b9392505050565b6000604051905090565b600080fd5b600080fd5b611aa2816119aa565b8114611aad57600080fd5b50565b600081359050611abf81611a99565b92915050565b60008060408385031215611adc57611adb611a8f565b5b6000611aea85828601611ab0565b9250506020611afb85828601611ab0565b9150509250929050565b600080fd5b6000601f19601f8301169050919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b611b5382611b0a565b810181811067ffffffffffffffff82111715611b7257611b71611b1b565b5b80604052505050565b6000611b85611a85565b9050611b918282611b4a565b919050565b600067ffffffffffffffff821115611bb157611bb0611b1b565b5b602082029050602081019050919050565b600080fd5b6000611bda611bd584611b96565b611b7b565b90508083825260208201905060208402830185811115611bfd57611bfc611bc2565b5b835b81811015611c265780611c128882611ab0565b845260208401935050602081019050611bff565b5050509392505050565b600082601f830112611c4557611c44611b05565b5b8135611c55848260208601611bc7565b91505092915050565b600060208284031215611c7457611c73611a8f565b5b600082013567ffffffffffffffff811115611c9257611c91611a94565b5b611c9e84828501611c30565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b611cdc816119aa565b82525050565b6000611cee8383611cd3565b60208301905092915050565b6000602082019050919050565b6000611d1282611ca7565b611d1c8185611cb2565b9350611d2783611cc3565b8060005b83811015611d58578151611d3f8882611ce2565b9750611d4a83611cfa565b925050600181019050611d2b565b5085935050505092915050565b60006020820190508181036000830152611d7f8184611d07565b905092915050565b6000819050919050565b611d9a81611d87565b8114611da557600080fd5b50565b600081359050611db781611d91565b92915050565b60008060408385031215611dd457611dd3611a8f565b5b6000611de285828601611da8565b9250506020611df385828601611da8565b9150509250929050565b611e0681611d87565b82525050565b6000602082019050611e216000830184611dfd565b92915050565b60048110611e3457600080fd5b50565b600081359050611e4681611e27565b92915050565b600060208284031215611e6257611e61611a8f565b5b6000611e7084828501611e37565b91505092915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60048110611eb957611eb8611e79565b5b50565b6000819050611eca82611ea8565b919050565b6000611eda82611ebc565b9050919050565b611eea81611ecf565b82525050565b6000602082019050611f056000830184611ee1565b92915050565b6000819050919050565b611f1e81611f0b565b8114611f2957600080fd5b50565b600081359050611f3b81611f15565b92915050565b600060208284031215611f5757611f56611a8f565b5b6000611f6584828501611f2c565b91505092915050565b611f7781611f0b565b82525050565b6000602082019050611f926000830184611f6e565b92915050565b600080fd5b600067ffffffffffffffff821115611fb857611fb7611b1b565b5b611fc182611b0a565b9050602081019050919050565b82818337600083830152505050565b6000611ff0611feb84611f9d565b611b7b565b90508281526020810184848401111561200c5761200b611f98565b5b612017848285611fce565b509392505050565b600082601f83011261203457612033611b05565b5b8135612044848260208601611fdd565b91505092915050565b6000806040838503121561206457612063611a8f565b5b600061207285828601611ab0565b925050602083013567ffffffffffffffff81111561209357612092611a94565b5b61209f8582860161201f565b9150509250929050565b600081519050919050565b60005b838110156120d25780820151818401526020810190506120b7565b60008484015250505050565b60006120e9826120a9565b6120f381856119c3565b93506121038185602086016120b4565b61210c81611b0a565b840191505092915050565b600060408201905061212c60008301856119b4565b818103602083015261213e81846120de565b90509392505050565b61215081611989565b811461215b57600080fd5b50565b60008135905061216d81612147565b92915050565b60006020828403121561218957612188611a8f565b5b60006121978482850161215e565b91505092915050565b6000602082840312156121b6576121b5611a8f565b5b60006121c484828501611da8565b91505092915050565b600080fd5b6000604082840312156121e8576121e76121cd565b5b81905092915050565b60006020828403121561220757612206611a8f565b5b600082013567ffffffffffffffff81111561222557612224611a94565b5b612231848285016121d2565b91505092915050565b600082825260208201905092915050565b6000612256826120a9565b612260818561223a565b93506122708185602086016120b4565b61227981611b0a565b840191505092915050565b600082825260208201905092915050565b60006122a082611ca7565b6122aa8185612284565b93506122b583611cc3565b8060005b838110156122e65781516122cd8882611ce2565b97506122d883611cfa565b9250506001810190506122b9565b5085935050505092915050565b60006040830160008301518482036000860152612310828261224b565b9150506020830151848203602086015261232a8282612295565b9150508091505092915050565b600081519050919050565b600082825260208201905092915050565b600061235e82612337565b6123688185612342565b93506123788185602086016120b4565b61238181611b0a565b840191505092915050565b600060408301600083015184820360008601526123a982826122f3565b915050602083015184820360208601526123c38282612353565b9150508091505092915050565b600060208201905081810360008301526123ea818461238c565b905092915050565b600060408284031215612408576124076121cd565b5b81905092915050565b60006020828403121561242757612426611a8f565b5b600082013567ffffffffffffffff81111561244557612444611a94565b5b612451848285016123f2565b91505092915050565b600080fd5b600080fd5b600080fd5b6000808335600160200384360303811261248657612485612464565b5b83810192508235915060208301925067ffffffffffffffff8211156124ae576124ad61245a565b5b6001820236038313156124c4576124c361245f565b5b509250929050565b60006124d8838561223a565b93506124e5838584611fce565b6124ee83611b0a565b840190509392505050565b6000808335600160200384360303811261251657612515612464565b5b83810192508235915060208301925067ffffffffffffffff82111561253e5761253d61245a565b5b6020820236038313156125545761255361245f565b5b509250929050565b600080fd5b82818337505050565b60006125768385612284565b93507f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8311156125a9576125a861255c565b5b6020830292506125ba838584612561565b82840190509392505050565b6000604083016125d96000840184612469565b85830360008701526125ec8382846124cc565b925050506125fd60208401846124f9565b858303602087015261261083828461256a565b925050508091505092915050565b6000602082019050818103600083015261263881846125c6565b905092915050565b60008190508260206003028201111561265c5761265b611bc2565b5b92915050565b60006020828403121561267857612677611a8f565b5b600082013567ffffffffffffffff81111561269657612695611a94565b5b6126a284828501612640565b91505092915050565b600060029050919050565b600081905092915050565b6000819050919050565b60006126d783836122f3565b905092915050565b6000602082019050919050565b60006126f7826126ab565b61270181856126b6565b935083602082028501612713856126c1565b8060005b8581101561274f578484038952815161273085826126cb565b945061273b836126df565b925060208a01995050600181019050612717565b50829750879550505050505092915050565b6000602082019050818103600083015261277b81846126ec565b905092915050565b600081600f0b9050919050565b61279981612783565b81146127a457600080fd5b50565b6000813590506127b681612790565b92915050565b6000806000606084860312156127d5576127d4611a8f565b5b60006127e386828701611da8565b93505060206127f4868287016127a7565b925050604061280586828701611ab0565b9150509250925092565b61281881612783565b82525050565b60006060820190506128336000830186611dfd565b612840602083018561280f565b61284d60408301846119b4565b949350505050565b600080fd5b60008083601f8401126128705761286f611b05565b5b8235905067ffffffffffffffff81111561288d5761288c612855565b5b6020830191508360018202830111156128a9576128a8611bc2565b5b9250929050565b600080600080606085870312156128ca576128c9611a8f565b5b60006128d88782880161215e565b94505060206128e987828801611ab0565b935050604085013567ffffffffffffffff81111561290a57612909611a94565b5b6129168782880161285a565b925092505092959194509250565b6000819050919050565b600061294961294461293f84611969565b612924565b611969565b9050919050565b600061295b8261292e565b9050919050565b600061296d82612950565b9050919050565b61297d81612962565b82525050565b60006020820190506129986000830184612974565b92915050565b600067ffffffffffffffff8211156129b9576129b8611b1b565b5b602082029050602081019050919050565b60006129dd6129d88461299e565b611b7b565b90508083825260208201905060208402830185811115612a00576129ff611bc2565b5b835b81811015612a295780612a15888261215e565b845260208401935050602081019050612a02565b5050509392505050565b600082601f830112612a4857612a47611b05565b5b8135612a588482602086016129ca565b91505092915050565b600060208284031215612a7757612a76611a8f565b5b600082013567ffffffffffffffff811115612a9557612a94611a94565b5b612aa184828501612a33565b91505092915050565b600081519050919050565b600082825260208201905092915050565b6000819050602082019050919050565b612adf81611989565b82525050565b6000612af18383612ad6565b60208301905092915050565b6000602082019050919050565b6000612b1582612aaa565b612b1f8185612ab5565b9350612b2a83612ac6565b8060005b83811015612b5b578151612b428882612ae5565b9750612b4d83612afd565b925050600181019050612b2e565b5085935050505092915050565b60006020820190508181036000830152612b828184612b0a565b905092915050565b6000602082019050612b9f600083018461199b565b92915050565b7f616c7761797320726576657274206572726f7200000000000000000000000000600082015250565b6000612bdb6013836119c3565b9150612be682612ba5565b602082019050919050565b60006020820190508181036000830152612c0a81612bce565b9050919050565b6000604082019050612c2660008301856119b4565b612c3360208301846119b4565b9392505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b6000612ca3826119aa565b9150612cae836119aa565b9250828201905080821115612cc657612cc5612c69565b5b92915050565b6000612cd7826119aa565b91507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8203612d0957612d08612c69565b5b600182019050919050565b6000612d1f82611d87565b9150612d2a83611d87565b925082820190508281121560008312168382126000841215161715612d5257612d51612c69565b5b92915050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052600160045260246000fd5b600081519050612d9681611d91565b92915050565b600060208284031215612db257612db1611a8f565b5b6000612dc084828501612d87565b91505092915050565b6000604082019050612dde6000830185611dfd565b612deb6020830184611dfd565b9392505050565b6000819050919050565b6000612e17612e12612e0d84612df2565b612924565b6119aa565b9050919050565b612e2781612dfc565b82525050565b6000819050919050565b6000612e52612e4d612e4884612e2d565b612924565b6119aa565b9050919050565b612e6281612e37565b82525050565b6000604082019050612e7d6000830185612e1e565b612e8a6020830184612e59565b9392505050565b600067ffffffffffffffff82169050919050565b612eae81612e91565b82525050565b60006060830160008301518482036000860152612ed1828261224b565b9150506020830151612ee66020860182612ea5565b506040830151612ef96040860182611cd3565b508091505092915050565b60006020820190508181036000830152612f1e8184612eb4565b905092915050565b7f6d79537472696e67000000000000000000000000000000000000000000000000600082015250565b6000612f5c6008836119c3565b9150612f6782612f26565b602082019050919050565b60006020820190508181036000830152612f8b81612f4f565b9050919050565b600080fd5b600080fd5b600060408284031215612fb257612fb1612f92565b5b612fbc6040611b7b565b9050600082013567ffffffffffffffff811115612fdc57612fdb612f97565b5b612fe88482850161201f565b600083015250602082013567ffffffffffffffff81111561300c5761300b612f97565b5b61301884828501611c30565b60208301525092915050565b600067ffffffffffffffff82111561303f5761303e611b1b565b5b61304882611b0a565b9050602081019050919050565b600061306861306384613024565b611b7b565b90508281526020810184848401111561308457613083611f98565b5b61308f848285611fce565b509392505050565b600082601f8301126130ac576130ab611b05565b5b81356130bc848260208601613055565b91505092915050565b6000604082840312156130db576130da612f92565b5b6130e56040611b7b565b9050600082013567ffffffffffffffff81111561310557613104612f97565b5b61311184828501612f9c565b600083015250602082013567ffffffffffffffff81111561313557613134612f97565b5b61314184828501613097565b60208301525092915050565b600061315936836130c5565b9050919050565b600080fd5b600080fd5b600080fd5b60008235600160400383360303811261318b5761318a613160565b5b80830191505092915050565b60006131a33683612f9c565b9050919050565b6000819050919050565b60006131cf6131ca6131c5846131aa565b612924565b6119aa565b9050919050565b6131df816131b4565b82525050565b6000819050919050565b600061320a613205613200846131e5565b612924565b6119aa565b9050919050565b61321a816131ef565b82525050565b600060408201905061323560008301856131d6565b6132426020830184613211565b9392505050565b7f736f6d6520696400000000000000000000000000000000000000000000000000600082015250565b600061327f6007836119c3565b915061328a82613249565b602082019050919050565b600060208201905081810360008301526132ae81613272565b9050919050565b600080833560016020038436030381126132d2576132d1613160565b5b80840192508235915067ffffffffffffffff8211156132f4576132f3613165565b5b6020830192506001820236038313156133105761330f61316a565b5b509250929050565b600081905092915050565b600061332f8385613318565b935061333c838584611fce565b82840190509392505050565b6000613355828486613323565b9150819050939250505056fea2646970667358221220dc16dcd20c7af4f539a58c96a664274d4c0fc03491d985f587a4b31d25c9774364736f6c63430008130033", + expectedResult: seth.Pragma{Major: 0, Minor: 8, Patch: 19}, + expectedError: nil, + }, + { + name: "Valid Bytecode [modern LinkToken]", + bytecode: "60c06040523480156200001157600080fd5b506040518060400160405280600f81526020016e21b430b4b72634b735902a37b5b2b760891b815250604051806040016040528060048152602001634c494e4b60e01b81525060126b033b2e3c9fd0803ce8000000338060008686818181600390816200007f91906200028c565b5060046200008e82826200028c565b5050506001600160a01b0384169150620000f190505760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600580546001600160a01b0319166001600160a01b0384811691909117909155811615620001245762000124816200013b565b50505060ff90911660805260a05250620003589050565b336001600160a01b03821603620001955760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401620000e8565b600680546001600160a01b0319166001600160a01b03838116918217909255600554604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b634e487b7160e01b600052604160045260246000fd5b600181811c908216806200021257607f821691505b6020821081036200023357634e487b7160e01b600052602260045260246000fd5b50919050565b601f8211156200028757600081815260208120601f850160051c81016020861015620002625750805b601f850160051c820191505b8181101562000283578281556001016200026e565b5050505b505050565b81516001600160401b03811115620002a857620002a8620001e7565b620002c081620002b98454620001fd565b8462000239565b602080601f831160018114620002f85760008415620002df5750858301515b600019600386901b1c1916600185901b17855562000283565b600085815260208120601f198616915b82811015620003295788860151825594840194600190910190840162000308565b5085821015620003485787850151600019600388901b60f8161c191681555b5050505050600190811b01905550565b60805160a051611e4c6200038c60003960008181610447015281816108c301526108ed015260006102710152611e4c6000f3fe608060405234801561001057600080fd5b50600436106101f05760003560e01c806379cc67901161010f578063c2e3273d116100a2578063d73dd62311610071578063d73dd6231461046b578063dd62ed3e1461047e578063f2fde38b146104c4578063f81094f3146104d757600080fd5b8063c2e3273d1461040c578063c630948d1461041f578063c64d0ebc14610432578063d5abeb011461044557600080fd5b80639dc29fac116100de5780639dc29fac146103c0578063a457c2d7146103d3578063a9059cbb146103e6578063aa271e1a146103f957600080fd5b806379cc67901461037557806386fe8b43146103885780638da5cb5b1461039057806395d89b41146103b857600080fd5b806340c10f19116101875780636618846311610156578063661884631461030f5780636b32810b1461032257806370a082311461033757806379ba50971461036d57600080fd5b806340c10f19146102c157806342966c68146102d65780634334614a146102e95780634f5632f8146102fc57600080fd5b806323b872dd116101c357806323b872dd14610257578063313ce5671461026a578063395093511461029b5780634000aea0146102ae57600080fd5b806301ffc9a7146101f557806306fdde031461021d578063095ea7b31461023257806318160ddd14610245575b600080fd5b6102086102033660046119b9565b6104ea565b60405190151581526020015b60405180910390f35b61022561061b565b6040516102149190611a5f565b610208610240366004611a9b565b6106ad565b6002545b604051908152602001610214565b610208610265366004611ac5565b6106c5565b60405160ff7f0000000000000000000000000000000000000000000000000000000000000000168152602001610214565b6102086102a9366004611a9b565b6106e9565b6102086102bc366004611b30565b610735565b6102d46102cf366004611a9b565b610858565b005b6102d46102e4366004611c19565b61097f565b6102086102f7366004611c32565b6109cc565b6102d461030a366004611c32565b6109d9565b61020861031d366004611a9b565b610a35565b61032a610a48565b6040516102149190611c4d565b610249610345366004611c32565b73ffffffffffffffffffffffffffffffffffffffff1660009081526020819052604090205490565b6102d4610a59565b6102d4610383366004611a9b565b610b5a565b61032a610ba9565b60055460405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610214565b610225610bb5565b6102d46103ce366004611a9b565b610bc4565b6102086103e1366004611a9b565b610bce565b6102086103f4366004611a9b565b610c9f565b610208610407366004611c32565b610cad565b6102d461041a366004611c32565b610cba565b6102d461042d366004611c32565b610d16565b6102d4610440366004611c32565b610d24565b7f0000000000000000000000000000000000000000000000000000000000000000610249565b6102d4610479366004611a9b565b610d80565b61024961048c366004611ca7565b73ffffffffffffffffffffffffffffffffffffffff918216600090815260016020908152604080832093909416825291909152205490565b6102d46104d2366004611c32565b610d8a565b6102d46104e5366004611c32565b610d9b565b60007fffffffff0000000000000000000000000000000000000000000000000000000082167f36372b0700000000000000000000000000000000000000000000000000000000148061057d57507fffffffff0000000000000000000000000000000000000000000000000000000082167f4000aea000000000000000000000000000000000000000000000000000000000145b806105c957507fffffffff0000000000000000000000000000000000000000000000000000000082167fe6599b4d00000000000000000000000000000000000000000000000000000000145b8061061557507fffffffff0000000000000000000000000000000000000000000000000000000082167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b60606003805461062a90611cda565b80601f016020809104026020016040519081016040528092919081815260200182805461065690611cda565b80156106a35780601f10610678576101008083540402835291602001916106a3565b820191906000526020600020905b81548152906001019060200180831161068657829003601f168201915b5050505050905090565b6000336106bb818585610df7565b5060019392505050565b6000336106d3858285610e2b565b6106de858585610efc565b506001949350505050565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff871684529091528120549091906106bb9082908690610730908790611d5c565b610df7565b60006107418484610c9f565b508373ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c1685856040516107a1929190611d6f565b60405180910390a373ffffffffffffffffffffffffffffffffffffffff84163b156106bb576040517fa4c0ed3600000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff85169063a4c0ed369061081c90339087908790600401611d90565b600060405180830381600087803b15801561083657600080fd5b505af115801561084a573d6000803e3d6000fd5b505050505060019392505050565b61086133610cad565b61089e576040517fe2c8c9d50000000000000000000000000000000000000000000000000000000081523360048201526024015b60405180910390fd5b813073ffffffffffffffffffffffffffffffffffffffff8216036108c157600080fd5b7f00000000000000000000000000000000000000000000000000000000000000001580159061092257507f00000000000000000000000000000000000000000000000000000000000000008261091660025490565b6109209190611d5c565b115b15610970578161093160025490565b61093b9190611d5c565b6040517fcbbf111300000000000000000000000000000000000000000000000000000000815260040161089591815260200190565b61097a8383610f2a565b505050565b610988336109cc565b6109c0576040517fc820b10b000000000000000000000000000000000000000000000000000000008152336004820152602401610895565b6109c98161101d565b50565b6000610615600983611027565b6109e1611056565b6109ec6009826110d9565b156109c95760405173ffffffffffffffffffffffffffffffffffffffff8216907f0a675452746933cefe3d74182e78db7afe57ba60eaa4234b5d85e9aa41b0610c90600090a250565b6000610a418383610bce565b9392505050565b6060610a5460076110fb565b905090565b60065473ffffffffffffffffffffffffffffffffffffffff163314610ada576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e6572000000000000000000006044820152606401610895565b600580547fffffffffffffffffffffffff00000000000000000000000000000000000000008082163390811790935560068054909116905560405173ffffffffffffffffffffffffffffffffffffffff909116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a350565b610b63336109cc565b610b9b576040517fc820b10b000000000000000000000000000000000000000000000000000000008152336004820152602401610895565b610ba58282611108565b5050565b6060610a5460096110fb565b60606004805461062a90611cda565b610ba58282610b5a565b33600081815260016020908152604080832073ffffffffffffffffffffffffffffffffffffffff8716845290915281205490919083811015610c92576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f7760448201527f207a65726f0000000000000000000000000000000000000000000000000000006064820152608401610895565b6106de8286868403610df7565b6000336106bb818585610efc565b6000610615600783611027565b610cc2611056565b610ccd60078261111d565b156109c95760405173ffffffffffffffffffffffffffffffffffffffff8216907fe46fef8bbff1389d9010703cf8ebb363fb3daf5bf56edc27080b67bc8d9251ea90600090a250565b610d1f81610cba565b6109c9815b610d2c611056565b610d3760098261111d565b156109c95760405173ffffffffffffffffffffffffffffffffffffffff8216907f92308bb7573b2a3d17ddb868b39d8ebec433f3194421abc22d084f89658c9bad90600090a250565b61097a82826106e9565b610d92611056565b6109c98161113f565b610da3611056565b610dae6007826110d9565b156109c95760405173ffffffffffffffffffffffffffffffffffffffff8216907fed998b960f6340d045f620c119730f7aa7995e7425c2401d3a5b64ff998a59e990600090a250565b813073ffffffffffffffffffffffffffffffffffffffff821603610e1a57600080fd5b610e25848484611235565b50505050565b73ffffffffffffffffffffffffffffffffffffffff8381166000908152600160209081526040808320938616835292905220547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8114610e255781811015610eef576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610895565b610e258484848403610df7565b813073ffffffffffffffffffffffffffffffffffffffff821603610f1f57600080fd5b610e258484846113e8565b73ffffffffffffffffffffffffffffffffffffffff8216610fa7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610895565b8060026000828254610fb99190611d5c565b909155505073ffffffffffffffffffffffffffffffffffffffff8216600081815260208181526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b6109c93382611657565b73ffffffffffffffffffffffffffffffffffffffff811660009081526001830160205260408120541515610a41565b60055473ffffffffffffffffffffffffffffffffffffffff1633146110d7576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e6572000000000000000000006044820152606401610895565b565b6000610a418373ffffffffffffffffffffffffffffffffffffffff841661181b565b60606000610a418361190e565b611113823383610e2b565b610ba58282611657565b6000610a418373ffffffffffffffffffffffffffffffffffffffff841661196a565b3373ffffffffffffffffffffffffffffffffffffffff8216036111be576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c660000000000000000006044820152606401610895565b600680547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff838116918217909255600554604051919216907fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae127890600090a350565b73ffffffffffffffffffffffffffffffffffffffff83166112d7576040517f08c379a0000000000000000000000000000000000000000000000000000000008152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f2061646460448201527f72657373000000000000000000000000000000000000000000000000000000006064820152608401610895565b73ffffffffffffffffffffffffffffffffffffffff821661137a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f20616464726560448201527f73730000000000000000000000000000000000000000000000000000000000006064820152608401610895565b73ffffffffffffffffffffffffffffffffffffffff83811660008181526001602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b73ffffffffffffffffffffffffffffffffffffffff831661148b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f20616460448201527f64726573730000000000000000000000000000000000000000000000000000006064820152608401610895565b73ffffffffffffffffffffffffffffffffffffffff821661152e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201527f65737300000000000000000000000000000000000000000000000000000000006064820152608401610895565b73ffffffffffffffffffffffffffffffffffffffff8316600090815260208190526040902054818110156115e4576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e742065786365656473206260448201527f616c616e636500000000000000000000000000000000000000000000000000006064820152608401610895565b73ffffffffffffffffffffffffffffffffffffffff848116600081815260208181526040808320878703905593871680835291849020805487019055925185815290927fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3610e25565b73ffffffffffffffffffffffffffffffffffffffff82166116fa576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f2061646472657360448201527f73000000000000000000000000000000000000000000000000000000000000006064820152608401610895565b73ffffffffffffffffffffffffffffffffffffffff8216600090815260208190526040902054818110156117b0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e60448201527f63650000000000000000000000000000000000000000000000000000000000006064820152608401610895565b73ffffffffffffffffffffffffffffffffffffffff83166000818152602081815260408083208686039055600280548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b6000818152600183016020526040812054801561190457600061183f600183611dce565b855490915060009061185390600190611dce565b90508181146118b857600086600001828154811061187357611873611de1565b906000526020600020015490508087600001848154811061189657611896611de1565b6000918252602080832090910192909255918252600188019052604090208390555b85548690806118c9576118c9611e10565b600190038181906000526020600020016000905590558560010160008681526020019081526020016000206000905560019350505050610615565b6000915050610615565b60608160000180548060200260200160405190810160405280929190818152602001828054801561195e57602002820191906000526020600020905b81548152602001906001019080831161194a575b50505050509050919050565b60008181526001830160205260408120546119b157508154600181810184556000848152602080822090930184905584548482528286019093526040902091909155610615565b506000610615565b6000602082840312156119cb57600080fd5b81357fffffffff0000000000000000000000000000000000000000000000000000000081168114610a4157600080fd5b6000815180845260005b81811015611a2157602081850181015186830182015201611a05565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000610a4160208301846119fb565b803573ffffffffffffffffffffffffffffffffffffffff81168114611a9657600080fd5b919050565b60008060408385031215611aae57600080fd5b611ab783611a72565b946020939093013593505050565b600080600060608486031215611ada57600080fd5b611ae384611a72565b9250611af160208501611a72565b9150604084013590509250925092565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b600080600060608486031215611b4557600080fd5b611b4e84611a72565b925060208401359150604084013567ffffffffffffffff80821115611b7257600080fd5b818601915086601f830112611b8657600080fd5b813581811115611b9857611b98611b01565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908382118183101715611bde57611bde611b01565b81604052828152896020848701011115611bf757600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b600060208284031215611c2b57600080fd5b5035919050565b600060208284031215611c4457600080fd5b610a4182611a72565b6020808252825182820181905260009190848201906040850190845b81811015611c9b57835173ffffffffffffffffffffffffffffffffffffffff1683529284019291840191600101611c69565b50909695505050505050565b60008060408385031215611cba57600080fd5b611cc383611a72565b9150611cd160208401611a72565b90509250929050565b600181811c90821680611cee57607f821691505b602082108103611d27577f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b50919050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561061557610615611d2d565b828152604060208201526000611d8860408301846119fb565b949350505050565b73ffffffffffffffffffffffffffffffffffffffff84168152826020820152606060408201526000611dc560608301846119fb565b95945050505050565b8181038181111561061557610615611d2d565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603160045260246000fdfea164736f6c6343000813000a", + expectedResult: seth.Pragma{Major: 0, Minor: 8, Patch: 19}, + expectedError: nil, + }, + { + name: "Valid Bytecode [Automation Registar Wrapper 2_1]", + bytecode: "0x60a06040523480156200001157600080fd5b5060405162002d8238038062002d8283398101604081905262000034916200043b565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be816200017a565b5050506001600160a01b038416608052620000da838362000225565b60005b81518110156200016f576200015a82828151811062000100576200010062000598565b60200260200101516000015183838151811062000121576200012162000598565b60200260200101516020015184848151811062000142576200014262000598565b6020026020010151604001516200029e60201b60201c565b806200016681620005ae565b915050620000dd565b50505050506200062f565b336001600160a01b03821603620001d45760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6200022f6200034c565b6040805180820182526001600160a01b0384168082526001600160601b0384166020928301819052600160a01b810282176004558351918252918101919091527f39ce5d867555f0b0183e358fce5b158e7ca4fecd7c01cb7e0e19f1e23285838a910160405180910390a15050565b620002a86200034c565b60ff83166000908152600360205260409020805483919060ff19166001836002811115620002da57620002da620005d6565b021790555060ff831660009081526003602052604090819020805464ffffffff00191661010063ffffffff851602179055517f830a6d06a4e2caac67eba04323de22bdb04f032dd8b3d6a0c52b503d9a7036a3906200033f90859085908590620005ec565b60405180910390a1505050565b6000546001600160a01b03163314620003a85760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000082565b565b80516001600160a01b0381168114620003c257600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715620004025762000402620003c7565b60405290565b604051601f8201601f191681016001600160401b0381118282101715620004335762000433620003c7565b604052919050565b600080600080608085870312156200045257600080fd5b6200045d85620003aa565b935060206200046e818701620003aa565b604087810151919550906001600160601b03811681146200048e57600080fd5b606088810151919550906001600160401b0380821115620004ae57600080fd5b818a0191508a601f830112620004c357600080fd5b815181811115620004d857620004d8620003c7565b620004e8868260051b0162000408565b818152868101925090840283018601908c8211156200050657600080fd5b928601925b81841015620005875784848e031215620005255760008081fd5b6200052f620003dd565b845160ff81168114620005425760008081fd5b81528488015160038110620005575760008081fd5b818901528487015163ffffffff81168114620005735760008081fd5b81880152835292840192918601916200050b565b999c989b5096995050505050505050565b634e487b7160e01b600052603260045260246000fd5b600060018201620005cf57634e487b7160e01b600052601160045260246000fd5b5060010190565b634e487b7160e01b600052602160045260246000fd5b60ff8416815260608101600384106200061557634e487b7160e01b600052602160045260246000fd5b83602083015263ffffffff83166040830152949350505050565b6080516127146200066e60003960008181610177015281816105d601528181610887015281816109bd01528181610f0e015261171b01526127146000f3fe608060405234801561001057600080fd5b506004361061011b5760003560e01c8063856853e6116100b2578063b5ff5b4111610081578063c4d252f511610066578063c4d252f5146103e3578063e8d4070d146103f6578063f2fde38b1461040957600080fd5b8063b5ff5b4114610369578063c3f909d41461037c57600080fd5b8063856853e61461027857806388b12d551461028b5780638da5cb5b14610338578063a4c0ed361461035657600080fd5b80633f678e11116100ee5780633f678e11146101f35780636c4cdfc31461021457806379ba5097146102275780637e776f7f1461022f57600080fd5b8063181f5a77146101205780631b6b6d2314610172578063212d0884146101be578063367b9b4f146101de575b600080fd5b61015c6040518060400160405280601981526020017f4175746f6d6174696f6e52656769737472617220322e312e300000000000000081525081565b6040516101699190611a74565b60405180910390f35b6101997f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610169565b6101d16101cc366004611aa4565b61041c565b6040516101699190611b29565b6101f16101ec366004611b9d565b6104a9565b005b610206610201366004611bd6565b61053b565b604051908152602001610169565b6101f1610222366004611c2e565b6106d3565b6101f161076d565b61026861023d366004611c63565b73ffffffffffffffffffffffffffffffffffffffff1660009081526005602052604090205460ff1690565b6040519015158152602001610169565b6101f1610286366004611de1565b61086f565b6102ff610299366004611f40565b60009081526002602090815260409182902082518084019093525473ffffffffffffffffffffffffffffffffffffffff8116808452740100000000000000000000000000000000000000009091046bffffffffffffffffffffffff169290910182905291565b6040805173ffffffffffffffffffffffffffffffffffffffff90931683526bffffffffffffffffffffffff909116602083015201610169565b60005473ffffffffffffffffffffffffffffffffffffffff16610199565b6101f1610364366004611f59565b6109a5565b6101f1610377366004611fb5565b610ce3565b60408051808201825260045473ffffffffffffffffffffffffffffffffffffffff8116808352740100000000000000000000000000000000000000009091046bffffffffffffffffffffffff16602092830181905283519182529181019190915201610169565b6101f16103f1366004611f40565b610dc2565b6101f1610404366004611ffe565b61104c565b6101f1610417366004611c63565b6112d9565b60408051606080820183526000808352602080840182905283850182905260ff86811683526003909152908490208451928301909452835492939192839116600281111561046c5761046c611abf565b600281111561047d5761047d611abf565b8152905463ffffffff610100820481166020840152650100000000009091041660409091015292915050565b6104b16112ed565b73ffffffffffffffffffffffffffffffffffffffff821660008181526005602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685151590811790915591519182527f20c6237dac83526a849285a9f79d08a483291bdd3a056a0ef9ae94ecee1ad356910160405180910390a25050565b6004546000907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff1661057961014084016101208501612109565b6bffffffffffffffffffffffff1610156105bf576040517fcd1c886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166323b872dd333061060f61014087016101208801612109565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff93841660048201529290911660248301526bffffffffffffffffffffffff1660448201526064016020604051808303816000875af1158015610696573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106ba9190612124565b506106cd6106c783612141565b33611370565b92915050565b6106db6112ed565b60408051808201825273ffffffffffffffffffffffffffffffffffffffff84168082526bffffffffffffffffffffffff8416602092830181905274010000000000000000000000000000000000000000810282176004558351918252918101919091527f39ce5d867555f0b0183e358fce5b158e7ca4fecd7c01cb7e0e19f1e23285838a910160405180910390a15050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146107f3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146108de576040517f018d10be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109966040518061014001604052808e81526020018d8d8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525073ffffffffffffffffffffffffffffffffffffffff808d16602083015263ffffffff8c1660408301528a16606082015260ff8916608082015260a0810188905260c0810187905260e081018690526bffffffffffffffffffffffff85166101009091015282611370565b50505050505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610a14576040517f018d10be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81818080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050505060208101517fffffffff0000000000000000000000000000000000000000000000000000000081167f856853e60000000000000000000000000000000000000000000000000000000014610aca576040517fe3d6792100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8484846000610adc8260048186612276565b810190610ae991906122a0565b509950505050505050505050806bffffffffffffffffffffffff168414610b3c576040517f55e97b0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8988886000610b4e8260048186612276565b810190610b5b91906122a0565b9a50505050505050505050508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614610bcc576040517ff8c5638e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004547401000000000000000000000000000000000000000090046bffffffffffffffffffffffff168d1015610c2e576040517fcd1c886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60003073ffffffffffffffffffffffffffffffffffffffff168d8d604051610c579291906123dd565b600060405180830381855af49150503d8060008114610c92576040519150601f19603f3d011682016040523d82523d6000602084013e610c97565b606091505b5050905080610cd2576040517f649bf81000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505050505050505050565b610ceb6112ed565b60ff8316600090815260036020526040902080548391907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001836002811115610d3857610d38611abf565b021790555060ff83166000908152600360205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ff1661010063ffffffff851602179055517f830a6d06a4e2caac67eba04323de22bdb04f032dd8b3d6a0c52b503d9a7036a390610db5908590859085906123ed565b60405180910390a1505050565b60008181526002602090815260409182902082518084019093525473ffffffffffffffffffffffffffffffffffffffff8116808452740100000000000000000000000000000000000000009091046bffffffffffffffffffffffff1691830191909152331480610e49575060005473ffffffffffffffffffffffffffffffffffffffff1633145b610e7f576040517f61685c2b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805173ffffffffffffffffffffffffffffffffffffffff16610ecd576040517f4b13b31e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260026020908152604080832083905583519184015190517fa9059cbb0000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169263a9059cbb92610f859260040173ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b6020604051808303816000875af1158015610fa4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fc89190612124565b90508061101c5781516040517fc2e4dce800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016107ea565b60405183907f3663fb28ebc87645eb972c9dad8521bf665c623f287e79f1c56f1eb374b82a2290600090a2505050565b6110546112ed565b60008181526002602090815260409182902082518084019093525473ffffffffffffffffffffffffffffffffffffffff8116808452740100000000000000000000000000000000000000009091046bffffffffffffffffffffffff16918301919091526110ed576040517f4b13b31e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008b8b8b8b8b8b8b8b8b60405160200161111099989796959493929190612461565b604051602081830303815290604052805190602001209050808314611161576040517f3f4d605300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60026000848152602001908152602001600020600080820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556000820160146101000a8154906bffffffffffffffffffffffff021916905550506112c96040518061014001604052808f81526020016040518060200160405280600081525081526020018e73ffffffffffffffffffffffffffffffffffffffff1681526020018d63ffffffff1681526020018c73ffffffffffffffffffffffffffffffffffffffff1681526020018b60ff1681526020018a8a8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060208082018a905260408051601f8a0183900483028101830182528981529201919089908990819084018382808284376000920191909152505050908252506020858101516bffffffffffffffffffffffff1691015282611647565b5050505050505050505050505050565b6112e16112ed565b6112ea81611876565b50565b60005473ffffffffffffffffffffffffffffffffffffffff16331461136e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016107ea565b565b608082015160009073ffffffffffffffffffffffffffffffffffffffff166113c4576040517f05bb467c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008360400151846060015185608001518660a001518760c001518860e0015189610100015160405160200161140097969594939291906124e7565b604051602081830303815290604052805190602001209050836040015173ffffffffffffffffffffffffffffffffffffffff16817f7684390ebb103102f7f48c71439c2408713f8d437782a6fab2756acc0e42c1b786600001518760200151886060015189608001518a60a001518b60e001518c61010001518d60c001518e610120015160405161149999989796959493929190612569565b60405180910390a360a084015160ff9081166000908152600360205260408082208151606081019092528054929361151c9383911660028111156114df576114df611abf565b60028111156114f0576114f0611abf565b8152905463ffffffff61010082048116602084015265010000000000909104166040909101528561196b565b156115845760a085015160ff166000908152600360205260409020805465010000000000900463ffffffff1690600561155483612653565b91906101000a81548163ffffffff021916908363ffffffff1602179055505061157d8583611647565b905061163f565b61012085015160008381526002602052604081205490916115ca917401000000000000000000000000000000000000000090046bffffffffffffffffffffffff16612676565b604080518082018252608089015173ffffffffffffffffffffffffffffffffffffffff90811682526bffffffffffffffffffffffff9384166020808401918252600089815260029091529390932091519251909316740100000000000000000000000000000000000000000291909216179055505b949350505050565b600480546040808501516060860151608087015160a088015160c089015160e08a01516101008b015196517f28f32f3800000000000000000000000000000000000000000000000000000000815260009973ffffffffffffffffffffffffffffffffffffffff909916988a988a986328f32f38986116d29891979096919590949193909291016124e7565b6020604051808303816000875af11580156116f1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061171591906126a2565b905060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16634000aea0848861012001518560405160200161176f91815260200190565b6040516020818303038152906040526040518463ffffffff1660e01b815260040161179c939291906126bb565b6020604051808303816000875af11580156117bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117df9190612124565b905080611830576040517fc2e4dce800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024016107ea565b81857fb9a292fb7e3edd920cd2d2829a3615a640c43fd7de0a0820aa0668feb4c37d4b88600001516040516118659190611a74565b60405180910390a350949350505050565b3373ffffffffffffffffffffffffffffffffffffffff8216036118f5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016107ea565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000808351600281111561198157611981611abf565b0361198e575060006106cd565b6001835160028111156119a3576119a3611abf565b1480156119d6575073ffffffffffffffffffffffffffffffffffffffff821660009081526005602052604090205460ff16155b156119e3575060006106cd565b826020015163ffffffff16836040015163ffffffff161015611a07575060016106cd565b50600092915050565b6000815180845260005b81811015611a3657602081850181015186830182015201611a1a565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000611a876020830184611a10565b9392505050565b803560ff81168114611a9f57600080fd5b919050565b600060208284031215611ab657600080fd5b611a8782611a8e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60038110611b25577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b6000606082019050611b3c828451611aee565b602083015163ffffffff8082166020850152806040860151166040850152505092915050565b73ffffffffffffffffffffffffffffffffffffffff811681146112ea57600080fd5b8035611a9f81611b62565b80151581146112ea57600080fd5b60008060408385031215611bb057600080fd5b8235611bbb81611b62565b91506020830135611bcb81611b8f565b809150509250929050565b600060208284031215611be857600080fd5b813567ffffffffffffffff811115611bff57600080fd5b82016101408185031215611a8757600080fd5b80356bffffffffffffffffffffffff81168114611a9f57600080fd5b60008060408385031215611c4157600080fd5b8235611c4c81611b62565b9150611c5a60208401611c12565b90509250929050565b600060208284031215611c7557600080fd5b8135611a8781611b62565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610140810167ffffffffffffffff81118282101715611cd357611cd3611c80565b60405290565b600082601f830112611cea57600080fd5b813567ffffffffffffffff80821115611d0557611d05611c80565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715611d4b57611d4b611c80565b81604052838152866020858801011115611d6457600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008083601f840112611d9657600080fd5b50813567ffffffffffffffff811115611dae57600080fd5b602083019150836020828501011115611dc657600080fd5b9250929050565b803563ffffffff81168114611a9f57600080fd5b6000806000806000806000806000806000806101608d8f031215611e0457600080fd5b67ffffffffffffffff8d351115611e1a57600080fd5b611e278e8e358f01611cd9565b9b5067ffffffffffffffff60208e01351115611e4257600080fd5b611e528e60208f01358f01611d84565b909b509950611e6360408e01611b84565b9850611e7160608e01611dcd565b9750611e7f60808e01611b84565b9650611e8d60a08e01611a8e565b955067ffffffffffffffff60c08e01351115611ea857600080fd5b611eb88e60c08f01358f01611cd9565b945067ffffffffffffffff60e08e01351115611ed357600080fd5b611ee38e60e08f01358f01611cd9565b935067ffffffffffffffff6101008e01351115611eff57600080fd5b611f108e6101008f01358f01611cd9565b9250611f1f6101208e01611c12565b9150611f2e6101408e01611b84565b90509295989b509295989b509295989b565b600060208284031215611f5257600080fd5b5035919050565b60008060008060608587031215611f6f57600080fd5b8435611f7a81611b62565b935060208501359250604085013567ffffffffffffffff811115611f9d57600080fd5b611fa987828801611d84565b95989497509550505050565b600080600060608486031215611fca57600080fd5b611fd384611a8e565b9250602084013560038110611fe757600080fd5b9150611ff560408501611dcd565b90509250925092565b60008060008060008060008060008060006101208c8e03121561202057600080fd5b67ffffffffffffffff808d35111561203757600080fd5b6120448e8e358f01611cd9565b9b5061205260208e01611b84565b9a5061206060408e01611dcd565b995061206e60608e01611b84565b985061207c60808e01611a8e565b97508060a08e0135111561208f57600080fd5b61209f8e60a08f01358f01611d84565b909750955060c08d01358110156120b557600080fd5b6120c58e60c08f01358f01611cd9565b94508060e08e013511156120d857600080fd5b506120e98d60e08e01358e01611d84565b81945080935050506101008c013590509295989b509295989b9093969950565b60006020828403121561211b57600080fd5b611a8782611c12565b60006020828403121561213657600080fd5b8151611a8781611b8f565b6000610140823603121561215457600080fd5b61215c611caf565b823567ffffffffffffffff8082111561217457600080fd5b61218036838701611cd9565b8352602085013591508082111561219657600080fd5b6121a236838701611cd9565b60208401526121b360408601611b84565b60408401526121c460608601611dcd565b60608401526121d560808601611b84565b60808401526121e660a08601611a8e565b60a084015260c08501359150808211156121ff57600080fd5b61220b36838701611cd9565b60c084015260e085013591508082111561222457600080fd5b61223036838701611cd9565b60e08401526101009150818501358181111561224b57600080fd5b61225736828801611cd9565b8385015250505061012061226c818501611c12565b9082015292915050565b6000808585111561228657600080fd5b8386111561229357600080fd5b5050820193919092039150565b60008060008060008060008060008060006101608c8e0312156122c257600080fd5b67ffffffffffffffff808d3511156122d957600080fd5b6122e68e8e358f01611cd9565b9b508060208e013511156122f957600080fd5b6123098e60208f01358f01611cd9565b9a5061231760408e01611b84565b995061232560608e01611dcd565b985061233360808e01611b84565b975061234160a08e01611a8e565b96508060c08e0135111561235457600080fd5b6123648e60c08f01358f01611cd9565b95508060e08e0135111561237757600080fd5b6123878e60e08f01358f01611cd9565b9450806101008e0135111561239b57600080fd5b506123ad8d6101008e01358e01611cd9565b92506123bc6101208d01611c12565b91506123cb6101408d01611b84565b90509295989b509295989b9093969950565b8183823760009101908152919050565b60ff84168152606081016124046020830185611aee565b63ffffffff83166040830152949350505050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600073ffffffffffffffffffffffffffffffffffffffff808c16835263ffffffff8b166020840152808a1660408401525060ff8816606083015260e060808301526124b060e083018789612418565b82810360a08401526124c28187611a10565b905082810360c08401526124d7818587612418565b9c9b505050505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808a16835263ffffffff8916602084015280881660408401525060ff8616606083015260e0608083015261253560e0830186611a10565b82810360a08401526125478186611a10565b905082810360c084015261255b8185611a10565b9a9950505050505050505050565b600061012080835261257d8184018d611a10565b90508281036020840152612591818c611a10565b905063ffffffff8a16604084015273ffffffffffffffffffffffffffffffffffffffff8916606084015260ff8816608084015282810360a08401526125d68188611a10565b905082810360c08401526125ea8187611a10565b905082810360e08401526125fe8186611a10565b9150506bffffffffffffffffffffffff83166101008301529a9950505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600063ffffffff80831681810361266c5761266c612624565b6001019392505050565b6bffffffffffffffffffffffff81811683821601908082111561269b5761269b612624565b5092915050565b6000602082840312156126b457600080fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff841681526bffffffffffffffffffffffff831660208201526060604082015260006126fe6060830184611a10565b9594505050505056fea164736f6c6343000810000a", + expectedResult: seth.Pragma{Major: 0, Minor: 8, Patch: 16}, + expectedError: nil, + }, + { + name: "Valid Bytecode, No metadata [Offchain Aggregator v1]", + bytecode: "0x60e06040523480156200001157600080fd5b5060405162005a3a38038062005a3a83398181016040526101808110156200003857600080fd5b815160208301516040808501516060860151608087015160a088015160c089015160e08a01516101008b01516101208c01516101408d01516101608e0180519a519c9e9b9d999c989b979a969995989497939692959194939182019284640100000000821115620000a857600080fd5b908301906020820185811115620000be57600080fd5b8251640100000000811182820188101715620000d957600080fd5b82525081516020918201929091019080838360005b8381101562000108578181015183820152602001620000ee565b50505050905090810190601f168015620001365780820380516001836020036101000a031916815260200191505b506040525050600080546001600160a01b03191633179055508b8b8b8b8b8b89620001658787878787620002ca565b600380546001600160a01b0319166001600160a01b0384169081179091556040516000907f4966a50c93f855342ccf6c5c0d358b85b91335b2acedc7da0932f691f351711a908290a3620001b981620003bc565b620001c362000653565b620001cd62000653565b60005b601f8160ff1610156200021d576001838260ff16601f8110620001ef57fe5b61ffff909216602092909202015260018260ff8316601f81106200020f57fe5b6020020152600101620001d0565b506200022d600583601f62000672565b506200023d600982601f6200070f565b505050505060f887901b7fff000000000000000000000000000000000000000000000000000000000000001660c052505083516200028693506030925060208501915062000740565b50620002928362000435565b6200029f6000806200050d565b50505050601791820b820b604090811b60805290820b90910b901b60a05250620007d9945050505050565b6040805160a0808201835263ffffffff88811680845288821660208086018290528984168688018190528985166060808901829052958a1660809889018190526002805463ffffffff1916871763ffffffff60201b191664010000000087021763ffffffff60401b19166801000000000000000085021763ffffffff60601b19166c0100000000000000000000000084021763ffffffff60801b1916600160801b830217905589519586529285019390935283880152928201529283015291517fd0d9486a2c673e2a4b57fc82e4c8a556b3e2b82dd5db07e2c04a920ca0f469b6929181900390910190a15050505050565b6004546001600160a01b0390811690821681146200043157600480546001600160a01b0319166001600160a01b03848116918217909255604080519284168352602083019190915280517f793cb73064f3c8cde7e187ae515511e6e56d1ee89bf08b82fa60fb70f8d489129281900390910190a15b5050565b6000546001600160a01b0316331462000495576040805162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015290519081900360640190fd5b602f546001600160a01b0390811690821681146200043157602f80546001600160a01b0319166001600160a01b03848116918217909255604080519284168352602083019190915280517f27b89aede8b560578baaa25ee5ce3852c5eecad1e114b941bbd89e1eb4bae6349281900390910190a15050565b6000546001600160a01b031633146200056d576040805162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015290519081900360640190fd5b60408051808201909152602e546001600160a01b03808216808452600160a01b90920463ffffffff1660208401528416141580620005bb57508163ffffffff16816020015163ffffffff1614155b156200064e576040805180820182526001600160a01b0385811680835263ffffffff8681166020948501819052602e80546001600160a01b031916841763ffffffff60a01b1916600160a01b8302179055865187860151875193168352948201528451919493909216927fb04e3a37abe9c0fcdfebdeae019a8e2b12ddf53f5d55ffb0caccc1bedaca1541928290030190a35b505050565b604051806103e00160405280601f906020820280368337509192915050565b600283019183908215620006fd5791602002820160005b83821115620006cb57835183826101000a81548161ffff021916908361ffff160217905550926020019260020160208160010104928301926001030262000689565b8015620006fb5782816101000a81549061ffff0219169055600201602081600101049283019260010302620006cb565b505b506200070b929150620007c2565b5090565b82601f8101928215620006fd579160200282015b82811115620006fd57825182559160200191906001019062000723565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282620007785760008555620006fd565b82601f106200079357805160ff1916838001178555620006fd565b82800160010185558215620006fd5791820182811115620006fd57825182559160200191906001019062000723565b5b808211156200070b5760008155600101620007c3565b60805160401c60a05160401c60c05160f81c6152216200081960003980610dbf525080611aaf528061354e525080610d1e528061352152506152216000f3fe608060405234801561001057600080fd5b50600436106102c85760003560e01c806398e5b12a1161017b578063c9807539116100d8578063eb4571631161008c578063f2fde38b11610071578063f2fde38b14610b46578063fbffd2c114610b6c578063feaf968c14610b92576102c8565b8063eb45716314610ae6578063eb5dcd6c14610b18576102c8565b8063e4902f82116100bd578063e4902f8214610a37578063e5fe457714610a74578063e76d516814610ade576102c8565b8063c98075391461091b578063d09dc33914610a2f576102c8565b8063b121e1471161012f578063b633620c11610114578063b633620c1461088d578063bd824706146108aa578063c1075329146108ef576102c8565b8063b121e1471461084a578063b5ab58dc14610870576102c8565b80639a6fc8f5116101605780639a6fc8f5146106ef5780639c849b30146107625780639e3ceeab14610824576102c8565b806398e5b12a146106c0578063996e8298146106e7576102c8565b806370da2f671161022957806381ff7048116101dd5780638ac28d5a116101c25780638ac28d5a146106625780638da5cb5b146106885780638e0566de14610690576102c8565b806381ff7048146106095780638205bf6a1461065a576102c8565b80637284e4161161020e5780637284e416146105a157806379ba5097146105a957806381411834146105b1576102c8565b806370da2f671461057557806370efdf2d1461057d576102c8565b80634fb174701161028057806354fd4d501161026557806354fd4d5014610438578063585aa7de14610440578063668a0f021461056d576102c8565b80634fb174701461040057806350d25bcd14610430576102c8565b806322adbc78116102b157806322adbc781461038257806329937268146103a1578063313ce567146103e2576102c8565b80630eafb25b146102cd578063181f5a7714610305575b600080fd5b6102f3600480360360208110156102e357600080fd5b50356001600160a01b0316610b9a565b60408051918252519081900360200190f35b61030d610ce5565b6040805160208082528351818301528351919283929083019185019080838360005b8381101561034757818101518382015260200161032f565b50505050905090810190601f1680156103745780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b61038a610d1c565b6040805160179290920b8252519081900360200190f35b6103a9610d40565b6040805163ffffffff96871681529486166020860152928516848401529084166060840152909216608082015290519081900360a00190f35b6103ea610dbd565b6040805160ff9092168252519081900360200190f35b61042e6004803603604081101561041657600080fd5b506001600160a01b0381358116916020013516610de1565b005b6102f36110c5565b6102f3611101565b61042e600480360360a081101561045657600080fd5b81019060208101813564010000000081111561047157600080fd5b82018360208201111561048357600080fd5b803590602001918460208302840111640100000000831117156104a557600080fd5b9193909290916020810190356401000000008111156104c357600080fd5b8201836020820111156104d557600080fd5b803590602001918460208302840111640100000000831117156104f757600080fd5b9193909260ff8335169267ffffffffffffffff60208201351692919060608101906040013564010000000081111561052e57600080fd5b82018360208201111561054057600080fd5b8035906020019184600183028401116401000000008311171561056257600080fd5b509092509050611106565b6102f3611a87565b61038a611aad565b610585611ad1565b604080516001600160a01b039092168252519081900360200190f35b61030d611ae0565b61042e611b94565b6105b9611c62565b60408051602080825283518183015283519192839290830191858101910280838360005b838110156105f55781810151838201526020016105dd565b505050509050019250505060405180910390f35b610611611cc3565b6040805163ffffffff94851681529290931660208301527fffffffffffffffffffffffffffffffff00000000000000000000000000000000168183015290519081900360600190f35b6102f3611ce4565b61042e6004803603602081101561067857600080fd5b50356001600160a01b0316611d3f565b610585611db9565b610698611dc8565b604080516001600160a01b03909316835263ffffffff90911660208301528051918290030190f35b6106c8611e0c565b6040805169ffffffffffffffffffff9092168252519081900360200190f35b610585612042565b6107186004803603602081101561070557600080fd5b503569ffffffffffffffffffff16612051565b604051808669ffffffffffffffffffff1681526020018581526020018481526020018381526020018269ffffffffffffffffffff1681526020019550505050505060405180910390f35b61042e6004803603604081101561077857600080fd5b81019060208101813564010000000081111561079357600080fd5b8201836020820111156107a557600080fd5b803590602001918460208302840111640100000000831117156107c757600080fd5b9193909290916020810190356401000000008111156107e557600080fd5b8201836020820111156107f757600080fd5b8035906020019184602083028401116401000000008311171561081957600080fd5b50909250905061219c565b61042e6004803603602081101561083a57600080fd5b50356001600160a01b03166123d5565b61042e6004803603602081101561086057600080fd5b50356001600160a01b03166124c3565b6102f36004803603602081101561088657600080fd5b50356125bc565b6102f3600480360360208110156108a357600080fd5b50356125f2565b61042e600480360360a08110156108c057600080fd5b5063ffffffff813581169160208101358216916040820135811691606081013582169160809091013516612647565b61042e6004803603604081101561090557600080fd5b506001600160a01b03813516906020013561278f565b61042e6004803603608081101561093157600080fd5b81019060208101813564010000000081111561094c57600080fd5b82018360208201111561095e57600080fd5b8035906020019184600183028401116401000000008311171561098057600080fd5b91939092909160208101903564010000000081111561099e57600080fd5b8201836020820111156109b057600080fd5b803590602001918460208302840111640100000000831117156109d257600080fd5b9193909290916020810190356401000000008111156109f057600080fd5b820183602082011115610a0257600080fd5b80359060200191846020830284011164010000000083111715610a2457600080fd5b919350915035612a98565b6102f3613983565b610a5d60048036036020811015610a4d57600080fd5b50356001600160a01b0316613a2d565b6040805161ffff9092168252519081900360200190f35b610a7c613ada565b604080517fffffffffffffffffffffffffffffffff00000000000000000000000000000000909616865263ffffffff909416602086015260ff9092168484015260170b606084015267ffffffffffffffff166080830152519081900360a00190f35b610585613bc9565b61042e60048036036040811015610afc57600080fd5b5080356001600160a01b0316906020013563ffffffff16613bd8565b61042e60048036036040811015610b2e57600080fd5b506001600160a01b0381358116916020013516613d6d565b61042e60048036036020811015610b5c57600080fd5b50356001600160a01b0316613ec8565b61042e60048036036020811015610b8257600080fd5b50356001600160a01b0316613f90565b610718613ff8565b6001600160a01b03811660009081526028602090815260408083208151808301909252805460ff808216845285948401916101009004166002811115610bdc57fe5b6002811115610be757fe5b9052509050600081602001516002811115610bfe57fe5b1415610c0e576000915050610ce0565b6040805160a08101825260025463ffffffff80821683526401000000008204811660208401526801000000000000000082048116938301939093526c01000000000000000000000000810483166060830181905270010000000000000000000000000000000090910490921660808201528251909160009160019060059060ff16601f8110610c9957fe5b601091828204019190066002029054906101000a900461ffff160361ffff1602633b9aca0002905060016009846000015160ff16601f8110610cd757fe5b01540301925050505b919050565b60408051808201909152601881527f4f6666636861696e41676772656761746f7220342e302e300000000000000000602082015290565b7f000000000000000000000000000000000000000000000000000000000000000081565b6040805160a08101825260025463ffffffff808216808452640100000000830482166020850181905268010000000000000000840483169585018690526c01000000000000000000000000840483166060860181905270010000000000000000000000000000000090940490921660809094018490529490939290565b7f000000000000000000000000000000000000000000000000000000000000000081565b6000546001600160a01b03163314610e40576040805162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015290519081900360640190fd5b6003546001600160a01b03908116908316811415610e5e57506110c1565b604080517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290516001600160a01b038516916370a08231916024808301926020929190829003018186803b158015610ebd57600080fd5b505afa158015610ed1573d6000803e3d6000fd5b505050506040513d6020811015610ee757600080fd5b50610ef2905061407c565b6000816001600160a01b03166370a08231306040518263ffffffff1660e01b815260040180826001600160a01b0316815260200191505060206040518083038186803b158015610f4157600080fd5b505afa158015610f55573d6000803e3d6000fd5b505050506040513d6020811015610f6b57600080fd5b5051604080517fa9059cbb0000000000000000000000000000000000000000000000000000000081526001600160a01b0386811660048301526024820184905291519293509084169163a9059cbb916044808201926020929091908290030181600087803b158015610fdc57600080fd5b505af1158015610ff0573d6000803e3d6000fd5b505050506040513d602081101561100657600080fd5b5051611059576040805162461bcd60e51b815260206004820152601f60248201527f7472616e736665722072656d61696e696e672066756e6473206661696c656400604482015290519081900360640190fd5b600380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0386811691821790925560405190918416907f4966a50c93f855342ccf6c5c0d358b85b91335b2acedc7da0932f691f351711a90600090a350505b5050565b602b54760100000000000000000000000000000000000000000000900463ffffffff166000908152602c6020526040902054601790810b900b90565b600481565b868560ff8616601f831115611162576040805162461bcd60e51b815260206004820152601060248201527f746f6f206d616e79207369676e65727300000000000000000000000000000000604482015290519081900360640190fd5b600081116111b7576040805162461bcd60e51b815260206004820152601a60248201527f7468726573686f6c64206d75737420626520706f736974697665000000000000604482015290519081900360640190fd5b8183146111f55760405162461bcd60e51b81526004018080602001828103825260248152602001806151f16024913960400191505060405180910390fd5b80600302831161124c576040805162461bcd60e51b815260206004820181905260248201527f6661756c74792d6f7261636c65207468726573686f6c6420746f6f2068696768604482015290519081900360640190fd5b6000546001600160a01b031633146112ab576040805162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015290519081900360640190fd5b6029541561144f57602980547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810191600091839081106112e857fe5b6000918252602082200154602a80546001600160a01b039092169350908490811061130f57fe5b6000918252602090912001546001600160a01b0316905061132f8161442c565b6001600160a01b0380831660009081526028602052604080822080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00009081169091559284168252902080549091169055602980548061138b57fe5b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055019055602a8054806113ee57fe5b60008281526020902081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90810180547fffffffffffffffffffffffff0000000000000000000000000000000000000000169055019055506112ab915050565b60005b8a81101561185d576000602860008e8e8581811061146c57fe5b602090810292909201356001600160a01b031683525081019190915260400160002054610100900460ff1660028111156114a257fe5b146114f4576040805162461bcd60e51b815260206004820152601760248201527f7265706561746564207369676e65722061646472657373000000000000000000604482015290519081900360640190fd5b6040805180820190915260ff8216815260016020820152602860008e8e8581811061151b57fe5b602090810292909201356001600160a01b031683525081810192909252604001600020825181547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff9091161780825591830151909182907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff166101008360028111156115a657fe5b02179055506000915060079050818c8c858181106115c057fe5b6001600160a01b036020918202939093013583168452830193909352604090910160002054169190911415905061163e576040805162461bcd60e51b815260206004820152601160248201527f7061796565206d75737420626520736574000000000000000000000000000000604482015290519081900360640190fd5b6000602860008c8c8581811061165057fe5b602090810292909201356001600160a01b031683525081019190915260400160002054610100900460ff16600281111561168657fe5b146116d8576040805162461bcd60e51b815260206004820152601c60248201527f7265706561746564207472616e736d6974746572206164647265737300000000604482015290519081900360640190fd5b6040805180820190915260ff8216815260026020820152602860008c8c858181106116ff57fe5b602090810292909201356001600160a01b031683525081810192909252604001600020825181547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660ff9091161780825591830151909182907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00ff1661010083600281111561178a57fe5b021790555090505060298c8c838181106117a057fe5b835460018101855560009485526020948590200180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b039590920293909301359390931692909217905550602a8a8a8381811061180257fe5b835460018181018655600095865260209586902090910180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0396909302949094013594909416179091555001611452565b50602b805460ff89167501000000000000000000000000000000000000000000027fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff909116179055602d80544363ffffffff9081166401000000009081027fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff84161780831660010183167fffffffffffffffffffffffffffffffffffffffffffffffffffffffff0000000090911617938490559091048116911661192930828f8f8f8f8f8f8f8f614638565b602b60000160006101000a8154816fffffffffffffffffffffffffffffffff021916908360801c02179055506000602b60000160106101000a81548164ffffffffff021916908364ffffffffff1602179055507f25d719d88a4512dd76c7442b910a83360845505894eb444ef299409e180f8fb982828f8f8f8f8f8f8f8f604051808b63ffffffff1681526020018a67ffffffffffffffff16815260200180602001806020018760ff1681526020018667ffffffffffffffff1681526020018060200184810384528c8c82818152602001925060200280828437600083820152601f01601f191690910185810384528a8152602090810191508b908b0280828437600083820152601f01601f191690910185810383528681526020019050868680828437600083820152604051601f909101601f19169092018290039f50909d5050505050505050505050505050a150505050505050505050505050565b602b54760100000000000000000000000000000000000000000000900463ffffffff1690565b7f000000000000000000000000000000000000000000000000000000000000000081565b602f546001600160a01b031690565b60308054604080516020601f60027fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff610100600188161502019095169490940493840181900481028201810190925282815260609390929091830182828015611b8a5780601f10611b5f57610100808354040283529160200191611b8a565b820191906000526020600020905b815481529060010190602001808311611b6d57829003601f168201915b5050505050905090565b6001546001600160a01b03163314611bf3576040805162461bcd60e51b815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e657200000000000000000000604482015290519081900360640190fd5b60008054337fffffffffffffffffffffffff0000000000000000000000000000000000000000808316821784556001805490911690556040516001600160a01b0390921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b6060602a805480602002602001604051908101604052809291908181526020018280548015611b8a57602002820191906000526020600020905b81546001600160a01b03168152600190910190602001808311611c9c575050505050905090565b602d54602b5463ffffffff808316926401000000009004169060801b909192565b602b54760100000000000000000000000000000000000000000000900463ffffffff166000908152602c60205260409020547801000000000000000000000000000000000000000000000000900467ffffffffffffffff1690565b6001600160a01b03818116600090815260076020526040902054163314611dad576040805162461bcd60e51b815260206004820152601760248201527f4f6e6c792070617965652063616e207769746864726177000000000000000000604482015290519081900360640190fd5b611db68161442c565b50565b6000546001600160a01b031681565b60408051808201909152602e546001600160a01b0381168083527401000000000000000000000000000000000000000090910463ffffffff16602090920182905291565b600080546001600160a01b0316331480611ee85750602f54604080517f6b14daf800000000000000000000000000000000000000000000000000000000815233600482018181526024830193845236604484018190526001600160a01b0390951694636b14daf894929360009391929190606401848480828437600083820152604051601f909101601f1916909201965060209550909350505081840390508186803b158015611ebb57600080fd5b505afa158015611ecf573d6000803e3d6000fd5b505050506040513d6020811015611ee557600080fd5b50515b611f39576040805162461bcd60e51b815260206004820152601d60248201527f4f6e6c79206f776e6572267265717565737465722063616e2063616c6c000000604482015290519081900360640190fd5b6040805160808082018352602b549081901b7fffffffffffffffffffffffffffffffff0000000000000000000000000000000016808352700100000000000000000000000000000000820464ffffffffff81166020808601919091527501000000000000000000000000000000000000000000840460ff9081168688015276010000000000000000000000000000000000000000000090940463ffffffff9081166060808801919091528751948552600884901c909116918401919091529216818501529251919233927f3ea16a923ff4b1df6526e854c9e3a995c43385d70e73359e10623c74f0b52037929181900390910190a2806060015160010163ffffffff1691505090565b6004546001600160a01b031690565b600080600080600063ffffffff8669ffffffffffffffffffff1611156040518060400160405280600f81526020017f4e6f20646174612070726573656e7400000000000000000000000000000000008152509061212c5760405162461bcd60e51b81526004018080602001828103825283818151815260200191508051906020019080838360005b838110156120f15781810151838201526020016120d9565b50505050905090810190601f16801561211e5780820380516001836020036101000a031916815260200191505b509250505060405180910390fd5b5050505063ffffffff83166000908152602c6020908152604091829020825180840190935254601781810b810b810b808552780100000000000000000000000000000000000000000000000090920467ffffffffffffffff1693909201839052949594900b939092508291508490565b6000546001600160a01b031633146121fb576040805162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015290519081900360640190fd5b82811461224f576040805162461bcd60e51b815260206004820181905260248201527f7472616e736d6974746572732e73697a6520213d207061796565732e73697a65604482015290519081900360640190fd5b60005b838110156123ce57600085858381811061226857fe5b905060200201356001600160a01b03169050600084848481811061228857fe5b6001600160a01b0385811660009081526007602090815260409091205492029390930135831693509091169050801580806122d45750826001600160a01b0316826001600160a01b0316145b612325576040805162461bcd60e51b815260206004820152601160248201527f706179656520616c726561647920736574000000000000000000000000000000604482015290519081900360640190fd5b6001600160a01b03848116600090815260076020526040902080547fffffffffffffffffffffffff000000000000000000000000000000000000000016858316908117909155908316146123be57826001600160a01b0316826001600160a01b0316856001600160a01b03167f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b360405160405180910390a45b5050600190920191506122529050565b5050505050565b6000546001600160a01b03163314612434576040805162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015290519081900360640190fd5b602f546001600160a01b0390811690821681146110c157602f80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03848116918217909255604080519284168352602083019190915280517f27b89aede8b560578baaa25ee5ce3852c5eecad1e114b941bbd89e1eb4bae6349281900390910190a15050565b6001600160a01b03818116600090815260086020526040902054163314612531576040805162461bcd60e51b815260206004820152601f60248201527f6f6e6c792070726f706f736564207061796565732063616e2061636365707400604482015290519081900360640190fd5b6001600160a01b0381811660008181526007602090815260408083208054337fffffffffffffffffffffffff000000000000000000000000000000000000000080831682179093556008909452828520805490921690915590519416939092849290917f78af32efdcad432315431e9b03d27e6cd98fb79c405fdc5af7c1714d9c0f75b39190a45050565b600063ffffffff8211156125d257506000610ce0565b5063ffffffff166000908152602c6020526040902054601790810b900b90565b600063ffffffff82111561260857506000610ce0565b5063ffffffff166000908152602c60205260409020547801000000000000000000000000000000000000000000000000900467ffffffffffffffff1690565b6004546000546001600160a01b0391821691163314806127215750604080517f6b14daf800000000000000000000000000000000000000000000000000000000815233600482018181526024830193845236604484018190526001600160a01b03861694636b14daf8946000939190606401848480828437600083820152604051601f909101601f1916909201965060209550909350505081840390508186803b1580156126f457600080fd5b505afa158015612708573d6000803e3d6000fd5b505050506040513d602081101561271e57600080fd5b50515b612772576040805162461bcd60e51b815260206004820181905260248201527f4f6e6c79206f776e65722662696c6c696e6741646d696e2063616e2063616c6c604482015290519081900360640190fd5b61277a61407c565b612787868686868661473c565b505050505050565b6000546001600160a01b0316331480612869575060048054604080517f6b14daf8000000000000000000000000000000000000000000000000000000008152339381018481526024820192835236604483018190526001600160a01b0390941694636b14daf8949093600093919291606401848480828437600083820152604051601f909101601f1916909201965060209550909350505081840390508186803b15801561283c57600080fd5b505afa158015612850573d6000803e3d6000fd5b505050506040513d602081101561286657600080fd5b50515b6128ba576040805162461bcd60e51b815260206004820181905260248201527f4f6e6c79206f776e65722662696c6c696e6741646d696e2063616e2063616c6c604482015290519081900360640190fd5b60006128c46148b6565b600354604080517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290519293506000926001600160a01b03909216916370a0823191602480820192602092909190829003018186803b15801561292e57600080fd5b505afa158015612942573d6000803e3d6000fd5b505050506040513d602081101561295857600080fd5b50519050818110156129b1576040805162461bcd60e51b815260206004820152601460248201527f696e73756666696369656e742062616c616e6365000000000000000000000000604482015290519081900360640190fd5b6003546001600160a01b031663a9059cbb856129cf85850387614a86565b6040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b158015612a1557600080fd5b505af1158015612a29573d6000803e3d6000fd5b505050506040513d6020811015612a3f57600080fd5b5051612a92576040805162461bcd60e51b815260206004820152601260248201527f696e73756666696369656e742066756e64730000000000000000000000000000604482015290519081900360640190fd5b50505050565b60005a9050612aab888888888888614aa0565b3614612afe576040805162461bcd60e51b815260206004820152601960248201527f7472616e736d6974206d65737361676520746f6f206c6f6e6700000000000000604482015290519081900360640190fd5b612b06615086565b6040805160808082018352602b549081901b7fffffffffffffffffffffffffffffffff00000000000000000000000000000000168252700100000000000000000000000000000000810464ffffffffff1660208301527501000000000000000000000000000000000000000000810460ff169282019290925276010000000000000000000000000000000000000000000090910463ffffffff166060808301919091529082526000908a908a90811015612bbf57600080fd5b813591602081013591810190606081016040820135640100000000811115612be657600080fd5b820183602082011115612bf857600080fd5b80359060200191846020830284011164010000000083111715612c1a57600080fd5b91908080602002602001604051908101604052809392919081815260200183836020028082843760009201919091525050505060408801525050506080840182905283515190925060589190911b907fffffffffffffffffffffffffffffffff00000000000000000000000000000000808316911614612ce1576040805162461bcd60e51b815260206004820152601560248201527f636f6e666967446967657374206d69736d617463680000000000000000000000604482015290519081900360640190fd5b608083015183516020015164ffffffffff808316911610612d49576040805162461bcd60e51b815260206004820152600c60248201527f7374616c65207265706f72740000000000000000000000000000000000000000604482015290519081900360640190fd5b83516040015160ff168911612da5576040805162461bcd60e51b815260206004820152601560248201527f6e6f7420656e6f756768207369676e6174757265730000000000000000000000604482015290519081900360640190fd5b601f891115612dfb576040805162461bcd60e51b815260206004820152601360248201527f746f6f206d616e79207369676e61747572657300000000000000000000000000604482015290519081900360640190fd5b868914612e4f576040805162461bcd60e51b815260206004820152601e60248201527f7369676e617475726573206f7574206f6620726567697374726174696f6e0000604482015290519081900360640190fd5b601f8460400151511115612eaa576040805162461bcd60e51b815260206004820152601e60248201527f6e756d206f62736572766174696f6e73206f7574206f6620626f756e64730000604482015290519081900360640190fd5b83600001516040015160020260ff1684604001515111612f11576040805162461bcd60e51b815260206004820152601e60248201527f746f6f206665772076616c75657320746f207472757374206d656469616e0000604482015290519081900360640190fd5b8867ffffffffffffffff81118015612f2857600080fd5b506040519080825280601f01601f191660200182016040528015612f53576020820181803683370190505b50606085015260005b60ff81168a1115612fc457868160ff1660208110612f7657fe5b1a60f81b85606001518260ff1681518110612f8d57fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a905350600101612f5c565b5083604001515167ffffffffffffffff81118015612fe157600080fd5b506040519080825280601f01601f19166020018201604052801561300c576020820181803683370190505b50602085015261301a6150ba565b60005b8560400151518160ff161015613120576000858260ff166020811061303e57fe5b1a90508281601f811061304d57fe5b6020020151156130a4576040805162461bcd60e51b815260206004820152601760248201527f6f6273657276657220696e646578207265706561746564000000000000000000604482015290519081900360640190fd5b6001838260ff16601f81106130b557fe5b91151560209283029190910152869060ff84169081106130d157fe5b1a60f81b87602001518360ff16815181106130e857fe5b60200101907effffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916908160001a9053505060010161301d565b503360009081526028602090815260408083208151808301909252805460ff8082168452929391929184019161010090910416600281111561315e57fe5b600281111561316957fe5b905250905060028160200151600281111561318057fe5b1480156131b45750602a816000015160ff168154811061319c57fe5b6000918252602090912001546001600160a01b031633145b613205576040805162461bcd60e51b815260206004820152601860248201527f756e617574686f72697a6564207472616e736d69747465720000000000000000604482015290519081900360640190fd5b5050835164ffffffffff90911660209091015250506040516000908a908a90808383808284376040519201829003909120945061324693506150ba92505050565b61324e6150d9565b60005b898110156134475760006001858760600151848151811061326e57fe5b60209101015160f81c601b018e8e8681811061328657fe5b905060200201358d8d8781811061329957fe5b9050602002013560405160008152602001604052604051808581526020018460ff1681526020018381526020018281526020019450505050506020604051602081039080840390855afa1580156132f4573d6000803e3d6000fd5b505060408051601f198101516001600160a01b03811660009081526028602090815290849020838501909452835460ff8082168552929650929450840191610100900416600281111561334357fe5b600281111561334e57fe5b905250925060018360200151600281111561336557fe5b146133b7576040805162461bcd60e51b815260206004820152601e60248201527f61646472657373206e6f7420617574686f72697a656420746f207369676e0000604482015290519081900360640190fd5b8251849060ff16601f81106133c857fe5b60200201511561341f576040805162461bcd60e51b815260206004820152601460248201527f6e6f6e2d756e69717565207369676e6174757265000000000000000000000000604482015290519081900360640190fd5b600184846000015160ff16601f811061343457fe5b9115156020909202015250600101613251565b5050505060005b6001826040015151038110156134f85760008260400151826001018151811061347357fe5b602002602001015160170b8360400151838151811061348e57fe5b602002602001015160170b13159050806134ef576040805162461bcd60e51b815260206004820152601760248201527f6f62736572766174696f6e73206e6f7420736f72746564000000000000000000604482015290519081900360640190fd5b5060010161344e565b5060408101518051600091906002810490811061351157fe5b602002602001015190508060170b7f000000000000000000000000000000000000000000000000000000000000000060170b1315801561357757507f000000000000000000000000000000000000000000000000000000000000000060170b8160170b13155b6135c8576040805162461bcd60e51b815260206004820152601e60248201527f6d656469616e206973206f7574206f66206d696e2d6d61782072616e67650000604482015290519081900360640190fd5b81516060908101805163ffffffff60019091018116909152604080518082018252601785810b80835267ffffffffffffffff42811660208086019182528a5189015188166000908152602c82528781209651875493519094167801000000000000000000000000000000000000000000000000029390950b77ffffffffffffffffffffffffffffffffffffffffffffffff9081167fffffffffffffffff0000000000000000000000000000000000000000000000009093169290921790911691909117909355875186015184890151848a01516080808c015188519586523386890181905291860181905260a0988601898152845199870199909952835194909916997ff6a97944f31ea060dfde0566e4167c1a1082551e64b60ecb14d599a9d023d451998c999298949793969095909492939185019260c086019289820192909102908190849084905b8381101561372b578181015183820152602001613713565b50505050905001838103825285818151815260200191508051906020019080838360005b8381101561376757818101518382015260200161374f565b50505050905090810190601f1680156137945780820380516001836020036101000a031916815260200191505b5097505050505050505060405180910390a281516060015160408051428152905160009263ffffffff16917f0109fc6f55cf40689f02fbaad7af7fe7bbac8a3d2186600afc7d3e10cac60271919081900360200190a381600001516060015163ffffffff168160170b7f0559884fd3a460db3073b7fc896cc77986f16e378210ded43186175bf646fc5f426040518082815260200191505060405180910390a36138498260000151606001518260170b614ab8565b5080518051602b8054602084015160408501516060909501517fffffffffffffffffffffffffffffffff0000000000000000000000000000000090921660809490941c939093177fffffffffffffffffffffff0000000000ffffffffffffffffffffffffffffffff1670010000000000000000000000000000000064ffffffffff90941693909302929092177fffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffffff16750100000000000000000000000000000000000000000060ff90941693909302929092177fffffffffffff00000000ffffffffffffffffffffffffffffffffffffffffffff1676010000000000000000000000000000000000000000000063ffffffff9283160217909155821061396a57fe5b613978828260200151614c28565b505050505050505050565b600354604080517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152905160009283926001600160a01b03909116916370a0823191602480820192602092909190829003018186803b1580156139ec57600080fd5b505afa158015613a00573d6000803e3d6000fd5b505050506040513d6020811015613a1657600080fd5b505190506000613a246148b6565b90910391505090565b6001600160a01b03811660009081526028602090815260408083208151808301909252805460ff808216845285948401916101009004166002811115613a6f57fe5b6002811115613a7a57fe5b9052509050600081602001516002811115613a9157fe5b1415613aa1576000915050610ce0565b60016005826000015160ff16601f8110613ab757fe5b601091828204019190066002029054906101000a900461ffff1603915050919050565b600080808080333214613b34576040805162461bcd60e51b815260206004820152601460248201527f4f6e6c792063616c6c61626c6520627920454f41000000000000000000000000604482015290519081900360640190fd5b5050602b5463ffffffff760100000000000000000000000000000000000000000000820481166000908152602c6020526040902054608083901b96700100000000000000000000000000000000909304600881901c909216955064ffffffffff9091169350601781900b92507801000000000000000000000000000000000000000000000000900467ffffffffffffffff1690565b6003546001600160a01b031690565b6000546001600160a01b03163314613c37576040805162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015290519081900360640190fd5b60408051808201909152602e546001600160a01b038082168084527401000000000000000000000000000000000000000090920463ffffffff1660208401528416141580613c9557508163ffffffff16816020015163ffffffff1614155b15613d68576040805180820182526001600160a01b0385811680835263ffffffff8681166020948501819052602e80547fffffffffffffffffffffffff00000000000000000000000000000000000000001684177fffffffffffffffff00000000ffffffffffffffffffffffffffffffffffffffff16740100000000000000000000000000000000000000008302179055865187860151875193168352948201528451919493909216927fb04e3a37abe9c0fcdfebdeae019a8e2b12ddf53f5d55ffb0caccc1bedaca1541928290030190a35b505050565b6001600160a01b03828116600090815260076020526040902054163314613ddb576040805162461bcd60e51b815260206004820152601d60248201527f6f6e6c792063757272656e742070617965652063616e20757064617465000000604482015290519081900360640190fd5b336001600160a01b0382161415613e39576040805162461bcd60e51b815260206004820152601760248201527f63616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015290519081900360640190fd5b6001600160a01b03808316600090815260086020526040902080548383167fffffffffffffffffffffffff000000000000000000000000000000000000000082168117909255909116908114613d68576040516001600160a01b038084169133918616907f84f7c7c80bb8ed2279b4aab5f61cd05e6374073d38f46d7f32de8c30e9e3836790600090a4505050565b6000546001600160a01b03163314613f27576040805162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015290519081900360640190fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000546001600160a01b03163314613fef576040805162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015290519081900360640190fd5b611db681614e75565b602b54760100000000000000000000000000000000000000000000900463ffffffff166000818152602c6020908152604091829020825180840190935254601781810b810b810b808552780100000000000000000000000000000000000000000000000090920467ffffffffffffffff1693909201839052929392900b9181908490565b6040805160a08101825260025463ffffffff80821683526401000000008204811660208401526801000000000000000082048116838501526c0100000000000000000000000082048116606084015270010000000000000000000000000000000090910416608082015260035482516103e081019384905291926001600160a01b0390911691600091600590601f908285855b82829054906101000a900461ffff1661ffff168152602001906002019060208260010104928301926001038202915080841161410f575050604080516103e0810191829052959650600095945060099350601f9250905082845b81548152602001906001019080831161416957505050505090506000602a8054806020026020016040519081016040528092919081815260200182805480156141db57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116141bd575b5050505050905060005b815181101561441057600060018483601f81106141fe57fe5b6020020151039050600060018684601f811061421657fe5b60200201510361ffff169050600082896060015163ffffffff168302633b9aca000201905060008111156144055760006007600087878151811061425657fe5b60200260200101516001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a90046001600160a01b03169050886001600160a01b031663a9059cbb82846040518363ffffffff1660e01b815260040180836001600160a01b0316815260200182815260200192505050602060405180830381600087803b1580156142eb57600080fd5b505af11580156142ff573d6000803e3d6000fd5b505050506040513d602081101561431557600080fd5b5051614368576040805162461bcd60e51b815260206004820152601260248201527f696e73756666696369656e742066756e64730000000000000000000000000000604482015290519081900360640190fd5b60018886601f811061437657fe5b61ffff909216602092909202015260018786601f811061439257fe5b602002018181525050886001600160a01b0316816001600160a01b03168787815181106143bb57fe5b60200260200101516001600160a01b03167fd0b1dac935d85bd54cf0a33b0d41d39f8cf53a968465fc7ea2377526b8ac712c856040518082815260200191505060405180910390a4505b5050506001016141e5565b5061441e600584601f6150f0565b50612787600983601f615186565b6001600160a01b03811660009081526028602090815260408083208151808301909252805460ff8082168452929391929184019161010090910416600281111561447257fe5b600281111561447d57fe5b9052509050600061448d83610b9a565b90508015613d68576001600160a01b0380841660009081526007602090815260408083205460035482517fa9059cbb000000000000000000000000000000000000000000000000000000008152918616600483018190526024830188905292519295169363a9059cbb9360448084019491939192918390030190829087803b15801561451857600080fd5b505af115801561452c573d6000803e3d6000fd5b505050506040513d602081101561454257600080fd5b5051614595576040805162461bcd60e51b815260206004820152601260248201527f696e73756666696369656e742066756e64730000000000000000000000000000604482015290519081900360640190fd5b60016005846000015160ff16601f81106145ab57fe5b601091828204019190066002026101000a81548161ffff021916908361ffff16021790555060016009846000015160ff16601f81106145e657fe5b01556003546040805184815290516001600160a01b039283169284811692908816917fd0b1dac935d85bd54cf0a33b0d41d39f8cf53a968465fc7ea2377526b8ac712c9181900360200190a450505050565b60008a8a8a8a8a8a8a8a8a8a604051602001808b6001600160a01b031681526020018a67ffffffffffffffff16815260200180602001806020018760ff1681526020018667ffffffffffffffff1681526020018060200184810384528c8c82818152602001925060200280828437600083820152601f01601f191690910185810384528a8152602090810191508b908b0280828437600083820152601f01601f191690910185810383528681526020019050868680828437600081840152601f19601f8201169050808301925050509d50505050505050505050505050506040516020818303038152906040528051906020012090509a9950505050505050505050565b6040805160a0808201835263ffffffff88811680845288821660208086018290528984168688018190528985166060808901829052958a166080988901819052600280547fffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000001687177fffffffffffffffffffffffffffffffffffffffffffffffff00000000ffffffff166401000000008702177fffffffffffffffffffffffffffffffffffffffff00000000ffffffffffffffff16680100000000000000008502177fffffffffffffffffffffffffffffffff00000000ffffffffffffffffffffffff166c010000000000000000000000008402177fffffffffffffffffffffffff00000000ffffffffffffffffffffffffffffffff16700100000000000000000000000000000000830217905589519586529285019390935283880152928201529283015291517fd0d9486a2c673e2a4b57fc82e4c8a556b3e2b82dd5db07e2c04a920ca0f469b6929181900390910190a15050505050565b604080516103e0810191829052600091829190600590601f908285855b82829054906101000a900461ffff1661ffff16815260200190600201906020826001010492830192600103820291508084116148d35790505050505050905060005b601f8110156149435760018282601f811061492c57fe5b60200201510361ffff169290920191600101614915565b506040805160a08101825260025463ffffffff8082168352640100000000820481166020808501919091526801000000000000000083048216848601526c0100000000000000000000000083048216606085018190527001000000000000000000000000000000009093049091166080840152602a805485518184028101840190965280865296909202633b9aca0002959293600093909291830182828015614a1557602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116149f7575b5050604080516103e0810191829052949550600094935060099250601f915082845b815481526020019060010190808311614a37575050505050905060005b8251811015614a7e5760018282601f8110614a6b57fe5b6020020151039590950194600101614a54565b505050505090565b600081831015614a97575081614a9a565b50805b92915050565b602083810286019082020160e4019695505050505050565b60408051808201909152602e546001600160a01b0381168083527401000000000000000000000000000000000000000090910463ffffffff166020830152614b0057506110c1565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff830163ffffffff8181166000818152602c60209081526040918290205486820151875184516024810196909652601792830b90920b604486018190528a8716606487015260848087018b90528551808803909101815260a490960190945291840180517fbeed9b51000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff9091161790529193614bd79391169190614f04565b6123ce576040805162461bcd60e51b815260206004820152601060248201527f696e73756666696369656e742067617300000000000000000000000000000000604482015290519081900360640190fd5b3360009081526028602090815260408083208151808301909252805460ff80821684529293919291840191610100909104166002811115614c6557fe5b6002811115614c7057fe5b9052506040805160a08101825260025463ffffffff80821683526401000000008204811660208401526801000000000000000082048116838501526c0100000000000000000000000082048116606084015270010000000000000000000000000000000090910416608082015281516103e081019283905292935091614d4091859190600590601f90826000855b82829054906101000a900461ffff1661ffff1681526020019060020190602082600101049283019260010382029150808411614cfe5790505050505050614f40565b614d4e90600590601f6150f0565b50600282602001516002811115614d6157fe5b14614db3576040805162461bcd60e51b815260206004820181905260248201527f73656e7420627920756e64657369676e61746564207472616e736d6974746572604482015290519081900360640190fd5b6000614dda633b9aca003a04836020015163ffffffff16846000015163ffffffff16614fb5565b90506010360260005a90506000614df98863ffffffff16858585614fdb565b6fffffffffffffffffffffffffffffffff1690506000620f4240866040015163ffffffff16830281614e2757fe5b049050856080015163ffffffff16633b9aca0002816009896000015160ff16601f8110614e5057fe5b015401016009886000015160ff16601f8110614e6857fe5b0155505050505050505050565b6004546001600160a01b0390811690821681146110c157600480547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b03848116918217909255604080519284168352602083019190915280517f793cb73064f3c8cde7e187ae515511e6e56d1ee89bf08b82fa60fb70f8d489129281900390910190a15050565b60005a6113888110614f385761138881039050846040820482031115614f38576000808451602086016000888af150600191505b509392505050565b614f486150ba565b60005b8351811015614fad576000848281518110614f6257fe5b016020015160f81c9050614f878482601f8110614f7b57fe5b60200201516001615067565b848260ff16601f8110614f9657fe5b61ffff909216602092909202015250600101614f4b565b509092915050565b60008383811015614fc857600285850304015b614fd28184614a86565b95945050505050565b600081851015615032576040805162461bcd60e51b815260206004820181905260248201527f6761734c6566742063616e6e6f742065786365656420696e697469616c476173604482015290519081900360640190fd5b818503830161179301633b9aca00858202026fffffffffffffffffffffffffffffffff811061505d57fe5b9695505050505050565b600061507f8261ffff168461ffff160161ffff614a86565b9392505050565b6040518060a001604052806150996151b4565b81526060602082018190526040820181905280820152600060809091015290565b604051806103e00160405280601f906020820280368337509192915050565b604080518082019091526000808252602082015290565b6002830191839082156151765791602002820160005b8382111561514657835183826101000a81548161ffff021916908361ffff1602179055509260200192600201602081600101049283019260010302615106565b80156151745782816101000a81549061ffff0219169055600201602081600101049283019260010302615146565b505b506151829291506151db565b5090565b82601f8101928215615176579160200282015b82811115615176578251825591602001919060010190615199565b60408051608081018252600080825260208201819052918101829052606081019190915290565b5b8082111561518257600081556001016151dc56fe6f7261636c6520616464726573736573206f7574206f6620726567697374726174696f6ea164736f6c6343000706000a", + expectedResult: seth.Pragma{}, + expectedError: errors.New(seth.MetadataNotFoundErr), + }, + { + name: "Valid Bytecode, No metadata [Ethereum Mainnet LinkToken]", + bytecode: "0x606060405236156100b75763ffffffff7c010000000000000000000000000000000000000000000000000000000060003504166306fdde0381146100bc578063095ea7b31461014757806318160ddd1461017d57806323b872dd146101a2578063313ce567146101de5780634000aea014610207578063661884631461028057806370a08231146102b657806395d89b41146102e7578063a9059cbb14610372578063d73dd623146103a8578063dd62ed3e146103de575b600080fd5b34156100c757600080fd5b6100cf610415565b60405160208082528190810183818151815260200191508051906020019080838360005b8381101561010c5780820151818401525b6020016100f3565b50505050905090810190601f1680156101395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561015257600080fd5b610169600160a060020a036004351660243561044c565b604051901515815260200160405180910390f35b341561018857600080fd5b610190610499565b60405190815260200160405180910390f35b34156101ad57600080fd5b610169600160a060020a03600435811690602435166044356104a9565b604051901515815260200160405180910390f35b34156101e957600080fd5b6101f16104f8565b60405160ff909116815260200160405180910390f35b341561021257600080fd5b61016960048035600160a060020a03169060248035919060649060443590810190830135806020601f820181900481020160405190810160405281815292919060208401838380828437509496506104fd95505050505050565b604051901515815260200160405180910390f35b341561028b57600080fd5b610169600160a060020a036004351660243561054c565b604051901515815260200160405180910390f35b34156102c157600080fd5b610190600160a060020a0360043516610648565b60405190815260200160405180910390f35b34156102f257600080fd5b6100cf610667565b60405160208082528190810183818151815260200191508051906020019080838360005b8381101561010c5780820151818401525b6020016100f3565b50505050905090810190601f1680156101395780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561037d57600080fd5b610169600160a060020a036004351660243561069e565b604051901515815260200160405180910390f35b34156103b357600080fd5b610169600160a060020a03600435166024356106eb565b604051901515815260200160405180910390f35b34156103e957600080fd5b610190600160a060020a0360043581169060243516610790565b60405190815260200160405180910390f35b60408051908101604052600f81527f436861696e4c696e6b20546f6b656e0000000000000000000000000000000000602082015281565b600082600160a060020a03811615801590610479575030600160a060020a031681600160a060020a031614155b151561048457600080fd5b61048e84846107bd565b91505b5b5092915050565b6b033b2e3c9fd0803ce800000081565b600082600160a060020a038116158015906104d6575030600160a060020a031681600160a060020a031614155b15156104e157600080fd5b6104ec85858561082a565b91505b5b509392505050565b601281565b600083600160a060020a0381161580159061052a575030600160a060020a031681600160a060020a031614155b151561053557600080fd5b6104ec85858561093c565b91505b5b509392505050565b600160a060020a033381166000908152600260209081526040808320938616835292905290812054808311156105a957600160a060020a0333811660009081526002602090815260408083209388168352929052908120556105e0565b6105b9818463ffffffff610a2316565b600160a060020a033381166000908152600260209081526040808320938916835292905220555b600160a060020a0333811660008181526002602090815260408083209489168084529490915290819020547f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925915190815260200160405180910390a3600191505b5092915050565b600160a060020a0381166000908152600160205260409020545b919050565b60408051908101604052600481527f4c494e4b00000000000000000000000000000000000000000000000000000000602082015281565b600082600160a060020a038116158015906106cb575030600160a060020a031681600160a060020a031614155b15156106d657600080fd5b61048e8484610a3a565b91505b5b5092915050565b600160a060020a033381166000908152600260209081526040808320938616835292905290812054610723908363ffffffff610afa16565b600160a060020a0333811660008181526002602090815260408083209489168084529490915290819020849055919290917f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591905190815260200160405180910390a35060015b92915050565b600160a060020a038083166000908152600260209081526040808320938516835292905220545b92915050565b600160a060020a03338116600081815260026020908152604080832094871680845294909152808220859055909291907f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259085905190815260200160405180910390a35060015b92915050565b600160a060020a03808416600081815260026020908152604080832033909516835293815283822054928252600190529182205461086e908463ffffffff610a2316565b600160a060020a0380871660009081526001602052604080822093909355908616815220546108a3908463ffffffff610afa16565b600160a060020a0385166000908152600160205260409020556108cc818463ffffffff610a2316565b600160a060020a03808716600081815260026020908152604080832033861684529091529081902093909355908616917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9086905190815260200160405180910390a3600191505b509392505050565b60006109488484610a3a565b5083600160a060020a031633600160a060020a03167fe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c16858560405182815260406020820181815290820183818151815260200191508051906020019080838360005b838110156109c35780820151818401525b6020016109aa565b50505050905090810190601f1680156109f05780820380516001836020036101000a031916815260200191505b50935050505060405180910390a3610a0784610b14565b15610a1757610a17848484610b23565b5b5060015b9392505050565b600082821115610a2f57fe5b508082035b92915050565b600160a060020a033316600090815260016020526040812054610a63908363ffffffff610a2316565b600160a060020a033381166000908152600160205260408082209390935590851681522054610a98908363ffffffff610afa16565b600160a060020a0380851660008181526001602052604090819020939093559133909116907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef9085905190815260200160405180910390a35060015b92915050565b600082820183811015610b0957fe5b8091505b5092915050565b6000813b908111905b50919050565b82600160a060020a03811663a4c0ed363385856040518463ffffffff167c01000000000000000000000000000000000000000000000000000000000281526004018084600160a060020a0316600160a060020a0316815260200183815260200180602001828103825283818151815260200191508051906020019080838360005b83811015610bbd5780820151818401525b602001610ba4565b50505050905090810190601f168015610bea5780820380516001836020036101000a031916815260200191505b50945050505050600060405180830381600087803b1515610c0a57600080fd5b6102c65a03f11515610c1b57600080fd5b5050505b505050505600a165627a7a72305820c5f438ff94e5ddaf2058efa0019e246c636c37a622e04bb67827c7374acad8d60029", + expectedResult: seth.Pragma{}, + expectedError: errors.New(seth.MetadataNotFoundErr), + }, + { + name: "Invalid bytecode [random string]", + bytecode: "hasd981y7971298e12hdasjkhdkjdkadhsakjdhsakj071982749812", + expectedResult: seth.Pragma{}, + expectedError: errors.New(seth.MetadataNotFoundErr), + }, + { + name: "Corrupted Bytecode [Automation Registar Wrapper 2_1] with solc marker changed", + bytecode: "0x60a06040523480156200001157600080fd5b5060405162002d8238038062002d8283398101604081905262000034916200043b565b33806000816200008b5760405162461bcd60e51b815260206004820152601860248201527f43616e6e6f7420736574206f776e657220746f207a65726f000000000000000060448201526064015b60405180910390fd5b600080546001600160a01b0319166001600160a01b0384811691909117909155811615620000be57620000be816200017a565b5050506001600160a01b038416608052620000da838362000225565b60005b81518110156200016f576200015a82828151811062000100576200010062000598565b60200260200101516000015183838151811062000121576200012162000598565b60200260200101516020015184848151811062000142576200014262000598565b6020026020010151604001516200029e60201b60201c565b806200016681620005ae565b915050620000dd565b50505050506200062f565b336001600160a01b03821603620001d45760405162461bcd60e51b815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c66000000000000000000604482015260640162000082565b600180546001600160a01b0319166001600160a01b0383811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6200022f6200034c565b6040805180820182526001600160a01b0384168082526001600160601b0384166020928301819052600160a01b810282176004558351918252918101919091527f39ce5d867555f0b0183e358fce5b158e7ca4fecd7c01cb7e0e19f1e23285838a910160405180910390a15050565b620002a86200034c565b60ff83166000908152600360205260409020805483919060ff19166001836002811115620002da57620002da620005d6565b021790555060ff831660009081526003602052604090819020805464ffffffff00191661010063ffffffff851602179055517f830a6d06a4e2caac67eba04323de22bdb04f032dd8b3d6a0c52b503d9a7036a3906200033f90859085908590620005ec565b60405180910390a1505050565b6000546001600160a01b03163314620003a85760405162461bcd60e51b815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e657200000000000000000000604482015260640162000082565b565b80516001600160a01b0381168114620003c257600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b604051606081016001600160401b0381118282101715620004025762000402620003c7565b60405290565b604051601f8201601f191681016001600160401b0381118282101715620004335762000433620003c7565b604052919050565b600080600080608085870312156200045257600080fd5b6200045d85620003aa565b935060206200046e818701620003aa565b604087810151919550906001600160601b03811681146200048e57600080fd5b606088810151919550906001600160401b0380821115620004ae57600080fd5b818a0191508a601f830112620004c357600080fd5b815181811115620004d857620004d8620003c7565b620004e8868260051b0162000408565b818152868101925090840283018601908c8211156200050657600080fd5b928601925b81841015620005875784848e031215620005255760008081fd5b6200052f620003dd565b845160ff81168114620005425760008081fd5b81528488015160038110620005575760008081fd5b818901528487015163ffffffff81168114620005735760008081fd5b81880152835292840192918601916200050b565b999c989b5096995050505050505050565b634e487b7160e01b600052603260045260246000fd5b600060018201620005cf57634e487b7160e01b600052601160045260246000fd5b5060010190565b634e487b7160e01b600052602160045260246000fd5b60ff8416815260608101600384106200061557634e487b7160e01b600052602160045260246000fd5b83602083015263ffffffff83166040830152949350505050565b6080516127146200066e60003960008181610177015281816105d601528181610887015281816109bd01528181610f0e015261171b01526127146000f3fe608060405234801561001057600080fd5b506004361061011b5760003560e01c8063856853e6116100b2578063b5ff5b4111610081578063c4d252f511610066578063c4d252f5146103e3578063e8d4070d146103f6578063f2fde38b1461040957600080fd5b8063b5ff5b4114610369578063c3f909d41461037c57600080fd5b8063856853e61461027857806388b12d551461028b5780638da5cb5b14610338578063a4c0ed361461035657600080fd5b80633f678e11116100ee5780633f678e11146101f35780636c4cdfc31461021457806379ba5097146102275780637e776f7f1461022f57600080fd5b8063181f5a77146101205780631b6b6d2314610172578063212d0884146101be578063367b9b4f146101de575b600080fd5b61015c6040518060400160405280601981526020017f4175746f6d6174696f6e52656769737472617220322e312e300000000000000081525081565b6040516101699190611a74565b60405180910390f35b6101997f000000000000000000000000000000000000000000000000000000000000000081565b60405173ffffffffffffffffffffffffffffffffffffffff9091168152602001610169565b6101d16101cc366004611aa4565b61041c565b6040516101699190611b29565b6101f16101ec366004611b9d565b6104a9565b005b610206610201366004611bd6565b61053b565b604051908152602001610169565b6101f1610222366004611c2e565b6106d3565b6101f161076d565b61026861023d366004611c63565b73ffffffffffffffffffffffffffffffffffffffff1660009081526005602052604090205460ff1690565b6040519015158152602001610169565b6101f1610286366004611de1565b61086f565b6102ff610299366004611f40565b60009081526002602090815260409182902082518084019093525473ffffffffffffffffffffffffffffffffffffffff8116808452740100000000000000000000000000000000000000009091046bffffffffffffffffffffffff169290910182905291565b6040805173ffffffffffffffffffffffffffffffffffffffff90931683526bffffffffffffffffffffffff909116602083015201610169565b60005473ffffffffffffffffffffffffffffffffffffffff16610199565b6101f1610364366004611f59565b6109a5565b6101f1610377366004611fb5565b610ce3565b60408051808201825260045473ffffffffffffffffffffffffffffffffffffffff8116808352740100000000000000000000000000000000000000009091046bffffffffffffffffffffffff16602092830181905283519182529181019190915201610169565b6101f16103f1366004611f40565b610dc2565b6101f1610404366004611ffe565b61104c565b6101f1610417366004611c63565b6112d9565b60408051606080820183526000808352602080840182905283850182905260ff86811683526003909152908490208451928301909452835492939192839116600281111561046c5761046c611abf565b600281111561047d5761047d611abf565b8152905463ffffffff610100820481166020840152650100000000009091041660409091015292915050565b6104b16112ed565b73ffffffffffffffffffffffffffffffffffffffff821660008181526005602090815260409182902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001685151590811790915591519182527f20c6237dac83526a849285a9f79d08a483291bdd3a056a0ef9ae94ecee1ad356910160405180910390a25050565b6004546000907401000000000000000000000000000000000000000090046bffffffffffffffffffffffff1661057961014084016101208501612109565b6bffffffffffffffffffffffff1610156105bf576040517fcd1c886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b73ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000166323b872dd333061060f61014087016101208801612109565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e086901b16815273ffffffffffffffffffffffffffffffffffffffff93841660048201529290911660248301526bffffffffffffffffffffffff1660448201526064016020604051808303816000875af1158015610696573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906106ba9190612124565b506106cd6106c783612141565b33611370565b92915050565b6106db6112ed565b60408051808201825273ffffffffffffffffffffffffffffffffffffffff84168082526bffffffffffffffffffffffff8416602092830181905274010000000000000000000000000000000000000000810282176004558351918252918101919091527f39ce5d867555f0b0183e358fce5b158e7ca4fecd7c01cb7e0e19f1e23285838a910160405180910390a15050565b60015473ffffffffffffffffffffffffffffffffffffffff1633146107f3576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4d7573742062652070726f706f736564206f776e65720000000000000000000060448201526064015b60405180910390fd5b60008054337fffffffffffffffffffffffff00000000000000000000000000000000000000008083168217845560018054909116905560405173ffffffffffffffffffffffffffffffffffffffff90921692909183917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e091a350565b3373ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000000000000000000000000000000000000000000016146108de576040517f018d10be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6109966040518061014001604052808e81526020018d8d8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525073ffffffffffffffffffffffffffffffffffffffff808d16602083015263ffffffff8c1660408301528a16606082015260ff8916608082015260a0810188905260c0810187905260e081018690526bffffffffffffffffffffffff85166101009091015282611370565b50505050505050505050505050565b3373ffffffffffffffffffffffffffffffffffffffff7f00000000000000000000000000000000000000000000000000000000000000001614610a14576040517f018d10be00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b81818080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050505060208101517fffffffff0000000000000000000000000000000000000000000000000000000081167f856853e60000000000000000000000000000000000000000000000000000000014610aca576040517fe3d6792100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8484846000610adc8260048186612276565b810190610ae991906122a0565b509950505050505050505050806bffffffffffffffffffffffff168414610b3c576040517f55e97b0d00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8988886000610b4e8260048186612276565b810190610b5b91906122a0565b9a50505050505050505050508073ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff1614610bcc576040517ff8c5638e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6004547401000000000000000000000000000000000000000090046bffffffffffffffffffffffff168d1015610c2e576040517fcd1c886700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60003073ffffffffffffffffffffffffffffffffffffffff168d8d604051610c579291906123dd565b600060405180830381855af49150503d8060008114610c92576040519150601f19603f3d011682016040523d82523d6000602084013e610c97565b606091505b5050905080610cd2576040517f649bf81000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b505050505050505050505050505050565b610ceb6112ed565b60ff8316600090815260036020526040902080548391907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166001836002811115610d3857610d38611abf565b021790555060ff83166000908152600360205260409081902080547fffffffffffffffffffffffffffffffffffffffffffffffffffffff00000000ff1661010063ffffffff851602179055517f830a6d06a4e2caac67eba04323de22bdb04f032dd8b3d6a0c52b503d9a7036a390610db5908590859085906123ed565b60405180910390a1505050565b60008181526002602090815260409182902082518084019093525473ffffffffffffffffffffffffffffffffffffffff8116808452740100000000000000000000000000000000000000009091046bffffffffffffffffffffffff1691830191909152331480610e49575060005473ffffffffffffffffffffffffffffffffffffffff1633145b610e7f576040517f61685c2b00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b805173ffffffffffffffffffffffffffffffffffffffff16610ecd576040517f4b13b31e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600082815260026020908152604080832083905583519184015190517fa9059cbb0000000000000000000000000000000000000000000000000000000081527f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff169263a9059cbb92610f859260040173ffffffffffffffffffffffffffffffffffffffff9290921682526bffffffffffffffffffffffff16602082015260400190565b6020604051808303816000875af1158015610fa4573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610fc89190612124565b90508061101c5781516040517fc2e4dce800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff90911660048201526024016107ea565b60405183907f3663fb28ebc87645eb972c9dad8521bf665c623f287e79f1c56f1eb374b82a2290600090a2505050565b6110546112ed565b60008181526002602090815260409182902082518084019093525473ffffffffffffffffffffffffffffffffffffffff8116808452740100000000000000000000000000000000000000009091046bffffffffffffffffffffffff16918301919091526110ed576040517f4b13b31e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008b8b8b8b8b8b8b8b8b60405160200161111099989796959493929190612461565b604051602081830303815290604052805190602001209050808314611161576040517f3f4d605300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60026000848152602001908152602001600020600080820160006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690556000820160146101000a8154906bffffffffffffffffffffffff021916905550506112c96040518061014001604052808f81526020016040518060200160405280600081525081526020018e73ffffffffffffffffffffffffffffffffffffffff1681526020018d63ffffffff1681526020018c73ffffffffffffffffffffffffffffffffffffffff1681526020018b60ff1681526020018a8a8080601f01602080910402602001604051908101604052809392919081815260200183838082843760009201919091525050509082525060208082018a905260408051601f8a0183900483028101830182528981529201919089908990819084018382808284376000920191909152505050908252506020858101516bffffffffffffffffffffffff1691015282611647565b5050505050505050505050505050565b6112e16112ed565b6112ea81611876565b50565b60005473ffffffffffffffffffffffffffffffffffffffff16331461136e576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601660248201527f4f6e6c792063616c6c61626c65206279206f776e65720000000000000000000060448201526064016107ea565b565b608082015160009073ffffffffffffffffffffffffffffffffffffffff166113c4576040517f05bb467c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008360400151846060015185608001518660a001518760c001518860e0015189610100015160405160200161140097969594939291906124e7565b604051602081830303815290604052805190602001209050836040015173ffffffffffffffffffffffffffffffffffffffff16817f7684390ebb103102f7f48c71439c2408713f8d437782a6fab2756acc0e42c1b786600001518760200151886060015189608001518a60a001518b60e001518c61010001518d60c001518e610120015160405161149999989796959493929190612569565b60405180910390a360a084015160ff9081166000908152600360205260408082208151606081019092528054929361151c9383911660028111156114df576114df611abf565b60028111156114f0576114f0611abf565b8152905463ffffffff61010082048116602084015265010000000000909104166040909101528561196b565b156115845760a085015160ff166000908152600360205260409020805465010000000000900463ffffffff1690600561155483612653565b91906101000a81548163ffffffff021916908363ffffffff1602179055505061157d8583611647565b905061163f565b61012085015160008381526002602052604081205490916115ca917401000000000000000000000000000000000000000090046bffffffffffffffffffffffff16612676565b604080518082018252608089015173ffffffffffffffffffffffffffffffffffffffff90811682526bffffffffffffffffffffffff9384166020808401918252600089815260029091529390932091519251909316740100000000000000000000000000000000000000000291909216179055505b949350505050565b600480546040808501516060860151608087015160a088015160c089015160e08a01516101008b015196517f28f32f3800000000000000000000000000000000000000000000000000000000815260009973ffffffffffffffffffffffffffffffffffffffff909916988a988a986328f32f38986116d29891979096919590949193909291016124e7565b6020604051808303816000875af11580156116f1573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061171591906126a2565b905060007f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff16634000aea0848861012001518560405160200161176f91815260200190565b6040516020818303038152906040526040518463ffffffff1660e01b815260040161179c939291906126bb565b6020604051808303816000875af11580156117bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906117df9190612124565b905080611830576040517fc2e4dce800000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201526024016107ea565b81857fb9a292fb7e3edd920cd2d2829a3615a640c43fd7de0a0820aa0668feb4c37d4b88600001516040516118659190611a74565b60405180910390a350949350505050565b3373ffffffffffffffffffffffffffffffffffffffff8216036118f5576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601760248201527f43616e6e6f74207472616e7366657220746f2073656c6600000000000000000060448201526064016107ea565b600180547fffffffffffffffffffffffff00000000000000000000000000000000000000001673ffffffffffffffffffffffffffffffffffffffff83811691821790925560008054604051929316917fed8889f560326eb138920d842192f0eb3dd22b4f139c87a2c57538e05bae12789190a350565b6000808351600281111561198157611981611abf565b0361198e575060006106cd565b6001835160028111156119a3576119a3611abf565b1480156119d6575073ffffffffffffffffffffffffffffffffffffffff821660009081526005602052604090205460ff16155b156119e3575060006106cd565b826020015163ffffffff16836040015163ffffffff161015611a07575060016106cd565b50600092915050565b6000815180845260005b81811015611a3657602081850181015186830182015201611a1a565b5060006020828601015260207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f83011685010191505092915050565b602081526000611a876020830184611a10565b9392505050565b803560ff81168114611a9f57600080fd5b919050565b600060208284031215611ab657600080fd5b611a8782611a8e565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b60038110611b25577f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b9052565b6000606082019050611b3c828451611aee565b602083015163ffffffff8082166020850152806040860151166040850152505092915050565b73ffffffffffffffffffffffffffffffffffffffff811681146112ea57600080fd5b8035611a9f81611b62565b80151581146112ea57600080fd5b60008060408385031215611bb057600080fd5b8235611bbb81611b62565b91506020830135611bcb81611b8f565b809150509250929050565b600060208284031215611be857600080fd5b813567ffffffffffffffff811115611bff57600080fd5b82016101408185031215611a8757600080fd5b80356bffffffffffffffffffffffff81168114611a9f57600080fd5b60008060408385031215611c4157600080fd5b8235611c4c81611b62565b9150611c5a60208401611c12565b90509250929050565b600060208284031215611c7557600080fd5b8135611a8781611b62565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b604051610140810167ffffffffffffffff81118282101715611cd357611cd3611c80565b60405290565b600082601f830112611cea57600080fd5b813567ffffffffffffffff80821115611d0557611d05611c80565b604051601f83017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0908116603f01168101908282118183101715611d4b57611d4b611c80565b81604052838152866020858801011115611d6457600080fd5b836020870160208301376000602085830101528094505050505092915050565b60008083601f840112611d9657600080fd5b50813567ffffffffffffffff811115611dae57600080fd5b602083019150836020828501011115611dc657600080fd5b9250929050565b803563ffffffff81168114611a9f57600080fd5b6000806000806000806000806000806000806101608d8f031215611e0457600080fd5b67ffffffffffffffff8d351115611e1a57600080fd5b611e278e8e358f01611cd9565b9b5067ffffffffffffffff60208e01351115611e4257600080fd5b611e528e60208f01358f01611d84565b909b509950611e6360408e01611b84565b9850611e7160608e01611dcd565b9750611e7f60808e01611b84565b9650611e8d60a08e01611a8e565b955067ffffffffffffffff60c08e01351115611ea857600080fd5b611eb88e60c08f01358f01611cd9565b945067ffffffffffffffff60e08e01351115611ed357600080fd5b611ee38e60e08f01358f01611cd9565b935067ffffffffffffffff6101008e01351115611eff57600080fd5b611f108e6101008f01358f01611cd9565b9250611f1f6101208e01611c12565b9150611f2e6101408e01611b84565b90509295989b509295989b509295989b565b600060208284031215611f5257600080fd5b5035919050565b60008060008060608587031215611f6f57600080fd5b8435611f7a81611b62565b935060208501359250604085013567ffffffffffffffff811115611f9d57600080fd5b611fa987828801611d84565b95989497509550505050565b600080600060608486031215611fca57600080fd5b611fd384611a8e565b9250602084013560038110611fe757600080fd5b9150611ff560408501611dcd565b90509250925092565b60008060008060008060008060008060006101208c8e03121561202057600080fd5b67ffffffffffffffff808d35111561203757600080fd5b6120448e8e358f01611cd9565b9b5061205260208e01611b84565b9a5061206060408e01611dcd565b995061206e60608e01611b84565b985061207c60808e01611a8e565b97508060a08e0135111561208f57600080fd5b61209f8e60a08f01358f01611d84565b909750955060c08d01358110156120b557600080fd5b6120c58e60c08f01358f01611cd9565b94508060e08e013511156120d857600080fd5b506120e98d60e08e01358e01611d84565b81945080935050506101008c013590509295989b509295989b9093969950565b60006020828403121561211b57600080fd5b611a8782611c12565b60006020828403121561213657600080fd5b8151611a8781611b8f565b6000610140823603121561215457600080fd5b61215c611caf565b823567ffffffffffffffff8082111561217457600080fd5b61218036838701611cd9565b8352602085013591508082111561219657600080fd5b6121a236838701611cd9565b60208401526121b360408601611b84565b60408401526121c460608601611dcd565b60608401526121d560808601611b84565b60808401526121e660a08601611a8e565b60a084015260c08501359150808211156121ff57600080fd5b61220b36838701611cd9565b60c084015260e085013591508082111561222457600080fd5b61223036838701611cd9565b60e08401526101009150818501358181111561224b57600080fd5b61225736828801611cd9565b8385015250505061012061226c818501611c12565b9082015292915050565b6000808585111561228657600080fd5b8386111561229357600080fd5b5050820193919092039150565b60008060008060008060008060008060006101608c8e0312156122c257600080fd5b67ffffffffffffffff808d3511156122d957600080fd5b6122e68e8e358f01611cd9565b9b508060208e013511156122f957600080fd5b6123098e60208f01358f01611cd9565b9a5061231760408e01611b84565b995061232560608e01611dcd565b985061233360808e01611b84565b975061234160a08e01611a8e565b96508060c08e0135111561235457600080fd5b6123648e60c08f01358f01611cd9565b95508060e08e0135111561237757600080fd5b6123878e60e08f01358f01611cd9565b9450806101008e0135111561239b57600080fd5b506123ad8d6101008e01358e01611cd9565b92506123bc6101208d01611c12565b91506123cb6101408d01611b84565b90509295989b509295989b9093969950565b8183823760009101908152919050565b60ff84168152606081016124046020830185611aee565b63ffffffff83166040830152949350505050565b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b600073ffffffffffffffffffffffffffffffffffffffff808c16835263ffffffff8b166020840152808a1660408401525060ff8816606083015260e060808301526124b060e083018789612418565b82810360a08401526124c28187611a10565b905082810360c08401526124d7818587612418565b9c9b505050505050505050505050565b600073ffffffffffffffffffffffffffffffffffffffff808a16835263ffffffff8916602084015280881660408401525060ff8616606083015260e0608083015261253560e0830186611a10565b82810360a08401526125478186611a10565b905082810360c084015261255b8185611a10565b9a9950505050505050505050565b600061012080835261257d8184018d611a10565b90508281036020840152612591818c611a10565b905063ffffffff8a16604084015273ffffffffffffffffffffffffffffffffffffffff8916606084015260ff8816608084015282810360a08401526125d68188611a10565b905082810360c08401526125ea8187611a10565b905082810360e08401526125fe8186611a10565b9150506bffffffffffffffffffffffff83166101008301529a9950505050505050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b600063ffffffff80831681810361266c5761266c612624565b6001019392505050565b6bffffffffffffffffffffffff81811683821601908082111561269b5761269b612624565b5092915050565b6000602082840312156126b457600080fd5b5051919050565b73ffffffffffffffffffffffffffffffffffffffff841681526bffffffffffffffffffffffff831660208201526060604082015260006126fe6060830184611a10565b9594505050505056fea164736e6c6343000810000a", + expectedResult: seth.Pragma{}, + expectedError: errors.New(seth.NotCompiledWithSolcErr), + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result, err := seth.DecodePragmaVersion(tt.bytecode) + + if tt.expectedError != nil { + require.Error(t, err) + require.Equal(t, tt.expectedError.Error(), err.Error()) + } else { + require.NoError(t, err) + } + require.Equal(t, tt.expectedResult, result) + }) + } +} + +func TestUtilDoesPragmaSupportCustomRevert(t *testing.T) { + tests := []struct { + name string + input seth.Pragma + expected bool + }{ + { + name: "Major version less than required", + input: seth.Pragma{Major: 0, Minor: 7, Patch: 3}, + expected: false, + }, + { + name: "Minor version less than required", + input: seth.Pragma{Major: 0, Minor: 7, Patch: 5}, + expected: false, + }, + { + name: "Patch version less than required", + input: seth.Pragma{Major: 0, Minor: 8, Patch: 3}, + expected: false, + }, + { + name: "Exact required version", + input: seth.Pragma{Major: 0, Minor: 8, Patch: 4}, + expected: true, + }, + { + name: "Higher minor version", + input: seth.Pragma{Major: 0, Minor: 9, Patch: 0}, + expected: true, + }, + { + name: "Higher patch version", + input: seth.Pragma{Major: 0, Minor: 8, Patch: 5}, + expected: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + result := seth.DoesPragmaSupportCustomRevert(tt.input) + require.Equal(t, tt.expected, result) + }) + } +} + +func TestUtilPendingNonce(t *testing.T) { + c := newClient(t) + c.Cfg.Network.PrivateKeys = append(c.Cfg.Network.PrivateKeys, c.Cfg.Network.PrivateKeys[0]) + c.Addresses = append(c.Addresses, c.Addresses[0]) + c.PrivateKeys = append(c.PrivateKeys, c.PrivateKeys[0]) + + type tc struct { + name string + keyNum int + timeout time.Duration + shouldFail bool + } + + tests := []tc{ + { + name: "processes all in time", + keyNum: 0, + timeout: 1 * time.Minute, + shouldFail: false, + }, + { + name: "times out", + keyNum: 1, + timeout: 1 * time.Second, + shouldFail: true, + }, + } + + for _, testCase := range tests { + started := make(chan struct{}) + nonce, err := c.Client.NonceAt(context.Background(), c.Addresses[testCase.keyNum], nil) + require.NoError(t, err, "Error getting nonce") + + nonceMutex := sync.Mutex{} + + var getNonceAndIncrement = func() uint64 { + nonceMutex.Lock() + defer nonceMutex.Unlock() + currentNonce := nonce + nonce++ + return currentNonce + } + + nonceCh := make(chan uint64, 100) + + go func() { + for nonce := range nonceCh { + _ = nonce + } + }() + + go func() { + for i := 1; i <= 5000; i++ { + go func(index int) { + seth.L.Debug().Msgf("Starting tx %d", index) + + opts := c.NewTXOpts() + nonceToUse := int64(getNonceAndIncrement()) + opts.Nonce = big.NewInt(nonceToUse) + defer seth.L.Debug().Msgf("Finished tx %d with nonce %d", index, nonceToUse) + + _, _, _, err := network_sub_contract.DeployNetworkDebugSubContract(opts, c.Client) + require.NoError(t, err, "Error adding counter") + + if index == 50 { + started <- struct{}{} + } + nonceCh <- uint64(nonceToUse) + }(i) + } + }() + + <-started + lastNonce, err := c.Client.NonceAt(context.Background(), c.Addresses[testCase.keyNum], nil) + require.NoError(t, err, "Error getting last nonce") + + pendingNonce, err := c.Client.PendingNonceAt(context.Background(), c.Addresses[testCase.keyNum]) + require.NoError(t, err, "Error getting pending nonce") + + require.Greater(t, int64(pendingNonce), int64(lastNonce), "Pending nonce should be greater than last nonce") + + if testCase.keyNum == 0 { + err = c.WaitUntilNoPendingTxForRootKey(testCase.timeout) + } else { + err = c.WaitUntilNoPendingTxForKeyNum(testCase.keyNum, testCase.timeout) + } + if testCase.shouldFail { + require.Error(t, err, "No error, when waiting for pending tx to be processed") + } else { + require.NoError(t, err, "Error waiting for pending tx") + } + } +} + +func TestUtilPendingNonce_InvalidKey(t *testing.T) { + c := newClient(t) + + invalidKeys := []int{-1, 100} + for _, key := range invalidKeys { + err := c.WaitUntilNoPendingTxForKeyNum(key, 1*time.Second) + require.Error(t, err, "No error, when passing invalid key num") + } +} diff --git a/sonar-project.properties b/sonar-project.properties index 2e14e6884..cda9a3aab 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -3,7 +3,7 @@ sonar.sources=. sonar.python.version=3.8 # Full exclusions from the static analysis -sonar.exclusions=**/docs/**/*, **/*.pb.go, **/*report.xml **/*.txt, **/*.abi, **/*.bin, **/*_codecgen.go +sonar.exclusions=**/docs/**/*, **/*.pb.go, **/*report.xml **/*.txt, **/*.abi, **/*.bin, **/*_codecgen.go, seth/contracts/**, seth/examples*/** # Coverage exclusions sonar.coverage.exclusions=**/*, **/*.* # Duplication exclusions From 5e55de97efe51a4da3035e56af7997f14732b8e9 Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Mon, 26 Aug 2024 15:37:53 +0200 Subject: [PATCH 3/8] remove last remaining imports of archived Seth (#1089) --- .github/workflows/seth-lint.yml | 1 + .github/workflows/seth-test-api.yml | 1 + .github/workflows/seth-test-bumping.yml | 1 + .github/workflows/seth-test-cli.yml | 1 + .../workflows/seth-test-decode-testnet.yml | 1 + .github/workflows/seth-test-decode.yml | 1 + .github/workflows/seth-test-others.yml | 1 + .github/workflows/seth-test-trace.yml | 1 + config/testconfig.go | 2 +- config/types.go | 2 +- go.mod | 18 +++++----- go.sum | 36 +++++++++---------- utils/seth/seth.go | 2 +- 13 files changed, 38 insertions(+), 30 deletions(-) diff --git a/.github/workflows/seth-lint.yml b/.github/workflows/seth-lint.yml index 158432384..ac68ad8f8 100644 --- a/.github/workflows/seth-lint.yml +++ b/.github/workflows/seth-lint.yml @@ -25,5 +25,6 @@ jobs: with: nix_path: nixpkgs=channel:nixos-unstable - name: Run tests + if: steps.changes.outputs.src == 'true' run: | nix develop -c make lint diff --git a/.github/workflows/seth-test-api.yml b/.github/workflows/seth-test-api.yml index 4a3fce74e..78c1a822f 100644 --- a/.github/workflows/seth-test-api.yml +++ b/.github/workflows/seth-test-api.yml @@ -30,6 +30,7 @@ jobs: with: nix_path: nixpkgs=channel:nixos-unstable - name: Run tests + if: steps.changes.outputs.src == 'true' env: SETH_NETWORK: ${{ matrix.network }} run: | diff --git a/.github/workflows/seth-test-bumping.yml b/.github/workflows/seth-test-bumping.yml index 6d01bc35e..5d2894975 100644 --- a/.github/workflows/seth-test-bumping.yml +++ b/.github/workflows/seth-test-bumping.yml @@ -30,6 +30,7 @@ jobs: with: nix_path: nixpkgs=channel:nixos-unstable - name: Run tests + if: steps.changes.outputs.src == 'true' env: SETH_NETWORK: ${{ matrix.network }} run: | diff --git a/.github/workflows/seth-test-cli.yml b/.github/workflows/seth-test-cli.yml index 9ab8094b5..68f3f4f89 100644 --- a/.github/workflows/seth-test-cli.yml +++ b/.github/workflows/seth-test-cli.yml @@ -30,6 +30,7 @@ jobs: with: nix_path: nixpkgs=channel:nixos-unstable - name: Run tests + if: steps.changes.outputs.src == 'true' env: SETH_NETWORK: ${{ matrix.network }} run: | diff --git a/.github/workflows/seth-test-decode-testnet.yml b/.github/workflows/seth-test-decode-testnet.yml index 13981695d..de81c3d17 100644 --- a/.github/workflows/seth-test-decode-testnet.yml +++ b/.github/workflows/seth-test-decode-testnet.yml @@ -32,6 +32,7 @@ jobs: with: nix_path: nixpkgs=channel:nixos-unstable - name: Run tests + if: steps.changes.outputs.src == 'true' env: SETH_NETWORK: ${{ matrix.network }} SETH_ROOT_PRIVATE_KEY: ${{ secrets.TESTNET_COMMON_KEYS }} diff --git a/.github/workflows/seth-test-decode.yml b/.github/workflows/seth-test-decode.yml index dfc78c514..51a726797 100644 --- a/.github/workflows/seth-test-decode.yml +++ b/.github/workflows/seth-test-decode.yml @@ -30,6 +30,7 @@ jobs: with: nix_path: nixpkgs=channel:nixos-unstable - name: Run tests + if: steps.changes.outputs.src == 'true' env: SETH_NETWORK: ${{ matrix.network }} run: | diff --git a/.github/workflows/seth-test-others.yml b/.github/workflows/seth-test-others.yml index e6df81892..a94b159ba 100644 --- a/.github/workflows/seth-test-others.yml +++ b/.github/workflows/seth-test-others.yml @@ -30,6 +30,7 @@ jobs: with: nix_path: nixpkgs=channel:nixos-unstable - name: Run tests + if: steps.changes.outputs.src == 'true' env: SETH_NETWORK: ${{ matrix.network }} run: | diff --git a/.github/workflows/seth-test-trace.yml b/.github/workflows/seth-test-trace.yml index 89e3a4b99..f7b7840c2 100644 --- a/.github/workflows/seth-test-trace.yml +++ b/.github/workflows/seth-test-trace.yml @@ -30,6 +30,7 @@ jobs: with: nix_path: nixpkgs=channel:nixos-unstable - name: Run tests + if: steps.changes.outputs.src == 'true' env: SETH_NETWORK: ${{ matrix.network }} run: | diff --git a/config/testconfig.go b/config/testconfig.go index 40f87e638..8a0f33b8c 100644 --- a/config/testconfig.go +++ b/config/testconfig.go @@ -7,9 +7,9 @@ import ( "github.com/joho/godotenv" "github.com/pkg/errors" - "github.com/smartcontractkit/seth" "github.com/smartcontractkit/chainlink-testing-framework/logging" + "github.com/smartcontractkit/chainlink-testing-framework/seth" ) func (c *TestConfig) GetLoggingConfig() *LoggingConfig { diff --git a/config/types.go b/config/types.go index 8717046fa..975376c28 100644 --- a/config/types.go +++ b/config/types.go @@ -1,6 +1,6 @@ package config -import "github.com/smartcontractkit/seth" +import "github.com/smartcontractkit/chainlink-testing-framework/seth" type SethConfig interface { GetSethConfig() *seth.Config diff --git a/go.mod b/go.mod index e4e9d3e33..0e8136cac 100644 --- a/go.mod +++ b/go.mod @@ -30,7 +30,6 @@ require ( github.com/prometheus/common v0.45.0 github.com/rs/zerolog v1.30.0 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/seth v1.1.1 github.com/smartcontractkit/wasp v0.4.1 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 @@ -38,10 +37,10 @@ require ( github.com/testcontainers/testcontainers-go v0.28.0 go.uber.org/atomic v1.11.0 go.uber.org/zap v1.26.0 - golang.org/x/net v0.18.0 + golang.org/x/net v0.25.0 golang.org/x/oauth2 v0.13.0 - golang.org/x/sync v0.5.0 - golang.org/x/text v0.14.0 + golang.org/x/sync v0.7.0 + golang.org/x/text v0.16.0 k8s.io/api v0.28.2 k8s.io/apimachinery v0.28.2 k8s.io/cli-runtime v0.28.2 @@ -284,13 +283,13 @@ require ( go.uber.org/ratelimit v0.3.0 // indirect go4.org/netipx v0.0.0-20230125063823-8449b0a6169f // indirect golang.org/x/arch v0.4.0 // indirect - golang.org/x/crypto v0.17.0 // indirect + golang.org/x/crypto v0.25.0 // indirect golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect golang.org/x/mod v0.19.0 // indirect - golang.org/x/sys v0.16.0 // indirect - golang.org/x/term v0.15.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.15.0 // indirect + golang.org/x/sys v0.22.0 // indirect + golang.org/x/term v0.22.0 // indirect + golang.org/x/time v0.6.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect google.golang.org/appengine v1.6.7 // indirect google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect @@ -319,6 +318,7 @@ require ( require ( github.com/aws/aws-sdk-go-v2/config v1.27.28 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.32.5 + github.com/smartcontractkit/chainlink-testing-framework/seth v1.2.0 ) require ( diff --git a/go.sum b/go.sum index 5774638a8..f54989b4c 100644 --- a/go.sum +++ b/go.sum @@ -1047,8 +1047,8 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ= github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= -github.com/smartcontractkit/seth v1.1.1 h1:6hvexjJD7ek8ht/CLlEwQcH21K2E/WEYwbSRdKInZmM= -github.com/smartcontractkit/seth v1.1.1/go.mod h1:cDfKHi/hJLpO9sRpVbrflrHCOV+MJPAMJHloExJnIXk= +github.com/smartcontractkit/chainlink-testing-framework/seth v1.2.0 h1:wEc6EKMOOK/9w6z/zv2/wPZsV/txctbYoVJ1sCxhXwI= +github.com/smartcontractkit/chainlink-testing-framework/seth v1.2.0/go.mod h1:upYGPS9lOBW2pJg6XD8TTNSD1GtRfayU2Ct5bcfvqFw= github.com/smartcontractkit/wasp v0.4.1 h1:qgIx2s+eCwH0OaBKaHEAHUQ1Z47bAgDu+ICS9IOqvGQ= github.com/smartcontractkit/wasp v0.4.1/go.mod h1:3qiofyI3pkbrc48a3CVshbMfgl74SiuPL/tm30d9Wb4= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= @@ -1232,8 +1232,8 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= -golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -1323,8 +1323,8 @@ golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg= -golang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -1346,8 +1346,8 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= -golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1431,15 +1431,15 @@ golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= -golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= -golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= -golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= +golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= +golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1451,14 +1451,14 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= +golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1512,8 +1512,8 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8= -golang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/utils/seth/seth.go b/utils/seth/seth.go index 8ec982d4f..04a3deb00 100644 --- a/utils/seth/seth.go +++ b/utils/seth/seth.go @@ -7,11 +7,11 @@ import ( "github.com/pkg/errors" "github.com/rs/zerolog" - pkg_seth "github.com/smartcontractkit/seth" "github.com/smartcontractkit/chainlink-testing-framework/blockchain" "github.com/smartcontractkit/chainlink-testing-framework/config" "github.com/smartcontractkit/chainlink-testing-framework/k8s/environment" + pkg_seth "github.com/smartcontractkit/chainlink-testing-framework/seth" ) var ErrInsufficientEphemeralKeys = ` From e6e15fd22c35c43d477374616ecd3c5cfceefed8 Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Tue, 27 Aug 2024 09:43:06 +0200 Subject: [PATCH 4/8] fix Seth badges in Readme (#1094) --- seth/README.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/seth/README.md b/seth/README.md index 91a80e42e..cbde8cdf6 100644 --- a/seth/README.md +++ b/seth/README.md @@ -2,12 +2,13 @@ Reliable and debug-friendly Ethereum client -[![Decoding tests](https://github.com/smartcontractkit/chainlink-testing-framework/seth/actions/workflows/seth-test-decode.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_decode.yml) -[![Tracing tests](https://github.com/smartcontractkit/chainlink-testing-framework/seth/actions/workflows/seth-test-trace.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_trace.yml) -[![Gas bumping tests](https://github.com/smartcontractkit/chainlink-testing-framework/seth/actions/workflows/seth-test-bumping.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_cli.yml) -[![API tests](https://github.com/smartcontractkit/chainlink-testing-framework/seth/actions/workflows/seth-test-api.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_api.yml) -[![CLI tests](https://github.com/smartcontractkit/chainlink-testing-framework/seth/actions/workflows/seth-test-cli.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_cli.yml) -[![Integration tests (testnets)](https://github.com/smartcontractkit/chainlink-testing-framework/seth/actions/workflows/seth-test-decode-testnet.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_decode_testnet.yml) +[![Go Report Card](https://goreportcard.com/badge/github.com/smartcontractkit/chainlink-testing-framework/seth)](https://goreportcard.com/report/github.com/smartcontractkit/chainlink-testing-framework/seth) +[![Decoding tests](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/seth-test-decode.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_decode.yml) +[![Tracing tests](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/seth-test-trace.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_trace.yml) +[![Gas bumping tests](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/seth-test-bumping.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_cli.yml) +[![API tests](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/seth-test-api.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_api.yml) +[![CLI tests](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/seth-test-cli.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_cli.yml) +[![Integration tests (testnets)](https://github.com/smartcontractkit/chainlink-testing-framework/actions/workflows/seth-test-decode-testnet.yml/badge.svg)](https://github.com/smartcontractkit/seth/actions/workflows/test_decode_testnet.yml)
# Content From bc2952bca532c58e10cf132e981b38637602bd23 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Tue, 27 Aug 2024 13:09:53 +0200 Subject: [PATCH 5/8] go mod tidy (#1095) --- go.mod | 36 +++++++++++++++--------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/go.mod b/go.mod index 0e8136cac..797f0b616 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,8 @@ require ( dario.cat/mergo v1.0.0 github.com/Masterminds/semver/v3 v3.2.1 github.com/avast/retry-go v3.0.0+incompatible + github.com/aws/aws-sdk-go-v2/config v1.27.28 + github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.32.5 github.com/aws/constructs-go/constructs/v10 v10.1.255 github.com/aws/jsii-runtime-go v1.75.0 github.com/barkimedes/go-deepcopy v0.0.0-20220514131651-17c30cfc62df @@ -30,6 +32,7 @@ require ( github.com/prometheus/common v0.45.0 github.com/rs/zerolog v1.30.0 github.com/slack-go/slack v0.12.2 + github.com/smartcontractkit/chainlink-testing-framework/seth v1.2.0 github.com/smartcontractkit/wasp v0.4.1 github.com/spf13/cobra v1.7.0 github.com/spf13/pflag v1.0.5 @@ -69,6 +72,18 @@ require ( github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect github.com/awalterschulze/gographviz v2.0.3+incompatible // indirect github.com/aws/aws-sdk-go v1.45.25 // indirect + github.com/aws/aws-sdk-go-v2 v1.30.4 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.17.28 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.30.4 // indirect + github.com/aws/smithy-go v1.20.4 // indirect github.com/benbjohnson/clock v1.3.5 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/bits-and-blooms/bitset v1.10.0 // indirect @@ -315,27 +330,6 @@ require ( sigs.k8s.io/yaml v1.3.0 // indirect ) -require ( - github.com/aws/aws-sdk-go-v2/config v1.27.28 - github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.32.5 - github.com/smartcontractkit/chainlink-testing-framework/seth v1.2.0 -) - -require ( - github.com/aws/aws-sdk-go-v2 v1.30.4 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.28 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.30.4 // indirect - github.com/aws/smithy-go v1.20.4 // indirect -) - // avoids ambigious imports of indirect dependencies exclude github.com/hashicorp/consul v1.2.1 From bd8c580392d62c83eb4775db05714de71b666669 Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Tue, 27 Aug 2024 13:29:45 +0200 Subject: [PATCH 6/8] seth: bump external seth import; rm replace (#1096) --- seth/go.mod | 4 +--- seth/go.sum | 8 ++++---- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/seth/go.mod b/seth/go.mod index e1b626776..a0d5a1491 100644 --- a/seth/go.mod +++ b/seth/go.mod @@ -12,7 +12,7 @@ require ( github.com/pelletier/go-toml/v2 v2.2.2 github.com/pkg/errors v0.9.1 github.com/rs/zerolog v1.30.0 - github.com/smartcontractkit/seth v1.2.0 + github.com/smartcontractkit/seth v1.2.1-0.20240827060008-860f949fcb26 github.com/stretchr/testify v1.9.0 github.com/urfave/cli/v2 v2.25.7 go.uber.org/ratelimit v0.3.0 @@ -56,5 +56,3 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect rsc.io/tmplfunc v0.0.3 // indirect ) - -replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 diff --git a/seth/go.sum b/seth/go.sum index 7ae4a8c18..fd243d6c0 100644 --- a/seth/go.sum +++ b/seth/go.sum @@ -76,6 +76,8 @@ github.com/go-ole/go-ole v1.2.5/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiU github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= @@ -146,8 +148,6 @@ github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lne github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= -github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= -github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= @@ -161,8 +161,8 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible h1:Bn1aCHHRnjv4Bl16T8rcaFjYSrGrIZvpiGO6P3Q4GpU= github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= -github.com/smartcontractkit/seth v1.2.0 h1:2BzO5siO7Ehuqsig/SB4szx+/2hZpOjZNFU9vFPmSM8= -github.com/smartcontractkit/seth v1.2.0/go.mod h1:+h2QvWqOQFBacL+w0KBMyJmVYbmXXTBkPbnR45JCfYQ= +github.com/smartcontractkit/seth v1.2.1-0.20240827060008-860f949fcb26 h1:9c3dbd38M8T/Hkc9LKOSRf3wxfXAFNJcLnEPOBOctNk= +github.com/smartcontractkit/seth v1.2.1-0.20240827060008-860f949fcb26/go.mod h1:KrhtzffZ+Pp5vyTkYErnnwjfjrGl8F9y3pxrbuZlVr0= github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= From bf3c491b583d853ff17496ab5a25f1b4e4cbd3fc Mon Sep 17 00:00:00 2001 From: Jordan Krage Date: Tue, 27 Aug 2024 13:58:10 +0200 Subject: [PATCH 7/8] bump wasp & seth; rm replace & exclude (#1097) --- go.mod | 29 ++++++++--------------- go.sum | 73 +++++++++++++++++++++++++++++++++------------------------- 2 files changed, 52 insertions(+), 50 deletions(-) diff --git a/go.mod b/go.mod index 797f0b616..340360a86 100644 --- a/go.mod +++ b/go.mod @@ -16,7 +16,7 @@ require ( github.com/docker/docker v25.0.2+incompatible github.com/docker/go-connections v0.5.0 github.com/ethereum/go-ethereum v1.13.8 - github.com/go-resty/resty/v2 v2.7.0 + github.com/go-resty/resty/v2 v2.11.0 github.com/google/go-github/v41 v41.0.0 github.com/google/uuid v1.6.0 github.com/imdario/mergo v0.3.16 @@ -32,16 +32,16 @@ require ( github.com/prometheus/common v0.45.0 github.com/rs/zerolog v1.30.0 github.com/slack-go/slack v0.12.2 - github.com/smartcontractkit/chainlink-testing-framework/seth v1.2.0 - github.com/smartcontractkit/wasp v0.4.1 - github.com/spf13/cobra v1.7.0 + github.com/smartcontractkit/chainlink-testing-framework/seth v1.2.1-0.20240827112945-bd8c580392d6 + github.com/smartcontractkit/wasp v0.4.8-0.20240826201742-8c8d2e49efb0 + github.com/spf13/cobra v1.8.0 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.9.0 github.com/testcontainers/testcontainers-go v0.28.0 go.uber.org/atomic v1.11.0 go.uber.org/zap v1.26.0 golang.org/x/net v0.25.0 - golang.org/x/oauth2 v0.13.0 + golang.org/x/oauth2 v0.15.0 golang.org/x/sync v0.7.0 golang.org/x/text v0.16.0 k8s.io/api v0.28.2 @@ -58,8 +58,6 @@ require ( github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect github.com/DataDog/zstd v1.5.2 // indirect - github.com/K-Phoen/grabana v0.21.17 // indirect - github.com/K-Phoen/sdk v0.12.2 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/sprig/v3 v3.2.3 // indirect @@ -160,7 +158,7 @@ require ( github.com/goccy/go-json v0.10.2 // indirect github.com/gofrs/flock v0.8.1 // indirect github.com/gogo/googleapis v1.4.1 // indirect - github.com/gogo/protobuf v1.3.3 // indirect + github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/status v1.1.1 // indirect github.com/golang-jwt/jwt/v4 v4.5.0 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -174,10 +172,9 @@ require ( github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/gorilla/websocket v1.5.0 // indirect - github.com/gosimple/slug v1.13.1 // indirect - github.com/gosimple/unidecode v1.0.1 // indirect github.com/grafana/dskit v0.0.0-20231120170505-765e343eda4f // indirect github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586 // indirect + github.com/grafana/grafana-foundation-sdk/go v0.0.0-20240326122733-6f96a993222b // indirect github.com/grafana/loki v1.6.2-0.20231215164305-b51b7d7b5503 // indirect github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608 // indirect github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd // indirect @@ -203,7 +200,7 @@ require ( github.com/jpillora/backoff v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/julienschmidt/httprouter v1.3.0 // indirect - github.com/klauspost/compress v1.17.1 // indirect + github.com/klauspost/compress v1.17.8 // indirect github.com/klauspost/cpuid/v2 v2.2.5 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect @@ -255,7 +252,6 @@ require ( github.com/prometheus/exporter-toolkit v0.10.1-0.20230714054209-2f4150c63f97 // indirect github.com/prometheus/procfs v0.12.0 // indirect github.com/prometheus/prometheus v0.47.2-0.20231010075449-4b9c19fe5510 // indirect - github.com/pyroscope-io/client v0.7.1 // indirect github.com/rivo/uniseg v0.4.4 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect github.com/rogpeppe/go-internal v1.11.0 // indirect @@ -268,6 +264,7 @@ require ( github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/shopspring/decimal v1.2.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect + github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449 // indirect github.com/soheilhy/cmux v0.1.5 // indirect github.com/sony/gobreaker v0.5.0 // indirect github.com/spf13/cast v1.3.1 // indirect @@ -306,7 +303,7 @@ require ( golang.org/x/time v0.6.0 // indirect golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/appengine v1.6.7 // indirect + google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20231002182017-d307bd883b97 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20231012201019-e917dd12ba7a // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20231009173412-8bfb1ae86b6c // indirect @@ -329,9 +326,3 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.3.0 // indirect sigs.k8s.io/yaml v1.3.0 // indirect ) - -// avoids ambigious imports of indirect dependencies -exclude github.com/hashicorp/consul v1.2.1 - -// replicating the replace directive on cosmos SDK -replace github.com/gogo/protobuf => github.com/regen-network/protobuf v1.3.3-alpha.regen.1 diff --git a/go.sum b/go.sum index f54989b4c..405e755d5 100644 --- a/go.sum +++ b/go.sum @@ -74,10 +74,6 @@ github.com/DataDog/zstd v1.5.2/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwS github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM= github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo= github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY= -github.com/K-Phoen/grabana v0.21.17 h1:mO/9DvJWC/qpTF/X5jQDm5eKgCBaCGypP/tEfXAvKfg= -github.com/K-Phoen/grabana v0.21.17/go.mod h1:vbASQt9UiQhX4lC3/opLpJMJ8m+hsTUU2FwkQMytHK4= -github.com/K-Phoen/sdk v0.12.2 h1:0QofDlKE+lloyBOzhjEEMW21061zts/WIpfpQ5NLLAs= -github.com/K-Phoen/sdk v0.12.2/go.mod h1:qmM0wO23CtoDux528MXPpYvS4XkRWkWX6rvX9Za8EVU= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -258,8 +254,8 @@ github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoY github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3 h1:qMCsGGgs+MAzDFyp9LpAe1Lqy/fY/qCovCm0qnXZOBM= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= @@ -441,8 +437,8 @@ github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU= github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI= github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo= -github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY= -github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I= +github.com/go-resty/resty/v2 v2.11.0 h1:i7jMfNOJYMp69lq7qozJP+bjgzfAzeOhuGlyDrqxT/8= +github.com/go-resty/resty/v2 v2.11.0/go.mod h1:iiP/OpA0CkcL3IGt1O0+/SIItFUbkkyw5BGXiVdTu+A= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= @@ -491,6 +487,10 @@ github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= github.com/gogo/googleapis v1.4.1 h1:1Yx4Myt7BxzvUr5ldGSbwYiZG6t9wGBZ+8/fX3Wvtq0= github.com/gogo/googleapis v1.4.1/go.mod h1:2lpHqI5OcWCtVElxXnPt+s8oJvMpySlOyM6xDCrzib4= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM= github.com/gogo/status v1.1.1 h1:DuHXlSFHNKqTQ+/ACf5Vs6r4X/dH2EgIzR9Vr+H65kg= github.com/gogo/status v1.1.1/go.mod h1:jpG3dM5QPcqu19Hg8lkUhBFBa3TcLs1DG7+2Jqci7oU= @@ -593,18 +593,20 @@ github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gosimple/slug v1.13.1 h1:bQ+kpX9Qa6tHRaK+fZR0A0M2Kd7Pa5eHPPsb1JpHD+Q= -github.com/gosimple/slug v1.13.1/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ= -github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o= -github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc= github.com/grafana/dskit v0.0.0-20231120170505-765e343eda4f h1:gyojr97YeWZ70pKNakWv5/tKwBHuLy3icnIeCo9gQr4= github.com/grafana/dskit v0.0.0-20231120170505-765e343eda4f/go.mod h1:8dsy5tQOkeNQyjXpm5mQsbCu3H5uzeBD35MzRQFznKU= github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586 h1:/of8Z8taCPftShATouOrBVy6GaTTjgQd/VfNiZp/VXQ= github.com/grafana/gomemcache v0.0.0-20231023152154-6947259a0586/go.mod h1:PGk3RjYHpxMM8HFPhKKo+vve3DdlPUELZLSDEFehPuU= +github.com/grafana/grafana-foundation-sdk/go v0.0.0-20240326122733-6f96a993222b h1:Msqs1nc2qWMxTriDCITKl58Td+7Md/RURmUmH7RXKns= +github.com/grafana/grafana-foundation-sdk/go v0.0.0-20240326122733-6f96a993222b/go.mod h1:WtWosval1KCZP9BGa42b8aVoJmVXSg0EvQXi9LDSVZQ= github.com/grafana/loki v1.6.2-0.20231215164305-b51b7d7b5503 h1:gdrsYbmk8822v6qvPwZO5DC6QjnAW7uKJ9YXnoUmV8c= github.com/grafana/loki v1.6.2-0.20231215164305-b51b7d7b5503/go.mod h1:d8seWXCEXkL42mhuIJYcGi6DxfehzoIpLrMQWJojvOo= github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608 h1:ZYk42718kSXOiIKdjZKljWLgBpzL5z1yutKABksQCMg= github.com/grafana/loki/pkg/push v0.0.0-20231124142027-e52380921608/go.mod h1:f3JSoxBTPXX5ec4FxxeC19nTBSxoTz+cBgS3cYLMcr0= +github.com/grafana/pyroscope-go v1.1.2 h1:7vCfdORYQMCxIzI3NlYAs3FcBP760+gWuYWOyiVyYx8= +github.com/grafana/pyroscope-go v1.1.2/go.mod h1:HSSmHo2KRn6FasBA4vK7BMiQqyQq8KSuBKvrhkXxYPU= +github.com/grafana/pyroscope-go/godeltaprof v0.1.8 h1:iwOtYXeeVSAeYefJNaxDytgjKtUuKQbJqgAIjlnicKg= +github.com/grafana/pyroscope-go/godeltaprof v0.1.8/go.mod h1:2+l7K7twW49Ct4wFluZD3tZ6e0SjanjcUUBPVD/UuGU= github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd h1:PpuIBO5P3e9hpqBD0O/HjhShYuM6XE0i/lbE6J94kww= github.com/grafana/regexp v0.0.0-20221122212121-6b5c0a4cb7fd/go.mod h1:M5qHK+eWfAv8VR/265dIuEpL3fNfeC21tXXp9itM24A= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= @@ -742,8 +744,8 @@ github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0 github.com/klauspost/compress v1.9.7/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= -github.com/klauspost/compress v1.17.1 h1:NE3C767s2ak2bweCZo3+rdP4U/HoyVXLv/X9f2gPS5g= -github.com/klauspost/compress v1.17.1/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= +github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= +github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg= @@ -988,12 +990,6 @@ github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= github.com/prometheus/prometheus v0.47.2-0.20231010075449-4b9c19fe5510 h1:6ksZ7t1hNOzGPPs8DK7SvXQf6UfWzi+W5Z7PCBl8gx4= github.com/prometheus/prometheus v0.47.2-0.20231010075449-4b9c19fe5510/go.mod h1:UC0TwJiF90m2T3iYPQBKnGu8gv3s55dF/EgpTq8gyvo= -github.com/pyroscope-io/client v0.7.1 h1:yFRhj3vbgjBxehvxQmedmUWJQ4CAfCHhn+itPsuWsHw= -github.com/pyroscope-io/client v0.7.1/go.mod h1:4h21iOU4pUOq0prKyDlvYRL+SCKsBc5wKiEtV+rJGqU= -github.com/pyroscope-io/godeltaprof v0.1.2 h1:MdlEmYELd5w+lvIzmZvXGNMVzW2Qc9jDMuJaPOR75g4= -github.com/pyroscope-io/godeltaprof v0.1.2/go.mod h1:psMITXp90+8pFenXkKIpNhrfmI9saQnPbba27VIaiQE= -github.com/regen-network/protobuf v1.3.3-alpha.regen.1 h1:OHEc+q5iIAXpqiqFKeLpu5NwTIkVXUs48vFMwzqpqY4= -github.com/regen-network/protobuf v1.3.3-alpha.regen.1/go.mod h1:2DjTFR1HhMQhiWC5sZ4OhQ3+NtdbZ6oBDKQwq5Ou+FI= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -1047,10 +1043,12 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/slack-go/slack v0.12.2 h1:x3OppyMyGIbbiyFhsBmpf9pwkUzMhthJMRNmNlA4LaQ= github.com/slack-go/slack v0.12.2/go.mod h1:hlGi5oXA+Gt+yWTPP0plCdRKmjsDxecdHxYQdlMQKOw= -github.com/smartcontractkit/chainlink-testing-framework/seth v1.2.0 h1:wEc6EKMOOK/9w6z/zv2/wPZsV/txctbYoVJ1sCxhXwI= -github.com/smartcontractkit/chainlink-testing-framework/seth v1.2.0/go.mod h1:upYGPS9lOBW2pJg6XD8TTNSD1GtRfayU2Ct5bcfvqFw= -github.com/smartcontractkit/wasp v0.4.1 h1:qgIx2s+eCwH0OaBKaHEAHUQ1Z47bAgDu+ICS9IOqvGQ= -github.com/smartcontractkit/wasp v0.4.1/go.mod h1:3qiofyI3pkbrc48a3CVshbMfgl74SiuPL/tm30d9Wb4= +github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449 h1:fX/xmGm1GBsD1ZZnooNT+eWA0hiTAqFlHzOC5CY4dy8= +github.com/smartcontractkit/chainlink-testing-framework/grafana v0.0.0-20240328204215-ac91f55f1449/go.mod h1:DC8sQMyTlI/44UCTL8QWFwb0bYNoXCfjwCv2hMivYZU= +github.com/smartcontractkit/chainlink-testing-framework/seth v1.2.1-0.20240827112945-bd8c580392d6 h1:ItZ75xmt+VHR/lw+GJwSWj9XICpgZ94dJ+I/5jdet7c= +github.com/smartcontractkit/chainlink-testing-framework/seth v1.2.1-0.20240827112945-bd8c580392d6/go.mod h1:afY3QmNgeR/VI1pRbGH8g3YXGy7C2RrFOwUzEFvL3L8= +github.com/smartcontractkit/wasp v0.4.8-0.20240826201742-8c8d2e49efb0 h1:jpcpzNqvmOkpit1zjv7+tAH6Ga1xvpJREkA52kx0crY= +github.com/smartcontractkit/wasp v0.4.8-0.20240826201742-8c8d2e49efb0/go.mod h1:gFD/Jh87jPSuuVTc9tACkZVY0h5SxnX96Tt8fYekdlA= github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= @@ -1066,8 +1064,8 @@ github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= -github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= -github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= @@ -1232,6 +1230,7 @@ golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5y golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= +golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -1269,6 +1268,7 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8= golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -1316,13 +1316,15 @@ golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1 golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -1331,8 +1333,8 @@ golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4Iltr golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.13.0 h1:jDDenyj+WgFtmV3zYVoi8aE2BwtXFLWOA67ZfNWftiY= -golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= +golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1346,6 +1348,7 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1430,6 +1433,7 @@ golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -1438,6 +1442,9 @@ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuX golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk= golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -1451,12 +1458,15 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1512,6 +1522,7 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -1543,8 +1554,9 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7 google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= @@ -1566,7 +1578,6 @@ google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200324203455-a04cca1dde73/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= From 18dd1ae01a61a960d8b07fd6e29bf6976c8234cc Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Wed, 28 Aug 2024 09:50:44 +0200 Subject: [PATCH 8/8] cleaner decode code (#1099) --- seth/client.go | 168 -------------------------------- seth/decode.go | 252 +++++++++++++++++++++++++++++++++++++++++++++++- seth/tracing.go | 17 +--- 3 files changed, 254 insertions(+), 183 deletions(-) diff --git a/seth/client.go b/seth/client.go index 5a75998cd..a009f38e4 100644 --- a/seth/client.go +++ b/seth/client.go @@ -3,7 +3,6 @@ package seth import ( "context" "crypto/ecdsa" - verr "errors" "fmt" "math/big" "net/http" @@ -420,173 +419,6 @@ func (m *Client) checkRPCHealth() error { return nil } -// Decode waits for transaction to be minted, then decodes transaction inputs, outputs, logs and events and -// depending on 'tracing_level' it either returns immediately or if the level matches it traces all calls. -// Where tracing results go depends on the 'trace_outputs' field in the config. -// If transaction was reverted the error returned will be revert error, not decoding error (that one, if any, will be logged). -// At the same time we also return decoded transaction, so contrary to go convention you might get both error and result. -// Last, but not least, if gas bumps are enabled, we will try to bump gas on transaction timeout and resubmit it with higher gas. -func (m *Client) Decode(tx *types.Transaction, txErr error) (*DecodedTransaction, error) { - if len(m.Errors) > 0 { - return nil, verr.Join(m.Errors...) - } - - // do not try to decode ABI error if contract deployment failed, because the error is not related to ABI - if txErr != nil { - //try to decode revert reason - reason, decodingErr := m.DecodeCustomABIErr(txErr) - - if decodingErr == nil { - return nil, errors.Wrap(txErr, reason) - } - - L.Trace(). - Msg("Skipping decoding, transaction submission failed. Nothing to decode") - return nil, txErr - } - - if tx == nil { - L.Trace(). - Msg("Skipping decoding, because transaction is nil. Nothing to decode") - return nil, nil - } - - l := L.With().Str("Transaction", tx.Hash().Hex()).Logger() - - // if transaction was not mined, we will retry it with gas bumping, but only if gas bumping is enabled - // and if the transaction was not mined in time, other errors will be returned as is - var receipt *types.Receipt - err := retry.Do( - func() error { - var err error - ctx, cancel := context.WithTimeout(context.Background(), m.Cfg.Network.TxnTimeout.Duration()) - receipt, err = m.WaitMined(ctx, l, m.Client, tx) - cancel() - - return err - }, retry.OnRetry(func(i uint, retryErr error) { - replacementTx, replacementErr := prepareReplacementTransaction(m, tx) - if replacementErr != nil { - L.Debug().Str("Replacement error", replacementErr.Error()).Str("Current error", retryErr.Error()).Uint("Attempt", i).Msg("Failed to prepare replacement transaction. Retrying without the original one") - return - } - L.Debug().Str("Current error", retryErr.Error()).Uint("Attempt", i).Msg("Waiting for transaction to be confirmed after gas bump") - tx = replacementTx - }), - retry.DelayType(retry.FixedDelay), - // unless attempts is at least 1 retry.Do won't execute at all - retry.Attempts(func() uint { - if m.Cfg.GasBumpRetries() == 0 { - return 1 - } - return m.Cfg.GasBumpRetries() - }()), - retry.RetryIf(func(err error) bool { - return m.Cfg.GasBumpRetries() != 0 && errors.Is(err, context.DeadlineExceeded) - }), - ) - - if err != nil { - L.Trace(). - Err(err). - Msg("Skipping decoding, because transaction was not minted. Nothing to decode") - return nil, err - } - - var revertErr error - if receipt.Status == 0 { - revertErr = m.callAndGetRevertReason(tx, receipt) - } - - decoded, decodeErr := m.decodeTransaction(l, tx, receipt) - - if decodeErr != nil && errors.Is(decodeErr, errors.New(ErrNoABIMethod)) { - if m.Cfg.hasOutput(TraceOutput_JSON) { - L.Trace(). - Err(decodeErr). - Msg("Failed to decode transaction. Saving transaction data hash as JSON") - - err = CreateOrAppendToJsonArray(m.Cfg.revertedTransactionsFile, tx.Hash().Hex()) - if err != nil { - l.Warn(). - Err(err). - Str("TXHash", tx.Hash().Hex()). - Msg("Failed to save reverted transaction hash to file") - } else { - l.Trace(). - Str("TXHash", tx.Hash().Hex()). - Msg("Saved reverted transaction to file") - } - } - m.printDecodedTXData(l, decoded) - return decoded, revertErr - } - - if m.Cfg.TracingLevel == TracingLevel_None { - L.Trace(). - Str("Transaction Hash", tx.Hash().Hex()). - Msg("Tracing level is NONE, skipping decoding") - m.printDecodedTXData(l, decoded) - return decoded, revertErr - } - - if m.Cfg.TracingLevel == TracingLevel_All || (m.Cfg.TracingLevel == TracingLevel_Reverted && revertErr != nil) { - traceErr := m.Tracer.TraceGethTX(decoded.Hash, revertErr) - if traceErr != nil { - if m.Cfg.hasOutput(TraceOutput_JSON) { - L.Trace(). - Err(traceErr). - Msg("Failed to trace call, but decoding was successful. Saving decoded data as JSON") - - path, saveErr := saveAsJson(decoded, filepath.Join(m.Cfg.ArtifactsDir, "traces"), decoded.Hash) - if saveErr != nil { - L.Warn(). - Err(saveErr). - Msg("Failed to save decoded call as JSON") - } else { - L.Trace(). - Str("Path", path). - Str("Tx hash", decoded.Hash). - Msg("Saved decoded transaction data to JSON") - } - } - - if strings.Contains(traceErr.Error(), "debug_traceTransaction does not exist") { - L.Warn(). - Err(err). - Msg("Debug API is either disabled or not available on the node. Disabling tracing") - - m.Cfg.TracingLevel = TracingLevel_None - } - - m.printDecodedTXData(l, decoded) - return decoded, revertErr - } - - if m.Cfg.hasOutput(TraceOutput_JSON) { - path, saveErr := saveAsJson(m.Tracer.GetDecodedCalls(decoded.Hash), filepath.Join(m.Cfg.ArtifactsDir, "traces"), decoded.Hash) - if saveErr != nil { - L.Warn(). - Err(saveErr). - Msg("Failed to save decoded call as JSON") - } else { - L.Trace(). - Str("Path", path). - Str("Tx hash", decoded.Hash). - Msg("Saved decoded call data to JSON") - } - } - } else { - L.Trace(). - Str("Transaction Hash", tx.Hash().Hex()). - Str("Tracing level", m.Cfg.TracingLevel). - Bool("Was reverted?", revertErr != nil). - Msg("Transaction doesn't match tracing level, skipping decoding") - } - - return decoded, revertErr -} - func (m *Client) TransferETHFromKey(ctx context.Context, fromKeyNum int, to string, value *big.Int, gasPrice *big.Int) error { if fromKeyNum > len(m.PrivateKeys) || fromKeyNum > len(m.Addresses) { return errors.Wrap(errors.New(ErrNoKeyLoaded), fmt.Sprintf("requested key: %d", fromKeyNum)) diff --git a/seth/decode.go b/seth/decode.go index c95f872a5..f488b89fb 100644 --- a/seth/decode.go +++ b/seth/decode.go @@ -4,10 +4,14 @@ import ( "bytes" "context" "encoding/hex" + verr "errors" "fmt" "math/big" + "path/filepath" "strconv" + "strings" + "github.com/avast/retry-go" "github.com/ethereum/go-ethereum" "github.com/ethereum/go-ethereum/accounts/abi" "github.com/ethereum/go-ethereum/common" @@ -89,6 +93,250 @@ func getDefaultDecodedCall() *DecodedCall { } } +// Decode waits for transaction to be minted, then decodes transaction inputs, outputs, logs and events and +// depending on 'tracing_level' it either returns immediately or if the level matches it traces all calls. +// Where tracing results are sent depends on the 'trace_outputs' field in the config. +// If transaction was reverted the error returned will be revert error, not decoding error (that, if any, will only be logged). +// At the same time we also return decoded transaction, so contrary to go convention you might get both error and result, +// because we want to return the decoded transaction even if it was reverted. +// Last, but not least, if gas bumps are enabled, we will try to bump gas on transaction mining timeout and resubmit it with higher gas. +func (m *Client) Decode(tx *types.Transaction, txErr error) (*DecodedTransaction, error) { + if len(m.Errors) > 0 { + return nil, verr.Join(m.Errors...) + } + + if m.DecodeSendErr(txErr) != nil { + return nil, txErr + } + + return m.DecodeTx(tx) +} + +// DecodeSendErr tries to decode the error and return the reason of the revert. If the error is not revert, it returns the original error. +// If the error is revert, but it cannot be decoded, it logs the error and returns the original error. +// If the error is revert, and it can be decoded, it returns the decoded error. +// This function is used to decode errors that are returned by the send transaction function. +func (m *Client) DecodeSendErr(txErr error) error { + if txErr == nil { + return nil + } + + reason, decodingErr := m.DecodeCustomABIErr(txErr) + + if decodingErr == nil { + return errors.Wrap(txErr, reason) + } + + L.Trace(). + Msg("No decode-able error found, returning original error") + return txErr +} + +// DecodeTx waits for transaction to be minted, then decodes transaction inputs, outputs, logs and events and +// depending on 'tracing_level' and transaction status (reverted or not) it either returns immediately or traces all calls. +// If transaction was reverted the error returned will be revert error, not decoding error (that, if any, will only be logged). +// At the same time we also return decoded transaction, so contrary to go convention you might get both error and result, +// because we want to return the decoded transaction even if it was reverted. +// Last, but not least, if gas bumps are enabled, we will try to bump gas on transaction mining timeout and resubmit it with higher gas. +func (m *Client) DecodeTx(tx *types.Transaction) (*DecodedTransaction, error) { + if tx == nil { + L.Trace(). + Msg("Skipping decoding, because transaction is nil. Nothing to decode") + return nil, nil + } + + l := L.With().Str("Transaction", tx.Hash().Hex()).Logger() + + var receipt *types.Receipt + var err error + tx, receipt, err = m.waitUntilMined(l, tx) + if err != nil { + return nil, err + } + + var revertErr error + if receipt.Status == 0 { + revertErr = m.callAndGetRevertReason(tx, receipt) + } + + decoded, decodeErr := m.decodeTransaction(l, tx, receipt) + + if decodeErr != nil && errors.Is(decodeErr, errors.New(ErrNoABIMethod)) { + m.handleTxDecodingError(l, *decoded, decodeErr) + return decoded, revertErr + } + + if m.Cfg.TracingLevel == TracingLevel_None { + m.handleDisabledTracing(l, *decoded) + return decoded, revertErr + } + + if m.Cfg.TracingLevel == TracingLevel_All || (m.Cfg.TracingLevel == TracingLevel_Reverted && revertErr != nil) { + decodedCalls, traceErr := m.Tracer.TraceGethTX(decoded.Hash) + if traceErr != nil { + m.handleTracingError(l, *decoded, traceErr, revertErr) + return decoded, revertErr + } + + m.handleSuccessfulTracing(l, *decoded, decodedCalls, revertErr) + } else { + l.Trace(). + Str("Tracing level", m.Cfg.TracingLevel). + Bool("Was reverted?", revertErr != nil). + Msg("Transaction doesn't match tracing level, skipping decoding") + } + + return decoded, revertErr +} + +func (m *Client) waitUntilMined(l zerolog.Logger, tx *types.Transaction) (*types.Transaction, *types.Receipt, error) { + // if transaction was not mined, we will retry it with gas bumping, but only if gas bumping is enabled + // and if the transaction was not mined in time, other errors will be returned as is + var receipt *types.Receipt + err := retry.Do( + func() error { + var err error + ctx, cancel := context.WithTimeout(context.Background(), m.Cfg.Network.TxnTimeout.Duration()) + receipt, err = m.WaitMined(ctx, l, m.Client, tx) + cancel() + + return err + }, retry.OnRetry(func(i uint, retryErr error) { + replacementTx, replacementErr := prepareReplacementTransaction(m, tx) + if replacementErr != nil { + L.Debug().Str("Replacement error", replacementErr.Error()).Str("Current error", retryErr.Error()).Uint("Attempt", i).Msg("Failed to prepare replacement transaction. Retrying without the original one") + return + } + l.Debug().Str("Current error", retryErr.Error()).Uint("Attempt", i).Msg("Waiting for transaction to be confirmed after gas bump") + tx = replacementTx + }), + retry.DelayType(retry.FixedDelay), + // unless attempts is at least 1 retry.Do won't execute at all + retry.Attempts(func() uint { + if m.Cfg.GasBumpRetries() == 0 { + return 1 + } + return m.Cfg.GasBumpRetries() + }()), + retry.RetryIf(func(err error) bool { + return m.Cfg.GasBumpRetries() != 0 && errors.Is(err, context.DeadlineExceeded) + }), + ) + + if err != nil { + l.Trace(). + Err(err). + Msg("Skipping decoding, because transaction was not mined. Nothing to decode") + return nil, nil, err + } + + return tx, receipt, nil +} + +func (m *Client) handleTxDecodingError(l zerolog.Logger, decoded DecodedTransaction, decodeErr error) { + tx := decoded.Transaction + + if m.Cfg.hasOutput(TraceOutput_JSON) { + l.Trace(). + Err(decodeErr). + Msg("Failed to decode transaction. Saving transaction data hash as JSON") + + err := CreateOrAppendToJsonArray(m.Cfg.revertedTransactionsFile, tx.Hash().Hex()) + if err != nil { + l.Warn(). + Err(err). + Str("TXHash", tx.Hash().Hex()). + Msg("Failed to save reverted transaction hash to file") + } else { + l.Trace(). + Str("TXHash", tx.Hash().Hex()). + Msg("Saved reverted transaction to file") + } + } + + if m.Cfg.hasOutput(TraceOutput_Console) { + m.printDecodedTXData(l, &decoded) + } +} + +func (m *Client) handleTracingError(l zerolog.Logger, decoded DecodedTransaction, traceErr, revertErr error) { + if m.Cfg.hasOutput(TraceOutput_JSON) { + l.Trace(). + Err(traceErr). + Msg("Failed to trace call, but decoding was successful. Saving decoded data as JSON") + + path, saveErr := saveAsJson(decoded, filepath.Join(m.Cfg.ArtifactsDir, "traces"), decoded.Hash) + if saveErr != nil { + l.Warn(). + Err(saveErr). + Msg("Failed to save decoded call as JSON") + } else { + l.Trace(). + Str("Path", path). + Str("Tx hash", decoded.Hash). + Msg("Saved decoded transaction data to JSON") + } + } + + if strings.Contains(traceErr.Error(), "debug_traceTransaction does not exist") { + l.Warn(). + Msg("Debug API is either disabled or not available on the node. Disabling tracing") + + l.Error(). + Err(revertErr). + Msg("Transaction was reverted, but we couldn't trace the transaction.") + + m.Cfg.TracingLevel = TracingLevel_None + } + + if m.Cfg.hasOutput(TraceOutput_Console) { + m.printDecodedTXData(l, &decoded) + } +} + +func (m *Client) handleSuccessfulTracing(l zerolog.Logger, decoded DecodedTransaction, decodedCalls []*DecodedCall, revertErr error) { + if m.Cfg.hasOutput(TraceOutput_JSON) { + path, saveErr := saveAsJson(m.Tracer.GetDecodedCalls(decoded.Hash), filepath.Join(m.Cfg.ArtifactsDir, "traces"), decoded.Hash) + if saveErr != nil { + l.Warn(). + Err(saveErr). + Msg("Failed to save decoded call as JSON") + } else { + l.Trace(). + Str("Path", path). + Str("Tx hash", decoded.Hash). + Msg("Saved decoded call data to JSON") + } + } + + if m.Cfg.hasOutput(TraceOutput_Console) { + m.Tracer.printDecodedCallData(L, decodedCalls, revertErr) + if err := m.Tracer.PrintTXTrace(decoded.Hash); err != nil { + l.Trace(). + Err(err). + Msg("Failed to print decoded call data") + } + } + + if m.Cfg.hasOutput(TraceOutput_DOT) { + if err := m.Tracer.generateDotGraph(decoded.Hash, decodedCalls, revertErr); err != nil { + l.Trace(). + Err(err). + Msg("Failed to generate DOT graph") + } + } +} + +func (m *Client) handleDisabledTracing(l zerolog.Logger, decoded DecodedTransaction) { + tx := decoded.Transaction + L.Trace(). + Str("Transaction Hash", tx.Hash().Hex()). + Msg("Tracing level is NONE, skipping decoding") + if m.Cfg.hasOutput(TraceOutput_Console) { + m.printDecodedTXData(l, &decoded) + } +} + func (d *DecodedCommonLog) MergeEventData(newEventData map[string]interface{}) { if d.EventData == nil { d.EventData = make(map[string]interface{}) @@ -223,7 +471,7 @@ func (m *Client) DecodeCustomABIErr(txErr error) (string, error) { return "", nil } if cerr.ErrorData() != nil { - L.Trace().Msg("Decoding custom ABI error from tx") + L.Trace().Msg("Decoding custom ABI error from tx error") for _, a := range m.ContractStore.ABIs { for k, abiError := range a.Errors { data, err := hex.DecodeString(cerr.ErrorData().(string)[2:]) @@ -245,7 +493,7 @@ func (m *Client) DecodeCustomABIErr(txErr error) (string, error) { } } } else { - L.Warn().Msg("No error data in tx") + L.Warn().Msg("No error data in tx submission error") } return "", nil } diff --git a/seth/tracing.go b/seth/tracing.go index 69f96fb05..820204106 100644 --- a/seth/tracing.go +++ b/seth/tracing.go @@ -147,7 +147,7 @@ func NewTracer(cs *ContractStore, abiFinder *ABIFinder, cfg *Config, contractAdd }, nil } -func (t *Tracer) TraceGethTX(txHash string, revertErr error) error { +func (t *Tracer) TraceGethTX(txHash string) ([]*DecodedCall, error) { fourByte, err := t.trace4Byte(txHash) if err != nil { L.Debug().Err(err).Msg("Failed to trace 4byte signatures. Some tracing data might be missing") @@ -159,7 +159,7 @@ func (t *Tracer) TraceGethTX(txHash string, revertErr error) error { callTrace, err := t.traceCallTracer(txHash) if err != nil { - return err + return []*DecodedCall{}, err } t.addTrace(txHash, &Trace{ @@ -171,19 +171,10 @@ func (t *Tracer) TraceGethTX(txHash string, revertErr error) error { decodedCalls, err := t.DecodeTrace(L, *t.getTrace(txHash)) if err != nil { - return err + return []*DecodedCall{}, err } - if len(decodedCalls) != 0 { - t.printDecodedCallData(L, decodedCalls, revertErr) - - err = t.generateDotGraph(txHash, decodedCalls, revertErr) - if err != nil { - return err - } - } - - return t.PrintTXTrace(txHash) + return decodedCalls, nil } func (t *Tracer) PrintTXTrace(txHash string) error {