Repository: umbracle/ethgo Branch: main Commit: 92266b0b0212 Files: 181 Total size: 661.5 KB Directory structure: gitextract_zpf77o7o/ ├── .github/ │ └── workflows/ │ ├── pr.yaml │ └── release.yaml ├── .gitignore ├── .goreleaser.yaml ├── 4byte/ │ ├── 4byte.go │ └── 4byte_test.go ├── CHANGELOG.md ├── LICENSE ├── Makefile ├── README.md ├── abi/ │ ├── abi.go │ ├── abi_test.go │ ├── decode.go │ ├── decode_test.go │ ├── encode.go │ ├── encoding_test.go │ ├── revert.go │ ├── revert_test.go │ ├── testing.go │ ├── topics.go │ ├── topics_test.go │ ├── type.go │ └── type_test.go ├── blocktracker/ │ ├── blocktracker.go │ └── blocktracker_test.go ├── builtin/ │ ├── ens/ │ │ ├── artifacts/ │ │ │ ├── ENS.abi │ │ │ └── Resolver.abi │ │ ├── ens.go │ │ ├── ens_artifacts.go │ │ ├── ens_resolver.go │ │ ├── ens_resolver_test.go │ │ ├── resolver.go │ │ ├── resolver_artifacts.go │ │ ├── utils.go │ │ └── utils_test.go │ └── erc20/ │ ├── artifacts/ │ │ └── ERC20.abi │ ├── erc20.go │ ├── erc20_artifacts.go │ └── erc20_test.go ├── cmd/ │ ├── abigen/ │ │ ├── abigen.go │ │ ├── gen.go │ │ └── testdata/ │ │ ├── testdata.abi │ │ ├── testdata.go │ │ └── testdata_artifacts.go │ ├── commands/ │ │ ├── 4byte.go │ │ ├── abigen.go │ │ ├── commands.go │ │ ├── ens.go │ │ ├── ens_resolve.go │ │ └── version.go │ ├── go.mod │ ├── go.sum │ ├── main.go │ └── version/ │ └── version.go ├── compiler/ │ ├── fixtures/ │ │ ├── ballot.sol │ │ └── simple_auction.sol │ ├── solidity.go │ └── solidity_test.go ├── contract/ │ ├── contract.go │ └── contract_test.go ├── e2e/ │ └── transaction_test.go ├── encoding.go ├── ens/ │ ├── address_mapping.go │ ├── ens.go │ └── ens_test.go ├── etherscan/ │ ├── etherscan.go │ └── etherscan_test.go ├── examples/ │ ├── contract-call-basic.go │ ├── contract-call-from.go │ ├── contract-deploy.go │ └── contract-transaction.go ├── go.mod ├── go.sum ├── jsonrpc/ │ ├── client.go │ ├── codec/ │ │ └── codec.go │ ├── debug.go │ ├── debug_test.go │ ├── eth.go │ ├── eth_test.go │ ├── net.go │ ├── net_test.go │ ├── subscribe.go │ ├── subscribe_test.go │ ├── transport/ │ │ ├── http.go │ │ ├── ipc.go │ │ ├── transport.go │ │ └── websocket.go │ ├── util.go │ ├── web3.go │ └── web3_test.go ├── keccak.go ├── keystore/ │ ├── utils.go │ ├── v3.go │ ├── v3_test.go │ ├── v4.go │ └── v4_test.go ├── networks.go ├── scripts/ │ ├── build-artifacts.sh │ ├── setup-ci.sh │ └── setup-geth.sh ├── signing/ │ ├── eip712.go │ └── eip712_test.go ├── structs.go ├── structs_encoding_test.go ├── structs_marshal.go ├── structs_marshal_rlp.go ├── structs_marshal_rlp_test.go ├── structs_marshal_test.go ├── structs_test.go ├── structs_unmarshal.go ├── testcases/ │ ├── accounts_test.go │ ├── contract_test.go │ ├── eip712_test.go │ ├── package.json │ ├── transaction_test.go │ └── util.go ├── testsuite/ │ ├── arbitrum-block-full.json │ ├── block-full.json │ ├── block-txn-hashes.json │ ├── receipts.json │ ├── transaction-call.json │ ├── transaction-contract-creation.json │ ├── transaction-eip1159.json │ ├── transaction-eip1559-notype.json │ ├── transaction-eip2930.json │ └── transaction-pending.json ├── testutil/ │ ├── contract.go │ ├── mock.go │ ├── server.go │ ├── server_test.go │ └── util.go ├── tracker/ │ ├── README.md │ ├── store/ │ │ ├── boltdb/ │ │ │ ├── bolt_store.go │ │ │ └── bolt_store_test.go │ │ ├── inmem/ │ │ │ ├── inmem_store.go │ │ │ └── inmem_store_test.go │ │ ├── postgresql/ │ │ │ ├── postgresql_store.go │ │ │ └── postgresql_store_test.go │ │ ├── store.go │ │ └── testing.go │ ├── tracker.go │ └── tracker_test.go ├── units.go ├── wallet/ │ ├── fixtures/ │ │ └── wallet_json.json │ ├── key.go │ ├── key_test.go │ ├── signer.go │ ├── signer_test.go │ ├── wallet_hd.go │ ├── wallet_hd_test.go │ ├── wallet_json.go │ ├── wallet_json_test.go │ ├── wallet_priv.go │ └── wallet_priv_test.go └── website/ ├── README.md ├── components/ │ ├── eip.jsx │ ├── godoc.jsx │ └── primitives.jsx ├── next.config.js ├── package.json ├── pages/ │ ├── _app.js │ ├── abi.mdx │ ├── cli/ │ │ ├── 4byte.mdx │ │ ├── abigen.mdx │ │ ├── ens_resolve.mdx │ │ ├── meta.json │ │ └── version.mdx │ ├── contract.mdx │ ├── index.mdx │ ├── integrations/ │ │ ├── 4byte.mdx │ │ ├── ens.mdx │ │ ├── etherscan.mdx │ │ └── meta.json │ ├── jsonrpc/ │ │ ├── eth.mdx │ │ ├── index.mdx │ │ ├── meta.json │ │ └── net.mdx │ ├── meta.json │ └── signers/ │ ├── signer.mdx │ └── wallet.mdx └── theme.config.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/pr.yaml ================================================ name: Unit tests on: [pull_request] jobs: build: runs-on: ubuntu-latest name: Go test steps: - uses: actions/checkout@v2 - uses: actions/setup-node@v2 with: node-version: "14" - name: Install ethers testcases run: cd ./testcases && npm install - name: Setup go uses: actions/setup-go@v1 with: go-version: "1.18.1" - name: "Setup" run: ./scripts/setup-ci.sh - name: "Setup geth" run: ./scripts/setup-geth.sh - name: Go test run: go test -v ./... ================================================ FILE: .github/workflows/release.yaml ================================================ name: Release on: workflow_dispatch: push: branches-ignore: - '**' tags: - 'v*.*.*' # to be used by fork patch-releases ^^ - 'v*.*.*-*' jobs: goreleaser: runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Fetch all tags run: git fetch --force --tags - name: Setup go uses: actions/setup-go@v1 with: go-version: '1.18.1' - name: Run GoReleaser uses: goreleaser/goreleaser-action@v2 with: distribution: goreleaser version: latest args: release --rm-dist env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} ================================================ FILE: .gitignore ================================================ bin/ pkg/ .idea # website node_modules .next dist/ ================================================ FILE: .goreleaser.yaml ================================================ before: hooks: - go mod tidy builds: - dir: cmd env: - CGO_ENABLED=0 goos: - linux - windows - darwin archives: - replacements: darwin: Darwin linux: Linux windows: Windows 386: i386 amd64: x86_64 checksum: name_template: 'checksums.txt' snapshot: name_template: "{{ incpatch .Version }}-next" changelog: sort: asc filters: exclude: - '^docs:' - '^test:' ================================================ FILE: 4byte/4byte.go ================================================ package fourbyte import ( "encoding/hex" "encoding/json" "io/ioutil" "net/http" ) const ( fourByteURL = "https://www.4byte.directory" ) // Resolve resolves a method/event signature func Resolve(str string) (string, error) { return get("/api/v1/signatures/?hex_signature=" + str) } // ResolveBytes resolves a method/event signature in bytes func ResolveBytes(b []byte) (string, error) { return Resolve(hex.EncodeToString(b)) } func get(path string) (string, error) { req, err := http.Get(fourByteURL + path) if err != nil { return "", err } defer req.Body.Close() data, err := ioutil.ReadAll(req.Body) if err != nil { return "", err } var result struct { Results []signatureResult } if err := json.Unmarshal(data, &result); err != nil { return "", err } if len(result.Results) == 0 { return "", nil } return result.Results[0].TextSignature, nil } type signatureResult struct { TextSignature string `json:"text_signature"` } ================================================ FILE: 4byte/4byte_test.go ================================================ package fourbyte import ( "testing" "github.com/stretchr/testify/assert" ) func Test4Byte(t *testing.T) { t.Skip("http api not stable, skip for now") var cases = []struct { in, out string }{ { "0xddf252ad", "Transfer(address,address,uint256)", }, { "0x42842e0e", "safeTransferFrom(address,address,uint256)", }, } for _, i := range cases { found, err := Resolve(i.in) assert.NoError(t, err) assert.Equal(t, i.out, found) } } ================================================ FILE: CHANGELOG.md ================================================ # 0.1.4 (Unreleased) - feat: Add override to `eth_call` request [[GH-240](https://github.com/umbracle/ethgo/issues/240)] - fix: Recovery of typed transactions [[GH-238](https://github.com/umbracle/ethgo/issues/238)] - fix: Parse `nonce` and `mixHash` on `Block` [[GH-228](https://github.com/umbracle/ethgo/issues/228)] - feat: `abi` decodes function string in multilines [[GH-212](https://github.com/umbracle/ethgo/issues/212)] - feat: `abi` DecodeStruct uses the `abi` tag instead of the default `mapstructure` [[GH-211](https://github.com/umbracle/ethgo/issues/211)] - feat: Implement `ens` reverse resolver [[GH-210](https://github.com/umbracle/ethgo/issues/210)] - fix: Jsonrpc eth_getLogs request cannot return string [[GH-209](https://github.com/umbracle/ethgo/issues/209)] # 0.1.3 (13 June, 2022) - Fix out-of-bounds reading of bytes during ABI decoding [[GH-205](https://github.com/umbracle/ethgo/issues/205)] - Update `fastrlp` to `59d5dd3` commit to fix a bug on bytes length check [[GH-204](https://github.com/umbracle/ethgo/issues/204)] - Fix out-of-bounds RLP unmarshal of transactions [[GH-203](https://github.com/umbracle/ethgo/issues/203)] # 0.1.2 (5 May, 2022) - Update `btcd` library to new `v0.22.1` - Add option in `contract` to send transactions with EIP-1559 [[GH-198](https://github.com/umbracle/ethgo/issues/198)] - Add custom `TxnOpts` to send a transaction in `contract` [[GH-195](https://github.com/umbracle/ethgo/issues/195)] - Add `ens resolve` command to resolve an ENS name [[GH-196](https://github.com/umbracle/ethgo/issues/196)] - Fix signing of typed transactions [[GH-197](https://github.com/umbracle/ethgo/issues/197)] - Fix. Use `ethgo.BlockNumber` input to make `Call` in contract [[GH-194](https://github.com/umbracle/ethgo/issues/194)] - Add `testcases` for contract signature and transaction signing [[GH-193](https://github.com/umbracle/ethgo/issues/193)] - Add `eth_feeHistory` rpc endpoint [[GH-192](https://github.com/umbracle/ethgo/issues/192)] - Update `testserver` to `go-ethereum:v1.10.15` [[GH-191](https://github.com/umbracle/ethgo/issues/191)] - Do not decode `to` in `Transaction` if not exists [[GH-190](https://github.com/umbracle/ethgo/issues/190)] # 0.1.1 (25 April, 2022) - Retrieve latest nonce when sending a transaction on `contract` [[GH-185](https://github.com/umbracle/ethgo/issues/185)] - Add `etherscan.GasPrice` function to return last block gas price [[GH-182](https://github.com/umbracle/ethgo/issues/182)] - Add `4byte` package and cli [[GH-178](https://github.com/umbracle/ethgo/issues/178)] - Install and use `ethers.js` spec tests for wallet private key decoding [[GH-177](https://github.com/umbracle/ethgo/issues/177)] - Add `GetLogs` function Etherscan to return logs by filter [[GH-170](https://github.com/umbracle/ethgo/issues/170)] - Add `Copy` function to major data types [[GH-169](https://github.com/umbracle/ethgo/issues/169)] - Parse `fixed bytes` type in event topic [[GH-168](https://github.com/umbracle/ethgo/issues/168)] - Introduce `NodeProvider` and update `Contract` and `abigen` format. [[GH-167](https://github.com/umbracle/ethgo/issues/167)] # 0.1.0 (5 March, 2022) - Initial public release. ================================================ FILE: LICENSE ================================================ Mozilla Public License Version 2.0 ================================== 1. Definitions -------------- 1.1. "Contributor" means each individual or legal entity that creates, contributes to the creation of, or owns Covered Software. 1.2. "Contributor Version" means the combination of the Contributions of others (if any) used by a Contributor and that particular Contributor's Contribution. 1.3. "Contribution" means Covered Software of a particular Contributor. 1.4. "Covered Software" means Source Code Form to which the initial Contributor has attached the notice in Exhibit A, the Executable Form of such Source Code Form, and Modifications of such Source Code Form, in each case including portions thereof. 1.5. "Incompatible With Secondary Licenses" means (a) that the initial Contributor has attached the notice described in Exhibit B to the Covered Software; or (b) that the Covered Software was made available under the terms of version 1.1 or earlier of the License, but not also under the terms of a Secondary License. 1.6. "Executable Form" means any form of the work other than Source Code Form. 1.7. "Larger Work" means a work that combines Covered Software with other material, in a separate file or files, that is not Covered Software. 1.8. "License" means this document. 1.9. "Licensable" means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently, any and all of the rights conveyed by this License. 1.10. "Modifications" means any of the following: (a) any file in Source Code Form that results from an addition to, deletion from, or modification of the contents of Covered Software; or (b) any new file in Source Code Form that contains any Covered Software. 1.11. "Patent Claims" of a Contributor means any patent claim(s), including without limitation, method, process, and apparatus claims, in any patent Licensable by such Contributor that would be infringed, but for the grant of the License, by the making, using, selling, offering for sale, having made, import, or transfer of either its Contributions or its Contributor Version. 1.12. "Secondary License" means either the GNU General Public License, Version 2.0, the GNU Lesser General Public License, Version 2.1, the GNU Affero General Public License, Version 3.0, or any later versions of those licenses. 1.13. "Source Code Form" means the form of the work preferred for making modifications. 1.14. "You" (or "Your") means an individual or a legal entity exercising rights under this License. For legal entities, "You" includes any entity that controls, is controlled by, or is under common control with You. For purposes of this definition, "control" means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity. 2. License Grants and Conditions -------------------------------- 2.1. Grants Each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license: (a) under intellectual property rights (other than patent or trademark) Licensable by such Contributor to use, reproduce, make available, modify, display, perform, distribute, and otherwise exploit its Contributions, either on an unmodified basis, with Modifications, or as part of a Larger Work; and (b) under Patent Claims of such Contributor to make, use, sell, offer for sale, have made, import, and otherwise transfer either its Contributions or its Contributor Version. 2.2. Effective Date The licenses granted in Section 2.1 with respect to any Contribution become effective for each Contribution on the date the Contributor first distributes such Contribution. 2.3. Limitations on Grant Scope The licenses granted in this Section 2 are the only rights granted under this License. No additional rights or licenses will be implied from the distribution or licensing of Covered Software under this License. Notwithstanding Section 2.1(b) above, no patent license is granted by a Contributor: (a) for any code that a Contributor has removed from Covered Software; or (b) for infringements caused by: (i) Your and any other third party's modifications of Covered Software, or (ii) the combination of its Contributions with other software (except as part of its Contributor Version); or (c) under Patent Claims infringed by Covered Software in the absence of its Contributions. This License does not grant any rights in the trademarks, service marks, or logos of any Contributor (except as may be necessary to comply with the notice requirements in Section 3.4). 2.4. Subsequent Licenses No Contributor makes additional grants as a result of Your choice to distribute the Covered Software under a subsequent version of this License (see Section 10.2) or under the terms of a Secondary License (if permitted under the terms of Section 3.3). 2.5. Representation Each Contributor represents that the Contributor believes its Contributions are its original creation(s) or it has sufficient rights to grant the rights to its Contributions conveyed by this License. 2.6. Fair Use This License is not intended to limit any rights You have under applicable copyright doctrines of fair use, fair dealing, or other equivalents. 2.7. Conditions Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in Section 2.1. 3. Responsibilities ------------------- 3.1. Distribution of Source Form All distribution of Covered Software in Source Code Form, including any Modifications that You create or to which You contribute, must be under the terms of this License. You must inform recipients that the Source Code Form of the Covered Software is governed by the terms of this License, and how they can obtain a copy of this License. You may not attempt to alter or restrict the recipients' rights in the Source Code Form. 3.2. Distribution of Executable Form If You distribute Covered Software in Executable Form then: (a) such Covered Software must also be made available in Source Code Form, as described in Section 3.1, and You must inform recipients of the Executable Form how they can obtain a copy of such Source Code Form by reasonable means in a timely manner, at a charge no more than the cost of distribution to the recipient; and (b) You may distribute such Executable Form under the terms of this License, or sublicense it under different terms, provided that the license for the Executable Form does not attempt to limit or alter the recipients' rights in the Source Code Form under this License. 3.3. Distribution of a Larger Work You may create and distribute a Larger Work under terms of Your choice, provided that You also comply with the requirements of this License for the Covered Software. If the Larger Work is a combination of Covered Software with a work governed by one or more Secondary Licenses, and the Covered Software is not Incompatible With Secondary Licenses, this License permits You to additionally distribute such Covered Software under the terms of such Secondary License(s), so that the recipient of the Larger Work may, at their option, further distribute the Covered Software under the terms of either this License or such Secondary License(s). 3.4. Notices You may not remove or alter the substance of any license notices (including copyright notices, patent notices, disclaimers of warranty, or limitations of liability) contained within the Source Code Form of the Covered Software, except that You may alter any license notices to the extent required to remedy known factual inaccuracies. 3.5. Application of Additional Terms You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, You may do so only on Your own behalf, and not on behalf of any Contributor. You must make it absolutely clear that any such warranty, support, indemnity, or liability obligation is offered by You alone, and You hereby agree to indemnify every Contributor for any liability incurred by such Contributor as a result of warranty, support, indemnity or liability terms You offer. You may include additional disclaimers of warranty and limitations of liability specific to any jurisdiction. 4. Inability to Comply Due to Statute or Regulation --------------------------------------------------- If it is impossible for You to comply with any of the terms of this License with respect to some or all of the Covered Software due to statute, judicial order, or regulation then You must: (a) comply with the terms of this License to the maximum extent possible; and (b) describe the limitations and the code they affect. Such description must be placed in a text file included with all distributions of the Covered Software under this License. Except to the extent prohibited by statute or regulation, such description must be sufficiently detailed for a recipient of ordinary skill to be able to understand it. 5. Termination -------------- 5.1. The rights granted under this License will terminate automatically if You fail to comply with any of its terms. However, if You become compliant, then the rights granted under this License from a particular Contributor are reinstated (a) provisionally, unless and until such Contributor explicitly and finally terminates Your grants, and (b) on an ongoing basis, if such Contributor fails to notify You of the non-compliance by some reasonable means prior to 60 days after You have come back into compliance. Moreover, Your grants from a particular Contributor are reinstated on an ongoing basis if such Contributor notifies You of the non-compliance by some reasonable means, this is the first time You have received notice of non-compliance with this License from such Contributor, and You become compliant prior to 30 days after Your receipt of the notice. 5.2. If You initiate litigation against any entity by asserting a patent infringement claim (excluding declaratory judgment actions, counter-claims, and cross-claims) alleging that a Contributor Version directly or indirectly infringes any patent, then the rights granted to You by any and all Contributors for the Covered Software under Section 2.1 of this License shall terminate. 5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user license agreements (excluding distributors and resellers) which have been validly granted by You or Your distributors under this License prior to termination shall survive termination. ************************************************************************ * * * 6. Disclaimer of Warranty * * ------------------------- * * * * Covered Software is provided under this License on an "as is" * * basis, without warranty of any kind, either expressed, implied, or * * statutory, including, without limitation, warranties that the * * Covered Software is free of defects, merchantable, fit for a * * particular purpose or non-infringing. The entire risk as to the * * quality and performance of the Covered Software is with You. * * Should any Covered Software prove defective in any respect, You * * (not any Contributor) assume the cost of any necessary servicing, * * repair, or correction. This disclaimer of warranty constitutes an * * essential part of this License. No use of any Covered Software is * * authorized under this License except under this disclaimer. * * * ************************************************************************ ************************************************************************ * * * 7. Limitation of Liability * * -------------------------- * * * * Under no circumstances and under no legal theory, whether tort * * (including negligence), contract, or otherwise, shall any * * Contributor, or anyone who distributes Covered Software as * * permitted above, be liable to You for any direct, indirect, * * special, incidental, or consequential damages of any character * * including, without limitation, damages for lost profits, loss of * * goodwill, work stoppage, computer failure or malfunction, or any * * and all other commercial damages or losses, even if such party * * shall have been informed of the possibility of such damages. This * * limitation of liability shall not apply to liability for death or * * personal injury resulting from such party's negligence to the * * extent applicable law prohibits such limitation. Some * * jurisdictions do not allow the exclusion or limitation of * * incidental or consequential damages, so this exclusion and * * limitation may not apply to You. * * * ************************************************************************ 8. Litigation ------------- Any litigation relating to this License may be brought only in the courts of a jurisdiction where the defendant maintains its principal place of business and such litigation shall be governed by laws of that jurisdiction, without reference to its conflict-of-law provisions. Nothing in this Section shall prevent a party's ability to bring cross-claims or counter-claims. 9. Miscellaneous ---------------- This License represents the complete agreement concerning the subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not be used to construe this License against a Contributor. 10. Versions of the License --------------------------- 10.1. New Versions Mozilla Foundation is the license steward. Except as provided in Section 10.3, no one other than the license steward has the right to modify or publish new versions of this License. Each version will be given a distinguishing version number. 10.2. Effect of New Versions You may distribute the Covered Software under the terms of the version of the License under which You originally received the Covered Software, or under the terms of any subsequent version published by the license steward. 10.3. Modified Versions If you create software not governed by this License, and you want to create a new license for such software, you may create and use a modified version of this License if you rename the license and remove any references to the name of the license steward (except to note that such modified license differs from this License). 10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses If You choose to distribute Source Code Form that is Incompatible With Secondary Licenses under the terms of this version of the License, the notice described in Exhibit B of this License must be attached. Exhibit A - Source Code Form License Notice ------------------------------------------- This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. Exhibit B - "Incompatible With Secondary Licenses" Notice --------------------------------------------------------- This Source Code Form is "Incompatible With Secondary Licenses", as defined by the Mozilla Public License, v. 2.0. ================================================ FILE: Makefile ================================================ .PHONY: build-artifacts build-artifacts: @echo "--> Build Artifacts" @sh -c ./scripts/build-artifacts.sh ================================================ FILE: README.md ================================================ # Eth-Go [![Chat Badge]][chat link] [chat badge]: https://img.shields.io/badge/chat-discord-%237289da [chat link]: https://discord.gg/5A6Qm2u4yK Ethgo is a lightweight SDK in Go to interact with Ethereum compatible blockchains. - Website: https://www.ethgoproject.io Ethgo provides the next key features: - **Simple**: Light and with a small number of direct dependencies. - **Ethereum ecosystem**: Native integration with other tools from the ecosystem like `ens` and `etherscan`. - **Command-line-interface**: Ethgo is both a Golang SDK library and a CLI. ================================================ FILE: abi/abi.go ================================================ package abi import ( "bytes" "encoding/json" "fmt" "hash" "io" "regexp" "strings" "sync" "github.com/umbracle/ethgo" "golang.org/x/crypto/sha3" ) // ABI represents the ethereum abi format type ABI struct { Constructor *Method Methods map[string]*Method MethodsBySignature map[string]*Method Events map[string]*Event Errors map[string]*Error } func (a *ABI) GetMethod(name string) *Method { m := a.Methods[name] return m } func (a *ABI) GetMethodBySignature(methodSignature string) *Method { m := a.MethodsBySignature[methodSignature] return m } func (a *ABI) addError(e *Error) { if len(a.Errors) == 0 { a.Errors = map[string]*Error{} } a.Errors[e.Name] = e } func (a *ABI) addEvent(e *Event) { if len(a.Events) == 0 { a.Events = map[string]*Event{} } name := overloadedName(e.Name, func(s string) bool { _, ok := a.Events[s] return ok }) a.Events[name] = e } func (a *ABI) addMethod(m *Method) { if len(a.Methods) == 0 { a.Methods = map[string]*Method{} } if len(a.MethodsBySignature) == 0 { a.MethodsBySignature = map[string]*Method{} } name := overloadedName(m.Name, func(s string) bool { _, ok := a.Methods[s] return ok }) a.Methods[name] = m a.MethodsBySignature[m.Sig()] = m } func overloadedName(rawName string, isAvail func(string) bool) string { name := rawName ok := isAvail(name) for idx := 0; ok; idx++ { name = fmt.Sprintf("%s%d", rawName, idx) ok = isAvail(name) } return name } // NewABI returns a parsed ABI struct func NewABI(s string) (*ABI, error) { return NewABIFromReader(bytes.NewReader([]byte(s))) } // MustNewABI returns a parsed ABI contract or panics if fails func MustNewABI(s string) *ABI { a, err := NewABI(s) if err != nil { panic(err) } return a } // NewABIFromReader returns an ABI object from a reader func NewABIFromReader(r io.Reader) (*ABI, error) { var abi *ABI dec := json.NewDecoder(r) if err := dec.Decode(&abi); err != nil { return nil, err } return abi, nil } // UnmarshalJSON implements json.Unmarshaler interface func (a *ABI) UnmarshalJSON(data []byte) error { var fields []struct { Type string Name string Constant bool Anonymous bool StateMutability string Inputs []*ArgumentStr Outputs []*ArgumentStr } if err := json.Unmarshal(data, &fields); err != nil { return err } for _, field := range fields { switch field.Type { case "constructor": if a.Constructor != nil { return fmt.Errorf("multiple constructor declaration") } input, err := NewTupleTypeFromArgs(field.Inputs) if err != nil { panic(err) } a.Constructor = &Method{ Inputs: input, } case "function", "": c := field.Constant if field.StateMutability == "view" || field.StateMutability == "pure" { c = true } inputs, err := NewTupleTypeFromArgs(field.Inputs) if err != nil { panic(err) } outputs, err := NewTupleTypeFromArgs(field.Outputs) if err != nil { panic(err) } method := &Method{ Name: field.Name, Const: c, Inputs: inputs, Outputs: outputs, } a.addMethod(method) case "event": input, err := NewTupleTypeFromArgs(field.Inputs) if err != nil { panic(err) } event := &Event{ Name: field.Name, Anonymous: field.Anonymous, Inputs: input, } a.addEvent(event) case "error": input, err := NewTupleTypeFromArgs(field.Inputs) if err != nil { panic(err) } errObj := &Error{ Name: field.Name, Inputs: input, } a.addError(errObj) case "fallback": case "receive": // do nothing default: return fmt.Errorf("unknown field type '%s'", field.Type) } } return nil } // Method is a callable function in the contract type Method struct { Name string Const bool Inputs *Type Outputs *Type } // Sig returns the signature of the method func (m *Method) Sig() string { return buildSignature(m.Name, m.Inputs) } // ID returns the id of the method func (m *Method) ID() []byte { k := acquireKeccak() k.Write([]byte(m.Sig())) dst := k.Sum(nil)[:4] releaseKeccak(k) return dst } // Encode encodes the inputs with this function func (m *Method) Encode(args interface{}) ([]byte, error) { data, err := Encode(args, m.Inputs) if err != nil { return nil, err } data = append(m.ID(), data...) return data, nil } // Decode decodes the output with this function func (m *Method) Decode(data []byte) (map[string]interface{}, error) { if len(data) == 0 { return nil, fmt.Errorf("empty response") } respInterface, err := Decode(m.Outputs, data) if err != nil { return nil, err } resp := respInterface.(map[string]interface{}) return resp, nil } // MustNewMethod creates a new solidity method object or fails func MustNewMethod(name string) *Method { method, err := NewMethod(name) if err != nil { panic(err) } return method } func NewMethod(name string) (*Method, error) { name, inputs, outputs, err := parseMethodSignature(name) if err != nil { return nil, err } m := &Method{Name: name, Inputs: inputs, Outputs: outputs} return m, nil } var ( funcRegexpWithReturn = regexp.MustCompile(`(\w*)\s*\((.*)\)(.*)\s*returns\s*\((.*)\)`) funcRegexpWithoutReturn = regexp.MustCompile(`(\w*)\s*\((.*)\)(.*)`) ) func parseMethodSignature(name string) (string, *Type, *Type, error) { name = strings.Replace(name, "\n", " ", -1) name = strings.Replace(name, "\t", " ", -1) name = strings.TrimPrefix(name, "function ") name = strings.TrimSpace(name) var funcName, inputArgs, outputArgs string if strings.Contains(name, "returns") { matches := funcRegexpWithReturn.FindAllStringSubmatch(name, -1) if len(matches) == 0 { return "", nil, nil, fmt.Errorf("no matches found") } funcName = strings.TrimSpace(matches[0][1]) inputArgs = strings.TrimSpace(matches[0][2]) outputArgs = strings.TrimSpace(matches[0][4]) } else { matches := funcRegexpWithoutReturn.FindAllStringSubmatch(name, -1) if len(matches) == 0 { return "", nil, nil, fmt.Errorf("no matches found") } funcName = strings.TrimSpace(matches[0][1]) inputArgs = strings.TrimSpace(matches[0][2]) } input, err := NewType("tuple(" + inputArgs + ")") if err != nil { return "", nil, nil, err } output, err := NewType("tuple(" + outputArgs + ")") if err != nil { return "", nil, nil, err } return funcName, input, output, nil } // Event is a triggered log mechanism type Event struct { Name string Anonymous bool Inputs *Type } // Sig returns the signature of the event func (e *Event) Sig() string { return buildSignature(e.Name, e.Inputs) } // ID returns the id of the event used during logs func (e *Event) ID() (res ethgo.Hash) { k := acquireKeccak() k.Write([]byte(e.Sig())) dst := k.Sum(nil) releaseKeccak(k) copy(res[:], dst) return } // MustNewEvent creates a new solidity event object or fails func MustNewEvent(name string) *Event { evnt, err := NewEvent(name) if err != nil { panic(err) } return evnt } // NewEvent creates a new solidity event object using the signature func NewEvent(name string) (*Event, error) { name, typ, err := parseEventOrErrorSignature("event ", name) if err != nil { return nil, err } return NewEventFromType(name, typ), nil } // Error is a solidity error object type Error struct { Name string Inputs *Type } // NewError creates a new solidity error object func NewError(name string) (*Error, error) { name, typ, err := parseEventOrErrorSignature("error ", name) if err != nil { return nil, err } return &Error{Name: name, Inputs: typ}, nil } func parseEventOrErrorSignature(prefix string, name string) (string, *Type, error) { if !strings.HasPrefix(name, prefix) { return "", nil, fmt.Errorf("prefix '%s' not found", prefix) } name = strings.TrimPrefix(name, prefix) if !strings.HasSuffix(name, ")") { return "", nil, fmt.Errorf("failed to parse input, expected 'name(types)'") } indx := strings.Index(name, "(") if indx == -1 { return "", nil, fmt.Errorf("failed to parse input, expected 'name(types)'") } funcName, signature := name[:indx], name[indx:] signature = "tuple" + signature typ, err := NewType(signature) if err != nil { return "", nil, err } return funcName, typ, nil } // NewEventFromType creates a new solidity event object using the name and type func NewEventFromType(name string, typ *Type) *Event { return &Event{Name: name, Inputs: typ} } // Match checks wheter the log is from this event func (e *Event) Match(log *ethgo.Log) bool { if len(log.Topics) == 0 { return false } if log.Topics[0] != e.ID() { return false } return true } // ParseLog parses a log with this event func (e *Event) ParseLog(log *ethgo.Log) (map[string]interface{}, error) { if !e.Match(log) { return nil, fmt.Errorf("log does not match this event") } return e.Inputs.ParseLog(log) } func buildSignature(name string, typ *Type) string { types := make([]string, len(typ.tuple)) for i, input := range typ.tuple { types[i] = strings.Replace(input.Elem.String(), "tuple", "", -1) } return fmt.Sprintf("%v(%v)", name, strings.Join(types, ",")) } // ArgumentStr encodes a type object type ArgumentStr struct { Name string Type string Indexed bool Components []*ArgumentStr InternalType string } var keccakPool = sync.Pool{ New: func() interface{} { return sha3.NewLegacyKeccak256() }, } func acquireKeccak() hash.Hash { return keccakPool.Get().(hash.Hash) } func releaseKeccak(k hash.Hash) { k.Reset() keccakPool.Put(k) } func NewABIFromList(humanReadableAbi []string) (*ABI, error) { res := &ABI{} for _, c := range humanReadableAbi { if strings.HasPrefix(c, "constructor") { typ, err := NewType("tuple" + strings.TrimPrefix(c, "constructor")) if err != nil { return nil, err } res.Constructor = &Method{ Inputs: typ, } } else if strings.HasPrefix(c, "function ") { method, err := NewMethod(c) if err != nil { return nil, err } res.addMethod(method) } else if strings.HasPrefix(c, "event ") { evnt, err := NewEvent(c) if err != nil { return nil, err } res.addEvent(evnt) } else if strings.HasPrefix(c, "error ") { errTyp, err := NewError(c) if err != nil { return nil, err } res.addError(errTyp) } else { return nil, fmt.Errorf("either event or function expected") } } return res, nil } ================================================ FILE: abi/abi_test.go ================================================ package abi import ( "reflect" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestAbi(t *testing.T) { methodOutput := &Method{ Name: "abc", Inputs: MustNewType("tuple()"), Outputs: MustNewType("tuple()"), } balanceFunc := &Method{ Name: "balanceOf", Const: true, Inputs: MustNewType("tuple(address owner)"), Outputs: MustNewType("tuple(uint256 balance)"), } cases := []struct { Input string Output *ABI }{ { Input: `[ { "name": "abc", "type": "function" }, { "name": "cde", "type": "event", "inputs": [ { "indexed": true, "name": "a", "type": "address" } ] }, { "name": "def", "type": "error", "inputs": [ { "indexed": true, "name": "a", "type": "address" } ] }, { "type": "function", "name": "balanceOf", "constant": true, "stateMutability": "view", "payable": false, "inputs": [ { "type": "address", "name": "owner" } ], "outputs": [ { "type": "uint256", "name": "balance" } ] } ]`, Output: &ABI{ Events: map[string]*Event{ "cde": { Name: "cde", Inputs: MustNewType("tuple(address indexed a)"), }, }, Methods: map[string]*Method{ "abc": methodOutput, "balanceOf": balanceFunc, }, MethodsBySignature: map[string]*Method{ "abc()": methodOutput, "balanceOf(address)": balanceFunc, }, Errors: map[string]*Error{ "def": { Name: "def", Inputs: MustNewType("tuple(address indexed a)"), }, }, }, }, } for _, c := range cases { t.Run("", func(t *testing.T) { abi, err := NewABI(c.Input) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(abi, c.Output) { t.Fatal("bad") } }) } } func TestAbi_InternalType(t *testing.T) { const abiStr = `[ { "inputs": [ { "components": [ { "internalType": "address", "type": "address" }, { "internalType": "uint256[4]", "type": "uint256[4]" } ], "internalType": "struct X", "name": "newSet", "type": "tuple[]" }, { "internalType": "custom_address", "name": "_to", "type": "address" } ], "outputs": [], "name": "transfer", "type": "function" } ]` abi, err := NewABI(abiStr) require.NoError(t, err) typ := abi.GetMethod("transfer").Inputs require.Equal(t, typ.tuple[0].Elem.InternalType(), "struct X") require.Equal(t, typ.tuple[0].Elem.elem.tuple[0].Elem.InternalType(), "address") require.Equal(t, typ.tuple[0].Elem.elem.tuple[1].Elem.InternalType(), "uint256[4]") require.Equal(t, typ.tuple[1].Elem.InternalType(), "custom_address") } func TestAbi_Polymorphism(t *testing.T) { // This ABI contains 2 "transfer" functions (polymorphism) const polymorphicABI = `[ { "inputs": [ { "internalType": "address", "name": "_to", "type": "address" }, { "internalType": "address", "name": "_token", "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" } ], "name": "transfer", "outputs": [ { "internalType": "bool", "name": "", "type": "bool" } ], "stateMutability": "nonpayable", "type": "function" } ]` abi, err := NewABI(polymorphicABI) if err != nil { t.Fatal(err) } assert.Len(t, abi.Methods, 2) assert.Equal(t, abi.GetMethod("transfer").Sig(), "transfer(address,address,uint256)") assert.Equal(t, abi.GetMethod("transfer0").Sig(), "transfer(address,uint256)") assert.NotEmpty(t, abi.GetMethodBySignature("transfer(address,address,uint256)")) assert.NotEmpty(t, abi.GetMethodBySignature("transfer(address,uint256)")) } func TestAbi_HumanReadable(t *testing.T) { cases := []string{ "constructor(string symbol, string name)", "function transferFrom(address from, address to, uint256 value)", "function balanceOf(address owner) view returns (uint256 balance)", "function balanceOf() view returns ()", "event Transfer(address indexed from, address indexed to, address value)", "error InsufficientBalance(address owner, uint256 balance)", "function addPerson(tuple(string name, uint16 age) person)", "function addPeople(tuple(string name, uint16 age)[] person)", "function getPerson(uint256 id) view returns (tuple(string name, uint16 age))", "event PersonAdded(uint256 indexed id, tuple(string name, uint16 age) person)", } vv, err := NewABIFromList(cases) assert.NoError(t, err) // make it nil to not compare it and avoid writing each method twice for the test vv.MethodsBySignature = nil expect := &ABI{ Constructor: &Method{ Inputs: MustNewType("tuple(string symbol, string name)"), }, Methods: map[string]*Method{ "transferFrom": { Name: "transferFrom", Inputs: MustNewType("tuple(address from, address to, uint256 value)"), Outputs: MustNewType("tuple()"), }, "balanceOf": { Name: "balanceOf", Inputs: MustNewType("tuple(address owner)"), Outputs: MustNewType("tuple(uint256 balance)"), }, "balanceOf0": { Name: "balanceOf", Inputs: MustNewType("tuple()"), Outputs: MustNewType("tuple()"), }, "addPerson": { Name: "addPerson", Inputs: MustNewType("tuple(tuple(string name, uint16 age) person)"), Outputs: MustNewType("tuple()"), }, "addPeople": { Name: "addPeople", Inputs: MustNewType("tuple(tuple(string name, uint16 age)[] person)"), Outputs: MustNewType("tuple()"), }, "getPerson": { Name: "getPerson", Inputs: MustNewType("tuple(uint256 id)"), Outputs: MustNewType("tuple(tuple(string name, uint16 age))"), }, }, Events: map[string]*Event{ "Transfer": { Name: "Transfer", Inputs: MustNewType("tuple(address indexed from, address indexed to, address value)"), }, "PersonAdded": { Name: "PersonAdded", Inputs: MustNewType("tuple(uint256 indexed id, tuple(string name, uint16 age) person)"), }, }, Errors: map[string]*Error{ "InsufficientBalance": { Name: "InsufficientBalance", Inputs: MustNewType("tuple(address owner, uint256 balance)"), }, }, } assert.Equal(t, expect, vv) } func TestAbi_ParseMethodSignature(t *testing.T) { cases := []struct { signature string name string input string output string }{ { // both input and output signature: "function approve(address to) returns (address)", name: "approve", input: "tuple(address)", output: "tuple(address)", }, { // no input signature: "function approve() returns (address)", name: "approve", input: "tuple()", output: "tuple(address)", }, { // no output signature: "function approve(address)", name: "approve", input: "tuple(address)", output: "tuple()", }, { // multiline signature: `function a( uint256 b, address[] c ) returns ( uint256[] d )`, name: "a", input: "tuple(uint256,address[])", output: "tuple(uint256[])", }, } for _, c := range cases { name, input, output, err := parseMethodSignature(c.signature) if err != nil { t.Fatal(err) } assert.Equal(t, name, c.name) if input != nil { assert.Equal(t, c.input, input.String()) } else { assert.Equal(t, c.input, "") } if input != nil { assert.Equal(t, c.output, output.String()) } else { assert.Equal(t, c.output, "") } } } ================================================ FILE: abi/decode.go ================================================ package abi import ( "encoding/binary" "fmt" "math/big" "reflect" "strconv" "github.com/mitchellh/mapstructure" "github.com/umbracle/ethgo" ) // Decode decodes the input with a given type func Decode(t *Type, input []byte) (interface{}, error) { if len(input) == 0 { return nil, fmt.Errorf("empty input") } val, _, err := decode(t, input) return val, err } // DecodeStruct decodes the input with a type to a struct func DecodeStruct(t *Type, input []byte, out interface{}) error { val, err := Decode(t, input) if err != nil { return err } dc := &mapstructure.DecoderConfig{ Result: out, WeaklyTypedInput: true, TagName: "abi", } ms, err := mapstructure.NewDecoder(dc) if err != nil { return err } if err = ms.Decode(val); err != nil { return err } return nil } func decode(t *Type, input []byte) (interface{}, []byte, error) { var data []byte var length int var err error // safe check, input should be at least 32 bytes if len(input) < 32 { return nil, nil, fmt.Errorf("incorrect length") } if t.isVariableInput() { length, err = readLength(input) if err != nil { return nil, nil, err } } else { data = input[:32] } switch t.kind { case KindTuple: return decodeTuple(t, input) case KindSlice: return decodeArraySlice(t, input[32:], length) case KindArray: return decodeArraySlice(t, input, t.size) } var val interface{} switch t.kind { case KindBool: val, err = decodeBool(data) case KindInt, KindUInt: val = readInteger(t, data) case KindString: val = string(input[32 : 32+length]) case KindBytes: val = input[32 : 32+length] case KindAddress: val, err = readAddr(data) case KindFixedBytes: val, err = readFixedBytes(t, data) case KindFunction: val, err = readFunctionType(t, data) default: return nil, nil, fmt.Errorf("decoding not available for type '%s'", t.kind) } return val, input[32:], err } var ( maxUint256 = big.NewInt(0).Add( big.NewInt(0).Exp(big.NewInt(2), big.NewInt(256), nil), big.NewInt(-1)) maxInt256 = big.NewInt(0).Add( big.NewInt(0).Exp(big.NewInt(2), big.NewInt(255), nil), big.NewInt(-1)) ) func readAddr(b []byte) (ethgo.Address, error) { res := ethgo.Address{} if len(b) != 32 { return res, fmt.Errorf("len is not correct") } copy(res[:], b[12:]) return res, nil } func readInteger(t *Type, b []byte) interface{} { switch t.t.Kind() { case reflect.Uint8: return b[len(b)-1] case reflect.Uint16: return binary.BigEndian.Uint16(b[len(b)-2:]) case reflect.Uint32: return binary.BigEndian.Uint32(b[len(b)-4:]) case reflect.Uint64: return binary.BigEndian.Uint64(b[len(b)-8:]) case reflect.Int8: return int8(b[len(b)-1]) case reflect.Int16: return int16(binary.BigEndian.Uint16(b[len(b)-2:])) case reflect.Int32: return int32(binary.BigEndian.Uint32(b[len(b)-4:])) case reflect.Int64: return int64(binary.BigEndian.Uint64(b[len(b)-8:])) default: ret := new(big.Int).SetBytes(b) if t.kind == KindUInt { return ret } if ret.Cmp(maxInt256) > 0 { ret.Add(maxUint256, big.NewInt(0).Neg(ret)) ret.Add(ret, big.NewInt(1)) ret.Neg(ret) } return ret } } func readFunctionType(t *Type, word []byte) ([24]byte, error) { res := [24]byte{} if !allZeros(word[24:32]) { return res, fmt.Errorf("function type expects the last 8 bytes to be empty but found: %b", word[24:32]) } copy(res[:], word[0:24]) return res, nil } func readFixedBytes(t *Type, word []byte) (interface{}, error) { array := reflect.New(t.t).Elem() reflect.Copy(array, reflect.ValueOf(word[0:t.size])) return array.Interface(), nil } func decodeTuple(t *Type, data []byte) (interface{}, []byte, error) { res := make(map[string]interface{}) orig := data origLen := len(orig) for indx, arg := range t.tuple { if len(data) < 32 { return nil, nil, fmt.Errorf("incorrect length") } entry := data if arg.Elem.isDynamicType() { offset, err := readOffset(data, origLen) if err != nil { return nil, nil, err } entry = orig[offset:] } val, tail, err := decode(arg.Elem, entry) if err != nil { return nil, nil, err } if !arg.Elem.isDynamicType() { data = tail } else { data = data[32:] } name := arg.Name if name == "" { name = strconv.Itoa(indx) } if _, ok := res[name]; !ok { res[name] = val } else { return nil, nil, fmt.Errorf("tuple with repeated values") } } return res, data, nil } func decodeArraySlice(t *Type, data []byte, size int) (interface{}, []byte, error) { if size < 0 { return nil, nil, fmt.Errorf("size is lower than zero") } if 32*size > len(data) { return nil, nil, fmt.Errorf("size is too big") } var res reflect.Value if t.kind == KindSlice { res = reflect.MakeSlice(t.t, size, size) } else if t.kind == KindArray { res = reflect.New(t.t).Elem() } orig := data origLen := len(orig) for indx := 0; indx < size; indx++ { isDynamic := t.elem.isDynamicType() if len(data) < 32 { return nil, nil, fmt.Errorf("incorrect length") } entry := data if isDynamic { offset, err := readOffset(data, origLen) if err != nil { return nil, nil, err } entry = orig[offset:] } val, tail, err := decode(t.elem, entry) if err != nil { return nil, nil, err } if !isDynamic { data = tail } else { data = data[32:] } res.Index(indx).Set(reflect.ValueOf(val)) } return res.Interface(), data, nil } func decodeBool(data []byte) (interface{}, error) { switch data[31] { case 0: return false, nil case 1: return true, nil default: return false, fmt.Errorf("bad boolean") } } func readOffset(data []byte, len int) (int, error) { offsetBig := big.NewInt(0).SetBytes(data[0:32]) if offsetBig.BitLen() > 63 { return 0, fmt.Errorf("offset larger than int64: %v", offsetBig.Int64()) } offset := int(offsetBig.Int64()) if offset > len { return 0, fmt.Errorf("offset insufficient %v require %v", len, offset) } return offset, nil } func readLength(data []byte) (int, error) { lengthBig := big.NewInt(0).SetBytes(data[0:32]) if lengthBig.BitLen() > 63 { return 0, fmt.Errorf("length larger than int64: %v", lengthBig.Int64()) } length := int(lengthBig.Uint64()) // if we trim the length in the data there should be enough // bytes to cover the length if length > len(data)-32 { return 0, fmt.Errorf("length insufficient %v require %v", len(data), length) } return length, nil } func allZeros(b []byte) bool { for _, i := range b { if i != 0 { return false } } return true } ================================================ FILE: abi/decode_test.go ================================================ package abi import ( "testing" "github.com/stretchr/testify/require" ) func TestDecode_BytesBound(t *testing.T) { typ := MustNewType("tuple(string)") decodeTuple(typ, nil) // it should not panic } func TestDecode_DynamicLengthOutOfBounds(t *testing.T) { input := []byte("00000000000000000000000000000000\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00 00000000000000000000000000") typ := MustNewType("tuple(bytes32, bytes, bytes)") _, err := Decode(typ, input) require.Error(t, err) } ================================================ FILE: abi/encode.go ================================================ package abi import ( "encoding/hex" "fmt" "math/big" "reflect" "strconv" "strings" "github.com/umbracle/ethgo" ) var ( zero = big.NewInt(0) one = big.NewInt(1) ) // Encode encodes a value func Encode(v interface{}, t *Type) ([]byte, error) { return encode(reflect.ValueOf(v), t) } func encode(v reflect.Value, t *Type) ([]byte, error) { if v.Kind() == reflect.Interface { v = v.Elem() } switch t.kind { case KindSlice, KindArray: return encodeSliceAndArray(v, t) case KindTuple: return encodeTuple(v, t) case KindString: return encodeString(v) case KindBool: return encodeBool(v) case KindAddress: return encodeAddress(v) case KindInt, KindUInt: return encodeNum(v) case KindBytes: return encodeBytes(v) case KindFixedBytes, KindFunction: return encodeFixedBytes(v) default: return nil, fmt.Errorf("encoding not available for type '%s'", t.kind) } } func encodeSliceAndArray(v reflect.Value, t *Type) ([]byte, error) { if v.Kind() != reflect.Array && v.Kind() != reflect.Slice { return nil, encodeErr(v, t.kind.String()) } if v.Kind() == reflect.Array && t.kind != KindArray { return nil, fmt.Errorf("expected array") } else if v.Kind() == reflect.Slice && t.kind != KindSlice { return nil, fmt.Errorf("expected slice") } if t.kind == KindArray && t.size != v.Len() { return nil, fmt.Errorf("array len incompatible") } var ret, tail []byte if t.isVariableInput() { ret = append(ret, packNum(v.Len())...) } offset := 0 isDynamic := t.elem.isDynamicType() if isDynamic { offset = getTypeSize(t.elem) * v.Len() } for i := 0; i < v.Len(); i++ { val, err := encode(v.Index(i), t.elem) if err != nil { return nil, err } if !isDynamic { ret = append(ret, val...) } else { ret = append(ret, packNum(offset)...) offset += len(val) tail = append(tail, val...) } } return append(ret, tail...), nil } func encodeTuple(v reflect.Value, t *Type) ([]byte, error) { if v.Kind() == reflect.Ptr { v = v.Elem() } var err error isList := true switch v.Kind() { case reflect.Slice, reflect.Array: case reflect.Map: isList = false case reflect.Struct: isList = false v, err = mapFromStruct(v) if err != nil { return nil, err } default: return nil, encodeErr(v, "tuple") } if v.Len() < len(t.tuple) { return nil, fmt.Errorf("expected at least the same length") } offset := 0 for _, elem := range t.tuple { offset += getTypeSize(elem.Elem) } var ret, tail []byte var aux reflect.Value for i, elem := range t.tuple { if isList { aux = v.Index(i) } else { name := elem.Name if name == "" { name = strconv.Itoa(i) } aux = v.MapIndex(reflect.ValueOf(name)) } if aux.Kind() == reflect.Invalid { return nil, fmt.Errorf("cannot get key %s", elem.Name) } val, err := encode(aux, elem.Elem) if err != nil { return nil, err } if elem.Elem.isDynamicType() { ret = append(ret, packNum(offset)...) tail = append(tail, val...) offset += len(val) } else { ret = append(ret, val...) } } return append(ret, tail...), nil } func convertArrayToBytes(value reflect.Value) reflect.Value { slice := reflect.MakeSlice(reflect.TypeOf([]byte{}), value.Len(), value.Len()) reflect.Copy(slice, value) return slice } func encodeFixedBytes(v reflect.Value) ([]byte, error) { if v.Kind() == reflect.Array { v = convertArrayToBytes(v) } if v.Kind() == reflect.String { value, err := decodeHex(v.String()) if err != nil { return nil, err } v = reflect.ValueOf(value) } return rightPad(v.Bytes(), 32), nil } func encodeAddress(v reflect.Value) ([]byte, error) { if v.Kind() == reflect.Array { v = convertArrayToBytes(v) } if v.Kind() == reflect.String { var addr ethgo.Address if err := addr.UnmarshalText([]byte(v.String())); err != nil { return nil, err } v = reflect.ValueOf(addr.Bytes()) } return leftPad(v.Bytes(), 32), nil } func encodeBytes(v reflect.Value) ([]byte, error) { if v.Kind() == reflect.Array { v = convertArrayToBytes(v) } if v.Kind() == reflect.String { value, err := decodeHex(v.String()) if err != nil { return nil, err } v = reflect.ValueOf(value) } return packBytesSlice(v.Bytes(), v.Len()) } func encodeString(v reflect.Value) ([]byte, error) { if v.Kind() != reflect.String { return nil, encodeErr(v, "string") } return packBytesSlice([]byte(v.String()), v.Len()) } func packBytesSlice(buf []byte, l int) ([]byte, error) { len, err := encodeNum(reflect.ValueOf(l)) if err != nil { return nil, err } return append(len, rightPad(buf, (l+31)/32*32)...), nil } func packNum(offset int) []byte { n, _ := encodeNum(reflect.ValueOf(offset)) return n } func encodeNum(v reflect.Value) ([]byte, error) { switch v.Kind() { case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64: return toU256(new(big.Int).SetUint64(v.Uint())), nil case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: return toU256(big.NewInt(v.Int())), nil case reflect.Ptr: if v.Type() != bigIntT { return nil, encodeErr(v.Elem(), "number") } return toU256(v.Interface().(*big.Int)), nil case reflect.Float64: return encodeNum(reflect.ValueOf(int64(v.Float()))) case reflect.String: n, ok := new(big.Int).SetString(v.String(), 10) if !ok { n, ok = new(big.Int).SetString(v.String()[2:], 16) if !ok { return nil, encodeErr(v, "number") } } return encodeNum(reflect.ValueOf(n)) default: return nil, encodeErr(v, "number") } } func encodeBool(v reflect.Value) ([]byte, error) { if v.Kind() != reflect.Bool { return nil, encodeErr(v, "bool") } if v.Bool() { return leftPad(one.Bytes(), 32), nil } return leftPad(zero.Bytes(), 32), nil } func encodeErr(v reflect.Value, t string) error { return fmt.Errorf("failed to encode %s as %s", v.Kind().String(), t) } func mapFromStruct(v reflect.Value) (reflect.Value, error) { res := map[string]interface{}{} typ := v.Type() for i := 0; i < v.NumField(); i++ { f := typ.Field(i) if f.PkgPath != "" { continue } tagValue := f.Tag.Get("abi") if tagValue == "-" { continue } name := strings.ToLower(f.Name) if tagValue != "" { name = tagValue } if _, ok := res[name]; !ok { res[name] = v.Field(i).Interface() } } return reflect.ValueOf(res), nil } var ( tt256 = new(big.Int).Lsh(big.NewInt(1), 256) // 2 ** 256 tt256m1 = new(big.Int).Sub(tt256, big.NewInt(1)) // 2 ** 256 - 1 ) // U256 converts a big Int into a 256bit EVM number. func toU256(n *big.Int) []byte { b := new(big.Int) b = b.Set(n) if b.Sign() < 0 || b.BitLen() > 256 { b.And(b, tt256m1) } return leftPad(b.Bytes(), 32) } func padBytes(b []byte, size int, left bool) []byte { l := len(b) if l == size { return b } if l > size { return b[l-size:] } tmp := make([]byte, size) if left { copy(tmp[size-l:], b) } else { copy(tmp, b) } return tmp } func leftPad(b []byte, size int) []byte { return padBytes(b, size, true) } func rightPad(b []byte, size int) []byte { return padBytes(b, size, false) } func encodeHex(b []byte) string { return "0x" + hex.EncodeToString(b) } func decodeHex(str string) ([]byte, error) { if strings.HasPrefix(str, "0x") { str = str[2:] } buf, err := hex.DecodeString(str) if err != nil { return nil, fmt.Errorf("could not decode hex: %v", err) } return buf, nil } ================================================ FILE: abi/encoding_test.go ================================================ package abi import ( "encoding/hex" "fmt" "math/big" "math/rand" "os" "reflect" "strconv" "testing" "time" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/compiler" "github.com/umbracle/ethgo/testutil" ) func mustDecodeHex(str string) []byte { buf, err := decodeHex(str) if err != nil { panic(fmt.Errorf("could not decode hex: %v", err)) } return buf } func TestEncoding(t *testing.T) { cases := []struct { Type string Input interface{} }{ { "uint40", big.NewInt(50), }, { "int256", big.NewInt(2), }, { "int256[]", []*big.Int{big.NewInt(1), big.NewInt(2)}, }, { "int256", big.NewInt(-10), }, { "bytes5", [5]byte{0x1, 0x2, 0x3, 0x4, 0x5}, }, { "bytes", mustDecodeHex("0x12345678911121314151617181920211"), }, { "string", "foobar", }, { "uint8[][2]", [2][]uint8{{1}, {1}}, }, { "address[]", []ethgo.Address{{1}, {2}}, }, { "bytes10[]", [][10]byte{ {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10}, {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0x10}, }, }, { "bytes[]", [][]byte{ mustDecodeHex("0x11"), mustDecodeHex("0x22"), }, }, { "uint32[2][3][4]", [4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}}, }, { "uint8[]", []uint8{1, 2}, }, { "string[]", []string{"hello", "foobar"}, }, { "string[2]", [2]string{"hello", "foobar"}, }, { "bytes32[][]", [][][32]uint8{{{1}, {2}}, {{3}, {4}, {5}}}, }, { "bytes32[][2]", [2][][32]uint8{{{1}, {2}}, {{3}, {4}, {5}}}, }, { "bytes32[3][2]", [2][3][32]uint8{{{1}, {2}, {3}}, {{3}, {4}, {5}}}, }, { "uint16[][2][]", [][2][]uint16{ {{0, 1}, {2, 3}}, {{4, 5}, {6, 7}}, }, }, { "tuple(bytes[] a)", map[string]interface{}{ "a": [][]byte{{0xf0, 0xf0, 0xf0}, {0xf0, 0xf0, 0xf0}}, }, }, { "tuple(uint32[2][][] a)", // `[{"type": "uint32[2][][]"}]`, map[string]interface{}{ "a": [][][2]uint32{{{uint32(1), uint32(200)}, {uint32(1), uint32(1000)}}, {{uint32(1), uint32(200)}, {uint32(1), uint32(1000)}}}, }, }, { "tuple(uint64[2] a)", map[string]interface{}{ "a": [2]uint64{1, 2}, }, }, { "tuple(uint32[2][3][4] a)", map[string]interface{}{ "a": [4][3][2]uint32{{{1, 2}, {3, 4}, {5, 6}}, {{7, 8}, {9, 10}, {11, 12}}, {{13, 14}, {15, 16}, {17, 18}}, {{19, 20}, {21, 22}, {23, 24}}}, }, }, { "tuple(int32[] a)", map[string]interface{}{ "a": []int32{1, 2}, }, }, { "tuple(int32 a, int32 b)", map[string]interface{}{ "a": int32(1), "b": int32(2), }, }, { "tuple(string a, int32 b)", map[string]interface{}{ "a": "Hello Worldxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "b": int32(2), }, }, { "tuple(int32[2] a, int32[] b)", map[string]interface{}{ "a": [2]int32{1, 2}, "b": []int32{4, 5, 6}, }, }, { // tuple with array slice "tuple(address[] a)", map[string]interface{}{ "a": []ethgo.Address{ {0x1}, }, }, }, { // First dynamic second static "tuple(int32[] a, int32[2] b)", map[string]interface{}{ "a": []int32{1, 2, 3}, "b": [2]int32{4, 5}, }, }, { // Both dynamic "tuple(int32[] a, int32[] b)", map[string]interface{}{ "a": []int32{1, 2, 3}, "b": []int32{4, 5, 6}, }, }, { "tuple(string a, int64 b)", map[string]interface{}{ "a": "hello World", "b": int64(266), }, }, { // tuple array "tuple(int32 a, int32 b)[2]", [2]map[string]interface{}{ { "a": int32(1), "b": int32(2), }, { "a": int32(3), "b": int32(4), }, }, }, { // tuple array with dynamic content "tuple(int32[] a)[2]", [2]map[string]interface{}{ { "a": []int32{1, 2, 3}, }, { "a": []int32{4, 5, 6}, }, }, }, { // tuple slice "tuple(int32 a, int32[] b)[]", []map[string]interface{}{ { "a": int32(1), "b": []int32{2, 3}, }, { "a": int32(4), "b": []int32{5, 6}, }, }, }, { // nested tuple "tuple(tuple(int32 c, int32[] d) a, int32[] b)", map[string]interface{}{ "a": map[string]interface{}{ "c": int32(5), "d": []int32{3, 4}, }, "b": []int32{1, 2}, }, }, { "tuple(uint8[2] a, tuple(uint8 e, uint32 f)[2] b, uint16 c, uint64[2][1] d)", map[string]interface{}{ "a": [2]uint8{uint8(1), uint8(2)}, "b": [2]map[string]interface{}{ { "e": uint8(10), "f": uint32(11), }, { "e": uint8(20), "f": uint32(21), }, }, "c": uint16(3), "d": [1][2]uint64{{uint64(4), uint64(5)}}, }, }, { "tuple(uint16 a, uint16 b)[1][]", [][1]map[string]interface{}{ { { "a": uint16(1), "b": uint16(2), }, }, { { "a": uint16(3), "b": uint16(4), }, }, { { "a": uint16(5), "b": uint16(6), }, }, { { "a": uint16(7), "b": uint16(8), }, }, }, }, { "tuple(uint64[][] a, tuple(uint8 a, uint32 b)[1] b, uint64 c)", map[string]interface{}{ "a": [][]uint64{ {3, 4}, }, "b": [1]map[string]interface{}{ { "a": uint8(1), "b": uint32(2), }, }, "c": uint64(10), }, }, } server := testutil.NewTestServer(t) for _, c := range cases { t.Run("", func(t *testing.T) { t.Parallel() tt, err := NewType(c.Type) if err != nil { t.Fatal(err) } if err := testEncodeDecode(t, server, tt, c.Input); err != nil { t.Fatal(err) } }) } } func TestEncodingBestEffort(t *testing.T) { strAddress := "0xdbb881a51CD4023E4400CEF3ef73046743f08da3" ethAddress := ethgo.HexToAddress(strAddress) overflowBigInt, _ := new(big.Int).SetString("50000000000000000000000000000000000000", 10) cases := []struct { Type string Input interface{} Expected interface{} }{ { "uint40", float64(50), big.NewInt(50), }, { "uint40", "50", big.NewInt(50), }, { "uint40", "0x32", big.NewInt(50), }, { "int256", float64(2), big.NewInt(2), }, { "int256", "50000000000000000000000000000000000000", overflowBigInt, }, { "int256", "0x259DA6542D43623D04C5112000000000", overflowBigInt, }, { "int256[]", []interface{}{float64(1), float64(2)}, []*big.Int{big.NewInt(1), big.NewInt(2)}, }, { "int256[]", []interface{}{"1", "2"}, []*big.Int{big.NewInt(1), big.NewInt(2)}, }, { "int256", float64(-10), big.NewInt(-10), }, { "int256", "-10", big.NewInt(-10), }, { "address[]", []interface{}{strAddress, strAddress}, []ethgo.Address{ethAddress, ethAddress}, }, { "uint8[]", []interface{}{float64(1), float64(2)}, []uint8{1, 2}, }, { "uint8[]", []interface{}{"1", "2"}, []uint8{1, 2}, }, { "bytes", "0x11", []uint8{17}, }, { "bytes32", "0x11", [32]uint8{17}, }, { "tuple(address a)", map[string]interface{}{ "a": strAddress, }, map[string]interface{}{ "a": ethAddress, }, }, { "tuple(address[] a)", map[string]interface{}{ "a": []interface{}{strAddress, strAddress}, }, map[string]interface{}{ "a": []ethgo.Address{ethAddress, ethAddress}, }, }, { "tuple(address a, int64 b)", map[string]interface{}{ "a": strAddress, "b": float64(266), }, map[string]interface{}{ "a": ethAddress, "b": int64(266), }, }, { "tuple(address a, int256 b)", map[string]interface{}{ "a": strAddress, "b": "50000000000000000000000000000000000000", }, map[string]interface{}{ "a": ethAddress, "b": overflowBigInt, }, }, { "tuple(address a, int256 b)", map[string]interface{}{ "a": strAddress, "b": "0x259DA6542D43623D04C5112000000000", }, map[string]interface{}{ "a": ethAddress, "b": overflowBigInt, }, }, } server := testutil.NewTestServer(t) for _, c := range cases { t.Run("", func(t *testing.T) { tt, err := NewType(c.Type) if err != nil { t.Fatal(err) } res1, err := Encode(c.Input, tt) if err != nil { t.Fatal(err) } res2, err := Decode(tt, res1) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(res2, c.Expected) { t.Fatal("bad") } if tt.kind == KindTuple { if err := testTypeWithContract(t, server, tt); err != nil { t.Fatal(err) } } }) } } func TestEncodingArguments(t *testing.T) { cases := []struct { Arg *ArgumentStr Input interface{} }{ { &ArgumentStr{ Type: "tuple", Components: []*ArgumentStr{ { Name: "", Type: "int32", }, { Name: "", Type: "int32", }, }, }, map[string]interface{}{ "0": int32(1), "1": int32(2), }, }, { &ArgumentStr{ Type: "tuple", Components: []*ArgumentStr{ { Name: "a", Type: "int32", }, { Name: "", Type: "int32", }, }, }, map[string]interface{}{ "a": int32(1), "1": int32(2), }, }, } server := testutil.NewTestServer(t) for _, c := range cases { t.Run("", func(t *testing.T) { tt, err := NewTypeFromArgument(c.Arg) if err != nil { t.Fatal(err) } if err := testEncodeDecode(t, server, tt, c.Input); err != nil { t.Fatal(err) } }) } } func testEncodeDecode(t *testing.T, server *testutil.TestServer, tt *Type, input interface{}) error { res1, err := Encode(input, tt) if err != nil { return err } res2, err := Decode(tt, res1) if err != nil { return err } if !reflect.DeepEqual(res2, input) { return fmt.Errorf("bad") } if tt.kind == KindTuple { if err := testTypeWithContract(t, server, tt); err != nil { return err } } return nil } func generateRandomArgs(n int) *Type { inputs := []*TupleElem{} for i := 0; i < randomInt(1, 10); i++ { ttt, err := NewType(randomType()) if err != nil { panic(err) } inputs = append(inputs, &TupleElem{ Name: fmt.Sprintf("arg%d", i), Elem: ttt, }) } return &Type{ kind: KindTuple, tuple: inputs, } } func TestRandomEncoding(t *testing.T) { rand.Seed(time.Now().UTC().UnixNano()) nStr := os.Getenv("RANDOM_TESTS") n, err := strconv.Atoi(nStr) if err != nil { n = 100 } server := testutil.NewTestServer(t) for i := 0; i < int(n); i++ { t.Run("", func(t *testing.T) { t.Parallel() tt := generateRandomArgs(randomInt(1, 4)) input := generateRandomType(tt) if err := testEncodeDecode(t, server, tt, input); err != nil { t.Fatal(err) } if err := testDecodePanic(tt, input); err != nil { t.Fatal(err) } }) } } func testDecodePanic(tt *Type, input interface{}) error { // test that the encoded input and random permutattions of the response do not cause // panics on Decode function res1, err := Encode(input, tt) if err != nil { return err } buf := make([]byte, len(res1)) // change each bit of the input with 1 for i := 0; i < len(res1); i++ { copy(buf, res1) buf[i] = 0xff Decode(tt, buf) } return nil } func testTypeWithContract(t *testing.T, server *testutil.TestServer, typ *Type) error { g := &generateContractImpl{} source := g.run(typ) output, err := compiler.NewSolidityCompiler("solc").CompileCode(source) if err != nil { return err } solcContract, ok := output.Contracts[":Sample"] if !ok { return fmt.Errorf("Expected the contract to be called Sample") } abi, err := NewABI(string(solcContract.Abi)) if err != nil { return err } binBuf, err := hex.DecodeString(solcContract.Bin) if err != nil { return err } txn := ðgo.Transaction{ Input: binBuf, } receipt, err := server.SendTxn(txn) if err != nil { return err } method, ok := abi.Methods["set"] if !ok { return fmt.Errorf("method set not found") } tt := method.Inputs val := generateRandomType(tt) data, err := method.Encode(val) if err != nil { return err } res, err := server.Call(ðgo.CallMsg{ To: &receipt.ContractAddress, Data: data, }) if err != nil { return err } if res != encodeHex(data[4:]) { // remove funct signature in data return fmt.Errorf("bad") } return nil } func TestEncodingStruct(t *testing.T) { typ := MustNewType("tuple(address aa, uint256 b)") type Obj struct { A ethgo.Address `abi:"aa"` B *big.Int } obj := Obj{ A: ethgo.Address{0x1}, B: big.NewInt(1), } encoded, err := typ.Encode(&obj) if err != nil { t.Fatal(err) } var obj2 Obj if err := typ.DecodeStruct(encoded, &obj2); err != nil { t.Fatal(err) } if !reflect.DeepEqual(obj, obj2) { t.Fatal("bad") } } func TestEncodingStruct_camcelCase(t *testing.T) { typ := MustNewType("tuple(address aA, uint256 b)") type Obj struct { A ethgo.Address `abi:"aA"` B *big.Int } obj := Obj{ A: ethgo.Address{0x1}, B: big.NewInt(1), } encoded, err := typ.Encode(&obj) if err != nil { t.Fatal(err) } var obj2 Obj if err := typ.DecodeStruct(encoded, &obj2); err != nil { t.Fatal(err) } if !reflect.DeepEqual(obj, obj2) { t.Fatal("bad") } } ================================================ FILE: abi/revert.go ================================================ package abi import ( "bytes" "fmt" ) var revertId = []byte{0x8, 0xC3, 0x79, 0xA0} func UnpackRevertError(b []byte) (string, error) { if !bytes.HasPrefix(b, revertId) { return "", fmt.Errorf("revert error prefix not found") } b = b[4:] tt := MustNewType("tuple(string)") vals, err := tt.Decode(b) if err != nil { return "", err } revVal := vals.(map[string]interface{})["0"].(string) return revVal, nil } ================================================ FILE: abi/revert_test.go ================================================ package abi import ( "testing" "github.com/stretchr/testify/assert" ) func TestUnpackRevertError(t *testing.T) { data := "08c379a00000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000000d72657665727420726561736f6e00000000000000000000000000000000000000" raw, err := decodeHex(data) assert.NoError(t, err) reason, err := UnpackRevertError(raw) assert.NoError(t, err) assert.Equal(t, "revert reason", reason) } ================================================ FILE: abi/testing.go ================================================ package abi import ( "fmt" "math/big" "math/rand" "reflect" "strings" "github.com/umbracle/ethgo" ) func randomInt(min, max int) int { return min + rand.Intn(max-min) } var randomTypes = []string{ "bool", "int", "uint", "array", "slice", "tuple", "address", "string", "bytes", "fixedBytes", } func randomNumberBits() int { return randomInt(1, 31) * 8 } func randomType() string { return pickRandomType(1) } func pickRandomType(d int) string { PICK: t := randomTypes[rand.Intn(len(randomTypes))] basicTypes := "bool,address,string,bytes,function" if strings.Contains(basicTypes, t) { return t } switch t { case "int": return fmt.Sprintf("int%d", randomNumberBits()) case "uint": return fmt.Sprintf("uint%d", randomNumberBits()) case "fixedBytes": return fmt.Sprintf("bytes%d", randomInt(1, 32)) } if d > 3 { // Allow only for 3 levels of depth goto PICK } r := pickRandomType(d + 1) switch t { case "slice": return fmt.Sprintf("%s[]", r) case "array": s := randomInt(1, 3) return fmt.Sprintf("%s[%d]", r, s) case "tuple": size := randomInt(1, 5) elems := []string{} for i := 0; i < size; i++ { elem := pickRandomType(d + 1) elems = append(elems, fmt.Sprintf("%s arg%d", elem, i)) } return fmt.Sprintf("tuple(%s)", strings.Join(elems, ",")) default: panic(fmt.Errorf("type not implemented: %s", t)) } } func generateNumber(t *Type) interface{} { b := make([]byte, t.size/8) if t.kind == KindUInt { rand.Read(b) } else { rand.Read(b[1:]) } num := big.NewInt(1).SetBytes(b) if t.size == 8 || t.size == 16 || t.size == 32 || t.size == 64 { return reflect.ValueOf(num.Int64()).Convert(t.t).Interface() } return num } func generateRandomType(t *Type) interface{} { switch t.kind { case KindInt: fallthrough case KindUInt: return generateNumber(t) case KindBool: if randomInt(0, 1) == 1 { return true } return false case KindAddress: buf := ethgo.Address{} rand.Read(buf[:]) return buf case KindString: return randString(randomInt(1, 100), letters) case KindBytes: buf := make([]byte, randomInt(1, 100)) rand.Read(buf) return buf case KindFixedBytes, KindFunction: buf := make([]byte, t.size) rand.Read(buf) val := reflect.New(t.t).Elem() for i := 0; i < len(buf); i++ { val.Index(i).Set(reflect.ValueOf(buf[i])) } return val.Interface() case KindSlice: size := randomInt(0, 5) val := reflect.MakeSlice(t.t, size, size) for i := 0; i < size; i++ { val.Index(i).Set(reflect.ValueOf(generateRandomType(t.elem))) } return val.Interface() case KindArray: val := reflect.New(t.t).Elem() for i := 0; i < t.size; i++ { val.Index(i).Set(reflect.ValueOf(generateRandomType(t.elem))) } return val.Interface() case KindTuple: vals := map[string]interface{}{} for _, i := range t.tuple { vals[i.Name] = generateRandomType(i.Elem) } return vals default: panic(fmt.Errorf("type not implemented: %s", t.kind.String())) } } const hexLetters = "0123456789abcdef" const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" func randString(n int, dict string) string { b := make([]byte, n) for i := range b { b[i] = dict[rand.Intn(len(dict))] } return string(b) } type generateContractImpl struct { structs []string } func (g *generateContractImpl) run(t *Type) string { var input, output, body []string for indx, i := range t.tuple { val := g.getValue(i.Elem) memory := "" if val == "bytes" || strings.Contains(val, "[") || strings.Contains(val, "struct") || strings.Contains(val, "string") { memory = " memory" } input = append(input, fmt.Sprintf("%s%s arg%d", val, memory, indx)) output = append(output, fmt.Sprintf("%s%s", val, memory)) body = append(body, fmt.Sprintf("arg%d", indx)) } contractTemplate := `pragma solidity ^0.5.5; pragma experimental ABIEncoderV2; contract Sample { // structs %s function set(%s) public view returns (%s) { return (%s); } }` contract := fmt.Sprintf( contractTemplate, strings.Join(g.structs, "\n"), strings.Join(input, ","), strings.Join(output, ","), strings.Join(body, ",")) return contract } func (g *generateContractImpl) getValue(t *Type) string { switch t.kind { case KindTuple: attrs := []string{} for indx, i := range t.tuple { attrs = append(attrs, fmt.Sprintf("%s attr%d;", g.getValue(i.Elem), indx)) } id := len(g.structs) str := fmt.Sprintf("struct struct%d {\n%s\n}\n", id, strings.Join(attrs, "\n")) g.structs = append(g.structs, str) return fmt.Sprintf("struct%d", id) case KindSlice: return fmt.Sprintf("%s[]", g.getValue(t.elem)) case KindArray: return fmt.Sprintf("%s[%d]", g.getValue(t.elem), t.size) default: return t.String() } } ================================================ FILE: abi/topics.go ================================================ package abi import ( "bytes" "fmt" "reflect" "github.com/umbracle/ethgo" ) // ParseLog parses an event log func ParseLog(args *Type, log *ethgo.Log) (map[string]interface{}, error) { var indexed, nonIndexed []*TupleElem for _, arg := range args.TupleElems() { if arg.Indexed { indexed = append(indexed, arg) } else { nonIndexed = append(nonIndexed, arg) } } // decode indexed fields indexedObjs, err := ParseTopics(&Type{kind: KindTuple, tuple: indexed}, log.Topics[1:]) if err != nil { return nil, err } var nonIndexedObjs map[string]interface{} if len(nonIndexed) > 0 { nonIndexedRaw, err := Decode(&Type{kind: KindTuple, tuple: nonIndexed}, log.Data) if err != nil { return nil, err } raw, ok := nonIndexedRaw.(map[string]interface{}) if !ok { return nil, fmt.Errorf("bad decoding") } nonIndexedObjs = raw } res := map[string]interface{}{} for _, arg := range args.TupleElems() { if arg.Indexed { res[arg.Name] = indexedObjs[0] indexedObjs = indexedObjs[1:] } else { res[arg.Name] = nonIndexedObjs[arg.Name] } } return res, nil } // ParseTopics parses topics from a log event func ParseTopics(args *Type, topics []ethgo.Hash) ([]interface{}, error) { if args.kind != KindTuple { return nil, fmt.Errorf("expected a tuple type") } if len(args.TupleElems()) != len(topics) { return nil, fmt.Errorf("bad length") } elems := []interface{}{} for indx, arg := range args.TupleElems() { elem, err := ParseTopic(arg.Elem, topics[indx]) if err != nil { return nil, err } elems = append(elems, elem) } return elems, nil } // ParseTopic parses an individual topic func ParseTopic(t *Type, topic ethgo.Hash) (interface{}, error) { switch t.kind { case KindBool: if bytes.Equal(topic[:], topicTrue[:]) { return true, nil } else if bytes.Equal(topic[:], topicFalse[:]) { return false, nil } return true, fmt.Errorf("is not a boolean") case KindInt, KindUInt: return readInteger(t, topic[:]), nil case KindAddress: return readAddr(topic[:]) case KindFixedBytes: return readFixedBytes(t, topic[:]) default: return nil, fmt.Errorf("topic parsing for type %s not supported", t.String()) } } // EncodeTopic encodes a topic func EncodeTopic(t *Type, val interface{}) (ethgo.Hash, error) { return encodeTopic(t, reflect.ValueOf(val)) } func encodeTopic(t *Type, val reflect.Value) (ethgo.Hash, error) { switch t.kind { case KindBool: return encodeTopicBool(val) case KindUInt, KindInt: return encodeTopicNum(t, val) case KindAddress: return encodeTopicAddress(val) } return ethgo.Hash{}, fmt.Errorf("not found") } var topicTrue, topicFalse ethgo.Hash func init() { topicTrue[31] = 1 } func encodeTopicAddress(val reflect.Value) (res ethgo.Hash, err error) { var b []byte b, err = encodeAddress(val) if err != nil { return } copy(res[:], b[:]) return } func encodeTopicNum(t *Type, val reflect.Value) (res ethgo.Hash, err error) { var b []byte b, err = encodeNum(val) if err != nil { return } copy(res[:], b[:]) return } func encodeTopicBool(v reflect.Value) (res ethgo.Hash, err error) { if v.Kind() != reflect.Bool { return ethgo.Hash{}, encodeErr(v, "bool") } if v.Bool() { return topicTrue, nil } return topicFalse, nil } ================================================ FILE: abi/topics_test.go ================================================ package abi import ( "fmt" "math/big" "reflect" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/testutil" ) func TestTopicEncoding(t *testing.T) { cases := []struct { Type string Val interface{} }{ { Type: "bool", Val: true, }, { Type: "bool", Val: false, }, { Type: "uint64", Val: uint64(20), }, { Type: "uint256", Val: big.NewInt(1000000), }, { Type: "address", Val: ethgo.Address{0x1}, }, } for _, c := range cases { tt, err := NewType(c.Type) assert.NoError(t, err) res, err := EncodeTopic(tt, c.Val) assert.NoError(t, err) val, err := ParseTopic(tt, res) assert.NoError(t, err) assert.Equal(t, val, c.Val) } } func TestIntegrationTopics(t *testing.T) { s := testutil.NewTestServer(t) type field struct { typ string indx bool val interface{} valStr string } cases := []struct { fields []field }{ { // uint fields: []field{ {"uint32", false, uint32(1), "1"}, {"uint8", true, uint8(10), "10"}, }, }, { // fixed bytes fields: []field{ {"bytes1", false, [1]byte{0x1}, "0x01"}, {"bytes1", true, [1]byte{0x1}, "0x01"}, }, }, } for _, c := range cases { cc := &testutil.Contract{} evnt := testutil.NewEvent("A") input := []string{} result := map[string]interface{}{} for indx, field := range c.fields { evnt.Add(field.typ, field.indx) input = append(input, field.valStr) result[fmt.Sprintf("val_%d", indx)] = field.val } cc.AddEvent(evnt) cc.EmitEvent("setA", "A", input...) // deploy the contract artifact, addr, err := s.DeployContract(cc) require.NoError(t, err) receipt, err := s.TxnTo(addr, "setA") require.NoError(t, err) // read the abi abi, err := NewABI(artifact.Abi) assert.NoError(t, err) // parse the logs found, err := ParseLog(abi.Events["A"].Inputs, receipt.Logs[0]) assert.NoError(t, err) if !reflect.DeepEqual(found, result) { t.Fatal("not equal") } } } ================================================ FILE: abi/type.go ================================================ package abi import ( "fmt" "math/big" "reflect" "regexp" "strconv" "strings" "github.com/umbracle/ethgo" ) // batch of predefined reflect types var ( boolT = reflect.TypeOf(bool(false)) uint8T = reflect.TypeOf(uint8(0)) uint16T = reflect.TypeOf(uint16(0)) uint32T = reflect.TypeOf(uint32(0)) uint64T = reflect.TypeOf(uint64(0)) int8T = reflect.TypeOf(int8(0)) int16T = reflect.TypeOf(int16(0)) int32T = reflect.TypeOf(int32(0)) int64T = reflect.TypeOf(int64(0)) addressT = reflect.TypeOf(ethgo.Address{}) stringT = reflect.TypeOf("") dynamicBytesT = reflect.SliceOf(reflect.TypeOf(byte(0))) functionT = reflect.ArrayOf(24, reflect.TypeOf(byte(0))) tupleT = reflect.TypeOf(map[string]interface{}{}) bigIntT = reflect.TypeOf(new(big.Int)) ) // Kind represents the kind of abi type type Kind int const ( // KindBool is a boolean KindBool Kind = iota // KindUInt is an uint KindUInt // KindInt is an int KindInt // KindString is a string KindString // KindArray is an array KindArray // KindSlice is a slice KindSlice // KindAddress is an address KindAddress // KindBytes is a bytes array KindBytes // KindFixedBytes is a fixed bytes KindFixedBytes // KindFixedPoint is a fixed point KindFixedPoint // KindTuple is a tuple KindTuple // KindFunction is a function KindFunction ) func (k Kind) String() string { names := [...]string{ "Bool", "Uint", "Int", "String", "Array", "Slice", "Address", "Bytes", "FixedBytes", "FixedPoint", "Tuple", "Function", } return names[k] } // TupleElem is an element of a tuple type TupleElem struct { Name string Elem *Type Indexed bool } // Type is an ABI type type Type struct { kind Kind size int elem *Type tuple []*TupleElem t reflect.Type itype string } func NewTupleType(inputs []*TupleElem) *Type { return &Type{ kind: KindTuple, tuple: inputs, t: tupleT, } } func NewTupleTypeFromArgs(inputs []*ArgumentStr) (*Type, error) { elems := []*TupleElem{} for _, i := range inputs { typ, err := NewTypeFromArgument(i) if err != nil { return nil, err } elems = append(elems, &TupleElem{ Name: i.Name, Elem: typ, Indexed: i.Indexed, }) } return NewTupleType(elems), nil } // ParseLog parses a log using this type func (t *Type) ParseLog(log *ethgo.Log) (map[string]interface{}, error) { return ParseLog(t, log) } // Decode decodes an object using this type func (t *Type) Decode(input []byte) (interface{}, error) { return Decode(t, input) } // DecodeStruct decodes an object using this type to the out param func (t *Type) DecodeStruct(input []byte, out interface{}) error { return DecodeStruct(t, input, out) } // InternalType returns the internal type func (t *Type) InternalType() string { return t.itype } // Encode encodes an object using this type func (t *Type) Encode(v interface{}) ([]byte, error) { return Encode(v, t) } func (t *Type) String() string { return t.Format(false) } // String returns the raw representation of the type func (t *Type) Format(includeArgs bool) string { switch t.kind { case KindTuple: rawAux := []string{} for _, i := range t.TupleElems() { name := i.Elem.Format(includeArgs) if i.Indexed { name += " indexed" } if includeArgs { if i.Name != "" { name += " " + i.Name } } rawAux = append(rawAux, name) } return fmt.Sprintf("tuple(%s)", strings.Join(rawAux, ",")) case KindArray: return fmt.Sprintf("%s[%d]", t.elem.Format(includeArgs), t.size) case KindSlice: return fmt.Sprintf("%s[]", t.elem.Format(includeArgs)) case KindBytes: return "bytes" case KindFixedBytes: return fmt.Sprintf("bytes%d", t.size) case KindString: return "string" case KindBool: return "bool" case KindAddress: return "address" case KindFunction: return "function" case KindUInt: return fmt.Sprintf("uint%d", t.size) case KindInt: return fmt.Sprintf("int%d", t.size) default: panic(fmt.Errorf("BUG: abi type not found %s", t.kind.String())) } } // Elem returns the elem value for slice and arrays func (t *Type) Elem() *Type { return t.elem } // Size returns the size of the type func (t *Type) Size() int { return t.size } // TupleElems returns the elems of the tuple func (t *Type) TupleElems() []*TupleElem { return t.tuple } // GoType returns the go type func (t *Type) GoType() reflect.Type { return t.t } // Kind returns the kind of the type func (t *Type) Kind() Kind { return t.kind } func (t *Type) isVariableInput() bool { return t.kind == KindSlice || t.kind == KindBytes || t.kind == KindString } func (t *Type) isDynamicType() bool { if t.kind == KindTuple { for _, elem := range t.tuple { if elem.Elem.isDynamicType() { return true } } return false } return t.kind == KindString || t.kind == KindBytes || t.kind == KindSlice || (t.kind == KindArray && t.elem.isDynamicType()) } func parseType(arg *ArgumentStr) (string, error) { if !strings.HasPrefix(arg.Type, "tuple") { return arg.Type, nil } if len(arg.Components) == 0 { return "tuple()", nil } // parse the arg components from the tuple str := []string{} for _, i := range arg.Components { aux, err := parseType(i) if err != nil { return "", err } if i.Indexed { str = append(str, aux+" indexed "+i.Name) } else { str = append(str, aux+" "+i.Name) } } return fmt.Sprintf("tuple(%s)%s", strings.Join(str, ","), strings.TrimPrefix(arg.Type, "tuple")), nil } // NewTypeFromArgument parses an abi type from an argument func NewTypeFromArgument(arg *ArgumentStr) (*Type, error) { str, err := parseType(arg) if err != nil { return nil, err } typ, err := NewType(str) if err != nil { return nil, err } // fill-in the `internalType` field into the type elems fillIn(typ, arg) return typ, nil } func fillIn(typ *Type, arg *ArgumentStr) error { typ.itype = arg.InternalType if len(arg.Components) == 0 { // no more items, nothing else to do return nil } // tuple types in the ABI with slices are represented as // tuple()[] or tuple()[2]. Thus, there might be element in the components // section of the abi but the next item not be a tuple. for { kind := typ.kind if kind == KindTuple { break } if kind != KindArray && kind != KindSlice { // error return fmt.Errorf("array or slice not found") } typ = typ.Elem() } if len(arg.Components) != len(typ.tuple) { // incorrect length return fmt.Errorf("incorrect size") } for indx, i := range arg.Components { fillIn(typ.tuple[indx].Elem, i) } return nil } // NewType parses a type in string format func NewType(s string) (*Type, error) { l := newLexer(s) l.nextToken() return readType(l) } // MustNewType parses a type in string format or panics if its invalid func MustNewType(s string) *Type { t, err := NewType(s) if err != nil { panic(err) } return t } func getTypeSize(t *Type) int { if t.kind == KindArray && !t.elem.isDynamicType() { if t.elem.kind == KindArray || t.elem.kind == KindTuple { return t.size * getTypeSize(t.elem) } return t.size * 32 } else if t.kind == KindTuple && !t.isDynamicType() { total := 0 for _, elem := range t.tuple { total += getTypeSize(elem.Elem) } return total } return 32 } var typeRegexp = regexp.MustCompile("^([[:alpha:]]+)([[:digit:]]*)$") func expectedToken(t tokenType) error { return fmt.Errorf("expected token %s", t.String()) } func notExpectedToken(t tokenType) error { return fmt.Errorf("token '%s' not expected", t.String()) } func readType(l *lexer) (*Type, error) { var tt *Type tok := l.nextToken() isTuple := false if tok.typ == tupleToken { if l.nextToken().typ != lparenToken { return nil, expectedToken(lparenToken) } isTuple = true } else if tok.typ == lparenToken { isTuple = true } if isTuple { var next token elems := []*TupleElem{} for { name := "" indexed := false elem, err := readType(l) if err != nil { if l.current.typ == rparenToken && len(elems) == 0 { // empty tuple 'tuple()' break } return nil, fmt.Errorf("failed to decode type: %v", err) } switch l.peek.typ { case strToken: l.nextToken() name = l.current.literal case indexedToken: l.nextToken() indexed = true if l.peek.typ == strToken { l.nextToken() name = l.current.literal } } elems = append(elems, &TupleElem{ Name: name, Elem: elem, Indexed: indexed, }) next = l.nextToken() if next.typ == commaToken { continue } else if next.typ == rparenToken { break } else { return nil, notExpectedToken(next.typ) } } tt = &Type{kind: KindTuple, tuple: elems, t: tupleT} } else if tok.typ != strToken { return nil, expectedToken(strToken) } else { // Check normal types elem, err := decodeSimpleType(tok.literal) if err != nil { return nil, err } tt = elem } // check for arrays at the end of the type for { if l.peek.typ != lbracketToken { break } l.nextToken() n := l.nextToken() var tAux *Type if n.typ == rbracketToken { tAux = &Type{kind: KindSlice, elem: tt, t: reflect.SliceOf(tt.t)} } else if n.typ == numberToken { size, err := strconv.ParseUint(n.literal, 10, 32) if err != nil { return nil, fmt.Errorf("failed to read array size '%s': %v", n.literal, err) } tAux = &Type{kind: KindArray, elem: tt, size: int(size), t: reflect.ArrayOf(int(size), tt.t)} if l.nextToken().typ != rbracketToken { return nil, expectedToken(rbracketToken) } } else { return nil, notExpectedToken(n.typ) } tt = tAux } return tt, nil } func decodeSimpleType(str string) (*Type, error) { match := typeRegexp.FindStringSubmatch(str) if len(match) == 0 { return nil, fmt.Errorf("type format is incorrect. Expected 'type''bytes' but found '%s'", str) } match = match[1:] var err error t := match[0] bytes := 0 ok := false if bytesStr := match[1]; bytesStr != "" { bytes, err = strconv.Atoi(bytesStr) if err != nil { return nil, fmt.Errorf("failed to parse bytes '%s': %v", bytesStr, err) } ok = true } // int and uint without bytes default to 256, 'bytes' may // have or not, the rest dont have bytes if t == "int" || t == "uint" { if !ok { bytes = 256 } } else if t != "bytes" && ok { return nil, fmt.Errorf("type %s does not expect bytes", t) } switch t { case "uint": var k reflect.Type switch bytes { case 8: k = uint8T case 16: k = uint16T case 32: k = uint32T case 64: k = uint64T default: if bytes%8 != 0 { panic(fmt.Errorf("number of bytes has to be M mod 8")) } k = bigIntT } return &Type{kind: KindUInt, size: int(bytes), t: k}, nil case "int": var k reflect.Type switch bytes { case 8: k = int8T case 16: k = int16T case 32: k = int32T case 64: k = int64T default: if bytes%8 != 0 { panic(fmt.Errorf("number of bytes has to be M mod 8")) } k = bigIntT } return &Type{kind: KindInt, size: int(bytes), t: k}, nil case "byte": bytes = 1 fallthrough case "bytes": if bytes == 0 { return &Type{kind: KindBytes, t: dynamicBytesT}, nil } return &Type{kind: KindFixedBytes, size: int(bytes), t: reflect.ArrayOf(int(bytes), reflect.TypeOf(byte(0)))}, nil case "string": return &Type{kind: KindString, t: stringT}, nil case "bool": return &Type{kind: KindBool, t: boolT}, nil case "address": return &Type{kind: KindAddress, t: addressT, size: 20}, nil case "function": return &Type{kind: KindFunction, size: 24, t: functionT}, nil default: return nil, fmt.Errorf("unknown type '%s'", t) } } type tokenType int const ( eofToken tokenType = iota strToken numberToken tupleToken lparenToken rparenToken lbracketToken rbracketToken commaToken indexedToken invalidToken ) func (t tokenType) String() string { names := [...]string{ "eof", "string", "number", "tuple", "(", ")", "[", "]", ",", "indexed", "", } return names[t] } type token struct { typ tokenType literal string } type lexer struct { input string current token peek token position int readPosition int ch byte } func newLexer(input string) *lexer { l := &lexer{input: input} l.readChar() return l } func (l *lexer) readChar() { if l.readPosition >= len(l.input) { l.ch = 0 } else { l.ch = l.input[l.readPosition] } l.position = l.readPosition l.readPosition++ } func (l *lexer) nextToken() token { l.current = l.peek l.peek = l.nextTokenImpl() return l.current } func (l *lexer) nextTokenImpl() token { var tok token // skip whitespace for l.ch == ' ' || l.ch == '\t' || l.ch == '\n' || l.ch == '\r' { l.readChar() } switch l.ch { case ',': tok.typ = commaToken case '(': tok.typ = lparenToken case ')': tok.typ = rparenToken case '[': tok.typ = lbracketToken case ']': tok.typ = rbracketToken case 0: tok.typ = eofToken default: if isLetter(l.ch) { tok.literal = l.readIdentifier() if tok.literal == "tuple" { tok.typ = tupleToken } else if tok.literal == "indexed" { tok.typ = indexedToken } else { tok.typ = strToken } return tok } else if isDigit(l.ch) { return token{numberToken, l.readNumber()} } else { tok.typ = invalidToken } } l.readChar() return tok } func (l *lexer) readIdentifier() string { pos := l.position for isLetter(l.ch) || isDigit(l.ch) { l.readChar() } return l.input[pos:l.position] } func (l *lexer) readNumber() string { position := l.position for isDigit(l.ch) { l.readChar() } return l.input[position:l.position] } func isDigit(ch byte) bool { return '0' <= ch && ch <= '9' } func isLetter(ch byte) bool { return 'a' <= ch && ch <= 'z' || 'A' <= ch && ch <= 'Z' || ch == '_' } ================================================ FILE: abi/type_test.go ================================================ package abi import ( "reflect" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestType(t *testing.T) { cases := []struct { s string a *ArgumentStr t *Type r string err bool }{ { s: "bool", a: simpleType("bool"), t: &Type{kind: KindBool, t: boolT}, }, { s: "uint32", a: simpleType("uint32"), t: &Type{kind: KindUInt, size: 32, t: uint32T}, }, { s: "int32", a: simpleType("int32"), t: &Type{kind: KindInt, size: 32, t: int32T}, }, { s: "int32[]", a: simpleType("int32[]"), t: &Type{kind: KindSlice, t: reflect.SliceOf(int32T), elem: &Type{kind: KindInt, size: 32, t: int32T}}, }, { s: "int", a: simpleType("int"), t: &Type{kind: KindInt, size: 256, t: bigIntT}, r: "int256", }, { s: "int[]", a: simpleType("int[]"), t: &Type{kind: KindSlice, t: reflect.SliceOf(bigIntT), elem: &Type{kind: KindInt, size: 256, t: bigIntT}}, r: "int256[]", }, { s: "bytes[2]", a: simpleType("bytes[2]"), t: &Type{ kind: KindArray, t: reflect.ArrayOf(2, dynamicBytesT), size: 2, elem: &Type{ kind: KindBytes, t: dynamicBytesT, }, }, }, { s: "address[]", a: simpleType("address[]"), t: &Type{kind: KindSlice, t: reflect.SliceOf(addressT), elem: &Type{kind: KindAddress, size: 20, t: addressT}}, }, { s: "string[]", a: simpleType("string[]"), t: &Type{ kind: KindSlice, t: reflect.SliceOf(stringT), elem: &Type{ kind: KindString, t: stringT, }, }, }, { s: "string[2]", a: simpleType("string[2]"), t: &Type{ kind: KindArray, size: 2, t: reflect.ArrayOf(2, stringT), elem: &Type{ kind: KindString, t: stringT, }, }, }, { s: "string[2][]", a: simpleType("string[2][]"), t: &Type{ kind: KindSlice, t: reflect.SliceOf(reflect.ArrayOf(2, stringT)), elem: &Type{ kind: KindArray, size: 2, t: reflect.ArrayOf(2, stringT), elem: &Type{ kind: KindString, t: stringT, }, }, }, }, { s: "tuple(int64 indexed arg0)", a: &ArgumentStr{ Type: "tuple", Components: []*ArgumentStr{ { Name: "arg0", Type: "int64", Indexed: true, }, }, }, t: &Type{ kind: KindTuple, t: tupleT, tuple: []*TupleElem{ { Name: "arg0", Elem: &Type{ kind: KindInt, size: 64, t: int64T, }, Indexed: true, }, }, }, }, { s: "tuple(int64 arg_0)[2]", a: &ArgumentStr{ Type: "tuple[2]", Components: []*ArgumentStr{ { Name: "arg_0", Type: "int64", }, }, }, t: &Type{ kind: KindArray, size: 2, t: reflect.ArrayOf(2, tupleT), elem: &Type{ kind: KindTuple, t: tupleT, tuple: []*TupleElem{ { Name: "arg_0", Elem: &Type{ kind: KindInt, size: 64, t: int64T, }, }, }, }, }, }, { s: "tuple(int64 a)[]", a: &ArgumentStr{ Type: "tuple[]", Components: []*ArgumentStr{ { Name: "a", Type: "int64", }, }, }, t: &Type{ kind: KindSlice, t: reflect.SliceOf(tupleT), elem: &Type{ kind: KindTuple, t: tupleT, tuple: []*TupleElem{ { Name: "a", Elem: &Type{ kind: KindInt, size: 64, t: int64T, }, }, }, }, }, }, { s: "tuple(int32 indexed arg0,tuple(int32 c) b_2)", a: &ArgumentStr{ Type: "tuple", Components: []*ArgumentStr{ { Name: "arg0", Type: "int32", Indexed: true, }, { Name: "b_2", Type: "tuple", Components: []*ArgumentStr{ { Name: "c", Type: "int32", }, }, }, }, }, t: &Type{ kind: KindTuple, t: tupleT, tuple: []*TupleElem{ { Name: "arg0", Elem: &Type{ kind: KindInt, size: 32, t: int32T, }, Indexed: true, }, { Name: "b_2", Elem: &Type{ kind: KindTuple, t: tupleT, tuple: []*TupleElem{ { Name: "c", Elem: &Type{ kind: KindInt, size: 32, t: int32T, }, }, }, }, }, }, }, }, { s: "tuple()", a: &ArgumentStr{ Type: "tuple", Components: []*ArgumentStr{}, }, t: &Type{ kind: KindTuple, t: tupleT, tuple: []*TupleElem{}, }, }, { // hidden tuple token s: "tuple((int32))", a: &ArgumentStr{ Type: "tuple", Components: []*ArgumentStr{ { Type: "tuple", Components: []*ArgumentStr{ { Type: "int32", }, }, }, }, }, t: &Type{ kind: KindTuple, t: tupleT, tuple: []*TupleElem{ { Elem: &Type{ kind: KindTuple, t: tupleT, tuple: []*TupleElem{ { Elem: &Type{ kind: KindInt, size: 32, t: int32T, }, }, }, }, }, }, }, r: "tuple(tuple(int32))", }, { s: "int[[", err: true, }, { s: "tuple[](a int32)", err: true, }, { s: "int32[a]", err: true, }, { s: "tuple(a int32", err: true, }, { s: "tuple(a int32,", err: true, }, } for _, c := range cases { t.Run("", func(t *testing.T) { e0, err := NewType(c.s) if err != nil && !c.err { t.Fatal(err) } if err == nil && c.err { t.Fatal("it should have failed") } if !c.err { // compare the string expected := c.s if c.r != "" { expected = c.r } assert.Equal(t, expected, e0.Format(true)) e1, err := NewTypeFromArgument(c.a) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(c.t, e0) { // fmt.Println(c.t.t) // fmt.Println(e0.t) t.Fatal("bad new type") } if !reflect.DeepEqual(c.t, e1) { t.Fatal("bad") } } }) } } func TestTypeArgument_InternalFields(t *testing.T) { arg := &ArgumentStr{ Type: "tuple", Components: []*ArgumentStr{ { Type: "tuple[]", Components: []*ArgumentStr{ { Type: "int32", InternalType: "c", }, }, InternalType: "b", }, }, } res, err := NewTypeFromArgument(arg) require.NoError(t, err) require.Equal(t, res.tuple[0].Elem.itype, "b") require.Equal(t, res.tuple[0].Elem.elem.tuple[0].Elem.itype, "c") } func TestSize(t *testing.T) { cases := []struct { Input string Size int }{ { "int32", 32, }, { "int32[]", 32, }, { "int32[2]", 32 * 2, }, { "int32[2][2]", 32 * 2 * 2, }, { "string", 32, }, { "string[]", 32, }, { "tuple(uint8 a, uint32 b)[1]", 64, }, } for _, c := range cases { t.Run("", func(t *testing.T) { tt, err := NewType(c.Input) if err != nil { t.Fatal(err) } size := getTypeSize(tt) if size != c.Size { t.Fatalf("expected size %d but found %d", c.Size, size) } }) } } func simpleType(s string) *ArgumentStr { return &ArgumentStr{ Type: s, } } ================================================ FILE: blocktracker/blocktracker.go ================================================ package blocktracker import ( "context" "fmt" "sync" "time" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/jsonrpc" ) // BlockProvider are the eth1x methods required by the block tracker type BlockProvider interface { GetBlockByHash(hash ethgo.Hash, full bool) (*ethgo.Block, error) GetBlockByNumber(i ethgo.BlockNumber, full bool) (*ethgo.Block, error) } const ( defaultMaxBlockBacklog = 10 ) // BlockTracker is an interface to track new blocks on the chain type BlockTracker struct { config *Config blocks []*ethgo.Block blocksLock sync.Mutex subscriber BlockTrackerInterface blockChs []chan *BlockEvent blockChsLock sync.Mutex provider BlockProvider once sync.Once closeCh chan struct{} } type Config struct { Tracker BlockTrackerInterface MaxBlockBacklog uint64 } func DefaultConfig() *Config { return &Config{ MaxBlockBacklog: defaultMaxBlockBacklog, } } type ConfigOption func(*Config) func WithBlockMaxBacklog(b uint64) ConfigOption { return func(c *Config) { c.MaxBlockBacklog = b } } func WithTracker(b BlockTrackerInterface) ConfigOption { return func(c *Config) { c.Tracker = b } } func NewBlockTracker(provider BlockProvider, opts ...ConfigOption) *BlockTracker { config := DefaultConfig() for _, opt := range opts { opt(config) } tracker := config.Tracker if tracker == nil { tracker = NewJSONBlockTracker(provider) } return &BlockTracker{ blocks: []*ethgo.Block{}, blockChs: []chan *BlockEvent{}, config: config, subscriber: tracker, provider: provider, closeCh: make(chan struct{}), } } func (b *BlockTracker) Subscribe() chan *BlockEvent { b.blockChsLock.Lock() defer b.blockChsLock.Unlock() ch := make(chan *BlockEvent, 1) b.blockChs = append(b.blockChs, ch) return ch } func (b *BlockTracker) AcquireLock() Lock { return NewLock(&b.blocksLock) } func (t *BlockTracker) Init() (err error) { var block *ethgo.Block t.once.Do(func() { block, err = t.provider.GetBlockByNumber(ethgo.Latest, false) if err != nil { return } if block.Number == 0 { return } blocks := make([]*ethgo.Block, t.config.MaxBlockBacklog) var i uint64 for i = 0; i < t.config.MaxBlockBacklog; i++ { blocks[t.config.MaxBlockBacklog-i-1] = block if block.Number == 0 { break } block, err = t.provider.GetBlockByHash(block.ParentHash, false) if err != nil { return } else if block == nil { // if block does not exist (for example reorg happened) GetBlockByHash will return nil, nil err = fmt.Errorf("block with hash %s not found", block.ParentHash) return } } if i != t.config.MaxBlockBacklog { // less than maxBacklog elements blocks = blocks[t.config.MaxBlockBacklog-i-1:] } t.blocks = blocks }) return err } func (b *BlockTracker) MaxBlockBacklog() uint64 { return b.config.MaxBlockBacklog } func (b *BlockTracker) LastBlocked() *ethgo.Block { target := b.blocks[len(b.blocks)-1] if target == nil { return nil } return target.Copy() } func (b *BlockTracker) BlocksBlocked() []*ethgo.Block { res := []*ethgo.Block{} for _, i := range b.blocks { res = append(res, i.Copy()) } return res } func (b *BlockTracker) Len() int { return len(b.blocks) } func (b *BlockTracker) Close() error { close(b.closeCh) return nil } func (b *BlockTracker) Start() error { ctx, cancelFn := context.WithCancel(context.Background()) go func() { <-b.closeCh cancelFn() }() // start the polling err := b.subscriber.Track(ctx, func(block *ethgo.Block) error { return b.HandleReconcile(block) }) if err != nil { return err } return err } func (t *BlockTracker) AddBlockLocked(block *ethgo.Block) error { if uint64(len(t.blocks)) == t.config.MaxBlockBacklog { // remove past blocks if there are more than maxReconcileBlocks t.blocks = t.blocks[1:] } if len(t.blocks) != 0 { lastNum := t.blocks[len(t.blocks)-1].Number if lastNum+1 != block.Number { return fmt.Errorf("bad number sequence. %d and %d", lastNum, block.Number) } } t.blocks = append(t.blocks, block) return nil } func (t *BlockTracker) blockAtIndex(hash ethgo.Hash) int { for indx, b := range t.blocks { if b.Hash == hash { return indx } } return -1 } func (t *BlockTracker) handleReconcileImpl(block *ethgo.Block) ([]*ethgo.Block, int, error) { // The block already exists if t.blockAtIndex(block.Hash) != -1 { return nil, -1, nil } // The state is empty if len(t.blocks) == 0 { return []*ethgo.Block{block}, -1, nil } // Append to the head of the chain if t.blocks[len(t.blocks)-1].Hash == block.ParentHash { return []*ethgo.Block{block}, -1, nil } // Fork in the middle of the chain if indx := t.blockAtIndex(block.ParentHash); indx != -1 { return []*ethgo.Block{block}, indx, nil } // Backfill. We dont know the parent of the block. // Need to query the chain untill we find a known block added := []*ethgo.Block{block} var indx int count := uint64(0) for { if count > t.config.MaxBlockBacklog { return nil, -1, fmt.Errorf("cannot reconcile more than max backlog values") } count++ parent, err := t.provider.GetBlockByHash(block.ParentHash, false) if err != nil { return nil, -1, fmt.Errorf("parent with hash retrieving error: %w", err) } else if parent == nil { // if block does not exist (for example reorg happened) GetBlockByHash will return nil, nil return nil, -1, fmt.Errorf("parent with hash %s not found", block.ParentHash) } added = append(added, parent) if indx = t.blockAtIndex(parent.ParentHash); indx != -1 { break } block = parent } // need the blocks in reverse order blocks := []*ethgo.Block{} for i := len(added) - 1; i >= 0; i-- { blocks = append(blocks, added[i]) } return blocks, indx, nil } func (t *BlockTracker) HandleBlockEvent(block *ethgo.Block) (*BlockEvent, error) { t.blocksLock.Lock() defer t.blocksLock.Unlock() blocks, indx, err := t.handleReconcileImpl(block) if err != nil { return nil, err } if len(blocks) == 0 { return nil, nil } blockEvnt := &BlockEvent{} // there are some blocks to remove if indx != -1 { for i := indx + 1; i < len(t.blocks); i++ { blockEvnt.Removed = append(blockEvnt.Removed, t.blocks[i]) } t.blocks = t.blocks[:indx+1] } // include the new blocks for _, block := range blocks { blockEvnt.Added = append(blockEvnt.Added, block) if err := t.AddBlockLocked(block); err != nil { return nil, err } } return blockEvnt, nil } func (t *BlockTracker) HandleReconcile(block *ethgo.Block) error { blockEvnt, err := t.HandleBlockEvent(block) if err != nil { return err } if blockEvnt == nil { return nil } t.blockChsLock.Lock() for _, ch := range t.blockChs { select { case ch <- blockEvnt: default: } } t.blockChsLock.Unlock() return nil } type BlockTrackerInterface interface { Track(context.Context, func(block *ethgo.Block) error) error } const ( defaultPollInterval = 1 * time.Second ) // JSONBlockTracker implements the BlockTracker interface using // the http jsonrpc endpoint type JSONBlockTracker struct { PollInterval time.Duration provider BlockProvider } // NewJSONBlockTracker creates a new json block tracker func NewJSONBlockTracker(provider BlockProvider) *JSONBlockTracker { return &JSONBlockTracker{ provider: provider, PollInterval: defaultPollInterval, } } // Track implements the BlockTracker interface. // This can take a long time so should be run concurrently. func (k *JSONBlockTracker) Track(ctx context.Context, handle func(block *ethgo.Block) error) error { var lastBlock *ethgo.Block for { select { case <-ctx.Done(): return ctx.Err() case <-time.After(k.PollInterval): block, err := k.provider.GetBlockByNumber(ethgo.Latest, false) if err != nil { return err } if lastBlock != nil && lastBlock.Hash == block.Hash { continue } if err := handle(block); err != nil { return err } lastBlock = block } } } // SubscriptionBlockTracker is an interface to track new blocks using // the newHeads subscription endpoint type SubscriptionBlockTracker struct { client *jsonrpc.Client } // NewSubscriptionBlockTracker creates a new block tracker using the subscription endpoint func NewSubscriptionBlockTracker(client *jsonrpc.Client) (*SubscriptionBlockTracker, error) { if !client.SubscriptionEnabled() { return nil, fmt.Errorf("subscription is not enabled") } s := &SubscriptionBlockTracker{ client: client, } return s, nil } // Track implements the BlockTracker interface // This can take a long time so should be run concurrently. // Note that the error return variable must be named so that subscription cancellation errors can be returned in defer. func (s *SubscriptionBlockTracker) Track(ctx context.Context, handle func(block *ethgo.Block) error) (err error) { data := make(chan []byte) defer close(data) // Subscribe with a callback. This callback must be unsubscribed // before the data channel is closed, otherwise panic (write to closed channel) callback := func(b []byte) { data <- b } unsubscribe, err := s.client.Subscribe("newHeads", callback) if err != nil { return err } defer func() { if cerr := unsubscribe(); cerr != nil { // Ensure subscription cancellation errors are returned via named return var if err == nil { err = cerr return } err = fmt.Errorf("failed to cancel: %s, after error %w", cerr.Error(), err) } }() for { select { case buf := <-data: var block ethgo.Block if err := block.UnmarshalJSON(buf); err != nil { return err } if err := handle(&block); err != nil { return err } case <-ctx.Done(): return ctx.Err() } } } type Lock struct { Locked bool lock *sync.Mutex } func NewLock(lock *sync.Mutex) Lock { return Lock{lock: lock} } func (l *Lock) Lock() { l.Locked = true l.lock.Lock() } func (l *Lock) Unlock() { l.Locked = false l.lock.Unlock() } // EventType is the type of the event type EventType int const ( // EventAdd happens when a new event is included in the chain EventAdd EventType = iota // EventDel may happen when there is a reorg and a past event is deleted EventDel ) // Event is an event emitted when a new log is included type Event struct { Type EventType Added []*ethgo.Log Removed []*ethgo.Log } // BlockEvent is an event emitted when a new block is included type BlockEvent struct { Type EventType Added []*ethgo.Block Removed []*ethgo.Block } ================================================ FILE: blocktracker/blocktracker_test.go ================================================ package blocktracker import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/jsonrpc" "github.com/umbracle/ethgo/testutil" ) func testListener(t *testing.T, server *testutil.TestServer, tracker BlockTrackerInterface) { ctx, cancelFn := context.WithCancel(context.Background()) defer cancelFn() blocks := make(chan *ethgo.Block) err := tracker.Track(ctx, func(block *ethgo.Block) error { blocks <- block return nil }) if err != nil { t.Fatal(err) } var lastBlock *ethgo.Block count := uint64(0) recv := func() { count++ select { case block := <-blocks: if lastBlock != nil { if lastBlock.Number+1 != block.Number { t.Fatalf("bad sequence %d %d", lastBlock.Number, block.Number) } } lastBlock = block case <-time.After(4 * time.Second): t.Fatal("timeout to receive block tracker block") } } server.ProcessBlock() recv() server.ProcessBlock() recv() } func TestBlockTracker_Listener_JsonRPC(t *testing.T) { t.Skip("Too brittle on CI, FIX") s := testutil.NewTestServer(t) c, _ := jsonrpc.NewClient(s.HTTPAddr()) defer c.Close() tracker := NewJSONBlockTracker(c.Eth()) tracker.PollInterval = 1 * time.Second testListener(t, s, tracker) } func TestBlockTracker_Listener_Websocket(t *testing.T) { t.Skip("Too brittle on CI, FIX") s := testutil.NewTestServer(t) c, _ := jsonrpc.NewClient(s.WSAddr()) defer c.Close() tracker, err := NewSubscriptionBlockTracker(c) if err != nil { t.Fatal(err) } testListener(t, s, tracker) } func TestBlockTracker_Lifecycle(t *testing.T) { t.Skip() s := testutil.NewTestServer(t) c, _ := jsonrpc.NewClient(s.HTTPAddr()) tr := NewBlockTracker(c.Eth()) assert.NoError(t, tr.Init()) go tr.Start() // try to mine a block at least every 1 second go func() { for i := 0; i < 10; i++ { s.ProcessBlock() time.After(1 * time.Second) } }() sub := tr.Subscribe() for i := 0; i < 10; i++ { select { case <-sub: case <-time.After(2 * time.Second): t.Fatal("bad") } } } func TestBlockTracker_PopulateBlocks(t *testing.T) { // more than maxBackLog blocks { l := testutil.MockList{} l.Create(0, 15, func(b *testutil.MockBlock) {}) m := &testutil.MockClient{} m.AddScenario(l) tt0 := NewBlockTracker(m) err := tt0.Init() if err != nil { t.Fatal(err) } if !testutil.CompareBlocks(l.ToBlocks()[5:], tt0.blocks) { t.Fatal("bad") } } // less than maxBackLog { l0 := testutil.MockList{} l0.Create(0, 5, func(b *testutil.MockBlock) {}) m1 := &testutil.MockClient{} m1.AddScenario(l0) tt1 := NewBlockTracker(m1) tt1.provider = m1 err := tt1.Init() if err != nil { panic(err) } if !testutil.CompareBlocks(l0.ToBlocks(), tt1.blocks) { t.Fatal("bad") } } } func TestBlockTracker_Events(t *testing.T) { type TestEvent struct { Added testutil.MockList Removed testutil.MockList } type Reconcile struct { block *testutil.MockBlock event *TestEvent } cases := []struct { Name string Scenario testutil.MockList History testutil.MockList Reconcile []Reconcile Expected testutil.MockList }{ { Name: "Empty history", Reconcile: []Reconcile{ { block: testutil.Mock(0x1), event: &TestEvent{ Added: testutil.MockList{ testutil.Mock(0x1), }, }, }, }, Expected: []*testutil.MockBlock{ testutil.Mock(1), }, }, { Name: "Repeated header", History: []*testutil.MockBlock{ testutil.Mock(0x1), }, Reconcile: []Reconcile{ { block: testutil.Mock(0x1), }, }, Expected: []*testutil.MockBlock{ testutil.Mock(0x1), }, }, { Name: "New head", History: testutil.MockList{ testutil.Mock(0x1), }, Reconcile: []Reconcile{ { block: testutil.Mock(0x2), event: &TestEvent{ Added: testutil.MockList{ testutil.Mock(0x2), }, }, }, }, Expected: testutil.MockList{ testutil.Mock(0x1), testutil.Mock(0x2), }, }, { Name: "Ignore block already on history", History: testutil.MockList{ testutil.Mock(0x1), testutil.Mock(0x2), testutil.Mock(0x3), }, Reconcile: []Reconcile{ { block: testutil.Mock(0x2), }, }, Expected: testutil.MockList{ testutil.Mock(0x1), testutil.Mock(0x2), testutil.Mock(0x3), }, }, { Name: "Multi Roll back", History: testutil.MockList{ testutil.Mock(0x1), testutil.Mock(0x2), testutil.Mock(0x3), testutil.Mock(0x4), }, Reconcile: []Reconcile{ { block: testutil.Mock(0x30).Parent(0x2), event: &TestEvent{ Added: testutil.MockList{ testutil.Mock(0x30).Parent(0x2), }, Removed: testutil.MockList{ testutil.Mock(0x3), testutil.Mock(0x4), }, }, }, }, Expected: testutil.MockList{ testutil.Mock(0x1), testutil.Mock(0x2), testutil.Mock(0x30).Parent(0x2), }, }, { Name: "Backfills missing blocks", Scenario: testutil.MockList{ testutil.Mock(0x3), testutil.Mock(0x4), }, History: testutil.MockList{ testutil.Mock(0x1), testutil.Mock(0x2), }, Reconcile: []Reconcile{ { block: testutil.Mock(0x5), event: &TestEvent{ Added: testutil.MockList{ testutil.Mock(0x3), testutil.Mock(0x4), testutil.Mock(0x5), }, }, }, }, Expected: testutil.MockList{ testutil.Mock(0x1), testutil.Mock(0x2), testutil.Mock(0x3), testutil.Mock(0x4), testutil.Mock(0x5), }, }, { Name: "Rolls back and backfills", Scenario: testutil.MockList{ testutil.Mock(0x30).Parent(0x2).Num(3), testutil.Mock(0x40).Parent(0x30).Num(4), }, History: testutil.MockList{ testutil.Mock(0x1), testutil.Mock(0x2), testutil.Mock(0x3), testutil.Mock(0x4), }, Reconcile: []Reconcile{ { block: testutil.Mock(0x50).Parent(0x40).Num(5), event: &TestEvent{ Added: testutil.MockList{ testutil.Mock(0x30).Parent(0x2).Num(3), testutil.Mock(0x40).Parent(0x30).Num(4), testutil.Mock(0x50).Parent(0x40).Num(5), }, Removed: testutil.MockList{ testutil.Mock(0x3), testutil.Mock(0x4), }, }, }, }, Expected: testutil.MockList{ testutil.Mock(0x1), testutil.Mock(0x2), testutil.Mock(0x30).Parent(0x2).Num(3), testutil.Mock(0x40).Parent(0x30).Num(4), testutil.Mock(0x50).Parent(0x40).Num(5), }, }, } for _, c := range cases { t.Run(c.Name, func(t *testing.T) { // safe check for now, we ma need to restart the tracker and mock client for every reconcile scenario? if len(c.Reconcile) != 1 { t.Fatal("only one reconcile supported so far") } m := &testutil.MockClient{} // add the full scenario with the logs m.AddScenario(c.Scenario) tt := NewBlockTracker(m) // build past block history for _, b := range c.History.ToBlocks() { tt.AddBlockLocked(b) } sub := tt.Subscribe() for _, b := range c.Reconcile { if err := tt.HandleReconcile(b.block.Block()); err != nil { t.Fatal(err) } if b.event == nil { continue } var blockEvnt *BlockEvent select { case blockEvnt = <-sub: case <-time.After(1 * time.Second): t.Fatal("block event timeout") } // check blocks if !testutil.CompareBlocks(b.event.Added.ToBlocks(), blockEvnt.Added) { t.Fatal("err") } if !testutil.CompareBlocks(b.event.Removed.ToBlocks(), blockEvnt.Removed) { t.Fatal("err") } } if !testutil.CompareBlocks(tt.blocks, c.Expected.ToBlocks()) { t.Fatal("bad") } }) } } ================================================ FILE: builtin/ens/artifacts/ENS.abi ================================================ [{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"label","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setSubnodeOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"ttl","type":"uint64"}],"name":"setTTL","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"ttl","outputs":[{"name":"","type":"uint64"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"resolver","type":"address"}],"name":"setResolver","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"label","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"resolver","type":"address"}],"name":"NewResolver","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"ttl","type":"uint64"}],"name":"NewTTL","type":"event"}] ================================================ FILE: builtin/ens/artifacts/Resolver.abi ================================================ [{"constant":true,"inputs":[{"name":"interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"},{"name":"contentTypes","type":"uint256"}],"name":"ABI","outputs":[{"name":"contentType","type":"uint256"},{"name":"data","type":"bytes"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"x","type":"bytes32"},{"name":"y","type":"bytes32"}],"name":"setPubkey","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"content","outputs":[{"name":"ret","type":"bytes32"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"addr","outputs":[{"name":"ret","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"contentType","type":"uint256"},{"name":"data","type":"bytes"}],"name":"setABI","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"name","outputs":[{"name":"ret","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"name","type":"string"}],"name":"setName","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"hash","type":"bytes32"}],"name":"setContent","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"pubkey","outputs":[{"name":"x","type":"bytes32"},{"name":"y","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"addr","type":"address"}],"name":"setAddr","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"ensAddr","type":"address"}],"payable":false,"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"a","type":"address"}],"name":"AddrChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes32"}],"name":"ContentChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"name","type":"string"}],"name":"NameChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"contentType","type":"uint256"}],"name":"ABIChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"x","type":"bytes32"},{"indexed":false,"name":"y","type":"bytes32"}],"name":"PubkeyChanged","type":"event"}] ================================================ FILE: builtin/ens/ens.go ================================================ // Code generated by ethgo/abigen. DO NOT EDIT. // Hash: bfee2618a5908e1a24f19dcce873d3b8e797374138dd7604f7b593db3cca5c17 // Version: 0.1.1 package ens import ( "fmt" "math/big" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/contract" "github.com/umbracle/ethgo/jsonrpc" ) var ( _ = big.NewInt _ = jsonrpc.NewClient ) // ENS is a solidity contract type ENS struct { c *contract.Contract } // DeployENS deploys a new ENS contract func DeployENS(provider *jsonrpc.Client, from ethgo.Address, args []interface{}, opts ...contract.ContractOption) (contract.Txn, error) { return contract.DeployContract(abiENS, binENS, args, opts...) } // NewENS creates a new instance of the contract at a specific address func NewENS(addr ethgo.Address, opts ...contract.ContractOption) *ENS { return &ENS{c: contract.NewContract(addr, abiENS, opts...)} } // calls // Owner calls the owner method in the solidity contract func (e *ENS) Owner(node [32]byte, block ...ethgo.BlockNumber) (retval0 ethgo.Address, err error) { var out map[string]interface{} var ok bool out, err = e.c.Call("owner", ethgo.EncodeBlock(block...), node) if err != nil { return } // decode outputs retval0, ok = out["0"].(ethgo.Address) if !ok { err = fmt.Errorf("failed to encode output at index 0") return } return } // Resolver calls the resolver method in the solidity contract func (e *ENS) Resolver(node [32]byte, block ...ethgo.BlockNumber) (retval0 ethgo.Address, err error) { var out map[string]interface{} var ok bool out, err = e.c.Call("resolver", ethgo.EncodeBlock(block...), node) if err != nil { return } // decode outputs retval0, ok = out["0"].(ethgo.Address) if !ok { err = fmt.Errorf("failed to encode output at index 0") return } return } // Ttl calls the ttl method in the solidity contract func (e *ENS) Ttl(node [32]byte, block ...ethgo.BlockNumber) (retval0 uint64, err error) { var out map[string]interface{} var ok bool out, err = e.c.Call("ttl", ethgo.EncodeBlock(block...), node) if err != nil { return } // decode outputs retval0, ok = out["0"].(uint64) if !ok { err = fmt.Errorf("failed to encode output at index 0") return } return } // txns // SetOwner sends a setOwner transaction in the solidity contract func (e *ENS) SetOwner(node [32]byte, owner ethgo.Address) (contract.Txn, error) { return e.c.Txn("setOwner", node, owner) } // SetResolver sends a setResolver transaction in the solidity contract func (e *ENS) SetResolver(node [32]byte, resolver ethgo.Address) (contract.Txn, error) { return e.c.Txn("setResolver", node, resolver) } // SetSubnodeOwner sends a setSubnodeOwner transaction in the solidity contract func (e *ENS) SetSubnodeOwner(node [32]byte, label [32]byte, owner ethgo.Address) (contract.Txn, error) { return e.c.Txn("setSubnodeOwner", node, label, owner) } // SetTTL sends a setTTL transaction in the solidity contract func (e *ENS) SetTTL(node [32]byte, ttl uint64) (contract.Txn, error) { return e.c.Txn("setTTL", node, ttl) } // events func (e *ENS) NewOwnerEventSig() ethgo.Hash { return e.c.GetABI().Events["NewOwner"].ID() } func (e *ENS) NewResolverEventSig() ethgo.Hash { return e.c.GetABI().Events["NewResolver"].ID() } func (e *ENS) NewTTLEventSig() ethgo.Hash { return e.c.GetABI().Events["NewTTL"].ID() } func (e *ENS) TransferEventSig() ethgo.Hash { return e.c.GetABI().Events["Transfer"].ID() } ================================================ FILE: builtin/ens/ens_artifacts.go ================================================ package ens import ( "encoding/hex" "fmt" "github.com/umbracle/ethgo/abi" ) var abiENS *abi.ABI // ENSAbi returns the abi of the ENS contract func ENSAbi() *abi.ABI { return abiENS } var binENS []byte // ENSBin returns the bin of the ENS contract func ENSBin() []byte { return binENS } func init() { var err error abiENS, err = abi.NewABI(abiENSStr) if err != nil { panic(fmt.Errorf("cannot parse ENS abi: %v", err)) } if len(binENSStr) != 0 { binENS, err = hex.DecodeString(binENSStr[2:]) if err != nil { panic(fmt.Errorf("cannot parse ENS bin: %v", err)) } } } var binENSStr = "0x6060604052341561000f57600080fd5b60008080526020527fad3228b676f7d3cd4284a5443f17f1962b36e491b30a40b2405849e597ba5fb58054600160a060020a033316600160a060020a0319909116179055610503806100626000396000f3006060604052600436106100825763ffffffff7c01000000000000000000000000000000000000000000000000000000006000350416630178b8bf811461008757806302571be3146100b957806306ab5923146100cf57806314ab9038146100f657806316a25cbd146101195780631896f70a1461014c5780635b0fc9c31461016e575b600080fd5b341561009257600080fd5b61009d600435610190565b604051600160a060020a03909116815260200160405180910390f35b34156100c457600080fd5b61009d6004356101ae565b34156100da57600080fd5b6100f4600435602435600160a060020a03604435166101c9565b005b341561010157600080fd5b6100f460043567ffffffffffffffff6024351661028b565b341561012457600080fd5b61012f600435610357565b60405167ffffffffffffffff909116815260200160405180910390f35b341561015757600080fd5b6100f4600435600160a060020a036024351661038e565b341561017957600080fd5b6100f4600435600160a060020a0360243516610434565b600090815260208190526040902060010154600160a060020a031690565b600090815260208190526040902054600160a060020a031690565b600083815260208190526040812054849033600160a060020a039081169116146101f257600080fd5b8484604051918252602082015260409081019051908190039020915083857fce0457fe73731f824cc272376169235128c118b49d344817417c6d108d155e8285604051600160a060020a03909116815260200160405180910390a3506000908152602081905260409020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03929092169190911790555050565b600082815260208190526040902054829033600160a060020a039081169116146102b457600080fd5b827f1d4f9bbfc9cab89d66e1a1562f2233ccbf1308cb4f63de2ead5787adddb8fa688360405167ffffffffffffffff909116815260200160405180910390a250600091825260208290526040909120600101805467ffffffffffffffff90921674010000000000000000000000000000000000000000027fffffffff0000000000000000ffffffffffffffffffffffffffffffffffffffff909216919091179055565b60009081526020819052604090206001015474010000000000000000000000000000000000000000900467ffffffffffffffff1690565b600082815260208190526040902054829033600160a060020a039081169116146103b757600080fd5b827f335721b01866dc23fbee8b6b2c7b1e14d6f05c28cd35a2c934239f94095602a083604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120600101805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03909216919091179055565b600082815260208190526040902054829033600160a060020a0390811691161461045d57600080fd5b827fd4735d920b0f87494915f556dd9b54c8f309026070caea5c737245152564d26683604051600160a060020a03909116815260200160405180910390a250600091825260208290526040909120805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a039092169190911790555600a165627a7a72305820f4c798d4c84c9912f389f64631e85e8d16c3e6644f8c2e1579936015c7d5f6660029" var abiENSStr = `[{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"resolver","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"owner","outputs":[{"name":"","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"label","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setSubnodeOwner","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"ttl","type":"uint64"}],"name":"setTTL","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"ttl","outputs":[{"name":"","type":"uint64"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"resolver","type":"address"}],"name":"setResolver","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"owner","type":"address"}],"name":"setOwner","outputs":[],"payable":false,"type":"function"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"label","type":"bytes32"},{"indexed":false,"name":"owner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"resolver","type":"address"}],"name":"NewResolver","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"ttl","type":"uint64"}],"name":"NewTTL","type":"event"}]` ================================================ FILE: builtin/ens/ens_resolver.go ================================================ package ens import ( "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/contract" "github.com/umbracle/ethgo/jsonrpc" "strings" ) type ENSResolver struct { e *ENS provider *jsonrpc.Eth } func NewENSResolver(addr ethgo.Address, provider *jsonrpc.Client) *ENSResolver { return &ENSResolver{NewENS(addr, contract.WithJsonRPC(provider.Eth())), provider.Eth()} } func (e *ENSResolver) Resolve(addr string, block ...ethgo.BlockNumber) (res ethgo.Address, err error) { addrHash := NameHash(addr) resolver, err := e.prepareResolver(addrHash, block...) if err != nil { return } res, err = resolver.Addr(addrHash, block...) return } func addressToReverseDomain(addr ethgo.Address) string { return strings.ToLower(strings.TrimPrefix(addr.String(), "0x")) + ".addr.reverse" } func (e *ENSResolver) ReverseResolve(addr ethgo.Address, block ...ethgo.BlockNumber) (res string, err error) { addrHash := NameHash(addressToReverseDomain(addr)) resolver, err := e.prepareResolver(addrHash, block...) if err != nil { return } res, err = resolver.Name(addrHash, block...) return } func (e *ENSResolver) prepareResolver(inputHash ethgo.Hash, block ...ethgo.BlockNumber) (*Resolver, error) { resolverAddr, err := e.e.Resolver(inputHash, block...) if err != nil { return nil, err } resolver := NewResolver(resolverAddr, contract.WithJsonRPC(e.provider)) return resolver, nil } ================================================ FILE: builtin/ens/ens_resolver_test.go ================================================ package ens import ( "encoding/hex" "testing" "github.com/stretchr/testify/assert" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/jsonrpc" "github.com/umbracle/ethgo/testutil" ) var ( mainnetAddr = ethgo.HexToAddress("0x314159265dD8dbb310642f98f50C066173C1259b") ) func TestResolveAddr(t *testing.T) { c, _ := jsonrpc.NewClient(testutil.TestInfuraEndpoint(t)) r := NewENSResolver(mainnetAddr, c) cases := []struct { Addr string Expected string }{ { Addr: "arachnid.eth", Expected: "0xfdb33f8ac7ce72d7d4795dd8610e323b4c122fbb", }, } for _, c := range cases { t.Run("", func(t *testing.T) { found, err := r.Resolve(c.Addr) assert.NoError(t, err) assert.Equal(t, "0x"+hex.EncodeToString(found[:]), c.Expected) }) } } ================================================ FILE: builtin/ens/resolver.go ================================================ // Code generated by ethgo/abigen. DO NOT EDIT. // Hash: 3d1ecdf4aa6a2c578e0c3bbb14cc28ae2c8ebc4495f7d6128959f961afd0f635 // Version: 0.1.1 package ens import ( "fmt" "math/big" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/contract" "github.com/umbracle/ethgo/jsonrpc" ) var ( _ = big.NewInt _ = jsonrpc.NewClient ) // Resolver is a solidity contract type Resolver struct { c *contract.Contract } // DeployResolver deploys a new Resolver contract func DeployResolver(provider *jsonrpc.Client, from ethgo.Address, args []interface{}, opts ...contract.ContractOption) (contract.Txn, error) { return contract.DeployContract(abiResolver, binResolver, args, opts...) } // NewResolver creates a new instance of the contract at a specific address func NewResolver(addr ethgo.Address, opts ...contract.ContractOption) *Resolver { return &Resolver{c: contract.NewContract(addr, abiResolver, opts...)} } // calls // ABI calls the ABI method in the solidity contract func (r *Resolver) ABI(node [32]byte, contentTypes *big.Int, block ...ethgo.BlockNumber) (retval0 *big.Int, retval1 []byte, err error) { var out map[string]interface{} var ok bool out, err = r.c.Call("ABI", ethgo.EncodeBlock(block...), node, contentTypes) if err != nil { return } // decode outputs retval0, ok = out["contentType"].(*big.Int) if !ok { err = fmt.Errorf("failed to encode output at index 0") return } retval1, ok = out["data"].([]byte) if !ok { err = fmt.Errorf("failed to encode output at index 1") return } return } // Addr calls the addr method in the solidity contract func (r *Resolver) Addr(node [32]byte, block ...ethgo.BlockNumber) (retval0 ethgo.Address, err error) { var out map[string]interface{} var ok bool out, err = r.c.Call("addr", ethgo.EncodeBlock(block...), node) if err != nil { return } // decode outputs retval0, ok = out["ret"].(ethgo.Address) if !ok { err = fmt.Errorf("failed to encode output at index 0") return } return } // Content calls the content method in the solidity contract func (r *Resolver) Content(node [32]byte, block ...ethgo.BlockNumber) (retval0 [32]byte, err error) { var out map[string]interface{} var ok bool out, err = r.c.Call("content", ethgo.EncodeBlock(block...), node) if err != nil { return } // decode outputs retval0, ok = out["ret"].([32]byte) if !ok { err = fmt.Errorf("failed to encode output at index 0") return } return } // Name calls the name method in the solidity contract func (r *Resolver) Name(node [32]byte, block ...ethgo.BlockNumber) (retval0 string, err error) { var out map[string]interface{} var ok bool out, err = r.c.Call("name", ethgo.EncodeBlock(block...), node) if err != nil { return } // decode outputs retval0, ok = out["ret"].(string) if !ok { err = fmt.Errorf("failed to encode output at index 0") return } return } // Pubkey calls the pubkey method in the solidity contract func (r *Resolver) Pubkey(node [32]byte, block ...ethgo.BlockNumber) (retval0 [32]byte, retval1 [32]byte, err error) { var out map[string]interface{} var ok bool out, err = r.c.Call("pubkey", ethgo.EncodeBlock(block...), node) if err != nil { return } // decode outputs retval0, ok = out["x"].([32]byte) if !ok { err = fmt.Errorf("failed to encode output at index 0") return } retval1, ok = out["y"].([32]byte) if !ok { err = fmt.Errorf("failed to encode output at index 1") return } return } // SupportsInterface calls the supportsInterface method in the solidity contract func (r *Resolver) SupportsInterface(interfaceID [4]byte, block ...ethgo.BlockNumber) (retval0 bool, err error) { var out map[string]interface{} var ok bool out, err = r.c.Call("supportsInterface", ethgo.EncodeBlock(block...), interfaceID) if err != nil { return } // decode outputs retval0, ok = out["0"].(bool) if !ok { err = fmt.Errorf("failed to encode output at index 0") return } return } // txns // SetABI sends a setABI transaction in the solidity contract func (r *Resolver) SetABI(node [32]byte, contentType *big.Int, data []byte) (contract.Txn, error) { return r.c.Txn("setABI", node, contentType, data) } // SetAddr sends a setAddr transaction in the solidity contract func (r *Resolver) SetAddr(node [32]byte, addr ethgo.Address) (contract.Txn, error) { return r.c.Txn("setAddr", node, addr) } // SetContent sends a setContent transaction in the solidity contract func (r *Resolver) SetContent(node [32]byte, hash [32]byte) (contract.Txn, error) { return r.c.Txn("setContent", node, hash) } // SetName sends a setName transaction in the solidity contract func (r *Resolver) SetName(node [32]byte, name string) (contract.Txn, error) { return r.c.Txn("setName", node, name) } // SetPubkey sends a setPubkey transaction in the solidity contract func (r *Resolver) SetPubkey(node [32]byte, x [32]byte, y [32]byte) (contract.Txn, error) { return r.c.Txn("setPubkey", node, x, y) } // events func (r *Resolver) ABIChangedEventSig() ethgo.Hash { return r.c.GetABI().Events["ABIChanged"].ID() } func (r *Resolver) AddrChangedEventSig() ethgo.Hash { return r.c.GetABI().Events["AddrChanged"].ID() } func (r *Resolver) ContentChangedEventSig() ethgo.Hash { return r.c.GetABI().Events["ContentChanged"].ID() } func (r *Resolver) NameChangedEventSig() ethgo.Hash { return r.c.GetABI().Events["NameChanged"].ID() } func (r *Resolver) PubkeyChangedEventSig() ethgo.Hash { return r.c.GetABI().Events["PubkeyChanged"].ID() } ================================================ FILE: builtin/ens/resolver_artifacts.go ================================================ package ens import ( "encoding/hex" "fmt" "github.com/umbracle/ethgo/abi" ) var abiResolver *abi.ABI // ResolverAbi returns the abi of the Resolver contract func ResolverAbi() *abi.ABI { return abiResolver } var binResolver []byte // ResolverBin returns the bin of the Resolver contract func ResolverBin() []byte { return binResolver } func init() { var err error abiResolver, err = abi.NewABI(abiResolverStr) if err != nil { panic(fmt.Errorf("cannot parse Resolver abi: %v", err)) } if len(binResolverStr) != 0 { binResolver, err = hex.DecodeString(binResolverStr[2:]) if err != nil { panic(fmt.Errorf("cannot parse Resolver bin: %v", err)) } } } var binResolverStr = "0x6060604052341561000f57600080fd5b6040516020806111b28339810160405280805160008054600160a060020a03909216600160a060020a0319909216919091179055505061115e806100546000396000f3006060604052600436106100ab5763ffffffff60e060020a60003504166301ffc9a781146100b057806310f13a8c146100e45780632203ab561461017e57806329cd62ea146102155780632dff6941146102315780633b3b57de1461025957806359d1d43c1461028b578063623195b014610358578063691f3431146103b457806377372213146103ca578063c3d014d614610420578063c869023314610439578063d5fa2b0014610467575b600080fd5b34156100bb57600080fd5b6100d0600160e060020a031960043516610489565b604051901515815260200160405180910390f35b34156100ef57600080fd5b61017c600480359060446024803590810190830135806020601f8201819004810201604051908101604052818152929190602084018383808284378201915050505050509190803590602001908201803590602001908080601f0160208091040260200160405190810160405281815292919060208401838380828437509496506105f695505050505050565b005b341561018957600080fd5b610197600435602435610807565b60405182815260406020820181815290820183818151815260200191508051906020019080838360005b838110156101d95780820151838201526020016101c1565b50505050905090810190601f1680156102065780820380516001836020036101000a031916815260200191505b50935050505060405180910390f35b341561022057600080fd5b61017c600435602435604435610931565b341561023c57600080fd5b610247600435610a30565b60405190815260200160405180910390f35b341561026457600080fd5b61026f600435610a46565b604051600160a060020a03909116815260200160405180910390f35b341561029657600080fd5b6102e1600480359060446024803590810190830135806020601f82018190048102016040519081016040528181529291906020840183838082843750949650610a6195505050505050565b60405160208082528190810183818151815260200191508051906020019080838360005b8381101561031d578082015183820152602001610305565b50505050905090810190601f16801561034a5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b341561036357600080fd5b61017c600480359060248035919060649060443590810190830135806020601f82018190048102016040519081016040528181529291906020840183838082843750949650610b8095505050505050565b34156103bf57600080fd5b6102e1600435610c7c565b34156103d557600080fd5b61017c600480359060446024803590810190830135806020601f82018190048102016040519081016040528181529291906020840183838082843750949650610d4295505050505050565b341561042b57600080fd5b61017c600435602435610e8c565b341561044457600080fd5b61044f600435610f65565b60405191825260208201526040908101905180910390f35b341561047257600080fd5b61017c600435600160a060020a0360243516610f82565b6000600160e060020a031982167f3b3b57de0000000000000000000000000000000000000000000000000000000014806104ec5750600160e060020a031982167fd8389dc500000000000000000000000000000000000000000000000000000000145b806105205750600160e060020a031982167f691f343100000000000000000000000000000000000000000000000000000000145b806105545750600160e060020a031982167f2203ab5600000000000000000000000000000000000000000000000000000000145b806105885750600160e060020a031982167fc869023300000000000000000000000000000000000000000000000000000000145b806105bc5750600160e060020a031982167f59d1d43c00000000000000000000000000000000000000000000000000000000145b806105f05750600160e060020a031982167f01ffc9a700000000000000000000000000000000000000000000000000000000145b92915050565b600080548491600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561064f57600080fd5b6102c65a03f1151561066057600080fd5b50505060405180519050600160a060020a031614151561067f57600080fd5b6000848152600160205260409081902083916005909101908590518082805190602001908083835b602083106106c65780518252601f1990920191602091820191016106a7565b6001836020036101000a038019825116818451168082178552505050505050905001915050908152602001604051809103902090805161070a929160200190611085565b50826040518082805190602001908083835b6020831061073b5780518252601f19909201916020918201910161071c565b6001836020036101000a0380198251168184511617909252505050919091019250604091505051908190039020847fd8c9334b1a9c2f9da342a0a2b32629c1a229b6445dad78947f674b44444a75508560405160208082528190810183818151815260200191508051906020019080838360005b838110156107c75780820151838201526020016107af565b50505050905090810190601f1680156107f45780820380516001836020036101000a031916815260200191505b509250505060405180910390a350505050565b6000610811611103565b60008481526001602081905260409091209092505b838311610924578284161580159061085f5750600083815260068201602052604081205460026000196101006001841615020190911604115b15610919578060060160008481526020019081526020016000208054600181600116156101000203166002900480601f01602080910402602001604051908101604052809291908181526020018280546001816001161561010002031660029004801561090d5780601f106108e25761010080835404028352916020019161090d565b820191906000526020600020905b8154815290600101906020018083116108f057829003601f168201915b50505050509150610929565b600290920291610826565b600092505b509250929050565b600080548491600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b151561098a57600080fd5b6102c65a03f1151561099b57600080fd5b50505060405180519050600160a060020a03161415156109ba57600080fd5b6040805190810160409081528482526020808301859052600087815260019091522060030181518155602082015160019091015550837f1d6f5e03d3f63eb58751986629a5439baee5079ff04f345becb66e23eb154e46848460405191825260208201526040908101905180910390a250505050565b6000908152600160208190526040909120015490565b600090815260016020526040902054600160a060020a031690565b610a69611103565b60008381526001602052604090819020600501908390518082805190602001908083835b60208310610aac5780518252601f199092019160209182019101610a8d565b6001836020036101000a03801982511681845116808217855250505050505090500191505090815260200160405180910390208054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610b735780601f10610b4857610100808354040283529160200191610b73565b820191906000526020600020905b815481529060010190602001808311610b5657829003601f168201915b5050505050905092915050565b600080548491600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610bd957600080fd5b6102c65a03f11515610bea57600080fd5b50505060405180519050600160a060020a0316141515610c0957600080fd5b6000198301831615610c1a57600080fd5b60008481526001602090815260408083208684526006019091529020828051610c47929160200190611085565b5082847faa121bbeef5f32f5961a2a28966e769023910fc9479059ee3495d4c1a696efe360405160405180910390a350505050565b610c84611103565b6001600083600019166000191681526020019081526020016000206002018054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610d365780601f10610d0b57610100808354040283529160200191610d36565b820191906000526020600020905b815481529060010190602001808311610d1957829003601f168201915b50505050509050919050565b600080548391600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610d9b57600080fd5b6102c65a03f11515610dac57600080fd5b50505060405180519050600160a060020a0316141515610dcb57600080fd5b6000838152600160205260409020600201828051610ded929160200190611085565b50827fb7d29e911041e8d9b843369e890bcb72c9388692ba48b65ac54e7214c4c348f78360405160208082528190810183818151815260200191508051906020019080838360005b83811015610e4d578082015183820152602001610e35565b50505050905090810190601f168015610e7a5780820380516001836020036101000a031916815260200191505b509250505060405180910390a2505050565b600080548391600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610ee557600080fd5b6102c65a03f11515610ef657600080fd5b50505060405180519050600160a060020a0316141515610f1557600080fd5b6000838152600160208190526040918290200183905583907f0424b6fe0d9c3bdbece0e7879dc241bb0c22e900be8b6c168b4ee08bd9bf83bc9084905190815260200160405180910390a2505050565b600090815260016020526040902060038101546004909101549091565b600080548391600160a060020a033381169216906302571be39084906040516020015260405160e060020a63ffffffff84160281526004810191909152602401602060405180830381600087803b1515610fdb57600080fd5b6102c65a03f11515610fec57600080fd5b50505060405180519050600160a060020a031614151561100b57600080fd5b60008381526001602052604090819020805473ffffffffffffffffffffffffffffffffffffffff1916600160a060020a03851617905583907f52d7d861f09ab3d26239d492e8968629f95e9e318cf0b73bfddc441522a15fd290849051600160a060020a03909116815260200160405180910390a2505050565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f106110c657805160ff19168380011785556110f3565b828001600101855582156110f3579182015b828111156110f35782518255916020019190600101906110d8565b506110ff929150611115565b5090565b60206040519081016040526000815290565b61112f91905b808211156110ff576000815560010161111b565b905600a165627a7a723058201ecacbc445b9fbcd91b0ab164389f69d7283b856883bc7437eeed1008345a4920029" var abiResolverStr = `[{"constant":true,"inputs":[{"name":"interfaceID","type":"bytes4"}],"name":"supportsInterface","outputs":[{"name":"","type":"bool"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"},{"name":"contentTypes","type":"uint256"}],"name":"ABI","outputs":[{"name":"contentType","type":"uint256"},{"name":"data","type":"bytes"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"x","type":"bytes32"},{"name":"y","type":"bytes32"}],"name":"setPubkey","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"content","outputs":[{"name":"ret","type":"bytes32"}],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"addr","outputs":[{"name":"ret","type":"address"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"contentType","type":"uint256"},{"name":"data","type":"bytes"}],"name":"setABI","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"name","outputs":[{"name":"ret","type":"string"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"name","type":"string"}],"name":"setName","outputs":[],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"hash","type":"bytes32"}],"name":"setContent","outputs":[],"payable":false,"type":"function"},{"constant":true,"inputs":[{"name":"node","type":"bytes32"}],"name":"pubkey","outputs":[{"name":"x","type":"bytes32"},{"name":"y","type":"bytes32"}],"payable":false,"type":"function"},{"constant":false,"inputs":[{"name":"node","type":"bytes32"},{"name":"addr","type":"address"}],"name":"setAddr","outputs":[],"payable":false,"type":"function"},{"inputs":[{"name":"ensAddr","type":"address"}],"payable":false,"type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"a","type":"address"}],"name":"AddrChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"hash","type":"bytes32"}],"name":"ContentChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"name","type":"string"}],"name":"NameChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":true,"name":"contentType","type":"uint256"}],"name":"ABIChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"node","type":"bytes32"},{"indexed":false,"name":"x","type":"bytes32"},{"indexed":false,"name":"y","type":"bytes32"}],"name":"PubkeyChanged","type":"event"}]` ================================================ FILE: builtin/ens/utils.go ================================================ package ens import ( "strings" "github.com/umbracle/ethgo" "golang.org/x/crypto/sha3" ) // NameHash returns the hash of an ENS name func NameHash(str string) (node ethgo.Hash) { if str == "" { return } aux := make([]byte, 32) hash := sha3.NewLegacyKeccak256() labels := strings.Split(str, ".") for i := len(labels) - 1; i >= 0; i-- { label := labels[i] hash.Write([]byte(label)) aux = hash.Sum(aux) // append the hash of the label to node hash.Reset() hash.Write(aux) aux = hash.Sum(aux[:0]) hash.Reset() } copy(node[:], aux) return } ================================================ FILE: builtin/ens/utils_test.go ================================================ package ens import ( "testing" "github.com/stretchr/testify/assert" ) func TestNameHash(t *testing.T) { cases := []struct { Name string Expected string }{ { Name: "eth", Expected: "0x93cdeb708b7545dc668eb9280176169d1c33cfd8ed6f04690a0bcc88a93fc4ae", }, { Name: "foo.eth", Expected: "0xde9b09fd7c5f901e23a3f19fecc54828e9c848539801e86591bd9801b019f84f", }, } for _, c := range cases { t.Run("", func(t *testing.T) { found := NameHash(c.Name) assert.Equal(t, c.Expected, found.String()) }) } } ================================================ FILE: builtin/erc20/artifacts/ERC20.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":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":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"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"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}] ================================================ FILE: builtin/erc20/erc20.go ================================================ // Code generated by ethgo/abigen. DO NOT EDIT. // Hash: a1a873d70d345feef023ee086fd6135b24d775444b950ee9d5ea411e72b0f373 // Version: 0.1.1 package erc20 import ( "fmt" "math/big" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/contract" "github.com/umbracle/ethgo/jsonrpc" ) var ( _ = big.NewInt _ = jsonrpc.NewClient ) // ERC20 is a solidity contract type ERC20 struct { c *contract.Contract } // NewERC20 creates a new instance of the contract at a specific address func NewERC20(addr ethgo.Address, opts ...contract.ContractOption) *ERC20 { return &ERC20{c: contract.NewContract(addr, abiERC20, opts...)} } // calls // Allowance calls the allowance method in the solidity contract func (e *ERC20) Allowance(owner ethgo.Address, spender ethgo.Address, block ...ethgo.BlockNumber) (retval0 *big.Int, err error) { var out map[string]interface{} var ok bool out, err = e.c.Call("allowance", ethgo.EncodeBlock(block...), owner, spender) if err != nil { return } // decode outputs retval0, ok = out["0"].(*big.Int) if !ok { err = fmt.Errorf("failed to encode output at index 0") return } return } // BalanceOf calls the balanceOf method in the solidity contract func (e *ERC20) BalanceOf(owner ethgo.Address, block ...ethgo.BlockNumber) (retval0 *big.Int, err error) { var out map[string]interface{} var ok bool out, err = e.c.Call("balanceOf", ethgo.EncodeBlock(block...), owner) if err != nil { return } // decode outputs retval0, ok = out["balance"].(*big.Int) if !ok { err = fmt.Errorf("failed to encode output at index 0") return } return } // Decimals calls the decimals method in the solidity contract func (e *ERC20) Decimals(block ...ethgo.BlockNumber) (retval0 uint8, err error) { var out map[string]interface{} var ok bool out, err = e.c.Call("decimals", ethgo.EncodeBlock(block...)) if err != nil { return } // decode outputs retval0, ok = out["0"].(uint8) if !ok { err = fmt.Errorf("failed to encode output at index 0") return } return } // Name calls the name method in the solidity contract func (e *ERC20) Name(block ...ethgo.BlockNumber) (retval0 string, err error) { var out map[string]interface{} var ok bool out, err = e.c.Call("name", ethgo.EncodeBlock(block...)) if err != nil { return } // decode outputs retval0, ok = out["0"].(string) if !ok { err = fmt.Errorf("failed to encode output at index 0") return } return } // Symbol calls the symbol method in the solidity contract func (e *ERC20) Symbol(block ...ethgo.BlockNumber) (retval0 string, err error) { var out map[string]interface{} var ok bool out, err = e.c.Call("symbol", ethgo.EncodeBlock(block...)) if err != nil { return } // decode outputs retval0, ok = out["0"].(string) if !ok { err = fmt.Errorf("failed to encode output at index 0") return } return } // TotalSupply calls the totalSupply method in the solidity contract func (e *ERC20) TotalSupply(block ...ethgo.BlockNumber) (retval0 *big.Int, err error) { var out map[string]interface{} var ok bool out, err = e.c.Call("totalSupply", ethgo.EncodeBlock(block...)) if err != nil { return } // decode outputs retval0, ok = out["0"].(*big.Int) if !ok { err = fmt.Errorf("failed to encode output at index 0") return } return } // txns // Approve sends a approve transaction in the solidity contract func (e *ERC20) Approve(spender ethgo.Address, value *big.Int) (contract.Txn, error) { return e.c.Txn("approve", spender, value) } // Transfer sends a transfer transaction in the solidity contract func (e *ERC20) Transfer(to ethgo.Address, value *big.Int) (contract.Txn, error) { return e.c.Txn("transfer", to, value) } // TransferFrom sends a transferFrom transaction in the solidity contract func (e *ERC20) TransferFrom(from ethgo.Address, to ethgo.Address, value *big.Int) (contract.Txn, error) { return e.c.Txn("transferFrom", from, to, value) } // events func (e *ERC20) ApprovalEventSig() ethgo.Hash { return e.c.GetABI().Events["Approval"].ID() } func (e *ERC20) TransferEventSig() ethgo.Hash { return e.c.GetABI().Events["Transfer"].ID() } ================================================ FILE: builtin/erc20/erc20_artifacts.go ================================================ package erc20 import ( "encoding/hex" "fmt" "github.com/umbracle/ethgo/abi" ) var abiERC20 *abi.ABI // ERC20Abi returns the abi of the ERC20 contract func ERC20Abi() *abi.ABI { return abiERC20 } var binERC20 []byte func init() { var err error abiERC20, err = abi.NewABI(abiERC20Str) if err != nil { panic(fmt.Errorf("cannot parse ERC20 abi: %v", err)) } if len(binERC20Str) != 0 { binERC20, err = hex.DecodeString(binERC20Str[2:]) if err != nil { panic(fmt.Errorf("cannot parse ERC20 bin: %v", err)) } } } var binERC20Str = "" var abiERC20Str = `[{"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":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":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[{"name":"_owner","type":"address"},{"name":"_spender","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"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"},{"anonymous":false,"inputs":[{"indexed":true,"name":"from","type":"address"},{"indexed":true,"name":"to","type":"address"},{"indexed":false,"name":"value","type":"uint256"}],"name":"Transfer","type":"event"}]` ================================================ FILE: builtin/erc20/erc20_test.go ================================================ package erc20 import ( "testing" "github.com/stretchr/testify/assert" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/contract" "github.com/umbracle/ethgo/jsonrpc" "github.com/umbracle/ethgo/testutil" ) var ( zeroX = ethgo.HexToAddress("0xe41d2489571d322189246dafa5ebde1f4699f498") ) func TestERC20Decimals(t *testing.T) { c, _ := jsonrpc.NewClient(testutil.TestInfuraEndpoint(t)) erc20 := NewERC20(zeroX, contract.WithJsonRPC(c.Eth())) decimals, err := erc20.Decimals() assert.NoError(t, err) if decimals != 18 { t.Fatal("bad") } } func TestERC20Name(t *testing.T) { c, _ := jsonrpc.NewClient(testutil.TestInfuraEndpoint(t)) erc20 := NewERC20(zeroX, contract.WithJsonRPC(c.Eth())) name, err := erc20.Name() assert.NoError(t, err) assert.Equal(t, name, "0x Protocol Token") } func TestERC20Symbol(t *testing.T) { c, _ := jsonrpc.NewClient(testutil.TestInfuraEndpoint(t)) erc20 := NewERC20(zeroX, contract.WithJsonRPC(c.Eth())) symbol, err := erc20.Symbol() assert.NoError(t, err) assert.Equal(t, symbol, "ZRX") } func TestTotalSupply(t *testing.T) { c, _ := jsonrpc.NewClient(testutil.TestInfuraEndpoint(t)) erc20 := NewERC20(zeroX, contract.WithJsonRPC(c.Eth())) supply, err := erc20.TotalSupply() assert.NoError(t, err) assert.Equal(t, supply.String(), "1000000000000000000000000000") } ================================================ FILE: cmd/abigen/abigen.go ================================================ package abigen import ( "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "io/ioutil" "os" "strings" "path/filepath" "github.com/umbracle/ethgo/compiler" ) func Parse(sources string, pckg string, output string) error { config := &config{ Package: pckg, Output: output, } if sources == "" { return fmt.Errorf("no source") } for _, source := range strings.Split(sources, ",") { matches, err := filepath.Glob(source) if err != nil { fmt.Printf("Failed to read files: %v", err) os.Exit(1) } if len(matches) == 0 { fmt.Printf("No match for source: %s\n", source) continue } for _, source := range matches { artifacts, err := process(source, config) if err != nil { fmt.Printf("Failed to parse sources: %v", err) os.Exit(1) } // hash the source file raw := sha256.Sum256([]byte(source)) hash := hex.EncodeToString(raw[:]) if err := gen(artifacts, config, hash); err != nil { fmt.Printf("Failed to generate sources: %v", err) os.Exit(1) } } } return nil } const ( solExt = 1 abiExt = 2 jsonExt = 3 ) func process(sources string, config *config) (map[string]*compiler.Artifact, error) { files := strings.Split(sources, ",") if len(files) == 0 { return nil, fmt.Errorf("input not found") } prev := -1 for _, f := range files { var ext int switch extt := filepath.Ext(f); extt { case ".abi": ext = abiExt case ".sol": ext = solExt case ".json": ext = jsonExt default: return nil, fmt.Errorf("file extension '%s' not found", extt) } if prev == -1 { prev = ext } else if ext != prev { return nil, fmt.Errorf("two file formats found") } } switch prev { case abiExt: return processAbi(files, config) case solExt: return processSolc(files) case jsonExt: return processJson(files) } return nil, nil } func processSolc(sources []string) (map[string]*compiler.Artifact, error) { c := compiler.NewSolidityCompiler("solc") raw, err := c.Compile(sources...) if err != nil { return nil, err } res := map[string]*compiler.Artifact{} for rawName, entry := range raw.Contracts { name := strings.Split(rawName, ":")[1] res[strings.Title(name)] = entry } return res, nil } func processAbi(sources []string, config *config) (map[string]*compiler.Artifact, error) { artifacts := map[string]*compiler.Artifact{} for _, abiPath := range sources { content, err := ioutil.ReadFile(abiPath) if err != nil { return nil, fmt.Errorf("failed to read abi file (%s): %v", abiPath, err) } // Use the name of the file to name the contract path, name := filepath.Split(abiPath) name = strings.TrimSuffix(name, filepath.Ext(name)) binPath := filepath.Join(path, name+".bin") bin, err := ioutil.ReadFile(binPath) if err != nil { // bin not found bin = []byte{} } artifacts[strings.Title(name)] = &compiler.Artifact{ Abi: string(content), Bin: string(bin), } } return artifacts, nil } type JSONArtifact struct { Bytecode string `json:"bytecode"` Abi json.RawMessage `json:"abi"` } func processJson(sources []string) (map[string]*compiler.Artifact, error) { artifacts := map[string]*compiler.Artifact{} for _, jsonPath := range sources { content, err := ioutil.ReadFile(jsonPath) if err != nil { return nil, fmt.Errorf("failed to read abi file (%s): %v", jsonPath, err) } // Use the name of the file to name the contract _, name := filepath.Split(jsonPath) name = strings.TrimSuffix(name, ".json") var art *JSONArtifact if err := json.Unmarshal(content, &art); err != nil { return nil, err } artifacts[strings.Title(name)] = &compiler.Artifact{ Abi: string(art.Abi), Bin: "0x" + art.Bytecode, } } return artifacts, nil } ================================================ FILE: cmd/abigen/gen.go ================================================ package abigen import ( "bytes" "fmt" "io/ioutil" "path/filepath" "reflect" "strings" "text/template" "github.com/umbracle/ethgo/abi" "github.com/umbracle/ethgo/cmd/version" "github.com/umbracle/ethgo/compiler" ) type config struct { Package string Output string } func cleanName(str string) string { return handleSnakeCase(strings.Trim(str, "_")) } func outputArg(str string) string { return str } func handleSnakeCase(str string) string { if !strings.Contains(str, "_") { return str } spl := strings.Split(str, "_") res := "" for indx, elem := range spl { if indx != 0 { elem = strings.Title(elem) } res += elem } return res } func funcName(str string) string { return strings.Title(handleSnakeCase(str)) } func encodeSimpleArg(typ *abi.Type) string { switch typ.Kind() { case abi.KindAddress: return "ethgo.Address" case abi.KindString: return "string" case abi.KindBool: return "bool" case abi.KindInt: return typ.GoType().String() case abi.KindUInt: return typ.GoType().String() case abi.KindFixedBytes: return fmt.Sprintf("[%d]byte", typ.Size()) case abi.KindBytes: return "[]byte" case abi.KindSlice: return "[]" + encodeSimpleArg(typ.Elem()) default: return fmt.Sprintf("input not done for type: %s", typ.String()) } } func encodeArg(str interface{}) string { arg, ok := str.(*abi.TupleElem) if !ok { panic("bad 1") } return encodeSimpleArg(arg.Elem) } func tupleLen(tuple interface{}) interface{} { if isNil(tuple) { return 0 } arg, ok := tuple.(*abi.Type) if !ok { panic("bad tuple") } return len(arg.TupleElems()) } func tupleElems(tuple interface{}) []interface{} { res := []interface{}{} if isNil(tuple) { return res } arg, ok := tuple.(*abi.Type) if !ok { panic("bad tuple") } for _, i := range arg.TupleElems() { res = append(res, i) } return res } func isNil(c interface{}) bool { return c == nil || (reflect.ValueOf(c).Kind() == reflect.Ptr && reflect.ValueOf(c).IsNil()) } func gen(artifacts map[string]*compiler.Artifact, config *config, hash string) error { funcMap := template.FuncMap{ "title": strings.Title, "clean": cleanName, "arg": encodeArg, "outputArg": outputArg, "funcName": funcName, "tupleElems": tupleElems, "tupleLen": tupleLen, } tmplAbi, err := template.New("eth-abi").Funcs(funcMap).Parse(templateAbiStr) if err != nil { return err } tmplBin, err := template.New("eth-abi").Funcs(funcMap).Parse(templateBinStr) if err != nil { return err } for name, artifact := range artifacts { // parse abi abi, err := abi.NewABI(artifact.Abi) if err != nil { return err } input := map[string]interface{}{ "Hash": hash, "Version": version.Version, "Ptr": strings.ToLower(string(name[0])), "Config": config, "Contract": artifact, "Abi": abi, "Name": name, } filename := strings.ToLower(name) var b bytes.Buffer if err := tmplAbi.Execute(&b, input); err != nil { return err } if err := ioutil.WriteFile(filepath.Join(config.Output, filename+".go"), []byte(b.Bytes()), 0644); err != nil { return err } b.Reset() if err := tmplBin.Execute(&b, input); err != nil { return err } if err := ioutil.WriteFile(filepath.Join(config.Output, filename+"_artifacts.go"), []byte(b.Bytes()), 0644); err != nil { return err } } return nil } var templateAbiStr = `// Code generated by ethgo/abigen. DO NOT EDIT. // Hash: {{.Hash}} // Version: {{.Version}} package {{.Config.Package}} import ( "fmt" "math/big" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/contract" "github.com/umbracle/ethgo/jsonrpc" ) var ( _ = big.NewInt _ = jsonrpc.NewClient ) // {{.Name}} is a solidity contract type {{.Name}} struct { c *contract.Contract } {{if .Contract.Bin}} // Deploy{{.Name}} deploys a new {{.Name}} contract func Deploy{{.Name}}(provider *jsonrpc.Client, from ethgo.Address, args []interface{}, opts ...contract.ContractOption) (contract.Txn, error) { return contract.DeployContract(abi{{.Name}}, bin{{.Name}}, args, opts...) } {{end}} // New{{.Name}} creates a new instance of the contract at a specific address func New{{.Name}}(addr ethgo.Address, opts ...contract.ContractOption) *{{.Name}} { return &{{.Name}}{c: contract.NewContract(addr, abi{{.Name}}, opts...)} } // calls {{range $key, $value := .Abi.Methods}}{{if .Const}} // {{funcName $key}} calls the {{$key}} method in the solidity contract func ({{$.Ptr}} *{{$.Name}}) {{funcName $key}}({{range $index, $val := tupleElems .Inputs}}{{if .Name}}{{clean .Name}}{{else}}val{{$index}}{{end}} {{arg .}}, {{end}}block ...ethgo.BlockNumber) ({{range $index, $val := tupleElems .Outputs}}retval{{$index}} {{arg .}}, {{end}}err error) { var out map[string]interface{} {{ $length := tupleLen .Outputs }}{{ if ne $length 0 }}var ok bool{{ end }} out, err = {{$.Ptr}}.c.Call("{{$key}}", ethgo.EncodeBlock(block...){{range $index, $val := tupleElems .Inputs}}, {{if .Name}}{{clean .Name}}{{else}}val{{$index}}{{end}}{{end}}) if err != nil { return } // decode outputs {{range $index, $val := tupleElems .Outputs}}retval{{$index}}, ok = out["{{if .Name}}{{.Name}}{{else}}{{$index}}{{end}}"].({{arg .}}) if !ok { err = fmt.Errorf("failed to encode output at index {{$index}}") return } {{end}} return } {{end}}{{end}} // txns {{range $key, $value := .Abi.Methods}}{{if not .Const}} // {{funcName $key}} sends a {{$key}} transaction in the solidity contract func ({{$.Ptr}} *{{$.Name}}) {{funcName $key}}({{range $index, $input := tupleElems .Inputs}}{{if $index}}, {{end}}{{clean .Name}} {{arg .}}{{end}}) (contract.Txn, error) { return {{$.Ptr}}.c.Txn("{{$key}}"{{range $index, $elem := tupleElems .Inputs}}, {{clean $elem.Name}}{{end}}) } {{end}}{{end}} // events {{range $key, $value := .Abi.Events}} func ({{$.Ptr}} *{{$.Name}}) {{funcName $key}}EventSig() ethgo.Hash { return {{$.Ptr}}.c.GetABI().Events["{{funcName $key}}"].ID() } {{end}}` var templateBinStr = `package {{.Config.Package}} import ( "encoding/hex" "fmt" "github.com/umbracle/ethgo/abi" ) var abi{{.Name}} *abi.ABI // {{.Name}}Abi returns the abi of the {{.Name}} contract func {{.Name}}Abi() *abi.ABI { return abi{{.Name}} } var bin{{.Name}} []byte {{if .Contract.Bin}} // {{.Name}}Bin returns the bin of the {{.Name}} contract func {{.Name}}Bin() []byte { return bin{{.Name}} } {{end}} func init() { var err error abi{{.Name}}, err = abi.NewABI(abi{{.Name}}Str) if err != nil { panic(fmt.Errorf("cannot parse {{.Name}} abi: %v", err)) } if len(bin{{.Name}}Str) != 0 { bin{{.Name}}, err = hex.DecodeString(bin{{.Name}}Str[2:]) if err != nil { panic(fmt.Errorf("cannot parse {{.Name}} bin: %v", err)) } } } var bin{{.Name}}Str = "{{.Contract.Bin}}" var abi{{.Name}}Str = ` + "`" + `{{.Contract.Abi}}` + "`\n" ================================================ FILE: cmd/abigen/testdata/testdata.abi ================================================ [ { "constant": false, "inputs": [ { "name": "_val1", "type": "address" }, { "name": "_val2", "type": "uint256" } ], "name": "txnBasicInput", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "callBasicInput", "outputs": [ { "name": "", "type": "uint256" }, { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "owner", "type": "address" }, { "indexed": true, "name": "spender", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "eventBasic", "type": "event" } ] ================================================ FILE: cmd/abigen/testdata/testdata.go ================================================ // Code generated by ethgo/abigen. DO NOT EDIT. // Hash: 3f1af52b391dcf1991b5cee7468a69f382cfa0f819eaff85474464c969fe7ea9 // Version: 0.1.1 package testdata import ( "fmt" "math/big" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/contract" "github.com/umbracle/ethgo/jsonrpc" ) var ( _ = big.NewInt _ = jsonrpc.NewClient ) // Testdata is a solidity contract type Testdata struct { c *contract.Contract } // NewTestdata creates a new instance of the contract at a specific address func NewTestdata(addr ethgo.Address, opts ...contract.ContractOption) *Testdata { return &Testdata{c: contract.NewContract(addr, abiTestdata, opts...)} } // calls // CallBasicInput calls the callBasicInput method in the solidity contract func (t *Testdata) CallBasicInput(block ...ethgo.BlockNumber) (retval0 *big.Int, retval1 ethgo.Address, err error) { var out map[string]interface{} var ok bool out, err = t.c.Call("callBasicInput", ethgo.EncodeBlock(block...)) if err != nil { return } // decode outputs retval0, ok = out["0"].(*big.Int) if !ok { err = fmt.Errorf("failed to encode output at index 0") return } retval1, ok = out["1"].(ethgo.Address) if !ok { err = fmt.Errorf("failed to encode output at index 1") return } return } // txns // TxnBasicInput sends a txnBasicInput transaction in the solidity contract func (t *Testdata) TxnBasicInput(val1 ethgo.Address, val2 *big.Int) (contract.Txn, error) { return t.c.Txn("txnBasicInput", val1, val2) } // events func (t *Testdata) EventBasicEventSig() ethgo.Hash { return t.c.GetABI().Events["EventBasic"].ID() } ================================================ FILE: cmd/abigen/testdata/testdata_artifacts.go ================================================ package testdata import ( "encoding/hex" "fmt" "github.com/umbracle/ethgo/abi" ) var abiTestdata *abi.ABI // TestdataAbi returns the abi of the Testdata contract func TestdataAbi() *abi.ABI { return abiTestdata } var binTestdata []byte func init() { var err error abiTestdata, err = abi.NewABI(abiTestdataStr) if err != nil { panic(fmt.Errorf("cannot parse Testdata abi: %v", err)) } if len(binTestdataStr) != 0 { binTestdata, err = hex.DecodeString(binTestdataStr[2:]) if err != nil { panic(fmt.Errorf("cannot parse Testdata bin: %v", err)) } } } var binTestdataStr = "" var abiTestdataStr = `[ { "constant": false, "inputs": [ { "name": "_val1", "type": "address" }, { "name": "_val2", "type": "uint256" } ], "name": "txnBasicInput", "outputs": [ { "name": "", "type": "bool" } ], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "callBasicInput", "outputs": [ { "name": "", "type": "uint256" }, { "name": "", "type": "address" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "anonymous": false, "inputs": [ { "indexed": true, "name": "owner", "type": "address" }, { "indexed": true, "name": "spender", "type": "address" }, { "indexed": false, "name": "value", "type": "uint256" } ], "name": "eventBasic", "type": "event" } ]` ================================================ FILE: cmd/commands/4byte.go ================================================ package commands import ( "github.com/mitchellh/cli" fourbyte "github.com/umbracle/ethgo/4byte" ) // FourByteCommand is the command to resolve 4byte actions type FourByteCommand struct { UI cli.Ui } // Help implements the cli.Command interface func (c *FourByteCommand) Help() string { return `Usage: ethgo 4byte [signature] Resolve a 4byte signature` } // Synopsis implements the cli.Command interface func (c *FourByteCommand) Synopsis() string { return "Resolve a 4byte signature" } // Run implements the cli.Command interface func (c *FourByteCommand) Run(args []string) int { if len(args) == 0 { c.UI.Output("No arguments provided") return 1 } found, err := fourbyte.Resolve(args[0]) if err != nil { c.UI.Output(err.Error()) return 1 } if found == "" { c.UI.Output("Resolve not found") return 1 } c.UI.Output(found) return 0 } ================================================ FILE: cmd/commands/abigen.go ================================================ package commands import ( flag "github.com/spf13/pflag" "github.com/umbracle/ethgo/cmd/abigen" ) // VersionCommand is the command to show the version of the agent type AbigenCommand struct { *baseCommand source string pckg string output string } // Help implements the cli.Command interface func (c *AbigenCommand) Help() string { return `Usage: ethgo abigen Compute the abigen. ` + c.Flags().FlagUsages() } // Synopsis implements the cli.Command interface func (c *AbigenCommand) Synopsis() string { return "Compute the abigen" } func (c *AbigenCommand) Flags() *flag.FlagSet { flags := c.baseCommand.Flags("abigen") flags.StringVar(&c.source, "source", "", "Source data") flags.StringVar(&c.pckg, "package", "main", "Name of the package") flags.StringVar(&c.output, "output", ".", "Output directory") return flags } // Run implements the cli.Command interface func (c *AbigenCommand) Run(args []string) int { flags := c.Flags() if err := flags.Parse(args); err != nil { c.UI.Error(err.Error()) return 1 } if err := abigen.Parse(c.source, c.pckg, c.output); err != nil { c.UI.Error(err.Error()) return 1 } return 0 } ================================================ FILE: cmd/commands/commands.go ================================================ package commands import ( "os" "github.com/mitchellh/cli" flag "github.com/spf13/pflag" ) func Commands() map[string]cli.CommandFactory { ui := &cli.BasicUi{ Reader: os.Stdin, Writer: os.Stdout, ErrorWriter: os.Stderr, } baseCommand := &baseCommand{ UI: ui, } return map[string]cli.CommandFactory{ "abigen": func() (cli.Command, error) { return &AbigenCommand{ baseCommand: baseCommand, }, nil }, "4byte": func() (cli.Command, error) { return &FourByteCommand{ UI: ui, }, nil }, "ens": func() (cli.Command, error) { return &EnsCommand{ UI: ui, }, nil }, "ens resolve": func() (cli.Command, error) { return &EnsResolveCommand{ UI: ui, }, nil }, "version": func() (cli.Command, error) { return &VersionCommand{ UI: ui, }, nil }, } } type baseCommand struct { UI cli.Ui } func (b *baseCommand) Flags(name string) *flag.FlagSet { flags := flag.NewFlagSet(name, 0) return flags } ================================================ FILE: cmd/commands/ens.go ================================================ package commands import ( "github.com/mitchellh/cli" ) // EnsCommand is the group command for ens type EnsCommand struct { UI cli.Ui } // Help implements the cli.Command interface func (c *EnsCommand) Help() string { return `Usage: ethgo ens Interact with ens` } // Synopsis implements the cli.Command interface func (c *EnsCommand) Synopsis() string { return "Interact with ens" } // Run implements the cli.Command interface func (c *EnsCommand) Run(args []string) int { return 0 } ================================================ FILE: cmd/commands/ens_resolve.go ================================================ package commands import ( "os" "github.com/mitchellh/cli" flag "github.com/spf13/pflag" "github.com/umbracle/ethgo/ens" ) func defaultJsonRPCProvider() string { if provider := os.Getenv("JSONRPC_PROVIDER"); provider != "" { return provider } return "http://localhost:8545" } // EnsResolveCommand is the command to resolve an ens name type EnsResolveCommand struct { UI cli.Ui provider string } // Help implements the cli.Command interface func (c *EnsResolveCommand) Help() string { return `Usage: ethgo ens resolve Resolve an ens name ` + c.Flags().FlagUsages() } // Synopsis implements the cli.Command interface func (c *EnsResolveCommand) Synopsis() string { return "Resolve an ens name" } func (c *EnsResolveCommand) Flags() *flag.FlagSet { flags := flag.NewFlagSet("ens resolve", flag.PanicOnError) flags.StringVar(&c.provider, "provider", defaultJsonRPCProvider(), "") return flags } // Run implements the cli.Command interface func (c *EnsResolveCommand) Run(args []string) int { flags := c.Flags() if err := flags.Parse(args); err != nil { c.UI.Error(err.Error()) return 1 } args = flags.Args() if len(args) != 1 { c.UI.Error("one argument expected") return 1 } e, err := ens.NewENS(ens.WithAddress(c.provider)) if err != nil { c.UI.Error(err.Error()) return 1 } addr, err := e.Resolve(args[0]) if err != nil { c.UI.Error(err.Error()) return 1 } c.UI.Output(addr.String()) return 0 } ================================================ FILE: cmd/commands/version.go ================================================ package commands import ( "github.com/mitchellh/cli" "github.com/umbracle/ethgo/cmd/version" ) // VersionCommand is the command to show the version of the agent type VersionCommand struct { UI cli.Ui } // Help implements the cli.Command interface func (c *VersionCommand) Help() string { return `Usage: ethgo version Display the Ethgo version` } // Synopsis implements the cli.Command interface func (c *VersionCommand) Synopsis() string { return "Display the Ethgo version" } // Run implements the cli.Command interface func (c *VersionCommand) Run(args []string) int { c.UI.Output(version.GetVersion()) return 0 } ================================================ FILE: cmd/go.mod ================================================ module github.com/umbracle/ethgo/cmd go 1.17 require ( github.com/mitchellh/cli v1.1.2 github.com/umbracle/ethgo v0.0.0-20220303093617-1621d9ff042b ) require github.com/spf13/pflag v1.0.5 require ( github.com/Masterminds/goutils v1.1.0 // indirect github.com/Masterminds/semver v1.5.0 // indirect github.com/Masterminds/sprig v2.22.0+incompatible // indirect github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 // indirect github.com/bgentry/speakeasy v0.1.0 // indirect github.com/btcsuite/btcd v0.22.1 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce // indirect github.com/fatih/color v1.7.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/uuid v1.1.2 // indirect github.com/gorilla/websocket v1.4.1 // indirect github.com/hashicorp/errwrap v1.0.0 // indirect github.com/hashicorp/go-multierror v1.0.0 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.11 // indirect github.com/klauspost/compress v1.4.1 // indirect github.com/klauspost/cpuid v1.2.0 // indirect github.com/mattn/go-colorable v0.0.9 // indirect github.com/mattn/go-isatty v0.0.3 // indirect github.com/mitchellh/copystructure v1.0.0 // indirect github.com/mitchellh/mapstructure v1.1.2 // indirect github.com/mitchellh/reflectwalk v1.0.0 // indirect github.com/posener/complete v1.1.1 // indirect github.com/tyler-smith/go-bip39 v1.1.0 // indirect github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasthttp v1.4.0 // indirect github.com/valyala/fastjson v1.4.1 // indirect golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 // indirect golang.org/x/text v0.3.2 // indirect ) replace github.com/umbracle/ethgo => ../ ================================================ FILE: cmd/go.sum ================================================ bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/Masterminds/goutils v1.1.0 h1:zukEsf/1JZwCMgHiK3GZftabmxiCw4apj3a28RPBiVg= github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= github.com/Masterminds/sprig v2.22.0+incompatible h1:z4yfnGrZ7netVz+0EDJ0Wi+5VZCSYp4Z0m2dk6cEM60= github.com/Masterminds/sprig v2.22.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= github.com/Microsoft/go-winio v0.4.13 h1:Hmi80lzZuI/CaYmlJp/b+FjZdRZhKu9c2mDVqKlLWVs= github.com/Microsoft/go-winio v0.4.13/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/boltdb/bolt v1.3.1/go.mod h1:clJnj/oiGkjum5o1McbSZDSLxVThjynRyGBgiAx27Ps= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c= github.com/btcsuite/btcd v0.22.1/go.mod h1:wqgTSL29+50LRkmOVknEdmt8ZojIzhuWvgu/iptuN7Y= 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/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce h1:YtWJF7RHm2pYCvA5t0RPmAaLUhREsKuKd+SLhxFbFeQ= github.com/btcsuite/btcutil v1.0.3-0.20201208143702-a53e38424cce/go.mod h1:0DVlHczLPewLcPGEIeUEzfOJhqGPQ0mJJRDBtD307+o= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/containerd/continuity v0.0.0-20191214063359-1097c8bae83b h1:pik3LX++5O3UiNWv45wfP/WT81l7ukBJzd3uUiifbSU= github.com/containerd/continuity v0.0.0-20191214063359-1097c8bae83b/go.mod h1:Dq467ZllaHgAtVp4p1xUQWBrFXR9s/wyoTpG8zOJGkY= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= 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.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 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/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 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/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/hashicorp/errwrap v1.0.0 h1:hLrqtEDnRye3+sgx6z4qVLNuviH3MR5aQ0ykNJa/UYA= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/go-multierror v1.0.0 h1:iVjPR7a6H0tWELX5NxNe7bYopibicUzc7uPribsnS6o= github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/huandu/xstrings v1.3.2 h1:L18LIDzqlW6xN2rEkpdV8+oL/IXWJ1APd+vsdYy4Wdw= github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= github.com/imdario/mergo v0.3.11 h1:3tnifQM4i+fbajXKBHXWEH+KvNHqojZ778UH75j3bGA= github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.4.1 h1:8VMb5+0wMgdBykOV96DwNwKFQ+WTI4pzYURP99CcB9E= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 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/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3 h1:ns/ykhmWi7G9O+8a448SecJU3nSMBXJfqQkl0upE1jI= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mitchellh/cli v1.1.2 h1:PvH+lL2B7IQ101xQL63Of8yFS2y+aDlsFcsqNc+u/Kw= github.com/mitchellh/cli v1.1.2/go.mod h1:6iaV0fGdElS6dPBx0EApTxHrcWvmJphyh2n8YBLPPZ4= 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/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= github.com/mitchellh/reflectwalk v1.0.0 h1:9D+8oIskB4VJBN5SFlmc27fSlIBZaov1Wpk/IfikLNY= github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ= github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/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/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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722 h1:10Nbw6cACsnQm7r34zlpJky+IzxVLRk6MKTS2d3Vp0E= github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722/go.mod h1:c8J0h9aULj2i3umrfyestM6jCq0LK0U6ly6bWy96nd4= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.4.0 h1:PuaTGZIw3mjYhhhbVbCQp8aciRZN9YdoB7MGX9Ko76A= github.com/valyala/fasthttp v1.4.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE= github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/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-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200510223506-06a226fb4e37/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/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-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= 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/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= 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.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= ================================================ FILE: cmd/main.go ================================================ package main import ( "fmt" "os" "github.com/mitchellh/cli" "github.com/umbracle/ethgo/cmd/commands" ) func main() { os.Exit(Run(os.Args[1:])) } // Run starts the cli func Run(args []string) int { commands := commands.Commands() cli := &cli.CLI{ Name: "ethgo", Args: args, Commands: commands, } exitCode, err := cli.Run() if err != nil { fmt.Fprintf(os.Stderr, "Error executing CLI: %s\n", err.Error()) return 1 } return exitCode } ================================================ FILE: cmd/version/version.go ================================================ package version import "fmt" var ( // GitCommit is the git commit that was compiled. GitCommit string // Version is the main version at the moment. Version = "0.1.3" // VersionPrerelease is a marker for the version. VersionPrerelease = "" ) // GetVersion returns a string representation of the version func GetVersion() string { version := Version release := VersionPrerelease if release != "" { version += fmt.Sprintf("-%s", release) if GitCommit != "" { version += fmt.Sprintf(" (%s)", GitCommit) } } return version } ================================================ FILE: compiler/fixtures/ballot.sol ================================================ pragma solidity >=0.4.22 <0.6.0; /// @title Voting with delegation. contract Ballot { // This declares a new complex type which will // be used for variables later. // It will represent a single voter. struct Voter { uint weight; // weight is accumulated by delegation bool voted; // if true, that person already voted address delegate; // person delegated to uint vote; // index of the voted proposal } // This is a type for a single proposal. struct Proposal { bytes32 name; // short name (up to 32 bytes) uint voteCount; // number of accumulated votes } address public chairperson; // This declares a state variable that // stores a `Voter` struct for each possible address. mapping(address => Voter) public voters; // A dynamically-sized array of `Proposal` structs. Proposal[] public proposals; /// Create a new ballot to choose one of `proposalNames`. constructor(bytes32[] memory proposalNames) public { chairperson = msg.sender; voters[chairperson].weight = 1; // For each of the provided proposal names, // create a new proposal object and add it // to the end of the array. for (uint i = 0; i < proposalNames.length; i++) { // `Proposal({...})` creates a temporary // Proposal object and `proposals.push(...)` // appends it to the end of `proposals`. proposals.push(Proposal({ name: proposalNames[i], voteCount: 0 })); } } // Give `voter` the right to vote on this ballot. // May only be called by `chairperson`. function giveRightToVote(address voter) public { // If the first argument of `require` evaluates // to `false`, execution terminates and all // changes to the state and to Ether balances // are reverted. // This used to consume all gas in old EVM versions, but // not anymore. // It is often a good idea to use `require` to check if // functions are called correctly. // As a second argument, you can also provide an // explanation about what went wrong. require( msg.sender == chairperson, "Only chairperson can give right to vote." ); require( !voters[voter].voted, "The voter already voted." ); require(voters[voter].weight == 0); voters[voter].weight = 1; } /// Delegate your vote to the voter `to`. function delegate(address to) public { // assigns reference Voter storage sender = voters[msg.sender]; require(!sender.voted, "You already voted."); require(to != msg.sender, "Self-delegation is disallowed."); // Forward the delegation as long as // `to` also delegated. // In general, such loops are very dangerous, // because if they run too long, they might // need more gas than is available in a block. // In this case, the delegation will not be executed, // but in other situations, such loops might // cause a contract to get "stuck" completely. while (voters[to].delegate != address(0)) { to = voters[to].delegate; // We found a loop in the delegation, not allowed. require(to != msg.sender, "Found loop in delegation."); } // Since `sender` is a reference, this // modifies `voters[msg.sender].voted` sender.voted = true; sender.delegate = to; Voter storage delegate_ = voters[to]; if (delegate_.voted) { // If the delegate already voted, // directly add to the number of votes proposals[delegate_.vote].voteCount += sender.weight; } else { // If the delegate did not vote yet, // add to her weight. delegate_.weight += sender.weight; } } /// Give your vote (including votes delegated to you) /// to proposal `proposals[proposal].name`. function vote(uint proposal) public { Voter storage sender = voters[msg.sender]; require(sender.weight != 0, "Has no right to vote"); require(!sender.voted, "Already voted."); sender.voted = true; sender.vote = proposal; // If `proposal` is out of the range of the array, // this will throw automatically and revert all // changes. proposals[proposal].voteCount += sender.weight; } /// @dev Computes the winning proposal taking all /// previous votes into account. function winningProposal() public view returns (uint winningProposal_) { uint winningVoteCount = 0; for (uint p = 0; p < proposals.length; p++) { if (proposals[p].voteCount > winningVoteCount) { winningVoteCount = proposals[p].voteCount; winningProposal_ = p; } } } // Calls winningProposal() function to get the index // of the winner contained in the proposals array and then // returns the name of the winner function winnerName() public view returns (bytes32 winnerName_) { winnerName_ = proposals[winningProposal()].name; } } ================================================ FILE: compiler/fixtures/simple_auction.sol ================================================ pragma solidity >=0.4.22 <0.6.0; contract SimpleAuction { // Parameters of the auction. Times are either // absolute unix timestamps (seconds since 1970-01-01) // or time periods in seconds. address payable public beneficiary; uint public auctionEndTime; // Current state of the auction. address public highestBidder; uint public highestBid; // Allowed withdrawals of previous bids mapping(address => uint) pendingReturns; // Set to true at the end, disallows any change. // By default initialized to `false`. bool ended; // Events that will be emitted on changes. event HighestBidIncreased(address bidder, uint amount); event AuctionEnded(address winner, uint amount); // The following is a so-called natspec comment, // recognizable by the three slashes. // It will be shown when the user is asked to // confirm a transaction. /// Create a simple auction with `_biddingTime` /// seconds bidding time on behalf of the /// beneficiary address `_beneficiary`. constructor( uint _biddingTime, address payable _beneficiary ) public { beneficiary = _beneficiary; auctionEndTime = now + _biddingTime; } /// Bid on the auction with the value sent /// together with this transaction. /// The value will only be refunded if the /// auction is not won. function bid() public payable { // No arguments are necessary, all // information is already part of // the transaction. The keyword payable // is required for the function to // be able to receive Ether. // Revert the call if the bidding // period is over. require( now <= auctionEndTime, "Auction already ended." ); // If the bid is not higher, send the // money back. require( msg.value > highestBid, "There already is a higher bid." ); if (highestBid != 0) { // Sending back the money by simply using // highestBidder.send(highestBid) is a security risk // because it could execute an untrusted contract. // It is always safer to let the recipients // withdraw their money themselves. pendingReturns[highestBidder] += highestBid; } highestBidder = msg.sender; highestBid = msg.value; emit HighestBidIncreased(msg.sender, msg.value); } /// Withdraw a bid that was overbid. function withdraw() public returns (bool) { uint amount = pendingReturns[msg.sender]; if (amount > 0) { // It is important to set this to zero because the recipient // can call this function again as part of the receiving call // before `send` returns. pendingReturns[msg.sender] = 0; if (!msg.sender.send(amount)) { // No need to call throw here, just reset the amount owing pendingReturns[msg.sender] = amount; return false; } } return true; } /// End the auction and send the highest bid /// to the beneficiary. function auctionEnd() public { // It is a good guideline to structure functions that interact // with other contracts (i.e. they call functions or send Ether) // into three phases: // 1. checking conditions // 2. performing actions (potentially changing conditions) // 3. interacting with other contracts // If these phases are mixed up, the other contract could call // back into the current contract and modify the state or cause // effects (ether payout) to be performed multiple times. // If functions called internally include interaction with external // contracts, they also have to be considered interaction with // external contracts. // 1. Conditions require(now >= auctionEndTime, "Auction not yet ended."); require(!ended, "auctionEnd has already been called."); // 2. Effects ended = true; emit AuctionEnded(highestBidder, highestBid); // 3. Interaction beneficiary.transfer(highestBid); } } ================================================ FILE: compiler/solidity.go ================================================ package compiler import ( "bytes" "encoding/json" "fmt" "io" "io/ioutil" "net/http" "os" "os/exec" "path/filepath" "strings" ) type Output struct { Contracts map[string]*Artifact Sources map[string]*Source Version string } type Source struct { AST map[string]interface{} } type Artifact struct { Abi string Bin string BinRuntime string `json:"bin-runtime"` SrcMap string `json:"srcmap"` SrcMapRuntime string `json:"srcmap-runtime"` } // Solidity is the solidity compiler type Solidity struct { path string } // NewSolidityCompiler instantiates a new solidity compiler func NewSolidityCompiler(path string) *Solidity { return &Solidity{path} } // CompileCode compiles a solidity code func (s *Solidity) CompileCode(code string) (*Output, error) { if code == "" { return nil, fmt.Errorf("code is empty") } output, err := s.compileImpl(code) if err != nil { return nil, err } return output, nil } // Compile implements the compiler interface func (s *Solidity) Compile(files ...string) (*Output, error) { if len(files) == 0 { return nil, fmt.Errorf("no input files") } return s.compileImpl("", files...) } func (s *Solidity) compileImpl(code string, files ...string) (*Output, error) { args := []string{ "--combined-json", "bin,bin-runtime,srcmap-runtime,abi,srcmap,ast", } if code != "" { args = append(args, "-") } if len(files) != 0 { args = append(args, files...) } var stdout, stderr bytes.Buffer cmd := exec.Command(s.path, args...) if code != "" { cmd.Stdin = strings.NewReader(code) } cmd.Stdout = &stdout cmd.Stderr = &stderr if err := cmd.Run(); err != nil { return nil, fmt.Errorf("failed to compile: %s", string(stderr.Bytes())) } var output *Output if err := json.Unmarshal(stdout.Bytes(), &output); err != nil { return nil, err } return output, nil } // DownloadSolidity downloads the solidity compiler func DownloadSolidity(version string, dst string, renameDst bool) error { url := "https://github.com/ethereum/solidity/releases/download/v" + version + "/solc-static-linux" // check if the dst is correct exists := false fi, err := os.Stat(dst) if err == nil { switch mode := fi.Mode(); { case mode.IsDir(): exists = true case mode.IsRegular(): return fmt.Errorf("dst is a file") } } else { if !os.IsNotExist(err) { return fmt.Errorf("failed to stat dst '%s': %v", dst, err) } } // create the destiny path if does not exists if !exists { if err := os.MkdirAll(dst, 0755); err != nil { return fmt.Errorf("cannot create dst path: %v", err) } } // rename binary name := "solidity" if renameDst { name += "-" + version } // tmp folder to download the binary tmpDir, err := ioutil.TempDir("/tmp", "solc-") if err != nil { return err } defer os.RemoveAll(tmpDir) path := filepath.Join(tmpDir, name) // Get the data resp, err := http.Get(url) if err != nil { return err } defer resp.Body.Close() // Create the file out, err := os.Create(path) if err != nil { return err } defer out.Close() // Write the body to file _, err = io.Copy(out, resp.Body) if err != nil { return err } // make binary executable if err := os.Chmod(path, 0755); err != nil { return err } // move file to dst if err := os.Rename(path, filepath.Join(dst, name)); err != nil { return err } return nil } ================================================ FILE: compiler/solidity_test.go ================================================ package compiler import ( "bytes" "io/ioutil" "os" "os/exec" "path/filepath" "reflect" "strings" "testing" "github.com/stretchr/testify/assert" ) var ( solcDir = "/tmp/ethgo-solc" solcPath = solcDir + "/solidity" ) func init() { _, err := os.Stat(solcDir) if err == nil { // already exists return } if !os.IsNotExist(err) { panic(err) } // solc folder does not exists if err := DownloadSolidity("0.5.5", solcDir, false); err != nil { panic(err) } } func TestSolidityInline(t *testing.T) { solc := NewSolidityCompiler(solcPath) cases := []struct { code string contracts []string }{ { ` pragma solidity >0.0.0; contract foo{} `, []string{ "foo", }, }, { ` pragma solidity >0.0.0; contract foo{} contract bar{} `, []string{ "bar", "foo", }, }, } for _, c := range cases { t.Run("", func(t *testing.T) { output, err := solc.CompileCode(c.code) if err != nil { t.Fatal(err) } result := map[string]struct{}{} for i := range output.Contracts { result[strings.TrimPrefix(i, ":")] = struct{}{} } // only one source file assert.Len(t, output.Sources, 1) expected := map[string]struct{}{} for _, i := range c.contracts { expected[i] = struct{}{} } if !reflect.DeepEqual(result, expected) { t.Fatal("bad") } }) } } func TestSolidity(t *testing.T) { solc := NewSolidityCompiler(solcPath) files := []string{ "./fixtures/ballot.sol", "./fixtures/simple_auction.sol", } output, err := solc.Compile(files...) if err != nil { t.Fatal(err) } if len(output.Contracts) != 2 { t.Fatal("two expected") } } func existsSolidity(t *testing.T, path string) bool { _, err := os.Stat(path) if err != nil { if os.IsNotExist(err) { return false } t.Fatal(err) } cmd := exec.Command(path, "--version") var stderr, stdout bytes.Buffer cmd.Stderr = &stderr cmd.Stdout = &stdout if err := cmd.Run(); err != nil { t.Fatalf("solidity version failed: %s", string(stderr.Bytes())) } if len(stdout.Bytes()) == 0 { t.Fatal("empty output") } return true } func TestDownloadSolidityCompiler(t *testing.T) { dst1, err := ioutil.TempDir("/tmp", "ethgo-") if err != nil { t.Fatal(err) } defer os.RemoveAll(dst1) if err := DownloadSolidity("0.5.5", dst1, true); err != nil { t.Fatal(err) } if existsSolidity(t, filepath.Join(dst1, "solidity")) { t.Fatal("it should not exist") } if !existsSolidity(t, filepath.Join(dst1, "solidity-0.5.5")) { t.Fatal("it should exist") } dst2, err := ioutil.TempDir("/tmp", "ethgo-") if err != nil { t.Fatal(err) } defer os.RemoveAll(dst2) if err := DownloadSolidity("0.5.5", dst2, false); err != nil { t.Fatal(err) } if !existsSolidity(t, filepath.Join(dst2, "solidity")) { t.Fatal("it should exist") } if existsSolidity(t, filepath.Join(dst2, "solidity-0.5.5")) { t.Fatal("it should not exist") } } ================================================ FILE: contract/contract.go ================================================ package contract import ( "encoding/hex" "fmt" "math/big" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/abi" "github.com/umbracle/ethgo/jsonrpc" "github.com/umbracle/ethgo/wallet" ) // Provider handles the interactions with the Ethereum 1x node type Provider interface { Call(ethgo.Address, []byte, *CallOpts) ([]byte, error) Txn(ethgo.Address, ethgo.Key, []byte) (Txn, error) } type jsonRPCNodeProvider struct { client *jsonrpc.Eth eip1559 bool } func (j *jsonRPCNodeProvider) Call(addr ethgo.Address, input []byte, opts *CallOpts) ([]byte, error) { msg := ðgo.CallMsg{ To: &addr, Data: input, } if opts.From != ethgo.ZeroAddress { msg.From = opts.From } rawStr, err := j.client.Call(msg, opts.Block) if err != nil { return nil, err } raw, err := hex.DecodeString(rawStr[2:]) if err != nil { return nil, err } return raw, nil } func (j *jsonRPCNodeProvider) Txn(addr ethgo.Address, key ethgo.Key, input []byte) (Txn, error) { txn := &jsonrpcTransaction{ opts: &TxnOpts{}, input: input, client: j.client, key: key, to: addr, eip1559: j.eip1559, } return txn, nil } type jsonrpcTransaction struct { to ethgo.Address input []byte hash ethgo.Hash opts *TxnOpts key ethgo.Key client *jsonrpc.Eth txn *ethgo.Transaction txnRaw []byte eip1559 bool } func (j *jsonrpcTransaction) Hash() ethgo.Hash { return j.hash } func (j *jsonrpcTransaction) WithOpts(opts *TxnOpts) { j.opts = opts } func (j *jsonrpcTransaction) Build() error { var err error from := j.key.Address() // estimate gas price if j.opts.GasPrice == 0 && !j.eip1559 { j.opts.GasPrice, err = j.client.GasPrice() if err != nil { return err } } // estimate gas limit if j.opts.GasLimit == 0 { msg := ðgo.CallMsg{ From: from, To: nil, Data: j.input, Value: j.opts.Value, GasPrice: j.opts.GasPrice, } if j.to != ethgo.ZeroAddress { msg.To = &j.to } j.opts.GasLimit, err = j.client.EstimateGas(msg) if err != nil { return err } } // calculate the nonce if j.opts.Nonce == 0 { j.opts.Nonce, err = j.client.GetNonce(from, ethgo.Latest) if err != nil { return fmt.Errorf("failed to calculate nonce: %v", err) } } chainID, err := j.client.ChainID() if err != nil { return err } // send transaction rawTxn := ðgo.Transaction{ From: from, Input: j.input, GasPrice: j.opts.GasPrice, Gas: j.opts.GasLimit, Value: j.opts.Value, Nonce: j.opts.Nonce, ChainID: chainID, } if j.to != ethgo.ZeroAddress { rawTxn.To = &j.to } if j.eip1559 { rawTxn.Type = ethgo.TransactionDynamicFee // use gas price as fee data gasPrice, err := j.client.GasPrice() if err != nil { return err } rawTxn.MaxFeePerGas = new(big.Int).SetUint64(gasPrice) rawTxn.MaxPriorityFeePerGas = new(big.Int).SetUint64(gasPrice) } j.txn = rawTxn return nil } func (j *jsonrpcTransaction) Do() error { if j.txn == nil { if err := j.Build(); err != nil { return err } } signer := wallet.NewEIP155Signer(j.txn.ChainID.Uint64()) signedTxn, err := signer.SignTx(j.txn, j.key) if err != nil { return err } txnRaw, err := signedTxn.MarshalRLPTo(nil) if err != nil { return err } j.txnRaw = txnRaw hash, err := j.client.SendRawTransaction(j.txnRaw) if err != nil { return err } j.hash = hash return nil } func (j *jsonrpcTransaction) Wait() (*ethgo.Receipt, error) { if (j.hash == ethgo.Hash{}) { panic("transaction not executed") } for { receipt, err := j.client.GetTransactionReceipt(j.hash) if err != nil { if err.Error() != "not found" { return nil, err } } if receipt != nil { return receipt, nil } } } // Txn is the transaction object returned type Txn interface { Hash() ethgo.Hash WithOpts(opts *TxnOpts) Do() error Wait() (*ethgo.Receipt, error) } type Opts struct { JsonRPCEndpoint string JsonRPCClient *jsonrpc.Eth Provider Provider Sender ethgo.Key EIP1559 bool } type ContractOption func(*Opts) func WithJsonRPCEndpoint(endpoint string) ContractOption { return func(o *Opts) { o.JsonRPCEndpoint = endpoint } } func WithJsonRPC(client *jsonrpc.Eth) ContractOption { return func(o *Opts) { o.JsonRPCClient = client } } func WithProvider(provider Provider) ContractOption { return func(o *Opts) { o.Provider = provider } } func WithSender(sender ethgo.Key) ContractOption { return func(o *Opts) { o.Sender = sender } } func WithEIP1559() ContractOption { return func(o *Opts) { o.EIP1559 = true } } func DeployContract(abi *abi.ABI, bin []byte, args []interface{}, opts ...ContractOption) (Txn, error) { a := NewContract(ethgo.Address{}, abi, opts...) a.bin = bin return a.Txn("constructor", args...) } func NewContract(addr ethgo.Address, abi *abi.ABI, opts ...ContractOption) *Contract { opt := &Opts{ JsonRPCEndpoint: "http://localhost:8545", } for _, c := range opts { c(opt) } var provider Provider if opt.Provider != nil { provider = opt.Provider } else if opt.JsonRPCClient != nil { provider = &jsonRPCNodeProvider{client: opt.JsonRPCClient, eip1559: opt.EIP1559} } else { client, _ := jsonrpc.NewClient(opt.JsonRPCEndpoint) provider = &jsonRPCNodeProvider{client: client.Eth(), eip1559: opt.EIP1559} } a := &Contract{ addr: addr, abi: abi, provider: provider, key: opt.Sender, } return a } // Contract is a wrapper to make abi calls to contract with a state provider type Contract struct { addr ethgo.Address abi *abi.ABI bin []byte provider Provider key ethgo.Key } func (a *Contract) GetABI() *abi.ABI { return a.abi } type TxnOpts struct { Value *big.Int GasPrice uint64 GasLimit uint64 Nonce uint64 } func (a *Contract) Txn(method string, args ...interface{}) (Txn, error) { if a.key == nil { return nil, fmt.Errorf("no key selected") } isContractDeployment := method == "constructor" var input []byte if isContractDeployment { input = append(input, a.bin...) } var abiMethod *abi.Method if isContractDeployment { if a.abi.Constructor != nil { abiMethod = a.abi.Constructor } } else { if abiMethod = a.abi.GetMethod(method); abiMethod == nil { return nil, fmt.Errorf("method %s not found", method) } } if abiMethod != nil { data, err := abi.Encode(args, abiMethod.Inputs) if err != nil { return nil, fmt.Errorf("failed to encode arguments: %v", err) } if isContractDeployment { input = append(input, data...) } else { input = append(abiMethod.ID(), data...) } } txn, err := a.provider.Txn(a.addr, a.key, input) if err != nil { return nil, err } return txn, nil } type CallOpts struct { Block ethgo.BlockNumber From ethgo.Address } func (a *Contract) Call(method string, block ethgo.BlockNumber, args ...interface{}) (map[string]interface{}, error) { m := a.abi.GetMethod(method) if m == nil { return nil, fmt.Errorf("method %s not found", method) } data, err := m.Encode(args) if err != nil { return nil, err } opts := &CallOpts{ Block: block, } if a.key != nil { opts.From = a.key.Address() } rawOutput, err := a.provider.Call(a.addr, data, opts) if err != nil { return nil, err } resp, err := m.Decode(rawOutput) if err != nil { return nil, err } return resp, nil } ================================================ FILE: contract/contract_test.go ================================================ package contract import ( "encoding/hex" "math/big" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/abi" "github.com/umbracle/ethgo/jsonrpc" "github.com/umbracle/ethgo/testutil" "github.com/umbracle/ethgo/wallet" ) var ( addr0 = "0x0000000000000000000000000000000000000000" addr0B = ethgo.HexToAddress(addr0) ) func TestContract_NoInput(t *testing.T) { s := testutil.NewTestServer(t) cc := &testutil.Contract{} cc.AddOutputCaller("set") contract, addr, err := s.DeployContract(cc) require.NoError(t, err) abi0, err := abi.NewABI(contract.Abi) assert.NoError(t, err) p, _ := jsonrpc.NewClient(s.HTTPAddr()) c := NewContract(addr, abi0, WithJsonRPC(p.Eth())) vals, err := c.Call("set", ethgo.Latest) assert.NoError(t, err) assert.Equal(t, vals["0"], big.NewInt(1)) abi1, err := abi.NewABIFromList([]string{ "function set() view returns (uint256)", }) assert.NoError(t, err) c1 := NewContract(addr, abi1, WithJsonRPC(p.Eth())) vals, err = c1.Call("set", ethgo.Latest) assert.NoError(t, err) assert.Equal(t, vals["0"], big.NewInt(1)) } func TestContract_IO(t *testing.T) { s := testutil.NewTestServer(t) cc := &testutil.Contract{} cc.AddDualCaller("setA", "address", "uint256") contract, addr, err := s.DeployContract(cc) require.NoError(t, err) abi, err := abi.NewABI(contract.Abi) assert.NoError(t, err) c := NewContract(addr, abi, WithJsonRPCEndpoint(s.HTTPAddr())) resp, err := c.Call("setA", ethgo.Latest, addr0B, 1000) assert.NoError(t, err) assert.Equal(t, resp["0"], addr0B) assert.Equal(t, resp["1"], big.NewInt(1000)) } func TestContract_From(t *testing.T) { s := testutil.NewTestServer(t) cc := &testutil.Contract{} cc.AddCallback(func() string { return `function example() public view returns (address) { return msg.sender; }` }) contract, addr, err := s.DeployContract(cc) require.NoError(t, err) abi, err := abi.NewABI(contract.Abi) assert.NoError(t, err) from := ethgo.Address{0x1} c := NewContract(addr, abi, WithSender(from), WithJsonRPCEndpoint(s.HTTPAddr())) resp, err := c.Call("example", ethgo.Latest) assert.NoError(t, err) assert.Equal(t, resp["0"], from) } func TestContract_Deploy(t *testing.T) { s := testutil.NewTestServer(t) // create an address and fund it key, _ := wallet.GenerateKey() s.Fund(key.Address()) p, _ := jsonrpc.NewClient(s.HTTPAddr()) cc := &testutil.Contract{} cc.AddConstructor("address", "uint256") artifact, err := cc.Compile() assert.NoError(t, err) abi, err := abi.NewABI(artifact.Abi) assert.NoError(t, err) bin, err := hex.DecodeString(artifact.Bin) assert.NoError(t, err) txn, err := DeployContract(abi, bin, []interface{}{ethgo.Address{0x1}, 1000}, WithJsonRPC(p.Eth()), WithSender(key)) assert.NoError(t, err) assert.NoError(t, txn.Do()) receipt, err := txn.Wait() assert.NoError(t, err) i := NewContract(receipt.ContractAddress, abi, WithJsonRPC(p.Eth())) resp, err := i.Call("val_0", ethgo.Latest) assert.NoError(t, err) assert.Equal(t, resp["0"], ethgo.Address{0x1}) resp, err = i.Call("val_1", ethgo.Latest) assert.NoError(t, err) assert.Equal(t, resp["0"], big.NewInt(1000)) } func TestContract_Transaction(t *testing.T) { s := testutil.NewTestServer(t) // create an address and fund it key, _ := wallet.GenerateKey() s.Fund(key.Address()) cc := &testutil.Contract{} cc.AddEvent(testutil.NewEvent("A").Add("uint256", true)) cc.EmitEvent("setA", "A", "1") artifact, addr, err := s.DeployContract(cc) require.NoError(t, err) abi, err := abi.NewABI(artifact.Abi) assert.NoError(t, err) // send multiple transactions contract := NewContract(addr, abi, WithJsonRPCEndpoint(s.HTTPAddr()), WithSender(key)) for i := 0; i < 10; i++ { txn, err := contract.Txn("setA") assert.NoError(t, err) err = txn.Do() assert.NoError(t, err) receipt, err := txn.Wait() assert.NoError(t, err) assert.Len(t, receipt.Logs, 1) } } func TestContract_CallAtBlock(t *testing.T) { s := testutil.NewTestServer(t) // create an address and fund it key, _ := wallet.GenerateKey() s.Fund(key.Address()) cc := &testutil.Contract{} cc.AddCallback(func() string { return ` uint256 val = 1; function getVal() public view returns (uint256) { return val; } function change() public payable { val = 2; }` }) artifact, addr, err := s.DeployContract(cc) require.NoError(t, err) abi, err := abi.NewABI(artifact.Abi) assert.NoError(t, err) contract := NewContract(addr, abi, WithJsonRPCEndpoint(s.HTTPAddr()), WithSender(key)) checkVal := func(block ethgo.BlockNumber, expected *big.Int) { resp, err := contract.Call("getVal", block) assert.NoError(t, err) assert.Equal(t, resp["0"], expected) } // initial value is 1 checkVal(ethgo.Latest, big.NewInt(1)) // send a transaction to update the state var receipt *ethgo.Receipt { txn, err := contract.Txn("change") assert.NoError(t, err) err = txn.Do() assert.NoError(t, err) receipt, err = txn.Wait() assert.NoError(t, err) } // validate the state at different blocks { // value at receipt block is 2 checkVal(ethgo.BlockNumber(receipt.BlockNumber), big.NewInt(2)) // value at previous block is 1 checkVal(ethgo.BlockNumber(receipt.BlockNumber-1), big.NewInt(1)) } } func TestContract_SendValueContractCall(t *testing.T) { s := testutil.NewTestServer(t) key, _ := wallet.GenerateKey() s.Fund(key.Address()) cc := &testutil.Contract{} cc.AddCallback(func() string { return ` function deposit() public payable { }` }) artifact, addr, err := s.DeployContract(cc) require.NoError(t, err) abi, err := abi.NewABI(artifact.Abi) assert.NoError(t, err) contract := NewContract(addr, abi, WithJsonRPCEndpoint(s.HTTPAddr()), WithSender(key)) balance := big.NewInt(1) txn, err := contract.Txn("deposit") txn.WithOpts(&TxnOpts{Value: balance}) assert.NoError(t, err) err = txn.Do() assert.NoError(t, err) _, err = txn.Wait() assert.NoError(t, err) client, _ := jsonrpc.NewClient(s.HTTPAddr()) found, err := client.Eth().GetBalance(addr, ethgo.Latest) assert.NoError(t, err) assert.Equal(t, found, balance) } func TestContract_EIP1559(t *testing.T) { s := testutil.NewTestServer(t) key, _ := wallet.GenerateKey() s.Fund(key.Address()) cc := &testutil.Contract{} cc.AddOutputCaller("example") artifact, addr, err := s.DeployContract(cc) require.NoError(t, err) abi, err := abi.NewABI(artifact.Abi) assert.NoError(t, err) client, _ := jsonrpc.NewClient(s.HTTPAddr()) contract := NewContract(addr, abi, WithJsonRPC(client.Eth()), WithSender(key), WithEIP1559()) txn, err := contract.Txn("example") assert.NoError(t, err) err = txn.Do() assert.NoError(t, err) _, err = txn.Wait() assert.NoError(t, err) // get transaction from rpc endpoint txnObj, err := client.Eth().GetTransactionByHash(txn.Hash()) assert.NoError(t, err) assert.Zero(t, txnObj.GasPrice) assert.NotZero(t, txnObj.Gas) assert.NotZero(t, txnObj.MaxFeePerGas) assert.NotZero(t, txnObj.MaxPriorityFeePerGas) } ================================================ FILE: e2e/transaction_test.go ================================================ package e2e import ( "math/big" "testing" "github.com/stretchr/testify/assert" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/jsonrpc" "github.com/umbracle/ethgo/testutil" "github.com/umbracle/ethgo/wallet" ) func TestSendSignedTransaction(t *testing.T) { s := testutil.NewTestServer(t) key, err := wallet.GenerateKey() assert.NoError(t, err) // add value to the new key value := big.NewInt(1000000000000000000) s.Transfer(key.Address(), value) c, _ := jsonrpc.NewClient(s.HTTPAddr()) found, _ := c.Eth().GetBalance(key.Address(), ethgo.Latest) assert.Equal(t, found, value) chainID, err := c.Eth().ChainID() assert.NoError(t, err) // send a signed transaction to := ethgo.Address{0x1} transferVal := big.NewInt(1000) gasPrice, err := c.Eth().GasPrice() assert.NoError(t, err) txn := ðgo.Transaction{ To: &to, Value: transferVal, Nonce: 0, GasPrice: gasPrice, } { msg := ðgo.CallMsg{ From: key.Address(), To: &to, Value: transferVal, GasPrice: gasPrice, } limit, err := c.Eth().EstimateGas(msg) assert.NoError(t, err) txn.Gas = limit } signer := wallet.NewEIP155Signer(chainID.Uint64()) txn, err = signer.SignTx(txn, key) assert.NoError(t, err) from, err := signer.RecoverSender(txn) assert.NoError(t, err) assert.Equal(t, from, key.Address()) data, err := txn.MarshalRLPTo(nil) assert.NoError(t, err) hash, err := c.Eth().SendRawTransaction(data) assert.NoError(t, err) _, err = s.WaitForReceipt(hash) assert.NoError(t, err) balance, err := c.Eth().GetBalance(to, ethgo.Latest) assert.NoError(t, err) assert.Equal(t, balance, transferVal) } ================================================ FILE: encoding.go ================================================ package ethgo import ( "encoding/hex" "math/big" "strconv" "strings" ) type ArgBig big.Int func (a *ArgBig) UnmarshalText(input []byte) error { buf, err := decodeToHex(input) if err != nil { return err } b := new(big.Int) b.SetBytes(buf) *a = ArgBig(*b) return nil } func (a ArgBig) MarshalText() ([]byte, error) { b := (*big.Int)(&a) return []byte("0x" + b.Text(16)), nil } type ArgUint64 uint64 func (b ArgUint64) MarshalText() ([]byte, error) { buf := make([]byte, 2, 10) copy(buf, `0x`) buf = strconv.AppendUint(buf, uint64(b), 16) return buf, nil } func (u *ArgUint64) UnmarshalText(input []byte) error { str := strings.TrimPrefix(string(input), "0x") if str == "" { str = "0" } num, err := strconv.ParseUint(str, 16, 64) if err != nil { return err } *u = ArgUint64(num) return nil } func (u *ArgUint64) Uint64() uint64 { return uint64(*u) } type ArgBytes []byte func (b ArgBytes) MarshalText() ([]byte, error) { return encodeToHex(b), nil } func (b *ArgBytes) UnmarshalText(input []byte) error { hh, err := decodeToHex(input) if err != nil { return nil } aux := make([]byte, len(hh)) copy(aux[:], hh[:]) *b = aux return nil } func (b *ArgBytes) Bytes() []byte { return *b } func decodeToHex(b []byte) ([]byte, error) { str := string(b) str = strings.TrimPrefix(str, "0x") if len(str)%2 != 0 { str = "0" + str } return hex.DecodeString(str) } func encodeToHex(b []byte) []byte { str := hex.EncodeToString(b) if len(str)%2 != 0 { str = "0" + str } return []byte("0x" + str) } ================================================ FILE: ens/address_mapping.go ================================================ package ens import "github.com/umbracle/ethgo" var defaultEnsAddr = ethgo.HexToAddress("0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e") var builtinEnsAddr = map[uint64]ethgo.Address{ 1: defaultEnsAddr, 3: defaultEnsAddr, 4: defaultEnsAddr, 5: defaultEnsAddr, } ================================================ FILE: ens/ens.go ================================================ package ens import ( "fmt" "log" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/builtin/ens" "github.com/umbracle/ethgo/jsonrpc" ) type EnsConfig struct { Logger *log.Logger Client *jsonrpc.Client Addr string Resolver ethgo.Address } type EnsOption func(*EnsConfig) func WithResolver(resolver ethgo.Address) EnsOption { return func(c *EnsConfig) { c.Resolver = resolver } } func WithLogger(logger *log.Logger) EnsOption { return func(c *EnsConfig) { c.Logger = logger } } func WithAddress(addr string) EnsOption { return func(c *EnsConfig) { c.Addr = addr } } func WithClient(client *jsonrpc.Client) EnsOption { return func(c *EnsConfig) { c.Client = client } } type ENS struct { config *EnsConfig } func NewENS(opts ...EnsOption) (*ENS, error) { config := &EnsConfig{} for _, opt := range opts { opt(config) } if config.Client == nil { // addr must be set if config.Addr == "" { return nil, fmt.Errorf("jsonrpc addr is empty") } client, err := jsonrpc.NewClient(config.Addr) if err != nil { return nil, err } config.Client = client } if config.Resolver == ethgo.ZeroAddress { // try to get the resolver address from the builtin list chainID, err := config.Client.Eth().ChainID() if err != nil { return nil, err } addr, ok := builtinEnsAddr[chainID.Uint64()] if !ok { return nil, fmt.Errorf("no builtin Ens resolver found for chain %s", chainID) } config.Resolver = addr } ens := &ENS{ config: config, } return ens, nil } func (e *ENS) Resolve(name string) (ethgo.Address, error) { resolver := ens.NewENSResolver(e.config.Resolver, e.config.Client) return resolver.Resolve(name) } func (e *ENS) ReverseResolve(addr ethgo.Address) (string, error) { resolver := ens.NewENSResolver(e.config.Resolver, e.config.Client) return resolver.ReverseResolve(addr) } ================================================ FILE: ens/ens_test.go ================================================ package ens import ( "testing" "github.com/stretchr/testify/assert" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/testutil" ) func TestENS_Resolve(t *testing.T) { ens, err := NewENS(WithAddress(testutil.TestInfuraEndpoint(t))) assert.NoError(t, err) addr, err := ens.Resolve("nick.eth") assert.NoError(t, err) assert.Equal(t, ethgo.HexToAddress("0xb8c2C29ee19D8307cb7255e1Cd9CbDE883A267d5"), addr) name, err := ens.ReverseResolve(ethgo.HexToAddress("0xb8c2C29ee19D8307cb7255e1Cd9CbDE883A267d5")) assert.NoError(t, err) assert.Equal(t, "nick.eth", name) } ================================================ FILE: etherscan/etherscan.go ================================================ package etherscan import ( "encoding/json" "fmt" "strconv" "strings" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/jsonrpc/codec" "github.com/valyala/fasthttp" ) // Etherscan is a provider using the Etherscan api type Etherscan struct { client fasthttp.Client url string apiKey string } // NewEtherscanFromNetwork creates a new client from the network id func NewEtherscanFromNetwork(n ethgo.Network, apiKey string) (*Etherscan, error) { var url string switch n { case ethgo.Mainnet: url = "https://api.etherscan.io" case ethgo.Ropsten: url = "https://ropsten.etherscan.io" case ethgo.Rinkeby: url = "https://rinkeby.etherscan.io" case ethgo.Goerli: url = "https://goerli.etherscan.io" default: return nil, fmt.Errorf("unknwon network id %d", n) } return NewEtherscan(url, apiKey), nil } // NewEtherscan creates a new Etherscan service from a url func NewEtherscan(url, apiKey string) *Etherscan { return &Etherscan{url: url, apiKey: apiKey} } type proxyResponse struct { Status string Message string Result json.RawMessage } // Query sends a query to Etherscan func (e *Etherscan) Query(module, action string, out interface{}, params map[string]string) error { url := fmt.Sprintf("%s/api?module=%s&action=%s", e.url, module, action) if len(params) != 0 { res := []string{} for k, v := range params { res = append(res, k+"="+v) } url += "&" + strings.Join(res, "&") } if e.apiKey != "" { url = url + "&apikey=" + e.apiKey } req := fasthttp.AcquireRequest() res := fasthttp.AcquireResponse() defer fasthttp.ReleaseRequest(req) defer fasthttp.ReleaseResponse(res) req.SetRequestURI(url) req.Header.SetMethod("GET") if err := e.client.Do(req, res); err != nil { return err } // Decode json-rpc response var result json.RawMessage if module == "proxy" { var response codec.Response if err := json.Unmarshal(res.Body(), &response); err != nil { return err } if response.Error != nil { return response.Error } result = response.Result } else { var response proxyResponse if err := json.Unmarshal(res.Body(), &response); err != nil { return err } result = response.Result } if err := json.Unmarshal(result, out); err != nil { return err } return nil } // BlockNumber returns the number of most recent block. func (e *Etherscan) BlockNumber() (uint64, error) { var out string if err := e.Query("proxy", "eth_blockNumber", &out, nil); err != nil { return 0, err } return parseUint64orHex(out) } // GetBlockByNumber returns information about a block by block number. func (e *Etherscan) GetBlockByNumber(i ethgo.BlockNumber, full bool) (*ethgo.Block, error) { var b *ethgo.Block params := map[string]string{ "tag": i.String(), "boolean": strconv.FormatBool(full), } if err := e.Query("proxy", "eth_getBlockByNumber", &b, params); err != nil { return nil, err } return b, nil } type ContractCode struct { SourceCode string ContractName string Runs string CompilerVersion string ConstructorArguments string } func (e *Etherscan) GetContractCode(addr ethgo.Address) (*ContractCode, error) { var out []*ContractCode err := e.Query("contract", "getsourcecode", &out, map[string]string{ "address": addr.String(), }) if err != nil { return nil, err } if len(out) != 1 { return nil, fmt.Errorf("incorrect values") } return out[0], nil } func (e *Etherscan) GasPrice() (uint64, error) { var out struct { LastBlock string `json:"LastBlock"` } if err := e.Query("gastracker", "gasoracle", &out, map[string]string{}); err != nil { return 0, err } num, err := strconv.Atoi(out.LastBlock) if err != nil { return 0, err } return uint64(num), nil } func (e *Etherscan) GetLogs(filter *ethgo.LogFilter) ([]*ethgo.Log, error) { if len(filter.Address) == 0 { return nil, fmt.Errorf("an address to filter is required") } strBlockNumber := func(b ethgo.BlockNumber) string { switch b { case ethgo.Latest: return "latest" case ethgo.Earliest: return "earliest" case ethgo.Pending: return "pending" } if b < 0 { panic("internal. blocknumber is negative") } return fmt.Sprintf("%d", uint64(b)) } params := map[string]string{ "address": filter.Address[0].String(), } if filter.From != nil { params["fromBlock"] = strBlockNumber(*filter.From) } if filter.To != nil { params["toBlock"] = strBlockNumber(*filter.To) } var out []*ethgo.Log if err := e.Query("logs", "getLogs", &out, params); err != nil { return nil, err } return out, nil } func parseUint64orHex(str string) (uint64, error) { base := 10 if strings.HasPrefix(str, "0x") { str = str[2:] base = 16 } return strconv.ParseUint(str, base, 64) } ================================================ FILE: etherscan/etherscan_test.go ================================================ package etherscan import ( "os" "testing" "github.com/stretchr/testify/assert" "github.com/umbracle/ethgo" ) func testEtherscanMainnet(t *testing.T) *Etherscan { apiKey := os.Getenv("ETHERSCAN_APIKEY") if apiKey == "" { t.Skip("Etherscan APIKey not specified") } return &Etherscan{url: "https://api.etherscan.io", apiKey: apiKey} } func TestNewEtherscan(t *testing.T) { wantUrl := "http://test.url/" wantApiKey := "abc123" e := NewEtherscan(wantUrl, wantApiKey) assert.Equal(t, wantUrl, e.url) assert.Equal(t, wantApiKey, e.apiKey) } func TestBlockByNumber(t *testing.T) { e := testEtherscanMainnet(t) n, err := e.BlockNumber() assert.NoError(t, err) assert.NotEqual(t, n, 0) } func TestGetBlockByNumber(t *testing.T) { e := testEtherscanMainnet(t) b, err := e.GetBlockByNumber(1, false) assert.NoError(t, err) assert.Equal(t, b.Number, uint64(1)) } func TestContract(t *testing.T) { e := testEtherscanMainnet(t) // uniswap v2. router code, err := e.GetContractCode(ethgo.HexToAddress("0x7a250d5630b4cf539739df2c5dacb4c659f2488d")) assert.NoError(t, err) assert.Equal(t, code.Runs, "999999") } func TestGetLogs(t *testing.T) { e := testEtherscanMainnet(t) from := ethgo.BlockNumber(379224) to := ethgo.Latest filter := ðgo.LogFilter{ From: &from, To: &to, Address: []ethgo.Address{ ethgo.HexToAddress("0x33990122638b9132ca29c723bdf037f1a891a70c"), }, } logs, err := e.GetLogs(filter) assert.NoError(t, err) assert.NotEmpty(t, logs) } func TestGasPrice(t *testing.T) { e := testEtherscanMainnet(t) gas, err := e.GasPrice() assert.NoError(t, err) assert.NotZero(t, gas) } ================================================ FILE: examples/contract-call-basic.go ================================================ package examples import ( "fmt" "math/big" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/abi" "github.com/umbracle/ethgo/contract" "github.com/umbracle/ethgo/jsonrpc" ) func handleErr(err error) { if err != nil { panic(err) } } // call a contract func contractCall() { var functions = []string{ "function totalSupply() view returns (uint256)", } abiContract, err := abi.NewABIFromList(functions) handleErr(err) // Matic token addr := ethgo.HexToAddress("0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0") client, err := jsonrpc.NewClient("https://mainnet.infura.io") handleErr(err) c := contract.NewContract(addr, abiContract, contract.WithJsonRPC(client.Eth())) res, err := c.Call("totalSupply", ethgo.Latest) handleErr(err) fmt.Printf("TotalSupply: %s", res["totalSupply"].(*big.Int)) } ================================================ FILE: examples/contract-call-from.go ================================================ package examples import ( "fmt" "math/big" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/abi" "github.com/umbracle/ethgo/contract" "github.com/umbracle/ethgo/jsonrpc" ) // call a contract and use a custom `from` address func contractCallFrom() { var functions = []string{ "function totalSupply() view returns (uint256)", } abiContract, err := abi.NewABIFromList(functions) handleErr(err) // Matic token addr := ethgo.HexToAddress("0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0") client, err := jsonrpc.NewClient("https://mainnet.infura.io") handleErr(err) // from address (msg.sender in solidity) from := ethgo.Address{0x1} c := contract.NewContract(addr, abiContract, contract.WithSender(from), contract.WithJsonRPC(client.Eth())) res, err := c.Call("totalSupply", ethgo.Latest) handleErr(err) fmt.Printf("TotalSupply: %s", res["totalSupply"].(*big.Int)) } ================================================ FILE: examples/contract-deploy.go ================================================ package examples import ( "fmt" "github.com/umbracle/ethgo/abi" "github.com/umbracle/ethgo/contract" ) func contractDeploy() { abiContract, err := abi.NewABIFromList([]string{}) handleErr(err) // bytecode of the contract bin := []byte{} txn, err := contract.DeployContract(abiContract, bin, []interface{}{}) handleErr(err) err = txn.Do() handleErr(err) receipt, err := txn.Wait() handleErr(err) fmt.Printf("Contract: %s", receipt.ContractAddress) } ================================================ FILE: examples/contract-transaction.go ================================================ package examples import ( "fmt" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/abi" "github.com/umbracle/ethgo/contract" "github.com/umbracle/ethgo/jsonrpc" "github.com/umbracle/ethgo/wallet" ) func contractTransaction() { var functions = []string{ "function transferFrom(address from, address to, uint256 value)", } abiContract, err := abi.NewABIFromList(functions) handleErr(err) // Matic token addr := ethgo.HexToAddress("0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0") client, err := jsonrpc.NewClient("https://mainnet.infura.io") handleErr(err) // wallet signer key, err := wallet.GenerateKey() handleErr(err) opts := []contract.ContractOption{ contract.WithJsonRPC(client.Eth()), contract.WithSender(key), } c := contract.NewContract(addr, abiContract, opts...) txn, err := c.Txn("transferFrom", ethgo.Latest) handleErr(err) err = txn.Do() handleErr(err) receipt, err := txn.Wait() handleErr(err) fmt.Printf("Transaction mined at: %s", receipt.TransactionHash) } ================================================ FILE: go.mod ================================================ module github.com/umbracle/ethgo go 1.18 require ( github.com/btcsuite/btcd v0.23.3 github.com/btcsuite/btcd/btcec/v2 v2.1.3 github.com/btcsuite/btcd/btcutil v1.1.0 github.com/gorilla/websocket v1.4.1 github.com/jmoiron/sqlx v1.2.0 github.com/lib/pq v1.2.0 github.com/mitchellh/mapstructure v1.4.1 github.com/ory/dockertest v3.3.5+incompatible github.com/stretchr/testify v1.8.0 github.com/tyler-smith/go-bip39 v1.1.0 github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722 github.com/valyala/fasthttp v1.4.0 github.com/valyala/fastjson v1.4.1 go.etcd.io/bbolt v1.3.6 golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad golang.org/x/text v0.8.0 pgregory.net/rapid v0.5.5 ) require ( github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 // indirect github.com/Microsoft/go-winio v0.6.0 // indirect github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 // indirect github.com/cenkalti/backoff v2.2.1+incompatible // indirect github.com/containerd/continuity v0.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect github.com/go-sql-driver/mysql v1.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/gotestyourself/gotestyourself v2.2.0+incompatible // indirect github.com/klauspost/compress v1.4.1 // indirect github.com/klauspost/cpuid v1.2.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.2 // indirect github.com/opencontainers/runc v1.1.5 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/sirupsen/logrus v1.8.1 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect golang.org/x/mod v0.9.0 // indirect golang.org/x/net v0.8.0 // indirect golang.org/x/sys v0.7.0 // indirect golang.org/x/tools v0.7.0 // indirect gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gotest.tools v2.2.0+incompatible // indirect ) ================================================ FILE: go.sum ================================================ github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg= github.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw= github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= github.com/btcsuite/btcd v0.22.0-beta.0.20220111032746-97732e52810c/go.mod h1:tjmYdS6MLJ5/s0Fj4DbLgSbDHbEqLJrtnHecBFkdz5M= github.com/btcsuite/btcd v0.23.3 h1:4KH/JKy9WiCd+iUS9Mu0Zp7Dnj17TGdKrg9xc/FGj24= github.com/btcsuite/btcd v0.23.3/go.mod h1:0QJIIN1wwIXF/3G/m87gIwGniDMDQqjVn4SZgnFpsYY= github.com/btcsuite/btcd/btcec/v2 v2.1.0/go.mod h1:2VzYrv4Gm4apmbVVsSq5bqf1Ec8v56E48Vt0Y/umPgA= github.com/btcsuite/btcd/btcec/v2 v2.1.3 h1:xM/n3yIhHAhHy04z4i43C8p4ehixJZMsnrVJkgl+MTE= github.com/btcsuite/btcd/btcec/v2 v2.1.3/go.mod h1:ctjw4H1kknNJmRN4iP1R7bTQ+v3GJkZBd6mui8ZsAZE= github.com/btcsuite/btcd/btcutil v1.0.0/go.mod h1:Uoxwv0pqYWhD//tfTiipkxNfdhG9UrLwaeswfjfdF0A= github.com/btcsuite/btcd/btcutil v1.1.0 h1:MO4klnGY+EWJdoWF12Wkuf4AWDBPMpZNeN/jRLrklUU= github.com/btcsuite/btcd/btcutil v1.1.0/go.mod h1:5OapHB7A2hBBWLm48mmw4MOHNJCcUBTwmWH/0Jn8VHE= 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/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= github.com/btcsuite/goleveldb v1.0.0/go.mod h1:QiK9vBlgftBg6rWQIj6wFzbPfRjiykIEhBH4obrXJ/I= github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/snappy-go v1.0.0/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4= github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/continuity v0.3.0 h1:nisirsYROK15TAMVukJOUyGJjz4BNQJBVsNvAXZJ/eg= github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/decred/dcrd/lru v1.0.0/go.mod h1:mxKOwFd7lFjN2GZYsiz/ecgqR6kkYAl+0pz0tEMk218= 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.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= 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/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 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.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 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.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 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/gorilla/websocket v1.4.1 h1:q7AeDBpnBk8AogcD4DSag/Ukw/KV+YhzLj2bP5HvKCM= github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gotestyourself/gotestyourself v2.2.0+incompatible h1:AQwinXlbQR2HvPjQZOmDhRqsv5mZf+Jb1RnSLxcqZcI= github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks= github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/klauspost/compress v1.4.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/compress v1.4.1 h1:8VMb5+0wMgdBykOV96DwNwKFQ+WTI4pzYURP99CcB9E= github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A= github.com/klauspost/cpuid v0.0.0-20180405133222-e7e905edc00e/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/mattn/go-sqlite3 v1.9.0 h1:pDRiWfl+++eC2FEFRy6jXmQlvp4Yh3z1MJKg4UeYM/4= github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU= github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/gomega v1.4.1/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= 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/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg= github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI= github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA= github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= 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/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= 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/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722 h1:10Nbw6cACsnQm7r34zlpJky+IzxVLRk6MKTS2d3Vp0E= github.com/umbracle/fastrlp v0.0.0-20220527094140-59d5dd30e722/go.mod h1:c8J0h9aULj2i3umrfyestM6jCq0LK0U6ly6bWy96nd4= github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasthttp v1.4.0 h1:PuaTGZIw3mjYhhhbVbCQp8aciRZN9YdoB7MGX9Ko76A= github.com/valyala/fasthttp v1.4.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk8LWSxF3s= github.com/valyala/fastjson v1.4.1 h1:hrltpHpIpkaxll8QltMU8c3QZ5+qIiCL8yKqPFJI/yE= github.com/valyala/fastjson v1.4.1/go.mod h1:nV6MsjxL2IMJQUoHDIrjEI7oLyeqK6aBD7EFWPsvP8o= github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU= go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/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-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY= golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/mod v0.9.0 h1:KENHtAZL2y3NLMYZeHY9DW8HW8V+kQyJsY/V9JlKvCs= golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180719180050-a680a1efc54d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/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-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/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.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.7.0 h1:W4OVu8VVOaIO0yzWMNdepAulS7YfoS3Zabrm8DOXXU4= golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s= 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/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.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= 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.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 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= gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= pgregory.net/rapid v0.5.5 h1:jkgx1TjbQPD/feRoK+S/mXw9e1uj6WilpHrXJowi6oA= pgregory.net/rapid v0.5.5/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= ================================================ FILE: jsonrpc/client.go ================================================ package jsonrpc import ( "github.com/umbracle/ethgo/jsonrpc/transport" ) // Client is the jsonrpc client type Client struct { transport transport.Transport endpoints endpoints } type endpoints struct { w *Web3 e *Eth n *Net d *Debug } type Config struct { headers map[string]string } type ConfigOption func(*Config) func WithHeaders(headers map[string]string) ConfigOption { return func(c *Config) { for k, v := range headers { c.headers[k] = v } } } func NewClient(addr string, opts ...ConfigOption) (*Client, error) { config := &Config{headers: map[string]string{}} for _, opt := range opts { opt(config) } c := &Client{} c.endpoints.w = &Web3{c} c.endpoints.e = &Eth{c} c.endpoints.n = &Net{c} c.endpoints.d = &Debug{c} t, err := transport.NewTransport(addr, config.headers) if err != nil { return nil, err } c.transport = t return c, nil } // Close closes the transport func (c *Client) Close() error { return c.transport.Close() } // Call makes a jsonrpc call func (c *Client) Call(method string, out interface{}, params ...interface{}) error { return c.transport.Call(method, out, params...) } // SetMaxConnsLimit sets the maximum number of connections that can be established with a host func (c *Client) SetMaxConnsLimit(count int) { c.transport.SetMaxConnsPerHost(count) } ================================================ FILE: jsonrpc/codec/codec.go ================================================ package codec import ( "encoding/json" "fmt" ) // Request is a jsonrpc request type Request struct { JsonRPC string `json:"jsonrpc"` ID uint64 `json:"id"` Method string `json:"method"` Params json.RawMessage `json:"params"` } // Response is a jsonrpc response type Response struct { ID uint64 `json:"id"` Result json.RawMessage `json:"result"` Error *ErrorObject `json:"error,omitempty"` } // ErrorObject is a jsonrpc error type ErrorObject struct { Code int `json:"code"` Message string `json:"message"` Data interface{} `json:"data,omitempty"` } // Subscription is a jsonrpc subscription type Subscription struct { ID string `json:"subscription"` Result json.RawMessage `json:"result"` } // Error implements error interface func (e *ErrorObject) Error() string { data, err := json.Marshal(e) if err != nil { return fmt.Sprintf("jsonrpc.internal marshal error: %v", err) } return string(data) } ================================================ FILE: jsonrpc/debug.go ================================================ package jsonrpc import "github.com/umbracle/ethgo" type Debug struct { c *Client } // Debug returns the reference to the debug namespace func (c *Client) Debug() *Debug { return c.endpoints.d } type TraceTransactionOptions struct { EnableMemory bool `json:"enableMemory"` DisableStack bool `json:"disableStack"` DisableStorage bool `json:"disableStorage"` EnableReturnData bool `json:"enableReturnData"` Timeout string `json:"timeout,omitempty"` Tracer string `json:"tracer,omitempty"` TracerConfig map[string]interface{} `json:"tracerConfig,omitempty"` } type TransactionTrace struct { Gas uint64 ReturnValue string StructLogs []*StructLogs } type StructLogs struct { Depth int Gas int GasCost int Op string Pc int Memory []string Stack []string Storage map[string]string } func (d *Debug) TraceTransaction(hash ethgo.Hash, opts TraceTransactionOptions) (*TransactionTrace, error) { var res *TransactionTrace err := d.c.Call("debug_traceTransaction", &res, hash, opts) return res, err } ================================================ FILE: jsonrpc/debug_test.go ================================================ package jsonrpc import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/umbracle/ethgo/testutil" ) func TestDebug_TraceTransaction(t *testing.T) { s := testutil.NewTestServer(t) c, _ := NewClient(s.HTTPAddr()) cc := &testutil.Contract{} cc.AddEvent(testutil.NewEvent("A").Add("address", true)) cc.EmitEvent("setA", "A", addr0.String()) _, addr, err := s.DeployContract(cc) require.NoError(t, err) r, err := s.TxnTo(addr, "setA2") require.NoError(t, err) trace, err := c.Debug().TraceTransaction(r.TransactionHash, TraceTransactionOptions{}) assert.NoError(t, err) assert.Greater(t, trace.Gas, uint64(20000)) assert.NotEmpty(t, trace.StructLogs) } ================================================ FILE: jsonrpc/eth.go ================================================ package jsonrpc import ( "encoding/hex" "encoding/json" "fmt" "math/big" "github.com/umbracle/ethgo" ) // Eth is the eth namespace type Eth struct { c *Client } // Eth returns the reference to the eth namespace func (c *Client) Eth() *Eth { return c.endpoints.e } // GetCode returns the code of a contract func (e *Eth) GetCode(addr ethgo.Address, block ethgo.BlockNumberOrHash) (string, error) { var res string if err := e.c.Call("eth_getCode", &res, addr, block.Location()); err != nil { return "", err } return res, nil } // Accounts returns a list of addresses owned by client. func (e *Eth) Accounts() ([]ethgo.Address, error) { var out []ethgo.Address if err := e.c.Call("eth_accounts", &out); err != nil { return nil, err } return out, nil } // GetStorageAt returns the value from a storage position at a given address. func (e *Eth) GetStorageAt(addr ethgo.Address, slot ethgo.Hash, block ethgo.BlockNumberOrHash) (ethgo.Hash, error) { var hash ethgo.Hash err := e.c.Call("eth_getStorageAt", &hash, addr, slot, block.Location()) return hash, err } // BlockNumber returns the number of most recent block. func (e *Eth) BlockNumber() (uint64, error) { var out string if err := e.c.Call("eth_blockNumber", &out); err != nil { return 0, err } return parseUint64orHex(out) } // GetBlockByNumber returns information about a block by block number. func (e *Eth) GetBlockByNumber(i ethgo.BlockNumber, full bool) (*ethgo.Block, error) { var b *ethgo.Block if err := e.c.Call("eth_getBlockByNumber", &b, i.String(), full); err != nil { return nil, err } return b, nil } // GetBlockByHash returns information about a block by hash. func (e *Eth) GetBlockByHash(hash ethgo.Hash, full bool) (*ethgo.Block, error) { var b *ethgo.Block if err := e.c.Call("eth_getBlockByHash", &b, hash, full); err != nil { return nil, err } return b, nil } // GetFilterChanges returns the filter changes for log filters func (e *Eth) GetFilterChanges(id string) ([]*ethgo.Log, error) { var logs []*ethgo.Log if err := e.c.Call("eth_getFilterChanges", &logs, id); err != nil { return nil, err } return logs, nil } // GetTransactionByHash returns a transaction by his hash func (e *Eth) GetTransactionByHash(hash ethgo.Hash) (*ethgo.Transaction, error) { var txn *ethgo.Transaction err := e.c.Call("eth_getTransactionByHash", &txn, hash) return txn, err } // GetFilterChangesBlock returns the filter changes for block filters func (e *Eth) GetFilterChangesBlock(id string) ([]ethgo.Hash, error) { var hashes []ethgo.Hash if err := e.c.Call("eth_getFilterChanges", &hashes, id); err != nil { return nil, err } return hashes, nil } // NewFilter creates a new log filter func (e *Eth) NewFilter(filter *ethgo.LogFilter) (string, error) { var id string err := e.c.Call("eth_newFilter", &id, filter) return id, err } // NewBlockFilter creates a new block filter func (e *Eth) NewBlockFilter() (string, error) { var id string err := e.c.Call("eth_newBlockFilter", &id, nil) return id, err } // UninstallFilter uninstalls a filter func (e *Eth) UninstallFilter(id string) (bool, error) { var res bool err := e.c.Call("eth_uninstallFilter", &res, id) return res, err } // SendRawTransaction sends a signed transaction in rlp format. func (e *Eth) SendRawTransaction(data []byte) (ethgo.Hash, error) { var hash ethgo.Hash hexData := "0x" + hex.EncodeToString(data) err := e.c.Call("eth_sendRawTransaction", &hash, hexData) return hash, err } // SendTransaction creates new message call transaction or a contract creation. func (e *Eth) SendTransaction(txn *ethgo.Transaction) (ethgo.Hash, error) { var hash ethgo.Hash err := e.c.Call("eth_sendTransaction", &hash, txn) return hash, err } // GetTransactionReceipt returns the receipt of a transaction by transaction hash. func (e *Eth) GetTransactionReceipt(hash ethgo.Hash) (*ethgo.Receipt, error) { var receipt *ethgo.Receipt err := e.c.Call("eth_getTransactionReceipt", &receipt, hash) return receipt, err } // GetNonce returns the nonce of the account func (e *Eth) GetNonce(addr ethgo.Address, blockNumber ethgo.BlockNumberOrHash) (uint64, error) { var nonce string if err := e.c.Call("eth_getTransactionCount", &nonce, addr, blockNumber.Location()); err != nil { return 0, err } return parseUint64orHex(nonce) } // GetBalance returns the balance of the account of given address. func (e *Eth) GetBalance(addr ethgo.Address, blockNumber ethgo.BlockNumberOrHash) (*big.Int, error) { var out string if err := e.c.Call("eth_getBalance", &out, addr, blockNumber.Location()); err != nil { return nil, err } b, ok := new(big.Int).SetString(out[2:], 16) if !ok { return nil, fmt.Errorf("failed to convert to big.int") } return b, nil } // GasPrice returns the current price per gas in wei. func (e *Eth) GasPrice() (uint64, error) { var out string if err := e.c.Call("eth_gasPrice", &out); err != nil { return 0, err } return parseUint64orHex(out) } // Call executes a new message call immediately without creating a transaction on the blockchain. func (e *Eth) Call(msg *ethgo.CallMsg, block ethgo.BlockNumber, override ...*ethgo.StateOverride) (string, error) { var out string if len(override) == 1 && override[0] != nil { if err := e.c.Call("eth_call", &out, msg, block.String(), override[0]); err != nil { return "", err } } else { if err := e.c.Call("eth_call", &out, msg, block.String()); err != nil { return "", err } } return out, nil } // EstimateGasContract estimates the gas to deploy a contract func (e *Eth) EstimateGasContract(bin []byte) (uint64, error) { var out string msg := map[string]interface{}{ "data": "0x" + hex.EncodeToString(bin), } if err := e.c.Call("eth_estimateGas", &out, msg); err != nil { return 0, err } return parseUint64orHex(out) } // EstimateGas generates and returns an estimate of how much gas is necessary to allow the transaction to complete. func (e *Eth) EstimateGas(msg *ethgo.CallMsg) (uint64, error) { var out string if err := e.c.Call("eth_estimateGas", &out, msg); err != nil { return 0, err } return parseUint64orHex(out) } // GetLogs returns an array of all logs matching a given filter object func (e *Eth) GetLogs(filter *ethgo.LogFilter) ([]*ethgo.Log, error) { var out []*ethgo.Log if err := e.c.Call("eth_getLogs", &out, filter); err != nil { return nil, err } return out, nil } // ChainID returns the id of the chain func (e *Eth) ChainID() (*big.Int, error) { var out string if err := e.c.Call("eth_chainId", &out); err != nil { return nil, err } return parseBigInt(out), nil } // FeeHistory is the result of the eth_feeHistory endpoint type FeeHistory struct { OldestBlock *big.Int `json:"oldestBlock"` Reward [][]*big.Int `json:"reward,omitempty"` BaseFee []*big.Int `json:"baseFeePerGas,omitempty"` GasUsedRatio []float64 `json:"gasUsedRatio"` } func (f *FeeHistory) UnmarshalJSON(data []byte) error { var raw struct { OldestBlock *ArgBig `json:"oldestBlock"` Reward [][]*ArgBig `json:"reward,omitempty"` BaseFee []*ArgBig `json:"baseFeePerGas,omitempty"` GasUsedRatio []float64 `json:"gasUsedRatio"` } if err := json.Unmarshal(data, &raw); err != nil { return err } if raw.OldestBlock != nil { f.OldestBlock = raw.OldestBlock.Big() } if raw.Reward != nil { f.Reward = [][]*big.Int{} for _, r := range raw.Reward { elem := []*big.Int{} for _, i := range r { elem = append(elem, i.Big()) } f.Reward = append(f.Reward, elem) } } if raw.BaseFee != nil { f.BaseFee = []*big.Int{} for _, i := range raw.BaseFee { f.BaseFee = append(f.BaseFee, i.Big()) } } f.GasUsedRatio = raw.GasUsedRatio return nil } // FeeHistory returns base fee per gas and transaction effective priority fee func (e *Eth) FeeHistory(blockCount uint64, newestBlock ethgo.BlockNumber, rewardPercentiles []float64) (*FeeHistory, error) { var out *FeeHistory if err := e.c.Call("eth_feeHistory", &out, blockCount, newestBlock.String(), rewardPercentiles); err != nil { return nil, err } return out, nil } // MaxPriorityFeePerGas returns a fee per gas that is an estimate of how much you can pay as a priority fee, or 'tip', // to get a transaction included in the current block (EIP-1559). func (e *Eth) MaxPriorityFeePerGas() (*big.Int, error) { var out string if err := e.c.Call("eth_maxPriorityFeePerGas", &out); err != nil { return big.NewInt(0), err } return parseBigInt(out), nil } ================================================ FILE: jsonrpc/eth_test.go ================================================ package jsonrpc import ( "bytes" "encoding/hex" "math/big" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/abi" "github.com/umbracle/ethgo/testutil" ) var ( addr0 = ethgo.Address{0x1} addr1 = ethgo.Address{0x2} ) func TestEthAccounts(t *testing.T) { testutil.MultiAddr(t, func(s *testutil.TestServer, addr string) { c, _ := NewClient(addr) defer c.Close() _, err := c.Eth().Accounts() assert.NoError(t, err) }) } func TestEthBlockNumber(t *testing.T) { testutil.MultiAddr(t, func(s *testutil.TestServer, addr string) { c, _ := NewClient(addr) defer c.Close() num, err := c.Eth().BlockNumber() require.NoError(t, err) for i := 0; i < 10; i++ { require.NoError(t, s.ProcessBlock()) // since it is concurrent, we cannot ensure sequential numbers newNum, err := c.Eth().BlockNumber() require.NoError(t, err) require.Greater(t, newNum, num) num = newNum } }) } func TestEthGetCode(t *testing.T) { s := testutil.NewTestServer(t) c, _ := NewClient(s.HTTPAddr()) cc := &testutil.Contract{} cc.AddEvent(testutil.NewEvent("A"). Add("address", true). Add("address", true)) cc.EmitEvent("setA1", "A", addr0.String(), addr1.String()) cc.EmitEvent("setA2", "A", addr1.String(), addr0.String()) _, addr, err := s.DeployContract(cc) require.NoError(t, err) code, err := c.Eth().GetCode(addr, ethgo.Latest) assert.NoError(t, err) assert.NotEqual(t, code, "0x") code2, err := c.Eth().GetCode(addr, ethgo.BlockNumber(0)) assert.NoError(t, err) assert.Equal(t, code2, "0x") } func TestEthGetBalance(t *testing.T) { s := testutil.NewTestServer(t) c, _ := NewClient(s.HTTPAddr()) balance, err := c.Eth().GetBalance(s.Account(0), ethgo.Latest) assert.NoError(t, err) assert.NotEqual(t, balance, big.NewInt(0)) balance, err = c.Eth().GetBalance(ethgo.Address{}, ethgo.Latest) assert.NoError(t, err) assert.Equal(t, balance, big.NewInt(0)) } func TestEthGetBlockByNumber(t *testing.T) { s := testutil.NewTestServer(t) c, _ := NewClient(s.HTTPAddr()) block, err := c.Eth().GetBlockByNumber(0, true) assert.NoError(t, err) assert.Equal(t, block.Number, uint64(0)) // query a non-sealed block block 1 has not been processed yet // it does not fail but returns nil latest, err := c.Eth().BlockNumber() require.NoError(t, err) block, err = c.Eth().GetBlockByNumber(ethgo.BlockNumber(latest+10000), true) assert.NoError(t, err) assert.Nil(t, block) } func TestEthGetBlockByHash(t *testing.T) { testutil.MultiAddr(t, func(s *testutil.TestServer, addr string) { c, _ := NewClient(addr) defer c.Close() // get block 0 first by number block, err := c.Eth().GetBlockByNumber(0, true) assert.NoError(t, err) assert.Equal(t, block.Number, uint64(0)) // get block 0 by hash block2, err := c.Eth().GetBlockByHash(block.Hash, true) assert.NoError(t, err) assert.Equal(t, block, block2) }) } func TestEthGasPrice(t *testing.T) { testutil.MultiAddr(t, func(s *testutil.TestServer, addr string) { c, _ := NewClient(addr) defer c.Close() _, err := c.Eth().GasPrice() assert.NoError(t, err) }) } func TestEthSendTransaction(t *testing.T) { s := testutil.NewTestServer(t) c, _ := NewClient(s.HTTPAddr()) txn := ðgo.Transaction{ From: s.Account(0), GasPrice: testutil.DefaultGasPrice, Gas: testutil.DefaultGasLimit, To: &testutil.DummyAddr, Value: big.NewInt(10), } hash, err := c.Eth().SendTransaction(txn) assert.NoError(t, err) var receipt *ethgo.Receipt for { receipt, err = c.Eth().GetTransactionReceipt(hash) if err != nil { t.Fatal(err) } if receipt != nil { break } } } func TestEthEstimateGas(t *testing.T) { s := testutil.NewTestServer(t) c, _ := NewClient(s.HTTPAddr()) cc := &testutil.Contract{} cc.AddEvent(testutil.NewEvent("A").Add("address", true)) cc.EmitEvent("setA", "A", addr0.String()) // estimate gas to deploy the contract solcContract, err := cc.Compile() assert.NoError(t, err) input, err := hex.DecodeString(solcContract.Bin) assert.NoError(t, err) gas, err := c.Eth().EstimateGasContract(input) assert.NoError(t, err) assert.Greater(t, gas, uint64(140000)) _, addr, err := s.DeployContract(cc) require.NoError(t, err) msg := ðgo.CallMsg{ From: s.Account(0), To: &addr, Data: testutil.MethodSig("setA"), } gas, err = c.Eth().EstimateGas(msg) assert.NoError(t, err) assert.NotEqual(t, gas, 0) } func TestEthGetLogs(t *testing.T) { s := testutil.NewTestServer(t) c, _ := NewClient(s.HTTPAddr()) cc := &testutil.Contract{} cc.AddEvent(testutil.NewEvent("A"). Add("address", true). Add("address", true)) cc.EmitEvent("setA1", "A", addr0.String(), addr1.String()) cc.EmitEvent("setA2", "A", addr1.String(), addr0.String()) _, addr, err := s.DeployContract(cc) require.NoError(t, err) r, err := s.TxnTo(addr, "setA2") require.NoError(t, err) filter := ðgo.LogFilter{ BlockHash: &r.BlockHash, } logs, err := c.Eth().GetLogs(filter) assert.NoError(t, err) assert.Len(t, logs, 1) log := logs[0] assert.Len(t, log.Topics, 3) assert.Equal(t, log.Address, addr) // first topic is the signature of the event assert.Equal(t, log.Topics[0].String(), cc.GetEvent("A").Sig()) // topics have 32 bytes and the addr are 20 bytes, then, assert.Equal wont work. // this is a workaround until we build some helper function to test this better assert.True(t, bytes.HasSuffix(log.Topics[1][:], addr1[:])) assert.True(t, bytes.HasSuffix(log.Topics[2][:], addr0[:])) } func TestEthChainID(t *testing.T) { testutil.MultiAddr(t, func(s *testutil.TestServer, addr string) { c, _ := NewClient(addr) defer c.Close() num, err := c.Eth().ChainID() assert.NoError(t, err) assert.Equal(t, num.Uint64(), uint64(1337)) // chainid of geth-dev }) } func TestEthCall(t *testing.T) { s := testutil.NewTestServer(t) c, _ := NewClient(s.HTTPAddr()) cc := &testutil.Contract{} // add global variables cc.AddCallback(func() string { return "uint256 val = 1;" }) // add setter method cc.AddCallback(func() string { return `function getValue() public returns (uint256) { return val; }` }) _, addr, err := s.DeployContract(cc) require.NoError(t, err) input := abi.MustNewMethod("function getValue() public returns (uint256)").ID() resp, err := c.Eth().Call(ðgo.CallMsg{To: &addr, Data: input}, ethgo.Latest) require.NoError(t, err) require.Equal(t, "0x0000000000000000000000000000000000000000000000000000000000000001", resp) nonce := uint64(1) // override the state override := ðgo.StateOverride{ addr: ethgo.OverrideAccount{ Nonce: &nonce, Balance: big.NewInt(1), StateDiff: &map[ethgo.Hash]ethgo.Hash{ // storage slot 0 stores the 'val' uint256 value {0x0}: {0x3}, }, }, } resp, err = c.Eth().Call(ðgo.CallMsg{To: &addr, Data: input}, ethgo.Latest, override) require.NoError(t, err) require.Equal(t, "0x0300000000000000000000000000000000000000000000000000000000000000", resp) } func TestEthGetNonce(t *testing.T) { s := testutil.NewTestServer(t) c, _ := NewClient(s.HTTPAddr()) receipt, err := s.ProcessBlockWithReceipt() assert.NoError(t, err) // query the balance with different options cases := []ethgo.BlockNumberOrHash{ ethgo.Latest, receipt.BlockHash, ethgo.BlockNumber(receipt.BlockNumber), } for _, ca := range cases { num, err := c.Eth().GetNonce(s.Account(0), ca) assert.NoError(t, err) assert.NotEqual(t, num, uint64(0)) } } func TestEthTransactionsInBlock(t *testing.T) { s := testutil.NewTestServer(t) c, _ := NewClient(s.HTTPAddr()) // block 0 does not have transactions _, err := c.Eth().GetBlockByNumber(0, false) assert.NoError(t, err) // Process a block with a transaction assert.NoError(t, s.ProcessBlock()) latest, err := c.Eth().BlockNumber() require.NoError(t, err) num := ethgo.BlockNumber(latest) // get a non-full block block0, err := c.Eth().GetBlockByNumber(num, false) assert.NoError(t, err) assert.NotEmpty(t, block0.TransactionsHashes, 1) assert.Empty(t, block0.Transactions, 0) // get a full block block1, err := c.Eth().GetBlockByNumber(num, true) assert.NoError(t, err) assert.Empty(t, block1.TransactionsHashes, 0) assert.NotEmpty(t, block1.Transactions, 1) for indx := range block0.TransactionsHashes { assert.Equal(t, block0.TransactionsHashes[indx], block1.Transactions[indx].Hash) } } func TestEthGetStorageAt(t *testing.T) { s := testutil.NewTestServer(t) c, _ := NewClient(s.HTTPAddr()) cc := &testutil.Contract{} // add global variables cc.AddCallback(func() string { return "uint256 val;" }) // add setter method cc.AddCallback(func() string { return `function setValue() public payable { val = 10; }` }) _, addr, err := s.DeployContract(cc) require.NoError(t, err) receipt, err := s.TxnTo(addr, "setValue") require.NoError(t, err) cases := []ethgo.BlockNumberOrHash{ ethgo.Latest, receipt.BlockHash, ethgo.BlockNumber(receipt.BlockNumber), } for _, ca := range cases { res, err := c.Eth().GetStorageAt(addr, ethgo.Hash{}, ca) assert.NoError(t, err) assert.True(t, strings.HasSuffix(res.String(), "a")) } } func TestEthFeeHistory(t *testing.T) { c, _ := NewClient(testutil.TestInfuraEndpoint(t)) lastBlock, err := c.Eth().BlockNumber() assert.NoError(t, err) fee, err := c.Eth().FeeHistory(1, ethgo.BlockNumber(lastBlock), []float64{25, 75}) assert.NoError(t, err) assert.NotNil(t, fee) } func TestEthMaxPriorityFeePerGas(t *testing.T) { s := testutil.NewTestServer(t) c, err := NewClient(s.HTTPAddr()) require.NoError(t, err) initialMaxPriorityFee, err := c.Eth().MaxPriorityFeePerGas() require.NoError(t, err) // wait for 2 blocks require.NoError(t, s.ProcessBlock()) require.NoError(t, s.ProcessBlock()) txn := ðgo.Transaction{ To: &testutil.DummyAddr, Value: ethgo.Gwei(1), Type: ethgo.TransactionDynamicFee, MaxPriorityFeePerGas: ethgo.Gwei(1), } latestBlock, err := c.Eth().BlockNumber() require.NoError(t, err) feeHistory, err := c.Eth().FeeHistory(1, ethgo.BlockNumber(latestBlock), nil) require.NoError(t, err) latestBaseFee := feeHistory.BaseFee[len(feeHistory.BaseFee)-1] txn.MaxFeePerGas = new(big.Int).Add(latestBaseFee, txn.MaxPriorityFeePerGas) receipt, err := s.SendTxn(txn) require.NoError(t, err) require.Equal(t, uint64(1), receipt.Status) newMaxPriorityFee, err := c.Eth().MaxPriorityFeePerGas() t.Log(initialMaxPriorityFee) t.Log(newMaxPriorityFee) require.NoError(t, err) require.True(t, initialMaxPriorityFee.Cmp(newMaxPriorityFee) <= 0) } ================================================ FILE: jsonrpc/net.go ================================================ package jsonrpc // Net is the net namespace type Net struct { c *Client } // Net returns the reference to the net namespace func (c *Client) Net() *Net { return c.endpoints.n } // Version returns the current network id func (n *Net) Version() (uint64, error) { var out string if err := n.c.Call("net_version", &out); err != nil { return 0, err } return parseUint64orHex(out) } // Listening returns true if client is actively listening for network connections func (n *Net) Listening() (bool, error) { var out bool err := n.c.Call("net_listening", &out) return out, err } // PeerCount returns number of peers currently connected to the client func (n *Net) PeerCount() (uint64, error) { var out string if err := n.c.Call("net_peerCount", &out); err != nil { return 0, err } return parseUint64orHex(out) } ================================================ FILE: jsonrpc/net_test.go ================================================ package jsonrpc import ( "testing" "github.com/stretchr/testify/assert" "github.com/umbracle/ethgo/testutil" ) func TestNetVersion(t *testing.T) { testutil.MultiAddr(t, func(s *testutil.TestServer, addr string) { c, _ := NewClient(addr) defer c.Close() _, err := c.Net().Version() assert.NoError(t, err) }) } func TestNetListening(t *testing.T) { testutil.MultiAddr(t, func(s *testutil.TestServer, addr string) { c, _ := NewClient(addr) defer c.Close() ok, err := c.Net().Listening() assert.NoError(t, err) assert.True(t, ok) }) } func TestNetPeerCount(t *testing.T) { testutil.MultiAddr(t, func(s *testutil.TestServer, addr string) { c, _ := NewClient(addr) defer c.Close() count, err := c.Net().PeerCount() assert.NoError(t, err) assert.Equal(t, count, uint64(0)) }) } ================================================ FILE: jsonrpc/subscribe.go ================================================ package jsonrpc import ( "fmt" "github.com/umbracle/ethgo/jsonrpc/transport" ) // SubscriptionEnabled returns true if the subscription endpoints are enabled func (c *Client) SubscriptionEnabled() bool { _, ok := c.transport.(transport.PubSubTransport) return ok } // Subscribe starts a new subscription func (c *Client) Subscribe(method string, callback func(b []byte)) (func() error, error) { pub, ok := c.transport.(transport.PubSubTransport) if !ok { return nil, fmt.Errorf("transport does not support the subscribe method") } close, err := pub.Subscribe(method, callback) return close, err } ================================================ FILE: jsonrpc/subscribe_test.go ================================================ package jsonrpc import ( "strings" "testing" "time" "github.com/stretchr/testify/assert" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/testutil" ) func TestSubscribeNewHead(t *testing.T) { testutil.MultiAddr(t, func(s *testutil.TestServer, addr string) { if strings.HasPrefix(addr, "http") { return } c, _ := NewClient(addr) defer c.Close() data := make(chan []byte) cancel, err := c.Subscribe("newHeads", func(b []byte) { data <- b }) if err != nil { t.Fatal(err) } var lastBlock *ethgo.Block recv := func(ok bool) { select { case buf := <-data: if !ok { t.Fatal("unexpected value") } var block ethgo.Block if err := block.UnmarshalJSON(buf); err != nil { t.Fatal(err) } if lastBlock != nil { if lastBlock.Number+1 != block.Number { t.Fatalf("bad sequence %d %d", lastBlock.Number, block.Number) } } lastBlock = &block case <-time.After(1 * time.Second): if ok { t.Fatal("timeout for new head") } } } s.ProcessBlock() recv(true) s.ProcessBlock() recv(true) assert.NoError(t, cancel()) s.ProcessBlock() recv(false) // subscription already closed assert.Error(t, cancel()) }) } ================================================ FILE: jsonrpc/transport/http.go ================================================ package transport import ( "encoding/json" "fmt" "github.com/umbracle/ethgo/jsonrpc/codec" "github.com/valyala/fasthttp" ) // HTTP is an http transport type HTTP struct { addr string client *fasthttp.Client headers map[string]string } func newHTTP(addr string, headers map[string]string) *HTTP { return &HTTP{ addr: addr, client: &fasthttp.Client{ DialDualStack: true, }, headers: headers, } } // Close implements the transport interface func (h *HTTP) Close() error { return nil } // Call implements the transport interface func (h *HTTP) Call(method string, out interface{}, params ...interface{}) error { // Encode json-rpc request request := codec.Request{ JsonRPC: "2.0", Method: method, } if len(params) > 0 { data, err := json.Marshal(params) if err != nil { return err } request.Params = data } else { request.Params = []byte{'[', ']'} } raw, err := json.Marshal(request) if err != nil { return err } req := fasthttp.AcquireRequest() res := fasthttp.AcquireResponse() defer fasthttp.ReleaseRequest(req) defer fasthttp.ReleaseResponse(res) req.SetRequestURI(h.addr) req.Header.SetMethod("POST") req.Header.SetContentType("application/json") for k, v := range h.headers { req.Header.Add(k, v) } req.SetBody(raw) if err := h.client.Do(req, res); err != nil { return err } if sc := res.StatusCode(); sc != fasthttp.StatusOK { return fmt.Errorf("status code is %d. response = %s", sc, string(res.Body())) } // Decode json-rpc response var response codec.Response if err := json.Unmarshal(res.Body(), &response); err != nil { return err } if response.Error != nil { return response.Error } if err := json.Unmarshal(response.Result, out); err != nil { return err } return nil } // SetMaxConnsPerHost sets the maximum number of connections that can be established with a host func (h *HTTP) SetMaxConnsPerHost(count int) { h.client.MaxConnsPerHost = count } ================================================ FILE: jsonrpc/transport/ipc.go ================================================ package transport import ( "encoding/json" "net" ) func newIPC(addr string) (Transport, error) { conn, err := net.Dial("unix", addr) if err != nil { return nil, err } codec := &ipcCodec{ buf: json.RawMessage{}, conn: conn, dec: json.NewDecoder(conn), } return newStream(codec) } type ipcCodec struct { buf json.RawMessage conn net.Conn dec *json.Decoder } func (i *ipcCodec) Close() error { return i.conn.Close() } func (i *ipcCodec) Read(b []byte) ([]byte, error) { i.buf = i.buf[:0] if err := i.dec.Decode(&i.buf); err != nil { return nil, err } b = append(b, i.buf...) return b, nil } func (i *ipcCodec) Write(b []byte) error { _, err := i.conn.Write(b) return err } ================================================ FILE: jsonrpc/transport/transport.go ================================================ package transport import ( "os" "strings" ) // Transport is an inteface for transport methods to send jsonrpc requests type Transport interface { // Call makes a jsonrpc request Call(method string, out interface{}, params ...interface{}) error // SetMaxConnsPerHost sets the maximum number of connections that can be established with a host SetMaxConnsPerHost(count int) // Close closes the transport connection if necessary Close() error } // PubSubTransport is a transport that allows subscriptions type PubSubTransport interface { // Subscribe starts a subscription to a new event Subscribe(method string, callback func(b []byte)) (func() error, error) } const ( wsPrefix = "ws://" wssPrefix = "wss://" ) // NewTransport creates a new transport object func NewTransport(url string, headers map[string]string) (Transport, error) { if strings.HasPrefix(url, wsPrefix) || strings.HasPrefix(url, wssPrefix) { t, err := newWebsocket(url, headers) if err != nil { return nil, err } return t, nil } if _, err := os.Stat(url); err == nil { // path exists, it could be an ipc path t, err := newIPC(url) if err != nil { return nil, err } return t, nil } return newHTTP(url, headers), nil } ================================================ FILE: jsonrpc/transport/websocket.go ================================================ package transport import ( "encoding/json" "fmt" "net/http" "sync" "sync/atomic" "time" "github.com/gorilla/websocket" "github.com/umbracle/ethgo/jsonrpc/codec" ) func newWebsocket(url string, headers map[string]string) (Transport, error) { wsHeaders := http.Header{} for k, v := range headers { wsHeaders.Add(k, v) } wsConn, _, err := websocket.DefaultDialer.Dial(url, wsHeaders) if err != nil { return nil, err } codec := &websocketCodec{ conn: wsConn, } return newStream(codec) } // ErrTimeout happens when the websocket requests times out var ErrTimeout = fmt.Errorf("ws timeout") type ackMessage struct { buf []byte err error } type callback func(b []byte, err error) type stream struct { seq uint64 codec Codec // call handlers handlerLock sync.Mutex handler map[uint64]callback // subscriptions subsLock sync.Mutex subs map[string]func(b []byte) closeCh chan struct{} timer *time.Timer } func newStream(codec Codec) (*stream, error) { w := &stream{ codec: codec, closeCh: make(chan struct{}), handler: map[uint64]callback{}, subs: map[string]func(b []byte){}, } go w.listen() return w, nil } // Close implements the the transport interface func (s *stream) Close() error { close(s.closeCh) return s.codec.Close() } func (s *stream) incSeq() uint64 { return atomic.AddUint64(&s.seq, 1) } func (s *stream) isClosed() bool { select { case <-s.closeCh: return true default: return false } } func (s *stream) listen() { buf := []byte{} for { var err error buf, err = s.codec.Read(buf[:0]) if err != nil { if !s.isClosed() { // log error } return } var resp codec.Response if err = json.Unmarshal(buf, &resp); err != nil { return } if resp.ID != 0 { go s.handleMsg(resp) } else { // handle subscription var respSub codec.Request if err = json.Unmarshal(buf, &respSub); err != nil { return } if respSub.Method == "eth_subscription" { go s.handleSubscription(respSub) } } } } func (s *stream) handleSubscription(response codec.Request) { var sub codec.Subscription if err := json.Unmarshal(response.Params, &sub); err != nil { panic(err) } s.subsLock.Lock() callback, ok := s.subs[sub.ID] s.subsLock.Unlock() if !ok { return } // call the callback function callback(sub.Result) } func (s *stream) handleMsg(response codec.Response) { s.handlerLock.Lock() callback, ok := s.handler[response.ID] if !ok { s.handlerLock.Unlock() return } // delete handler delete(s.handler, response.ID) s.handlerLock.Unlock() if response.Error != nil { callback(nil, response.Error) } else { callback(response.Result, nil) } } func (s *stream) setHandler(id uint64, ack chan *ackMessage) { callback := func(b []byte, err error) { select { case ack <- &ackMessage{b, err}: default: } } s.handlerLock.Lock() s.handler[id] = callback s.handlerLock.Unlock() s.timer = time.AfterFunc(15*time.Second, func() { s.handlerLock.Lock() delete(s.handler, id) s.handlerLock.Unlock() select { case ack <- &ackMessage{nil, ErrTimeout}: default: } }) } // Call implements the transport interface func (s *stream) Call(method string, out interface{}, params ...interface{}) error { seq := s.incSeq() request := codec.Request{ JsonRPC: "2.0", ID: seq, Method: method, } if len(params) > 0 { data, err := json.Marshal(params) if err != nil { return err } request.Params = data } ack := make(chan *ackMessage) s.setHandler(seq, ack) raw, err := json.Marshal(request) if err != nil { return err } if err := s.codec.Write(raw); err != nil { return err } resp := <-ack if resp.err != nil { return resp.err } if err := json.Unmarshal(resp.buf, out); err != nil { return err } return nil } func (s *stream) unsubscribe(id string) error { s.subsLock.Lock() defer s.subsLock.Unlock() if _, ok := s.subs[id]; !ok { return fmt.Errorf("subscription %s not found", id) } delete(s.subs, id) var result bool if err := s.Call("eth_unsubscribe", &result, id); err != nil { return err } if !result { return fmt.Errorf("failed to unsubscribe") } return nil } func (s *stream) setSubscription(id string, callback func(b []byte)) { s.subsLock.Lock() defer s.subsLock.Unlock() s.subs[id] = callback } // Subscribe implements the PubSubTransport interface func (s *stream) Subscribe(method string, callback func(b []byte)) (func() error, error) { var out string if err := s.Call("eth_subscribe", &out, method); err != nil { return nil, err } s.setSubscription(out, callback) cancel := func() error { return s.unsubscribe(out) } return cancel, nil } // SetMaxConnsPerHost implements the transport interface func (s *stream) SetMaxConnsPerHost(count int) { } type websocketCodec struct { conn *websocket.Conn } func (w *websocketCodec) Close() error { return w.conn.Close() } func (w *websocketCodec) Write(b []byte) error { return w.conn.WriteMessage(websocket.TextMessage, b) } func (w *websocketCodec) Read(b []byte) ([]byte, error) { _, buf, err := w.conn.ReadMessage() if err != nil { return nil, err } b = append(b, buf...) return b, nil } // Codec is the codec to write and read messages type Codec interface { Read([]byte) ([]byte, error) Write([]byte) error Close() error } ================================================ FILE: jsonrpc/util.go ================================================ package jsonrpc import ( "encoding/hex" "fmt" "math/big" "strconv" "strings" ) type ArgBig big.Int func (a *ArgBig) UnmarshalText(input []byte) error { buf, err := parseHexBytes(string(input)) if err != nil { return err } b := new(big.Int) b.SetBytes(buf) *a = ArgBig(*b) return nil } func (a *ArgBig) Big() *big.Int { b := big.Int(*a) return &b } func encodeUintToHex(i uint64) string { return fmt.Sprintf("0x%x", i) } func parseBigInt(str string) *big.Int { str = strings.TrimPrefix(str, "0x") num := new(big.Int) num.SetString(str, 16) return num } func parseUint64orHex(str string) (uint64, error) { base := 10 if strings.HasPrefix(str, "0x") { str = str[2:] base = 16 } return strconv.ParseUint(str, base, 64) } func encodeToHex(b []byte) string { return "0x" + hex.EncodeToString(b) } func parseHexBytes(str string) ([]byte, error) { if !strings.HasPrefix(str, "0x") { return nil, fmt.Errorf("it does not have 0x prefix") } str = strings.TrimPrefix(str, "0x") if len(str)%2 != 0 { str = "0" + str } buf, err := hex.DecodeString(str) if err != nil { return nil, err } return buf, nil } ================================================ FILE: jsonrpc/web3.go ================================================ package jsonrpc // Web3 is the web3 namespace type Web3 struct { c *Client } // Web3 returns the reference to the web3 namespace func (c *Client) Web3() *Web3 { return c.endpoints.w } // ClientVersion returns the current client version func (w *Web3) ClientVersion() (string, error) { var out string err := w.c.Call("web3_clientVersion", &out) return out, err } // Sha3 returns Keccak-256 (not the standardized SHA3-256) of the given data func (w *Web3) Sha3(val []byte) ([]byte, error) { var out string if err := w.c.Call("web3_sha3", &out, encodeToHex(val)); err != nil { return nil, err } return parseHexBytes(out) } ================================================ FILE: jsonrpc/web3_test.go ================================================ package jsonrpc import ( "testing" "github.com/stretchr/testify/assert" "github.com/umbracle/ethgo/testutil" "golang.org/x/crypto/sha3" ) func TestWeb3ClientVersion(t *testing.T) { testutil.MultiAddr(t, func(s *testutil.TestServer, addr string) { c, _ := NewClient(addr) defer c.Close() _, err := c.Web3().ClientVersion() assert.NoError(t, err) }) } func TestWeb3Sha3(t *testing.T) { testutil.MultiAddr(t, func(s *testutil.TestServer, addr string) { c, _ := NewClient(addr) defer c.Close() src := []byte{0x1, 0x2, 0x3} found, err := c.Web3().Sha3(src) assert.NoError(t, err) k := sha3.NewLegacyKeccak256() k.Write(src) expected := k.Sum(nil) assert.Equal(t, expected, found) }) } ================================================ FILE: keccak.go ================================================ package ethgo import "golang.org/x/crypto/sha3" // Keccak256 calculates the Keccak256 func Keccak256(v ...[]byte) []byte { h := sha3.NewLegacyKeccak256() for _, i := range v { h.Write(i) } return h.Sum(nil) } ================================================ FILE: keystore/utils.go ================================================ package keystore import ( "crypto/aes" "crypto/cipher" "crypto/rand" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "strings" "golang.org/x/crypto/pbkdf2" "golang.org/x/crypto/scrypt" ) func getRand(size int) []byte { buf := make([]byte, size) rand.Read(buf) return buf } type hexString []byte func (h hexString) MarshalJSON() ([]byte, error) { str := "\"" + hex.EncodeToString(h) + "\"" return []byte(str), nil } func (h *hexString) UnmarshalJSON(data []byte) error { raw := string(data) raw = strings.Trim(raw, "\"") data, err := hex.DecodeString(raw) if err != nil { return err } *h = data return nil } func aesCTR(key, cipherText, iv []byte) ([]byte, error) { block, err := aes.NewCipher(key) if err != nil { return nil, err } stream := cipher.NewCTR(block, iv) dst := make([]byte, len(cipherText)) stream.XORKeyStream(dst, cipherText) return dst, nil } type pbkdf2Params struct { Dklen int `json:"dklen"` Salt hexString `json:"salt"` C int `json:"c"` Prf string `json:"prf"` } func (p *pbkdf2Params) Key(password []byte) []byte { return pbkdf2.Key(password, p.Salt, p.C, p.Dklen, sha256.New) } type scryptParams struct { Dklen int `json:"dklen"` Salt hexString `json:"salt"` N int `json:"n"` P int `json:"p"` R int `json:"r"` } func (s *scryptParams) Key(password []byte) ([]byte, error) { return scrypt.Key(password, s.Salt, s.N, s.R, s.P, s.Dklen) } func applyKdf(fn string, password, paramsRaw []byte) ([]byte, error) { var key []byte if fn == "pbkdf2" { var params pbkdf2Params if err := json.Unmarshal(paramsRaw, ¶ms); err != nil { return nil, err } if params.Prf != "hmac-sha256" { return nil, fmt.Errorf("not found") } key = params.Key(password) } else if fn == "scrypt" { var params scryptParams err := json.Unmarshal(paramsRaw, ¶ms) if err != nil { return nil, err } key, err = params.Key(password) if err != nil { return nil, err } } else { return nil, fmt.Errorf("kdf '%s' not supported", fn) } return key, nil } ================================================ FILE: keystore/v3.go ================================================ package keystore import ( "bytes" "crypto/aes" "encoding/json" "fmt" "github.com/umbracle/ethgo" ) // EncryptV3 encrypts data in v3 format func EncryptV3(content []byte, password string, customScrypt ...int) ([]byte, error) { // default scrypt values scryptN, scryptP := 1<<18, 1 if len(customScrypt) >= 1 { scryptN = customScrypt[0] } if len(customScrypt) >= 2 { scryptP = customScrypt[1] } iv := getRand(aes.BlockSize) scrypt := scryptParams{ N: scryptN, R: 8, P: scryptP, Dklen: 32, Salt: hexString(getRand(32)), } kdf, err := scrypt.Key([]byte(password)) if err != nil { return nil, err } cipherText, err := aesCTR(kdf[:16], content, iv) if err != nil { return nil, err } // generate mac mac := ethgo.Keccak256(kdf[16:32], cipherText) v3 := &v3Encoding{ Version: 3, Crypto: &cryptoEncoding{ Cipher: "aes-128-ctr", CipherText: hexString(cipherText), CipherParams: struct{ IV hexString }{ IV: hexString(iv), }, KDF: "scrypt", KDFParams: scrypt, Mac: hexString(mac), }, } encrypted, err := v3.Marshal() if err != nil { return nil, err } return encrypted, nil } // DecryptV3 decodes bytes in the v3 keystore format func DecryptV3(content []byte, password string) ([]byte, error) { encoding := v3Encoding{} if err := encoding.Unmarshal(content); err != nil { return nil, err } if encoding.Version != 3 { return nil, fmt.Errorf("only version 3 supported") } if encoding.Crypto.Cipher != "aes-128-ctr" { return nil, fmt.Errorf("cipher %s not supported", encoding.Crypto.Cipher) } // decode the kdf kdf, err := applyKdf(encoding.Crypto.KDF, []byte(password), encoding.Crypto.KDFParamsRaw) if err != nil { return nil, err } // validate mac mac := ethgo.Keccak256(kdf[16:32], encoding.Crypto.CipherText) if !bytes.Equal(mac, encoding.Crypto.Mac) { return nil, fmt.Errorf("incorrect mac") } dst, err := aesCTR(kdf[:16], encoding.Crypto.CipherText, encoding.Crypto.CipherParams.IV) if err != nil { return nil, err } return dst, nil } type v3Encoding struct { ID string `json:"id"` Version int64 `json:"version"` Crypto *cryptoEncoding `json:"crypto"` } func (j *v3Encoding) Marshal() ([]byte, error) { params, err := json.Marshal(j.Crypto.KDFParams) if err != nil { return nil, err } j.Crypto.KDFParamsRaw = json.RawMessage(params) return json.Marshal(j) } func (j *v3Encoding) Unmarshal(data []byte) error { return json.Unmarshal(data, j) } type cryptoEncoding struct { Cipher string `json:"cipher"` CipherParams struct { IV hexString } `json:"cipherparams"` CipherText hexString `json:"ciphertext"` KDF string `json:"kdf"` KDFParams interface{} KDFParamsRaw json.RawMessage `json:"kdfparams"` Mac hexString `json:"mac"` } ================================================ FILE: keystore/v3_test.go ================================================ package keystore import ( "testing" "github.com/stretchr/testify/assert" ) func TestV3_EncodeDecode(t *testing.T) { data := []byte{0x1, 0x2} password := "abcd" encrypted, err := EncryptV3(data, password) assert.NoError(t, err) found, err := DecryptV3(encrypted, password) assert.NoError(t, err) assert.Equal(t, data, found) } ================================================ FILE: keystore/v4.go ================================================ package keystore import ( "bytes" "crypto/sha256" "encoding/json" "fmt" "strings" "golang.org/x/text/unicode/norm" ) func EncryptV4(content []byte, password string) ([]byte, error) { password = normalizePassword(password) // decryption key scrypt := scryptParams{ N: 1 << 18, R: 8, P: 1, Dklen: 32, Salt: hexString(getRand(32)), } key, err := scrypt.Key([]byte(password)) if err != nil { return nil, err } // decrypt iv := getRand(16) cipherText, err := aesCTR(key[:16], content, iv) if err != nil { return nil, err } // checksum hash := sha256.New() hash.Write(key[16:32]) hash.Write(cipherText) checksum := hash.Sum(nil) kdfParams, err := json.Marshal(scrypt) if err != nil { return nil, err } cipherParams, err := json.Marshal(&cipherParams{Iv: hexString(iv)}) if err != nil { return nil, err } encoding := &v4Encoding{ Version: 4, Crypto: &v4crypto{ Kdf: &v4Module{ Function: "scrypt", Params: kdfParams, }, Cipher: &v4Module{ Function: "aes-128-ctr", Params: cipherParams, Message: hexString(cipherText), }, Checksum: &v4Module{ Function: "sha256", Message: hexString(checksum), }, }, } return encoding.Marshal() } type cipherParams struct { Iv hexString `json:"iv"` } func DecryptV4(content []byte, password string) ([]byte, error) { encoding := v4Encoding{} if err := encoding.Unmarshal(content); err != nil { return nil, err } if encoding.Version != 4 { return nil, fmt.Errorf("only version 4 supported") } password = normalizePassword(password) // decryption key key, err := applyKdf(encoding.Crypto.Kdf.Function, []byte(password), encoding.Crypto.Kdf.Params) if err != nil { return nil, err } // checksum hash := sha256.New() hash.Write(key[16:32]) hash.Write(encoding.Crypto.Cipher.Message) checksum := hash.Sum(nil) if !bytes.Equal(checksum, encoding.Crypto.Checksum.Message) { return nil, fmt.Errorf("bad checksum") } // decrypt var msg []byte if encoding.Crypto.Cipher.Function == "aes-128-ctr" { var params cipherParams if err := json.Unmarshal(encoding.Crypto.Cipher.Params, ¶ms); err != nil { return nil, err } res, err := aesCTR(key[:16], encoding.Crypto.Cipher.Message, params.Iv) if err != nil { return nil, err } msg = res } else { return nil, fmt.Errorf("cipher '%s' not supported", encoding.Crypto.Cipher.Function) } return msg, nil } type v4Encoding struct { Crypto *v4crypto `json:"crypto"` Description string `json:"description"` PubKey hexString `json:"pubkey"` Path string `json:"path"` Version int `json:"version"` Uuid string `json:"uuid"` } func (j *v4Encoding) Marshal() ([]byte, error) { return json.Marshal(j) } func (j *v4Encoding) Unmarshal(data []byte) error { return json.Unmarshal(data, j) } type v4crypto struct { Kdf *v4Module `json:"kdf"` Checksum *v4Module `json:"checksum"` Cipher *v4Module `json:"cipher"` } type v4Module struct { Function string `json:"function"` Params json.RawMessage `json:"params"` Message hexString `json:"message"` } // normalizePassword normalizes the password following the next rules // https://eips.ethereum.org/EIPS/eip-2335#password-requirements func normalizePassword(password string) string { str := norm.NFKD.String(password) skip := func(i byte) bool { // skip runes in the range 0x00 - 0x1F, 0x80 - 0x9F and 0x7F if i == 0x7F { return true } if 0x00 <= i && i <= 0x1F { return true } if 0x80 <= i && i <= 0x9F { return true } return false } normalized := strings.Builder{} for _, r := range str { elem := string(r) if len(elem) == 1 { if skip(elem[0]) { continue } } normalized.WriteRune(r) } return normalized.String() } ================================================ FILE: keystore/v4_test.go ================================================ package keystore import ( "testing" "github.com/stretchr/testify/assert" ) func TestV4_EncodeDecode(t *testing.T) { data := []byte{0x1, 0x2} password := "abcd" encrypted, err := EncryptV4(data, password) assert.NoError(t, err) found, err := DecryptV4(encrypted, password) assert.NoError(t, err) assert.Equal(t, data, found) } func TestV4_NormalizePassword(t *testing.T) { cases := []struct { input string output string }{ { "𝔱𝔢𝔰𝔱𝔭𝔞𝔰𝔰𝔴𝔬𝔯𝔡🔑", "testpassword🔑", }, { string([]byte{ 0x00, 0x1F, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, }), "password", }, } for _, c := range cases { found := normalizePassword(c.input) assert.Equal(t, c.output, found) } } ================================================ FILE: networks.go ================================================ package ethgo // Network is a chain id type Network uint64 const ( // Mainnet is the mainnet network Mainnet Network = 1 // Ropsten is the POW testnet Ropsten Network = 3 // Rinkeby is a POW testnet Rinkeby Network = 4 // Goerli is the Clique testnet Goerli Network = 5 ) ================================================ FILE: scripts/build-artifacts.sh ================================================ #!/usr/bin/env bash set -o errexit cd cmd echo "--> Build ENS" ENS_ARTIFACTS=../builtin/ens/artifacts go run main.go abigen --source ${ENS_ARTIFACTS}/ENS.abi,${ENS_ARTIFACTS}/Resolver.abi --output ../builtin/ens --package ens echo "--> Build ERC20" ERC20_ARTIFACTS=../builtin/erc20/artifacts go run main.go abigen --source ${ERC20_ARTIFACTS}/ERC20.abi --output ../builtin/erc20 --package erc20 echo "--> Build Testdata" go run main.go abigen --source ./abigen/testdata/testdata.abi --output ./abigen/testdata --package testdata ================================================ FILE: scripts/setup-ci.sh ================================================ #!/usr/bin/env bash # set -o errexit install_solidity() { VERSION="0.5.5" DOWNLOAD=https://github.com/ethereum/solidity/releases/download/v${VERSION}/solc-static-linux curl -L $DOWNLOAD > /tmp/solc chmod +x /tmp/solc mv /tmp/solc /usr/local/bin/solc } install_solidity ================================================ FILE: scripts/setup-geth.sh ================================================ #!/bin/bash # Start geth test server docker run --name geth-test -d -p 8545:8545 -p 8546:8546 ethereum/client-go:v1.10.15 \ --dev \ --datadir /eth1data \ --ipcpath /eth1data/geth.ipc \ --http --http.addr 0.0.0.0 --http.vhosts=* --http.api eth,net,web3,debug \ --ws --ws.addr 0.0.0.0 \ --verbosity 4 # Wait for geth to be running while ! nc -z localhost 8545; do sleep 0.1 # wait for 1/10 of the second before check again done ================================================ FILE: signing/eip712.go ================================================ package signing import ( "encoding/hex" "fmt" "math/big" "reflect" "sort" "strings" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/abi" ) type EIP712MessageBuilder[T any] struct { Types map[string][]*EIP712Type PrimaryType string Domain *EIP712Domain } func (e *EIP712MessageBuilder[T]) GetEncodedType() string { return encodeType(e.PrimaryType, e.Types) } func NewEIP712MessageBuilder[T any](domain *EIP712Domain) *EIP712MessageBuilder[T] { var t T types := map[string][]*EIP712Type{} primaryType := decodeStructType(reflect.ValueOf(t).Type(), &types) builder := &EIP712MessageBuilder[T]{ Types: types, PrimaryType: primaryType, Domain: domain, } return builder } func decodeStructType(typ reflect.Type, result *map[string][]*EIP712Type) string { if typ.Kind() != reflect.Struct { panic(fmt.Sprintf("struct expected but found %s", typ.Kind())) } name := typ.Name() types := []*EIP712Type{} for i := 0; i < typ.NumField(); i++ { field := typ.Field(i) fieldName := field.Name // use a tag as a name (if any) if tagVal := field.Tag.Get("eip712"); tagVal != "" { fieldName = tagVal } fieldType := decodeTypes(field.Type, result) types = append(types, &EIP712Type{ Type: fieldType, Name: fieldName, }) } (*result)[name] = types return name } func isByteSlice(t reflect.Type) bool { return t.Kind() == reflect.Slice && t.Elem().Kind() == reflect.Uint8 } func isByteArray(t reflect.Type) bool { return t.Kind() == reflect.Array && t.Elem().Kind() == reflect.Uint8 } var ( addressT = reflect.TypeOf(ethgo.Address{}) bigIntT = reflect.TypeOf(new(big.Int)) ) func decodeTypes(val reflect.Type, result *map[string][]*EIP712Type) string { if val == addressT { return "address" } if val == bigIntT { return "uint256" } switch val.Kind() { case reflect.Array: if val.Elem().Kind() == reflect.Uint8 { // [x]byte return fmt.Sprintf("[%d]byte", val.Len()) } return fmt.Sprintf("%s[%d]", decodeTypes(val.Elem(), result), val.Len()) case reflect.Slice: if val.Elem().Kind() == reflect.Uint8 { // []byte return "bytes" } return decodeTypes(val.Elem(), result) + "[]" case reflect.Struct: return decodeStructType(val, result) case reflect.String: return "string" case reflect.Uint8: return "uint8" case reflect.Uint16: return "uint16" case reflect.Uint32: return "uint32" case reflect.Uint64: return "uint64" case reflect.Ptr: return decodeTypes(val.Elem(), result) default: } panic(fmt.Sprintf("type %s not found", val.Kind())) } func (e *EIP712MessageBuilder[T]) Build(obj *T) *EIP712TypedData { message := structToMap(reflect.ValueOf(obj)) res := &EIP712TypedData{ Types: e.Types, PrimaryType: e.PrimaryType, Message: message, Domain: e.Domain, } return res } func structToMap(v reflect.Value) map[string]interface{} { if v.Kind() == reflect.Ptr { v = v.Elem() } typ := v.Type() result := make(map[string]interface{}) for i := 0; i < typ.NumField(); i++ { field := typ.Field(i) fieldValue := v.Field(i) fieldName := field.Name // use a tag as a name (if any) if tagVal := field.Tag.Get("eip712"); tagVal != "" { fieldName = tagVal } if field.Type == bigIntT { result[fieldName] = fieldValue.Interface() continue } if fieldValue.Kind() == reflect.Ptr { fieldValue = fieldValue.Elem() } if fieldValue.Kind() == reflect.Struct { // the field is a struct, handle recursively as another map result[fieldName] = structToMap(fieldValue) } else if fieldValue.Kind() == reflect.Slice || fieldValue.Kind() == reflect.Array { if field.Type.Elem().Kind() == reflect.Uint8 { // []byte return as it is result[fieldName] = fieldValue.Interface() } else { // the field is an slice, return a list of interfaces var arr []interface{} for j := 0; j < fieldValue.Len(); j++ { arr = append(arr, structToMap(fieldValue.Index(j))) } if fieldValue.Kind() == reflect.Array { // conver to array result[fieldName] = sliceToArray(arr) } else { result[fieldName] = arr } } } else { // primary field, return its interface form result[fieldName] = fieldValue.Interface() } } return result } func sliceToArray(slice interface{}) interface{} { sliceValue := reflect.ValueOf(slice) elemType := sliceValue.Type().Elem() length := sliceValue.Len() // Create a new slice with the same length and capacity as the original slice newSlice := reflect.MakeSlice(reflect.SliceOf(elemType), length, length) // Copy the elements from the original slice to the new slice reflect.Copy(newSlice, sliceValue) // Convert the new slice to an array arrayType := reflect.ArrayOf(length, elemType) arrayValue := reflect.New(arrayType).Elem() reflect.Copy(arrayValue, newSlice) // Return the array value as an interface{} return arrayValue.Interface() } type EIP712Type struct { Name string Type string } type EIP712TypedData struct { Types map[string][]*EIP712Type `json:"types"` PrimaryType string `json:"primaryType"` Domain *EIP712Domain `json:"domain"` Message map[string]interface{} `json:"message"` } func (t *EIP712TypedData) Hash() ([]byte, error) { a, err := t.Domain.hashStruct() if err != nil { return nil, err } b, err := hashStruct(t.PrimaryType, t.Types, t.Message) if err != nil { return nil, err } res := []byte{} res = append(res, 0x19, 0x1) res = append(res, a...) res = append(res, b...) return ethgo.Keccak256(res), nil } type EIP712Domain struct { Name string `json:"name"` Version string `json:"version"` VerifyingContract string `json:"verifyingContract"` ChainId *big.Int `json:"chainId"` Salt []byte `json:"salt"` } func hashStruct(primary string, types map[string][]*EIP712Type, data map[string]interface{}) ([]byte, error) { a1 := encodeType(primary, types) a2, err := encodeData(primary, types, data) if err != nil { return nil, err } typeHash := ethgo.Keccak256([]byte(a1)) input := []byte{} input = append(input, typeHash...) input = append(input, a2...) result := ethgo.Keccak256(input) return result, nil } func (e *EIP712Domain) hashStruct() ([]byte, error) { a1, a2 := e.getObjs() return hashStruct("EIP712Domain", map[string][]*EIP712Type{"EIP712Domain": a1}, a2) } func (e *EIP712Domain) getObjs() ([]*EIP712Type, map[string]interface{}) { var types []*EIP712Type data := map[string]interface{}{} addType := func(name, typ string) { types = append(types, &EIP712Type{Name: name, Type: typ}) } if len(e.Name) != 0 { addType("name", "string") data["name"] = e.Name } if len(e.Version) != 0 { addType("version", "string") data["version"] = e.Version } if e.ChainId != nil { addType("chainId", "uint256") data["chainId"] = e.ChainId } if len(e.VerifyingContract) != 0 { addType("verifyingContract", "address") data["verifyingContract"] = e.VerifyingContract } if len(e.Salt) != 0 { addType("salt", "bytes32") data["salt"] = e.Salt } return types, data } func encodeData(primary string, types map[string][]*EIP712Type, data map[string]interface{}) ([]byte, error) { fields := types[primary] result := []byte{} for _, field := range fields { val, ok := data[field.Name] if !ok { return nil, fmt.Errorf("field '%s' not found", field.Name) } res, err := encodeItem(field.Type, types, val) if err != nil { return nil, err } result = append(result, res...) } return result, nil } func encodeItem(typ string, types map[string][]*EIP712Type, val interface{}) ([]byte, error) { var res []byte // handle array if typ[len(typ)-1:] == "]" { subType := typ[:strings.LastIndex(typ, "[")] var subElem []byte v := reflect.ValueOf(val) for i := 0; i < v.Len(); i++ { elemRes, err := encodeItem(subType, types, v.Index(i).Interface()) if err != nil { return nil, err } subElem = append(subElem, elemRes...) } res = ethgo.Keccak256(subElem) } else if _, ok := types[typ]; ok { // if the item is a struct, handle it var err error if res, err = hashStruct(typ, types, val.(map[string]interface{})); err != nil { return nil, err } } else if typ == "string" { // dynamic string type valStr, ok := val.(string) if !ok { return nil, fmt.Errorf("string type not found") } res = ethgo.Keccak256([]byte(valStr)) } else if typ == "bytes" { // dynamic length bytes, it can be either a string or []byte if valStr, ok := val.(string); ok { // the string must start with 0x valBytes, err := decodeHexString(valStr) if err != nil { return nil, err } res = ethgo.Keccak256(valBytes) } else if valBytes, ok := val.([]byte); ok { res = ethgo.Keccak256(valBytes) } } else { // encode basic item typ, err := abi.NewType(typ) if err != nil { return nil, err } res, err = abi.Encode(val, typ) if err != nil { return nil, err } } return res, nil } func getDependencies(primary string, types map[string][]*EIP712Type) []string { // two types cannot be encoded twice visited := map[string]struct{}{} // a queue of the types to encode parseTypes := []string{ primary, } deps := sort.StringSlice{} for len(parseTypes) != 0 { var typ string typ, parseTypes = parseTypes[0], parseTypes[1:] for _, field := range types[typ] { typ := field.Type // remove any array items from the name (i.e. Struct[][3]) if indx := strings.Index(typ, "["); indx != -1 { typ = typ[:indx] } if _, ok := types[typ]; ok { if _, ok := visited[typ]; !ok { // its a type and not visited yet deps = append(deps, typ) parseTypes = append(parseTypes, typ) visited[typ] = struct{}{} } } } } deps.Sort() // the primary is always the first field res := []string{primary} res = append(res, deps...) return res } func encodeType(primary string, types map[string][]*EIP712Type) string { deps := getDependencies(primary, types) encodedType := "" for _, dep := range deps { strFields := []string{} for _, field := range types[dep] { strFields = append(strFields, field.Type+" "+field.Name) } encodedType += fmt.Sprintf("%s(%s)", dep, strings.Join(strFields, ",")) } return encodedType } func decodeHexString(str string) ([]byte, error) { if !strings.HasPrefix(str, "0x") { return nil, fmt.Errorf("0x prefix not found") } buf, err := hex.DecodeString(str[2:]) if err != nil { return nil, err } return buf, nil } ================================================ FILE: signing/eip712_test.go ================================================ package signing import ( "math/big" "testing" "github.com/stretchr/testify/require" "github.com/umbracle/ethgo" ) type Message struct { A uint64 `eip712:"a"` C *big.Int `eip712:"c"` Msg1 *Message2 `eip712:"msg1"` Msg2 []Message2 `eip712:"msg2"` Msg3 [3]Message2 `eip712:"msg3"` } type Message2 struct { B uint64 `eip712:"b"` Addr ethgo.Address `eip712:"addr"` } func TestBuildMessage_Encode(t *testing.T) { domain := &EIP712Domain{ Name: "name1", } b := NewEIP712MessageBuilder[Message](domain) require.Equal(t, "Message(uint64 a,uint256 c,Message2 msg1,Message2[] msg2,Message2[3] msg3)Message2(uint64 b,address addr)", b.GetEncodedType()) msg := &Message{ C: big.NewInt(1), Msg1: &Message2{}, Msg2: []Message2{ {B: 1}, }, } typedMsg := b.Build(msg) _, ok := typedMsg.Message["msg1"].(interface{}) require.True(t, ok) _, ok = typedMsg.Message["msg2"].([]interface{}) require.True(t, ok) _, ok = typedMsg.Message["msg3"].([3]interface{}) require.True(t, ok) _, err := typedMsg.Hash() require.NoError(t, err) } func TestBuildMessage_BasicTypes(t *testing.T) { domain := &EIP712Domain{ Name: "name1", } type Message struct { A uint64 B uint32 C uint16 D uint8 E [32]byte F string } b := NewEIP712MessageBuilder[Message](domain) require.Equal(t, "Message(uint64 A,uint32 B,uint16 C,uint8 D,[32]byte E,string F)", b.GetEncodedType()) } ================================================ FILE: structs.go ================================================ package ethgo import ( "encoding/hex" "fmt" "math/big" "strconv" "strings" ) var ( // ZeroAddress is an address of all zeros ZeroAddress = Address{} // ZeroHash is a hash of all zeros ZeroHash = Hash{} ) // Address is an Ethereum address type Address [20]byte // HexToAddress converts an hex string value to an address object func HexToAddress(str string) Address { a := Address{} a.UnmarshalText(completeHex(str, 20)) return a } // BytesToAddress converts bytes to an address object func BytesToAddress(b []byte) Address { var a Address size := len(b) min := min(size, 20) copy(a[20-min:], b[len(b)-min:]) return a } // Address implements the ethgo.Key interface Address method. func (a Address) Address() Address { return a } // Sign implements the ethgo.Key interface Sign method. func (a Address) Sign(hash []byte) ([]byte, error) { panic("an address cannot sign messages") } // UnmarshalText implements the unmarshal interface func (a *Address) UnmarshalText(b []byte) error { return unmarshalTextByte(a[:], b, 20) } // MarshalText implements the marshal interface func (a Address) MarshalText() ([]byte, error) { return []byte(a.String()), nil } // Bytes returns the bytes of the Address func (a Address) Bytes() []byte { return a[:] } func (a Address) String() string { return a.checksumEncode() } func (a Address) checksumEncode() string { address := strings.ToLower(hex.EncodeToString(a[:])) hash := hex.EncodeToString(Keccak256([]byte(address))) ret := "0x" for i := 0; i < len(address); i++ { character := string(address[i]) num, _ := strconv.ParseInt(string(hash[i]), 16, 64) if num > 7 { ret += strings.ToUpper(character) } else { ret += character } } return ret } // Hash is an Ethereum hash type Hash [32]byte // HexToHash converts an hex string value to a hash object func HexToHash(str string) Hash { h := Hash{} h.UnmarshalText(completeHex(str, 32)) return h } // BytesToHash converts bytes to a hash object func BytesToHash(b []byte) Hash { var h Hash size := len(b) min := min(size, 32) copy(h[32-min:], b[len(b)-min:]) return h } // UnmarshalText implements the unmarshal interface func (h *Hash) UnmarshalText(b []byte) error { return unmarshalTextByte(h[:], b, 32) } // MarshalText implements the marshal interface func (h Hash) MarshalText() ([]byte, error) { return []byte(h.String()), nil } // Bytes returns the bytes of the Hash func (h Hash) Bytes() []byte { return h[:] } func (h Hash) String() string { return "0x" + hex.EncodeToString(h[:]) } func (h Hash) Location() string { return h.String() } type Block struct { Number uint64 Hash Hash ParentHash Hash Sha3Uncles Hash TransactionsRoot Hash StateRoot Hash ReceiptsRoot Hash Miner Address Difficulty *big.Int ExtraData []byte GasLimit uint64 GasUsed uint64 Timestamp uint64 MixHash Hash Nonce [8]byte Transactions []*Transaction TransactionsHashes []Hash Uncles []Hash BaseFee *big.Int } func (b *Block) Copy() *Block { bb := new(Block) *bb = *b if b.Difficulty != nil { bb.Difficulty = new(big.Int).Set(b.Difficulty) } bb.ExtraData = append(bb.ExtraData[:0], b.ExtraData...) bb.Transactions = make([]*Transaction, len(b.Transactions)) for indx, txn := range b.Transactions { bb.Transactions[indx] = txn.Copy() } return bb } type TransactionType uint8 const ( TransactionLegacy TransactionType = 0 // eip-2930 TransactionAccessList TransactionType = 1 // eip-1559 TransactionDynamicFee TransactionType = 2 ) type Transaction struct { Type TransactionType // legacy values Hash Hash From Address To *Address Input []byte GasPrice uint64 Gas uint64 Value *big.Int Nonce uint64 V []byte R []byte S []byte // jsonrpc values BlockHash Hash BlockNumber uint64 TxnIndex uint64 // eip-2930 values ChainID *big.Int AccessList AccessList // eip-1559 values MaxPriorityFeePerGas *big.Int MaxFeePerGas *big.Int } func (t *Transaction) Copy() *Transaction { tt := new(Transaction) *tt = *t if t.To != nil { to := Address(*t.To) tt.To = &to } tt.Input = append(tt.Input[:0], t.Input...) if t.Value != nil { tt.Value = new(big.Int).Set(t.Value) } tt.V = append(tt.V[:0], t.V...) tt.R = append(tt.R[:0], t.R...) tt.S = append(tt.S[:0], t.S...) if t.ChainID != nil { tt.ChainID = new(big.Int).Set(t.ChainID) } if t.MaxPriorityFeePerGas != nil { tt.MaxPriorityFeePerGas = new(big.Int).Set(t.MaxPriorityFeePerGas) } if t.MaxFeePerGas != nil { tt.MaxFeePerGas = new(big.Int).Set(t.MaxFeePerGas) } tt.AccessList = t.AccessList.Copy() return tt } type AccessEntry struct { Address Address `json:"address"` Storage []Hash `json:"storageKeys"` } type AccessList []AccessEntry func (a *AccessList) Copy() AccessList { aa := AccessList{} for _, i := range *a { entry := AccessEntry{ Address: i.Address, Storage: []Hash{}, } entry.Storage = append(entry.Storage, i.Storage...) aa = append(aa, entry) } return aa } type CallMsg struct { From Address To *Address Data []byte GasPrice uint64 Gas *big.Int Value *big.Int } type LogFilter struct { Address []Address Topics [][]*Hash BlockHash *Hash From *BlockNumber To *BlockNumber } func (l *LogFilter) SetFromUint64(num uint64) { b := BlockNumber(num) l.From = &b } func (l *LogFilter) SetToUint64(num uint64) { b := BlockNumber(num) l.To = &b } func (l *LogFilter) SetTo(b BlockNumber) { l.To = &b } type Receipt struct { TransactionHash Hash TransactionIndex uint64 ContractAddress Address BlockHash Hash From Address BlockNumber uint64 GasUsed uint64 CumulativeGasUsed uint64 LogsBloom []byte Logs []*Log Status uint64 To *Address } func (r *Receipt) Copy() *Receipt { rr := new(Receipt) *rr = *r rr.LogsBloom = append(rr.LogsBloom[:0], r.LogsBloom...) rr.Logs = make([]*Log, len(r.Logs)) for indx, log := range r.Logs { rr.Logs[indx] = log.Copy() } return rr } type Log struct { Removed bool LogIndex uint64 TransactionIndex uint64 TransactionHash Hash BlockHash Hash BlockNumber uint64 Address Address Topics []Hash Data []byte } func (l *Log) Copy() *Log { ll := new(Log) *ll = *l ll.Data = append(ll.Data[:0], l.Data...) return ll } type BlockNumber int const ( Latest BlockNumber = -1 Earliest BlockNumber = -2 Pending BlockNumber = -3 ) func (b BlockNumber) Location() string { return b.String() } func (b BlockNumber) String() string { switch b { case Latest: return "latest" case Earliest: return "earliest" case Pending: return "pending" } if b < 0 { panic("internal. blocknumber is negative") } return fmt.Sprintf("0x%x", uint64(b)) } func EncodeBlock(block ...BlockNumber) BlockNumber { if len(block) != 1 { return Latest } return block[0] } type BlockNumberOrHash interface { Location() string } func min(i, j int) int { if i < j { return i } return j } type Key interface { Address() Address Sign(hash []byte) ([]byte, error) } func completeHex(str string, num int) []byte { num = num * 2 str = strings.TrimPrefix(str, "0x") size := len(str) if size < num { for i := size; i < num; i++ { str = "0" + str } } else { diff := size - num str = str[diff:] } return []byte("0x" + str) } type OverrideAccount struct { Nonce *uint64 Code *[]byte Balance *big.Int State *map[Hash]Hash StateDiff *map[Hash]Hash } type StateOverride map[Address]OverrideAccount ================================================ FILE: structs_encoding_test.go ================================================ package ethgo import ( "bytes" "encoding/json" "io/ioutil" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func compactJSON(s string) string { buffer := new(bytes.Buffer) if err := json.Compact(buffer, []byte(s)); err != nil { panic(err) } return buffer.String() } func TestDecodeL2Block(t *testing.T) { c := readTestsuite(t, "./testsuite/arbitrum-block-full.json") block := new(Block) require.NoError(t, block.UnmarshalJSON(c[0].content)) for _, txn := range block.Transactions { require.NotEqual(t, txn.Type, 0) } } func TestEncodingJSON_Block(t *testing.T) { for _, c := range readTestsuite(t, "./testsuite/block-*.json") { content := []byte(compactJSON(string(c.content))) txn := new(Block) // unmarshal err := txn.UnmarshalJSON(content) assert.NoError(t, err) // marshal back res2, err := txn.MarshalJSON() assert.NoError(t, err) assert.Equal(t, content, res2) } } func TestEncodingJSON_Transaction(t *testing.T) { for _, c := range readTestsuite(t, "./testsuite/transaction-*.json") { content := []byte(compactJSON(string(c.content))) txn := new(Transaction) // unmarshal err := txn.UnmarshalJSON(content) assert.NoError(t, err) if c.name == "testsuite/transaction-eip1559-notype.json" { continue } // marshal back res2, err := txn.MarshalJSON() assert.NoError(t, err) assert.Equal(t, content, res2) } } type testFile struct { name string content []byte } func readTestsuite(t *testing.T, pattern string) (res []*testFile) { files, err := filepath.Glob(pattern) if err != nil { t.Fatal(err) } if len(files) == 0 { t.Fatal("no test files found") } for _, f := range files { data, err := ioutil.ReadFile(f) if err != nil { t.Fatal(err) } res = append(res, &testFile{ name: f, content: data, }) } return } ================================================ FILE: structs_marshal.go ================================================ package ethgo import ( "encoding/hex" "fmt" "math/big" "github.com/valyala/fastjson" ) var defaultArena fastjson.ArenaPool // MarshalJSON implements the marshal interface func (l *Log) MarshalJSON() ([]byte, error) { a := defaultArena.Get() defer a.Reset() o := a.NewObject() if l.Removed { o.Set("removed", a.NewTrue()) } else { o.Set("removed", a.NewFalse()) } o.Set("logIndex", a.NewString(fmt.Sprintf("0x%x", l.LogIndex))) o.Set("transactionIndex", a.NewString(fmt.Sprintf("0x%x", l.TransactionIndex))) o.Set("transactionHash", a.NewString(l.TransactionHash.String())) o.Set("blockHash", a.NewString(l.BlockHash.String())) o.Set("blockNumber", a.NewString(fmt.Sprintf("0x%x", l.BlockNumber))) o.Set("address", a.NewString(l.Address.String())) o.Set("data", a.NewString("0x"+hex.EncodeToString(l.Data))) vv := a.NewArray() for indx, topic := range l.Topics { vv.SetArrayItem(indx, a.NewString(topic.String())) } o.Set("topics", vv) res := o.MarshalTo(nil) defaultArena.Put(a) return res, nil } // MarshalJSON implements the marshal interface func (t *Block) MarshalJSON() ([]byte, error) { if t.Difficulty == nil { t.Difficulty = new(big.Int) } a := defaultArena.Get() defer a.Reset() o := a.NewObject() o.Set("number", a.NewString(fmt.Sprintf("0x%x", t.Number))) o.Set("hash", a.NewString(t.Hash.String())) o.Set("parentHash", a.NewString(t.ParentHash.String())) o.Set("sha3Uncles", a.NewString(t.Sha3Uncles.String())) o.Set("transactionsRoot", a.NewString(t.TransactionsRoot.String())) o.Set("stateRoot", a.NewString(t.StateRoot.String())) o.Set("receiptsRoot", a.NewString(t.ReceiptsRoot.String())) o.Set("miner", a.NewString(t.Miner.String())) o.Set("gasLimit", a.NewString(fmt.Sprintf("0x%x", t.GasLimit))) o.Set("gasUsed", a.NewString(fmt.Sprintf("0x%x", t.GasUsed))) o.Set("timestamp", a.NewString(fmt.Sprintf("0x%x", t.Timestamp))) o.Set("difficulty", a.NewString(fmt.Sprintf("0x%x", t.Difficulty))) o.Set("extraData", a.NewString("0x"+hex.EncodeToString(t.ExtraData))) o.Set("mixHash", a.NewString("0x"+hex.EncodeToString(t.MixHash[:]))) o.Set("nonce", a.NewString("0x"+hex.EncodeToString(t.Nonce[:]))) if t.BaseFee != nil { o.Set("baseFee", a.NewString(fmt.Sprintf("0x%x", t.BaseFee))) } // uncles if len(t.Uncles) != 0 { uncles := a.NewArray() for indx, uncle := range t.Uncles { uncles.SetArrayItem(indx, a.NewString(uncle.String())) } o.Set("uncles", uncles) } // transactions if len(t.TransactionsHashes) != 0 { txns := a.NewArray() for indx, txn := range t.TransactionsHashes { txns.SetArrayItem(indx, a.NewString(txn.String())) } o.Set("transactions", txns) } if len(t.Transactions) != 0 { txns := a.NewArray() for indx, txn := range t.Transactions { txns.SetArrayItem(indx, txn.marshalJSON(a)) } o.Set("transactions", txns) } res := o.MarshalTo(nil) defaultArena.Put(a) return res, nil } // MarshalJSON implements the Marshal interface. func (t *Transaction) MarshalJSON() ([]byte, error) { a := defaultArena.Get() defer a.Reset() v := t.marshalJSON(a) res := v.MarshalTo(nil) defaultArena.Put(a) return res, nil } func (t *Transaction) marshalJSON(a *fastjson.Arena) *fastjson.Value { o := a.NewObject() o.Set("type", a.NewString(fmt.Sprintf("0x%x", t.Type))) o.Set("hash", a.NewString(t.Hash.String())) o.Set("from", a.NewString(t.From.String())) if len(t.Input) != 0 { o.Set("input", a.NewString("0x"+hex.EncodeToString(t.Input))) } if t.Value != nil { o.Set("value", a.NewString(fmt.Sprintf("0x%x", t.Value))) } if t.Type == TransactionDynamicFee { if t.MaxPriorityFeePerGas != nil { o.Set("maxPriorityFeePerGas", a.NewString(fmt.Sprintf("0x%x", t.MaxPriorityFeePerGas))) } if t.MaxFeePerGas != nil { o.Set("maxFeePerGas", a.NewString(fmt.Sprintf("0x%x", t.MaxFeePerGas))) } } else { o.Set("gasPrice", a.NewString(fmt.Sprintf("0x%x", t.GasPrice))) } // gas limit fields if t.Gas != 0 { o.Set("gas", a.NewString(fmt.Sprintf("0x%x", t.Gas))) } if t.Nonce != 0 { // we can remove this once we include support for custom nonces o.Set("nonce", a.NewString(fmt.Sprintf("0x%x", t.Nonce))) } if t.To == nil { o.Set("to", a.NewNull()) } else { o.Set("to", a.NewString(t.To.String())) } o.Set("v", a.NewString("0x"+hex.EncodeToString(t.V))) o.Set("r", a.NewString("0x"+hex.EncodeToString(t.R))) o.Set("s", a.NewString("0x"+hex.EncodeToString(t.S))) if t.BlockHash == ZeroHash { // The transaction is a pending transaction o.Set("blockHash", a.NewNull()) o.Set("blockNumber", a.NewNull()) o.Set("transactionIndex", a.NewNull()) } else { // The transaction has valid metadata fields, fill them o.Set("blockHash", a.NewString(t.BlockHash.String())) o.Set("blockNumber", a.NewString(fmt.Sprintf("0x%x", t.BlockNumber))) o.Set("transactionIndex", a.NewString(fmt.Sprintf("0x%x", t.TxnIndex))) } if t.ChainID != nil { o.Set("chainId", a.NewString(fmt.Sprintf("0x%x", t.ChainID))) } if t.AccessList != nil { o.Set("accessList", t.AccessList.marshalJSON(a)) } return o } func (t *AccessList) marshalJSON(a *fastjson.Arena) *fastjson.Value { arr := a.NewArray() for indx, elem := range *t { arrElem := a.NewObject() arrElem.Set("address", a.NewString(elem.Address.String())) strg := a.NewArray() for subIndx, elem := range elem.Storage { strg.SetArrayItem(subIndx, a.NewString(elem.String())) } arrElem.Set("storageKeys", strg) arr.SetArrayItem(indx, arrElem) } return arr } // MarshalJSON implements the Marshal interface. func (c *CallMsg) MarshalJSON() ([]byte, error) { a := defaultArena.Get() defer a.Reset() o := a.NewObject() o.Set("from", a.NewString(c.From.String())) if c.To != nil { o.Set("to", a.NewString(c.To.String())) } if len(c.Data) != 0 { o.Set("data", a.NewString("0x"+hex.EncodeToString(c.Data))) } if c.GasPrice != 0 { o.Set("gasPrice", a.NewString(fmt.Sprintf("0x%x", c.GasPrice))) } if c.Value != nil { o.Set("value", a.NewString(fmt.Sprintf("0x%x", c.Value))) } if c.Gas != nil { o.Set("gas", a.NewString(fmt.Sprintf("0x%x", c.Gas))) } res := o.MarshalTo(nil) defaultArena.Put(a) return res, nil } // MarshalJSON implements the Marshal interface. func (l *LogFilter) MarshalJSON() ([]byte, error) { a := defaultArena.Get() defer a.Reset() o := a.NewObject() if len(l.Address) == 1 { o.Set("address", a.NewString(l.Address[0].String())) } else if len(l.Address) > 1 { v := a.NewArray() for indx, addr := range l.Address { v.SetArrayItem(indx, a.NewString(addr.String())) } } v := a.NewArray() for indx, topics := range l.Topics { if topics == nil { v.SetArrayItem(indx, a.NewNull()) continue } innerTopicArray := a.NewArray() for innerIndx, innerTopic := range topics { if innerTopic == nil { innerTopicArray.SetArrayItem(innerIndx, a.NewNull()) continue } innerTopicArray.SetArrayItem(innerIndx, a.NewString(innerTopic.String())) } v.SetArrayItem(indx, innerTopicArray) } o.Set("topics", v) if l.BlockHash != nil { o.Set("blockHash", a.NewString((*l.BlockHash).String())) } if l.From != nil { o.Set("fromBlock", a.NewString((*l.From).String())) } if l.To != nil { o.Set("toBlock", a.NewString((*l.To).String())) } res := o.MarshalTo(nil) defaultArena.Put(a) return res, nil } func (s StateOverride) MarshalJSON() ([]byte, error) { a := defaultArena.Get() defer a.Reset() o := a.NewObject() for addr, obj := range s { oo := a.NewObject() if obj.Nonce != nil { oo.Set("nonce", a.NewString(fmt.Sprintf("0x%x", *obj.Nonce))) } if obj.Balance != nil { oo.Set("balance", a.NewString(fmt.Sprintf("0x%x", obj.Balance))) } if obj.Code != nil { oo.Set("code", a.NewString("0x"+hex.EncodeToString(*obj.Code))) } if obj.State != nil { ooo := a.NewObject() for k, v := range *obj.State { ooo.Set(k.String(), a.NewString(v.String())) } oo.Set("state", ooo) } if obj.StateDiff != nil { ooo := a.NewObject() for k, v := range *obj.StateDiff { ooo.Set(k.String(), a.NewString(v.String())) } oo.Set("stateDiff", ooo) } o.Set(addr.String(), oo) } res := o.MarshalTo(nil) defaultArena.Put(a) return res, nil } ================================================ FILE: structs_marshal_rlp.go ================================================ package ethgo import ( "fmt" "math/big" "github.com/umbracle/fastrlp" ) // GetHash returns the Hash of the transaction func (t *Transaction) GetHash() (hash Hash, err error) { var rlpEncode []byte if rlpEncode, err = t.MarshalRLPTo(nil); err != nil { return Hash{}, err } return BytesToHash(Keccak256(rlpEncode)), nil } // MarshalRLPTo marshals the transaction to a []byte destination func (t *Transaction) MarshalRLPTo(dst []byte) ([]byte, error) { raw, err := fastrlp.MarshalRLP(t) if err != nil { return nil, err } if t.Type == TransactionLegacy { return raw, nil } // append type byte return append([]byte{byte(t.Type)}, raw...), nil } // MarshalRLPWith marshals the transaction to RLP with a specific fastrlp.Arena func (t *Transaction) MarshalRLPWith(arena *fastrlp.Arena) (*fastrlp.Value, error) { vv := arena.NewArray() if t.Type != 0 { // either dynamic and access type vv.Set(arena.NewBigInt(t.ChainID)) } vv.Set(arena.NewUint(t.Nonce)) if t.Type == TransactionDynamicFee { // dynamic fee uses vv.Set(arena.NewBigInt(t.MaxPriorityFeePerGas)) vv.Set(arena.NewBigInt(t.MaxFeePerGas)) } else { // legacy and access type use gas price vv.Set(arena.NewUint(t.GasPrice)) } vv.Set(arena.NewUint(t.Gas)) // Address may be empty if t.To != nil { vv.Set(arena.NewBytes((*t.To)[:])) } else { vv.Set(arena.NewNull()) } vv.Set(arena.NewBigInt(t.Value)) vv.Set(arena.NewCopyBytes(t.Input)) if t.Type != 0 { // either dynamic and access type accessList, err := t.AccessList.MarshalRLPWith(arena) if err != nil { return nil, err } vv.Set(accessList) } // signature values vv.Set(arena.NewCopyBytes(t.V)) vv.Set(arena.NewCopyBytes(t.R)) vv.Set(arena.NewCopyBytes(t.S)) if t.Type == TransactionLegacy { return vv, nil } return vv, nil } func (t *Transaction) UnmarshalRLP(buf []byte) error { t.Hash = BytesToHash(Keccak256(buf)) if len(buf) < 1 { return fmt.Errorf("expecting 1 byte but 0 byte provided") } if buf[0] <= 0x7f { // it includes a type byte switch typ := buf[0]; typ { case 1: t.Type = TransactionAccessList case 2: t.Type = TransactionDynamicFee default: return fmt.Errorf("type byte %d not found", typ) } buf = buf[1:] } if err := fastrlp.UnmarshalRLP(buf, t); err != nil { return err } return nil } func (t *Transaction) UnmarshalRLPWith(v *fastrlp.Value) error { elems, err := v.GetElems() if err != nil { return err } getElem := func() *fastrlp.Value { v := elems[0] elems = elems[1:] return v } var num int switch t.Type { case TransactionLegacy: num = 9 case TransactionAccessList: // legacy + chain id + access list num = 11 case TransactionDynamicFee: // access list txn + gas fee 1 + gas fee 2 - gas price num = 12 default: return fmt.Errorf("transaction type %d not found", t.Type) } if numElems := len(elems); numElems != num { return fmt.Errorf("not enough elements to decode transaction, expected %d but found %d", num, numElems) } if t.Type != 0 { t.ChainID = new(big.Int) if err := getElem().GetBigInt(t.ChainID); err != nil { return err } } // nonce if t.Nonce, err = getElem().GetUint64(); err != nil { return err } if t.Type == TransactionDynamicFee { // dynamic fee uses t.MaxPriorityFeePerGas = new(big.Int) if err := getElem().GetBigInt(t.MaxPriorityFeePerGas); err != nil { return err } t.MaxFeePerGas = new(big.Int) if err := getElem().GetBigInt(t.MaxFeePerGas); err != nil { return err } } else { // legacy and access type use gas price if t.GasPrice, err = getElem().GetUint64(); err != nil { return err } } // gas if t.Gas, err = getElem().GetUint64(); err != nil { return err } // to vv, _ := getElem().Bytes() if len(vv) == 20 { // address addr := BytesToAddress(vv) t.To = &addr } else { // reset To t.To = nil } // value t.Value = new(big.Int) if err := getElem().GetBigInt(t.Value); err != nil { return err } // input if t.Input, err = getElem().GetBytes(t.Input[:0]); err != nil { return err } if t.Type != 0 { if err := t.AccessList.UnmarshalRLPWith(getElem()); err != nil { return err } } // V if t.V, err = getElem().GetBytes(t.V); err != nil { return err } // R if t.R, err = getElem().GetBytes(t.R); err != nil { return err } // S if t.S, err = getElem().GetBytes(t.S); err != nil { return err } return nil } func (a *AccessList) MarshalRLPTo(dst []byte) ([]byte, error) { return fastrlp.MarshalRLP(a) } func (a *AccessList) MarshalRLPWith(arena *fastrlp.Arena) (*fastrlp.Value, error) { if len(*a) == 0 { return arena.NewNullArray(), nil } v := arena.NewArray() for _, i := range *a { acct := arena.NewArray() acct.Set(arena.NewCopyBytes(i.Address[:])) if len(i.Storage) == 0 { acct.Set(arena.NewNullArray()) } else { strV := arena.NewArray() for _, v := range i.Storage { strV.Set(arena.NewCopyBytes(v[:])) } acct.Set(strV) } v.Set(acct) } return v, nil } func (a *AccessList) UnmarshalRLP(buf []byte) error { return fastrlp.UnmarshalRLP(buf, a) } func (a *AccessList) UnmarshalRLPWith(v *fastrlp.Value) error { if v.Type() == fastrlp.TypeArrayNull { // empty return nil } elems, err := v.GetElems() if err != nil { return err } for _, elem := range elems { entry := AccessEntry{} acctElems, err := elem.GetElems() if err != nil { return err } if len(acctElems) != 2 { return fmt.Errorf("two elems expected but %d found", len(acctElems)) } // decode 'address' if err = acctElems[0].GetAddr(entry.Address[:]); err != nil { return err } // decode 'storage' if acctElems[1].Type() != fastrlp.TypeArrayNull { storageElems, err := acctElems[1].GetElems() if err != nil { return err } entry.Storage = make([]Hash, len(storageElems)) for indx, storage := range storageElems { // decode storage if err = storage.GetHash(entry.Storage[indx][:]); err != nil { return err } } } (*a) = append((*a), entry) } return nil } ================================================ FILE: structs_marshal_rlp_test.go ================================================ package ethgo import ( "fmt" "testing" "github.com/stretchr/testify/assert" "github.com/umbracle/fastrlp" ) func TestEncodingRLP_Transaction_Fuzz(t *testing.T) { testTransaction := func(t *testing.T, typ TransactionType) { obj := &Transaction{} err := fastrlp.Fuzz(100, obj, fastrlp.WithDefaults(func(obj fastrlp.FuzzObject) { obj.(*Transaction).Type = typ }), fastrlp.WithPostHook(func(obj fastrlp.FuzzObject) error { // Test that the hash from unmarshal is the same as the one computed txn := obj.(*Transaction) cHash, err := txn.GetHash() if err != nil { return err } if cHash != txn.Hash { return fmt.Errorf("hash not equal") } return nil }), ) assert.NoError(t, err) } t.Run("legacy", func(t *testing.T) { testTransaction(t, TransactionLegacy) }) t.Run("accesslist", func(t *testing.T) { testTransaction(t, TransactionAccessList) }) t.Run("dynamicfee", func(t *testing.T) { testTransaction(t, TransactionDynamicFee) }) } func TestEncodingRLP_AccessList_Fuzz(t *testing.T) { obj := &AccessList{} if err := fastrlp.Fuzz(100, obj); err != nil { t.Fatal(err) } } ================================================ FILE: structs_marshal_test.go ================================================ package ethgo import ( "encoding/json" "math/big" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func generateHashPtr(input string) *Hash { res := HexToHash(input) return &res } func TestLogFilter_MarshalJSON(t *testing.T) { testTable := []struct { name string topics [][]*Hash }{ { "match any topic", [][]*Hash{ nil, {}, }, }, { "match single topic in pos. 1", [][]*Hash{ { generateHashPtr("0xa"), }, }, }, { "match single topic in pos. 2", [][]*Hash{ {}, { generateHashPtr("0xb"), }, }, }, { "match topic in pos. 1 AND pos. 2", [][]*Hash{ { generateHashPtr("0xa"), }, { generateHashPtr("0xb"), }, }, }, { "match topic A or B in pos. 1 AND C or D in pos. 2", [][]*Hash{ { generateHashPtr("0xa"), generateHashPtr("0xb"), }, { generateHashPtr("0xc"), generateHashPtr("0xd"), }, }, }, } defaultLogFilter := &LogFilter{ Address: []Address{HexToAddress("0x123")}, Topics: nil, BlockHash: generateHashPtr("0xabc"), } for _, testCase := range testTable { t.Run(testCase.name, func(t *testing.T) { defaultLogFilter.Topics = testCase.topics // Marshal it to JSON output, marshalErr := defaultLogFilter.MarshalJSON() if marshalErr != nil { t.Fatalf("Unable to marshal value, %v", marshalErr) } // Unmarshal it from JSON reverseOutput := &LogFilter{} unmarshalErr := json.Unmarshal(output, reverseOutput) if unmarshalErr != nil { t.Fatalf("Unable to unmarshal value, %v", unmarshalErr) } // Assert that the original and unmarshalled values match assert.Equal(t, defaultLogFilter, reverseOutput) }) } } func TestMarshal_StateOverride(t *testing.T) { nonce := uint64(1) code := []byte{0x1} o := StateOverride{ {0x0}: OverrideAccount{ Nonce: &nonce, Balance: big.NewInt(1), Code: &code, State: &map[Hash]Hash{ {0x1}: {0x1}, }, StateDiff: &map[Hash]Hash{ {0x1}: {0x1}, }, }, } res, err := o.MarshalJSON() require.NoError(t, err) expected := `{"0x0000000000000000000000000000000000000000":{"nonce":"0x1","balance":"0x1","code":"0x01","state":{"0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000"},"stateDiff":{"0x0100000000000000000000000000000000000000000000000000000000000000":"0x0100000000000000000000000000000000000000000000000000000000000000"}}}` require.Equal(t, expected, string(res)) } ================================================ FILE: structs_test.go ================================================ package ethgo import ( _ "embed" "encoding/json" "math/big" "reflect" "testing" "github.com/stretchr/testify/assert" ) func TestAddress_Checksum(t *testing.T) { cases := []struct { src, dst string }{ { "0x5aaeb6053f3e94c9b9a09f33669435e7ef1beaed", "0x5aAeb6053F3E94C9b9A09f33669435E7Ef1BeAed", }, { "0xfb6916095ca1df60bb79ce92ce3ea74c37c5d359", "0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359", }, { "0xdbf03b407c01e7cd3cbea99509d93f8dddc8c6fb", "0xdbF03B407c01E7cD3CBea99509d93f8DDDC8C6FB", }, { "0xd1220a0cf47c7b9be7a2e6ba89f429762e7b9adb", "0xD1220A0cf47c7B9Be7A2E6BA89F429762e7b9aDb", }, } for _, c := range cases { addr := HexToAddress(c.src) assert.Equal(t, addr.String(), c.dst) } } func TestAddress_HexToString(t *testing.T) { assert.Equal(t, HexToAddress("0x1").String(), "0x0000000000000000000000000000000000000001") assert.Equal(t, HexToAddress("00000000000000000000000000000000000000001").String(), "0x0000000000000000000000000000000000000001") assert.Equal(t, HexToAddress("0000000000000000000000000000000000000001").String(), "0x0000000000000000000000000000000000000001") } func TestHash_HexToString(t *testing.T) { assert.Equal(t, HexToHash("1").String(), "0x0000000000000000000000000000000000000000000000000000000000000001") } func TestBlock_Copy(t *testing.T) { b := &Block{ Difficulty: big.NewInt(1), Transactions: []*Transaction{}, ExtraData: []byte{0x1, 0x2}, } b1 := b.Copy() if !reflect.DeepEqual(b, b1) { t.Fatal("incorrect block copy") } } func TestTransaction_Copy(t *testing.T) { txn := &Transaction{ GasPrice: 10, Input: []byte{0x1, 0x2}, V: []byte{0x1, 0x2}, R: []byte{0x1, 0x2}, S: []byte{0x1, 0x2}, AccessList: AccessList{ AccessEntry{ Address: Address{0x1}, Storage: []Hash{ {0x1}, }, }, }, } txn1 := txn.Copy() if !reflect.DeepEqual(txn, txn1) { t.Fatal("incorrect transaction") } } func TestReceipt_Copy(t *testing.T) { r := &Receipt{ LogsBloom: []byte{0x1, 0x2}, Logs: []*Log{ {LogIndex: 1, Topics: []Hash{{0x1}}}, }, GasUsed: 10, } rr := r.Copy() if !reflect.DeepEqual(r, rr) { t.Fatal("incorrect receipt") } } func TestLog_Copy(t *testing.T) { l := &Log{ Data: []byte{0x1, 0x2}, BlockHash: Hash{0x1}, } ll := l.Copy() if !reflect.DeepEqual(l, ll) { t.Fatal("incorrect receipt") } } //go:embed testsuite/receipts.json var receiptsFixtures []byte func TestReceipt_Unmarshal(t *testing.T) { var cases []json.RawMessage assert.NoError(t, json.Unmarshal(receiptsFixtures, &cases)) for _, c := range cases { receipt := &Receipt{} assert.NoError(t, receipt.UnmarshalJSON(c)) } } ================================================ FILE: structs_unmarshal.go ================================================ package ethgo import ( "encoding/hex" "fmt" "math/big" "strconv" "strings" "github.com/valyala/fastjson" ) var defaultPool fastjson.ParserPool // UnmarshalJSON implements the unmarshal interface func (b *Block) UnmarshalJSON(buf []byte) error { p := defaultPool.Get() defer defaultPool.Put(p) v, err := p.Parse(string(buf)) if err != nil { return err } if err := decodeHash(&b.Hash, v, "hash"); err != nil { return err } if err := decodeHash(&b.ParentHash, v, "parentHash"); err != nil { return err } if err := decodeHash(&b.Sha3Uncles, v, "sha3Uncles"); err != nil { return err } if err := decodeHash(&b.TransactionsRoot, v, "transactionsRoot"); err != nil { return err } if err := decodeHash(&b.StateRoot, v, "stateRoot"); err != nil { return err } if err := decodeHash(&b.ReceiptsRoot, v, "receiptsRoot"); err != nil { return err } if err := decodeAddr(&b.Miner, v, "miner"); err != nil { return err } if b.Number, err = decodeUint(v, "number"); err != nil { return err } if b.GasLimit, err = decodeUint(v, "gasLimit"); err != nil { return err } if b.GasUsed, err = decodeUint(v, "gasUsed"); err != nil { return err } if err = decodeHash(&b.MixHash, v, "mixHash"); err != nil { return err } if err = decodeNonce(&b.Nonce, v, "nonce"); err != nil { return err } if b.Timestamp, err = decodeUint(v, "timestamp"); err != nil { return err } if b.Difficulty, err = decodeBigInt(b.Difficulty, v, "difficulty"); err != nil { return err } if b.ExtraData, err = decodeBytes(b.ExtraData[:0], v, "extraData"); err != nil { return err } if b.BaseFee, err = decodeBigInt(b.BaseFee, v, "baseFee"); err != nil { if err.Error() != "field 'baseFee' not found" { return err } } b.TransactionsHashes = b.TransactionsHashes[:0] b.Transactions = b.Transactions[:0] elems := v.GetArray("transactions") if len(elems) != 0 { if elems[0].Type() == fastjson.TypeString { // hashes (non full block) for _, elem := range elems { var h Hash if err := h.UnmarshalText(elem.GetStringBytes()); err != nil { return err } b.TransactionsHashes = append(b.TransactionsHashes, h) } } else { // structs (full block) for _, elem := range elems { txn := new(Transaction) if err := txn.unmarshalJSON(elem); err != nil { panic(err) } b.Transactions = append(b.Transactions, txn) } } } // uncles b.Uncles = b.Uncles[:0] for _, elem := range v.GetArray("uncles") { var h Hash if err := h.UnmarshalText(elem.GetStringBytes()); err != nil { return err } b.Uncles = append(b.Uncles, h) } return nil } // UnmarshalJSON implements the unmarshal interface func (t *Transaction) UnmarshalJSON(buf []byte) error { p := defaultPool.Get() defer defaultPool.Put(p) v, err := p.Parse(string(buf)) if err != nil { return err } return t.unmarshalJSON(v) } // isKeySet is a helper function for checking if a key has any value != nil, // or if it's been set at all func isKeySet(v *fastjson.Value, key string) bool { value := v.Get(key) return value != nil && value.Type() != fastjson.TypeNull } func (t *Transaction) unmarshalJSON(v *fastjson.Value) error { exists := func(names ...string) error { for _, name := range names { if !v.Exists(name) { return fmt.Errorf("'%s' not found", name) } } return nil } if isKeySet(v, "type") { txnType, err := decodeUint(v, "type") if err != nil { return err } t.Type = TransactionType(txnType) } else { if isKeySet(v, "chainId") { if isKeySet(v, "maxFeePerGas") { t.Type = TransactionDynamicFee } else { t.Type = TransactionAccessList } } else { t.Type = TransactionLegacy } } var err error if err := decodeHash(&t.Hash, v, "hash"); err != nil { return err } if err = decodeAddr(&t.From, v, "from"); err != nil { return err } if t.Type == TransactionDynamicFee { if t.MaxPriorityFeePerGas, err = decodeBigInt(t.MaxPriorityFeePerGas, v, "maxPriorityFeePerGas"); err != nil { return err } if t.MaxFeePerGas, err = decodeBigInt(t.MaxFeePerGas, v, "maxFeePerGas"); err != nil { return err } } else if t.Type == TransactionLegacy || t.Type == TransactionAccessList { if t.GasPrice, err = decodeUint(v, "gasPrice"); err != nil { return err } } if t.Input, err = decodeBytes(t.Input[:0], v, "input"); err != nil { return err } if t.Value, err = decodeBigInt(t.Value, v, "value"); err != nil { return err } if t.Nonce, err = decodeUint(v, "nonce"); err != nil { return err } { // Do not decode 'to' if it doesn't exist. if err := exists("to"); err == nil { if v.Get("to").String() != "null" { var to Address if err = decodeAddr(&to, v, "to"); err != nil { return err } t.To = &to } } } if t.V, err = decodeBytes(t.V[:0], v, "v"); err != nil { return err } if t.R, err = decodeBytes(t.R[:0], v, "r"); err != nil { return err } if t.S, err = decodeBytes(t.S[:0], v, "s"); err != nil { return err } if t.Type == TransactionDynamicFee || t.Type == TransactionAccessList { if t.ChainID, err = decodeBigInt(t.ChainID, v, "chainId"); err != nil { return err } if isKeySet(v, "accessList") { if err := t.AccessList.unmarshalJSON(v.Get("accessList")); err != nil { return err } } } if t.Gas, err = decodeUint(v, "gas"); err != nil { return err } // Check if the block hash field is set // If it's not -> the transaction is a pending txn, so these fields should be omitted // If it is -> the transaction is a sealed txn, so these fields should be included if isKeySet(v, "blockHash") { // The transaction is not a pending transaction, read data // Grab the block hash if err = decodeHash(&t.BlockHash, v, "blockHash"); err != nil { return err } // Grab the block number if t.BlockNumber, err = decodeUint(v, "blockNumber"); err != nil { return err } // Grab the transaction index if t.TxnIndex, err = decodeUint(v, "transactionIndex"); err != nil { return err } } return nil } func (t *AccessList) unmarshalJSON(v *fastjson.Value) error { elems, err := v.Array() if err != nil { return err } for _, elem := range elems { entry := AccessEntry{} if err = decodeAddr(&entry.Address, elem, "address"); err != nil { return err } storage, err := elem.Get("storageKeys").Array() if err != nil { return err } entry.Storage = make([]Hash, len(storage)) for indx, stg := range storage { b, err := stg.StringBytes() if err != nil { return err } if err := entry.Storage[indx].UnmarshalText(b); err != nil { return err } } *t = append(*t, entry) } return nil } // UnmarshalJSON implements the unmarshal interface func (r *Receipt) UnmarshalJSON(buf []byte) error { p := defaultPool.Get() defer defaultPool.Put(p) v, err := p.Parse(string(buf)) if err != nil { return nil } if err := decodeAddr(&r.From, v, "from"); err != nil { return err } if fieldNotFull(v, "contractAddress") { if err := decodeAddr(&r.ContractAddress, v, "contractAddress"); err != nil { return err } } if err := decodeHash(&r.TransactionHash, v, "transactionHash"); err != nil { return err } if err := decodeHash(&r.BlockHash, v, "blockHash"); err != nil { return err } if r.TransactionIndex, err = decodeUint(v, "transactionIndex"); err != nil { return err } if r.BlockNumber, err = decodeUint(v, "blockNumber"); err != nil { return err } if r.GasUsed, err = decodeUint(v, "gasUsed"); err != nil { return err } if r.CumulativeGasUsed, err = decodeUint(v, "cumulativeGasUsed"); err != nil { return err } if r.LogsBloom, err = decodeBytes(r.LogsBloom[:0], v, "logsBloom", 256); err != nil { return err } if v.Exists("status") { // post-byzantium fork if r.Status, err = decodeUint(v, "status"); err != nil { return err } } if v.Exists("to") { // Do not decode 'to' if it doesn't exist. if v.Get("to").String() != "null" { var to Address if err = decodeAddr(&to, v, "to"); err != nil { return err } r.To = &to } } // logs r.Logs = r.Logs[:0] for _, elem := range v.GetArray("logs") { log := new(Log) if err := log.unmarshalJSON(elem); err != nil { return err } r.Logs = append(r.Logs, log) } return nil } func (lf *LogFilter) UnmarshalJSON(buf []byte) error { p := defaultPool.Get() defer defaultPool.Put(p) v, err := p.Parse(string(buf)) if err != nil { return fmt.Errorf("unable to parse input, %w", err) } // Unmarshal the address field lf.Address = lf.Address[:0] appendAddress := func(addressRaw []byte) error { address := new(Address) if err := address.UnmarshalText(addressRaw); err != nil { return err } lf.Address = append(lf.Address, *address) return nil } for _, addressValue := range v.GetArray("address") { addressRaw, err := addressValue.StringBytes() if err != nil { return err } if err := appendAddress(addressRaw); err != nil { return err } } // The address field can also be a single value if addressRaw := v.GetStringBytes("address"); addressRaw != nil { if err := appendAddress(addressRaw); err != nil { return err } } // Unmarshal the block hash lf.BlockHash = nil if v.Exists("blockHash") { extractedHash := &Hash{} if err := decodeHash(extractedHash, v, "blockHash"); err != nil { return err } lf.BlockHash = extractedHash } // decodeBlockNum is a helper method for extracting a BlockNumber decodeBlockNum := func(key string) (*BlockNumber, error) { numRaw, err := decodeInt64(v, key) if err != nil { return nil, err } blockNum := BlockNumber(numRaw) return &blockNum, nil } // Unmarshal the from field lf.From = nil if v.Exists("fromBlock") { if lf.From, err = decodeBlockNum("fromBlock"); err != nil { return err } } // Unmarshal the to field lf.To = nil if v.Exists("toBlock") { if lf.To, err = decodeBlockNum("toBlock"); err != nil { return err } } // Unmarshal the topics lf.Topics = lf.Topics[:0] for _, topicsValue := range v.GetArray("topics") { // Check if the index is set if topicsValue == nil || topicsValue.String() == "null" { lf.Topics = append(lf.Topics, nil) continue } innerTopics, err := topicsValue.Array() if err != nil { return err } resTopics := make([]*Hash, 0) for _, innerTopic := range innerTopics { hashValRaw, err := innerTopic.StringBytes() if err != nil { return err } hashVal := &Hash{} if err := hashVal.UnmarshalText(hashValRaw); err != nil { return err } resTopics = append(resTopics, hashVal) } lf.Topics = append(lf.Topics, resTopics) } return nil } // UnmarshalJSON implements the unmarshal interface func (r *Log) UnmarshalJSON(buf []byte) error { p := defaultPool.Get() defer defaultPool.Put(p) v, err := p.Parse(string(buf)) if err != nil { return nil } return r.unmarshalJSON(v) } func (r *Log) unmarshalJSON(v *fastjson.Value) error { var err error if v.Exists("removed") { // it is empty in etherscan API endpoint if r.Removed, err = decodeBool(v, "removed"); err != nil { return err } } if r.LogIndex, err = decodeUint(v, "logIndex"); err != nil { return err } if r.BlockNumber, err = decodeUint(v, "blockNumber"); err != nil { return err } if r.TransactionIndex, err = decodeUint(v, "transactionIndex"); err != nil { return err } if err := decodeHash(&r.TransactionHash, v, "transactionHash"); err != nil { return err } if v.Exists("blockHash") { // it is empty in etherscan API endpoint if err := decodeHash(&r.BlockHash, v, "blockHash"); err != nil { return err } } if err := decodeAddr(&r.Address, v, "address"); err != nil { return err } if r.Data, err = decodeBytes(r.Data[:0], v, "data"); err != nil { return err } r.Topics = r.Topics[:0] for _, topic := range v.GetArray("topics") { var t Hash b, err := topic.StringBytes() if err != nil { return err } if err := t.UnmarshalText(b); err != nil { return err } r.Topics = append(r.Topics, t) } return nil } func fieldNotFull(v *fastjson.Value, key string) bool { vv := v.Get(key) if vv == nil { return false } if vv.String() == "null" { return false } return true } func decodeBigInt(b *big.Int, v *fastjson.Value, key string) (*big.Int, error) { vv := v.Get(key) if vv == nil { return nil, fmt.Errorf("field '%s' not found", key) } str := vv.String() str = strings.Trim(str, "\"") if !strings.HasPrefix(str, "0x") { return nil, fmt.Errorf("field '%s' does not have 0x prefix: '%s'", key, str) } if b == nil { b = new(big.Int) } var ok bool b, ok = b.SetString(str[2:], 16) if !ok { return nil, fmt.Errorf("field '%s' failed to decode big int: '%s'", key, str) } return b, nil } func decodeBytes(dst []byte, v *fastjson.Value, key string, bits ...int) ([]byte, error) { vv := v.Get(key) if vv == nil { return nil, fmt.Errorf("field '%s' not found", key) } str := vv.String() str = strings.Trim(str, "\"") if !strings.HasPrefix(str, "0x") { return nil, fmt.Errorf("field '%s' does not have 0x prefix: '%s'", key, str) } str = str[2:] if len(str)%2 != 0 { str = "0" + str } buf, err := hex.DecodeString(str) if err != nil { return nil, err } if len(bits) > 0 && bits[0] != len(buf) { return nil, fmt.Errorf("field '%s' invalid length, expected %d but found %d: %s", key, bits[0], len(buf), str) } dst = append(dst, buf...) return dst, nil } func decodeUint(v *fastjson.Value, key string) (uint64, error) { vv := v.Get(key) if vv == nil { return 0, fmt.Errorf("field '%s' not found", key) } str := vv.String() str = strings.Trim(str, "\"") if !strings.HasPrefix(str, "0x") { return 0, fmt.Errorf("field '%s' does not have 0x prefix: '%s'", key, str) } str = str[2:] if str == "" { str = "0" } num, err := strconv.ParseUint(str, 16, 64) if err != nil { return 0, fmt.Errorf("field '%s' failed to decode uint: %s", key, str) } return num, nil } func decodeInt64(v *fastjson.Value, key string) (int64, error) { vv := v.Get(key) if vv == nil { return 0, fmt.Errorf("field '%s' not found", key) } str := vv.String() str = strings.Trim(str, "\"") if !strings.HasPrefix(str, "0x") { return 0, fmt.Errorf("field '%s' does not have 0x prefix: '%s'", key, str) } str = str[2:] if str == "" { str = "0" } num, err := strconv.ParseInt(str, 16, 64) if err != nil { return 0, fmt.Errorf("field '%s' failed to decode int64: %s", key, str) } return num, nil } func decodeNonce(n *[8]byte, v *fastjson.Value, key string) error { b := v.GetStringBytes(key) if len(b) == 0 { return fmt.Errorf("field '%s' not found", key) } if err := unmarshalTextByte(n[:], b, 8); err != nil { return err } return nil } func decodeHash(h *Hash, v *fastjson.Value, key string) error { b := v.GetStringBytes(key) if len(b) == 0 { return fmt.Errorf("field '%s' not found", key) } // Make sure the memory location is initialized if h == nil { h = &Hash{} } h.UnmarshalText(b) return nil } func decodeAddr(a *Address, v *fastjson.Value, key string) error { b := v.GetStringBytes(key) if len(b) == 0 { return fmt.Errorf("field '%s' not found", key) } a.UnmarshalText(b) return nil } func decodeBool(v *fastjson.Value, key string) (bool, error) { vv := v.Get(key) if vv == nil { return false, fmt.Errorf("field '%s' not found", key) } str := vv.String() if str == "false" { return false, nil } else if str == "true" { return true, nil } return false, fmt.Errorf("field '%s' with content '%s' cannot be decoded as bool", key, str) } func unmarshalTextByte(dst, src []byte, size int) error { str := string(src) str = strings.Trim(str, "\"") if !strings.HasPrefix(str, "0x") { return fmt.Errorf("0x prefix not found") } str = str[2:] b, err := hex.DecodeString(str) if err != nil { return err } if len(b) != size { return fmt.Errorf("length %d is not correct, expected %d", len(b), size) } copy(dst, b) return nil } ================================================ FILE: testcases/accounts_test.go ================================================ package testcases import ( "encoding/hex" "strings" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/wallet" ) func TestAccounts(t *testing.T) { var walletSpec []struct { Address string `json:"address"` Checksum string `json:"checksumAddress"` Name string `json:"name"` PrivateKey *string `json:"privateKey,omitempty"` } ReadTestCase(t, "accounts", &walletSpec) msg := []byte("msg") for _, spec := range walletSpec { // test that an string address can be checksumed addr := ethgo.HexToAddress(spec.Address) assert.Equal(t, addr.String(), spec.Checksum) if spec.PrivateKey != nil { // test that we can decode the private key priv, err := hex.DecodeString(strings.TrimPrefix(*spec.PrivateKey, "0x")) assert.NoError(t, err) key, err := wallet.NewWalletFromPrivKey(priv) assert.NoError(t, err) assert.Equal(t, key.Address().String(), spec.Checksum) // test that we can sign and recover address sig, err := key.SignMsg(msg) require.NoError(t, err) recoveredAddr, err := wallet.EcrecoverMsg(msg, sig) require.NoError(t, err) require.Equal(t, recoveredAddr, addr) } } } ================================================ FILE: testcases/contract_test.go ================================================ package testcases import ( "encoding/hex" "testing" "github.com/stretchr/testify/assert" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/abi" "github.com/umbracle/ethgo/testutil" ) func TestContract_Signatures(t *testing.T) { var signatures []struct { Name string `json:"name"` Signature string `json:"signature"` SigHash string `json:"sigHash"` Abi string `json:"abi"` } ReadTestCase(t, "contract-signatures", &signatures) for _, c := range signatures { m, err := abi.NewMethod(c.Signature) assert.NoError(t, err) sigHash := "0x" + hex.EncodeToString(m.ID()) assert.Equal(t, sigHash, c.SigHash) } } func TestContract_Interface(t *testing.T) { t.Skip() server := testutil.NewTestServer(t) var calls []struct { Name string `json:"name"` Interface string `json:"interface"` Bytecode ethgo.ArgBytes `json:"bytecode"` Result ethgo.ArgBytes `json:"result"` Values string `json:"values"` } ReadTestCase(t, "contract-interface", &calls) for _, c := range calls { a, err := abi.NewABI(c.Interface) assert.NoError(t, err) method := a.GetMethod("test") receipt, err := server.SendTxn(ðgo.Transaction{ Input: c.Bytecode.Bytes(), }) assert.NoError(t, err) outputRaw, err := server.Call(ðgo.CallMsg{ To: &receipt.ContractAddress, Data: method.ID(), }) assert.NoError(t, err) output, err := hex.DecodeString(outputRaw[2:]) assert.NoError(t, err) _, err = method.Decode(output) assert.NoError(t, err) } } ================================================ FILE: testcases/eip712_test.go ================================================ package testcases import ( "encoding/hex" "math/big" "testing" "github.com/stretchr/testify/require" "github.com/umbracle/ethgo/signing" ) type eip712Testcase struct { Name string Domain struct { Name *string Version *string VerifyingContract *string ChainId *uint64 Salt *string } Type string Seed string PrimaryType string Types map[string][]*signing.EIP712Type Data map[string]interface{} Encoded string Digest string } func (e *eip712Testcase) getDomain() *signing.EIP712Domain { d := &signing.EIP712Domain{} if name := e.Domain.Name; name != nil { d.Name = *name } if version := e.Domain.Version; version != nil { d.Version = *version } if contract := e.Domain.VerifyingContract; contract != nil { d.VerifyingContract = *contract } if chain := e.Domain.ChainId; chain != nil { d.ChainId = new(big.Int).SetUint64(*chain) } if salt := e.Domain.Salt; salt != nil { buf, _ := hex.DecodeString((*salt)[2:]) d.Salt = buf } return d } func TestEIP712(t *testing.T) { var cases []eip712Testcase ReadTestCase(t, "eip712", &cases) for indx, c := range cases { typedData := &signing.EIP712TypedData{ Types: c.Types, PrimaryType: c.PrimaryType, Domain: c.getDomain(), Message: c.Data, } digest, err := typedData.Hash() require.NoError(t, err) if c.Digest != "0x"+hex.EncodeToString(digest) { t.Fatalf("wrong digest: %d", indx) } } } ================================================ FILE: testcases/package.json ================================================ { "name": "testsuite", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "@ethersproject/testcases": "^5.6.0" } } ================================================ FILE: testcases/transaction_test.go ================================================ package testcases import ( "math/big" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/wallet" ) func getUint64FromBigInt(b *ethgo.ArgBig) (uint64, bool) { g := (*big.Int)(b) if !g.IsUint64() { return 0, false } return g.Uint64(), true } func TestTransactions(t *testing.T) { var transactions []struct { Name string `json:"name"` AccountAddress ethgo.Address `json:"accountAddress"` PrivateKey ethgo.ArgBytes `json:"privateKey"` SignedTransaction ethgo.ArgBytes `json:"signedTransactionChainId5"` Data *ethgo.ArgBytes `json:"data,omitempty"` Value *ethgo.ArgBig `json:"value,omitempty"` To *ethgo.Address `json:"to,omitempty"` GasLimit *ethgo.ArgBig `json:"gasLimit,omitempty"` Nonce *ethgo.ArgUint64 `json:"nonce,omitempty"` GasPrice *ethgo.ArgBig `json:"gasPrice,omitempty"` } ReadTestCase(t, "transactions", &transactions) for _, c := range transactions { key, err := wallet.NewWalletFromPrivKey(c.PrivateKey) assert.NoError(t, err) assert.Equal(t, key.Address(), c.AccountAddress) txn := ðgo.Transaction{ ChainID: big.NewInt(5), } if c.Data != nil { txn.Input = *c.Data } if c.Value != nil { txn.Value = (*big.Int)(c.Value) } if c.To != nil { txn.To = c.To } if c.GasLimit != nil { gasLimit, isUint64 := getUint64FromBigInt(c.GasLimit) if !isUint64 { return } txn.Gas = gasLimit } if c.Nonce != nil { txn.Nonce = c.Nonce.Uint64() } if c.GasPrice != nil { gasPrice, isUint64 := getUint64FromBigInt(c.GasPrice) if !isUint64 { return } txn.GasPrice = gasPrice } signer := wallet.NewEIP155Signer(5) signedTxn, err := signer.SignTx(txn, key) assert.NoError(t, err) txnRaw, err := signedTxn.MarshalRLPTo(nil) assert.NoError(t, err) assert.Equal(t, txnRaw, c.SignedTransaction.Bytes()) sender, err := signer.RecoverSender(signedTxn) require.NoError(t, err) require.Equal(t, sender, key.Address()) } } func TestTypedTransactions(t *testing.T) { var transactions []struct { Name string `json:"name"` AccountAddress ethgo.Address `json:"address"` Key ethgo.ArgBytes `json:"key"` Signed ethgo.ArgBytes `json:"signed"` Tx struct { Type ethgo.TransactionType Data *ethgo.ArgBytes `json:"data,omitempty"` GasLimit *ethgo.ArgBig `json:"gasLimit,omitempty"` MaxPriorityFeePerGas *ethgo.ArgBig `json:"maxPriorityFeePerGas,omitempty"` MaxFeePerGas *ethgo.ArgBig `json:"maxFeePerGas,omitempty"` Nonce uint64 `json:"nonce,omitempty"` To *ethgo.Address `json:"to,omitempty"` Value *ethgo.ArgBig `json:"value,omitempty"` GasPrice *ethgo.ArgBig `json:"gasPrice,omitempty"` ChainID uint64 `json:"chainId,omitempty"` AccessList ethgo.AccessList `json:"accessList,omitempty"` } } ReadTestCase(t, "typed-transactions", &transactions) for _, c := range transactions { key, err := wallet.NewWalletFromPrivKey(c.Key) assert.NoError(t, err) assert.Equal(t, key.Address(), c.AccountAddress) chainID := big.NewInt(int64(c.Tx.ChainID)) txn := ðgo.Transaction{ ChainID: chainID, Type: c.Tx.Type, MaxPriorityFeePerGas: (*big.Int)(c.Tx.MaxPriorityFeePerGas), MaxFeePerGas: (*big.Int)(c.Tx.MaxFeePerGas), AccessList: c.Tx.AccessList, } if c.Tx.Data != nil { txn.Input = *c.Tx.Data } if c.Tx.Value != nil { txn.Value = (*big.Int)(c.Tx.Value) } if c.Tx.To != nil { txn.To = c.Tx.To } if c.Tx.GasLimit != nil { gasLimit, isUint64 := getUint64FromBigInt(c.Tx.GasLimit) if !isUint64 { return } txn.Gas = gasLimit } txn.Nonce = c.Tx.Nonce if c.Tx.GasPrice != nil { gasPrice, isUint64 := getUint64FromBigInt(c.Tx.GasPrice) if !isUint64 { return } txn.GasPrice = gasPrice } signer := wallet.NewEIP155Signer(chainID.Uint64()) signedTxn, err := signer.SignTx(txn, key) assert.NoError(t, err) txnRaw, err := signedTxn.MarshalRLPTo(nil) assert.NoError(t, err) assert.Equal(t, txnRaw, c.Signed.Bytes()) sender, err := signer.RecoverSender(signedTxn) require.NoError(t, err) require.Equal(t, sender, key.Address()) } } ================================================ FILE: testcases/util.go ================================================ package testcases import ( "compress/gzip" "encoding/json" "os" "path" "path/filepath" "runtime" "testing" "github.com/stretchr/testify/assert" ) func ReadTestCase(t *testing.T, name string, target interface{}) { _, b, _, _ := runtime.Caller(0) d := path.Join(path.Dir(b)) testsuiteDir := filepath.Join(filepath.Dir(d), "testcases", "node_modules") if _, err := os.Stat(testsuiteDir); os.IsNotExist(err) { t.Skip("testcases not downloaded") } path := filepath.Join(testsuiteDir, "@ethersproject/testcases/testcases", name+".json.gz") f, err := os.Open(path) assert.NoError(t, err) zr, err := gzip.NewReader(f) assert.NoError(t, err) decoder := json.NewDecoder(zr) assert.NoError(t, decoder.Decode(target)) } ================================================ FILE: testsuite/arbitrum-block-full.json ================================================ { "baseFeePerGas": "0x5f5e100", "difficulty": "0x1", "extraData": "0x0000000000000000000000000000000000000000000000000000000000000000", "gasLimit": "0x4000000000000", "gasUsed": "0xac938d", "hash": "0xc4cfe190b18fa6cfc6f80e5e0df16fdc78c054e750b98caf952443e653cc5049", "l1BlockNumber": "0x4c62e8", "logsBloom": "0x00000000000000000000040000008000000000000000000000001000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000000000020000000000000000000800000000000000040000000000000000000000000000000000000000000000400000000000000000000000000000000000100000000000000000000080000000000004000000000000000000000000000010000000000000000000000000000000000000000000000000000000000028000000000000000000000000200000000000002000000000000000004000000000", "miner": "0xc573c69f8f638d2954c9618b03765fc17701a1e0", "mixHash": "0x000000000000000000000000004c62e8000000000000000a0000000000000000", "nonce": "0x0000000000000002", "number": "0x1", "parentHash": "0xde35c12458419b05d0250becd32113a6ceee6788b66fcdc8552ce97b5532c770", "receiptsRoot": "0x8067122eda9f9c348c529d16c6c25a78538ec7e41ff175bbedaba74f89ffe94b", "sendCount": "0x0", "sendRoot": "0x0000000000000000000000000000000000000000000000000000000000000000", "sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347", "size": "0xc271", "stateRoot": "0x99228a91d977cd3a0de4a864b680f8d1b4a2a05a7de3a5d7839f1c5cf4689099", "timestamp": "0x6593e174", "totalDifficulty": "0x2", "transactions": [ { "blockHash": "0xc4cfe190b18fa6cfc6f80e5e0df16fdc78c054e750b98caf952443e653cc5049", "blockNumber": "0x1", "from": "0x00000000000000000000000000000000000a4b05", "gas": "0x0", "gasPrice": "0x0", "hash": "0x3572220ffdd1ca5c613894c630a2e77e590525ef26e1bfc19a295a2fd55b1084", "input": "0x6bf6a42d0000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000004c62e80000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000006593e174", "nonce": "0x0", "to": "0x00000000000000000000000000000000000a4b05", "transactionIndex": "0x0", "value": "0x0", "type": "0x6a", "chainId": "0xda8c8b77", "v": "0x0", "r": "0x0", "s": "0x0" }, { "blockHash": "0xc4cfe190b18fa6cfc6f80e5e0df16fdc78c054e750b98caf952443e653cc5049", "blockNumber": "0x1", "from": "0xc573c69f8f638d2954c9618b03765fc17701a1e0", "gas": "0x5b8d80", "gasPrice": "0x5f5e100", "maxFeePerGas": "0x5f5e100", "hash": "0x07fcc2df6293de1451ca79c4299ff16845d2572a41985a0426806aeb09b22d23", "input": "0xc9f95d3200000000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000022f250524940000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005f5e10000000000000000000000000000000000000000000000000000000000005b8d8000000000000000000000000000000000000000000000000000000d72a2471400000000000000000000000000bee947aec820389c1560c87bd96e2723bad05b61000000000000000000000000bee947aec820389c1560c87bd96e2723bad05b61000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000005f5a608060405234801561001057600080fd5b50615f3a806100206000396000f3fe60806040523480156200001157600080fd5b50600436106200002e5760003560e01c8063b1c7a8701462000033575b600080fd5b6200004a6200004436600462001528565b6200004c565b005b6000620000b9620000866040518060400160405280601181526020017027b93134ba2619283937bc3ca0b236b4b760791b81525062000304565b6040516200009760208201620014b8565b6020820181038252601f19601f8201166040525080519060200120306200033a565b90506001600160a01b0381163b15620000e5576040516377e0068560e11b815260040160405180910390fd5b5060006200011c6040518060400160405280601181526020017027b93134ba2619283937bc3ca0b236b4b760791b81525062000304565b6040516200012a90620014b8565b8190604051809103906000f59050801580156200014b573d6000803e3d6000fd5b50905060006200016c6200016360a08d018d62001603565b86858762000364565b9050600062000189620001808d8062001603565b8d898762000689565b9050620001a86200019e60208e018e62001603565b8c8487876200088c565b620001c4620001bb60408e018e62001603565b8b848762000cb4565b6001600160a01b03881615620002035762000203620001e760608e018e62001603565b8e8060800190620001f9919062001603565b8c8c878a62000ef4565b6200029360006200023c6040518060400160405280601081526020016f13dc989a5d130c935d5b1d1a58d85b1b60821b81525062000304565b6200028d8f8060c0019062000252919062001603565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250620012ee92505050565b6200131c565b5060405163f2fde38b60e01b81526001600160a01b0384169063f2fde38b90620002c290859060040162001654565b600060405180830381600087803b158015620002dd57600080fd5b505af1158015620002f2573d6000803e3d6000fd5b50505050505050505050505050505050565b60008146336040516020016200031d939291906200168e565b604051602081830303815290604052805190602001209050919050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b600080620003f084620003ac6040518060400160405280601b81526020017f4f726269744c32557067726164654578656375746f7250726f7879000000000081525062000304565b620003ea6040518060400160405280601b81526020017a4f726269744c32557067726164654578656375746f724c6f67696360281b81525062000304565b6200142d565b90506000620004796000620004386040518060400160405280601b81526020017a4f726269744c32557067726164654578656375746f724c6f67696360281b81525062000304565b6200028d8b8b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250620012ee92505050565b60405163266a23b160e21b81529091506001600160a01b038616906399a88ec490620004ac9085908590600401620016c9565b600060405180830381600087803b158015620004c757600080fd5b505af1158015620004dc573d6000803e3d6000fd5b5060009250829150620004ec9050565b60405190808252806020026020018201604052801562000516578160200160208202803683370190505b5060405163251b648160e21b81529091506001600160a01b0383169063946d9204906200054c9061dead908590600401620016e3565b600060405180830381600087803b1580156200056757600080fd5b505af11580156200057c573d6000803e3d6000fd5b5060009250600291506200058d9050565b604051908082528060200260200182016040528015620005b7578160200160208202803683370190505b5090508781600081518110620005d157620005d162001741565b60200260200101906001600160a01b031690816001600160a01b031681525050858160018151811062000608576200060862001741565b6001600160a01b03928316602091820292909201015260405163251b648160e21b81529085169063946d920490620006479087908590600401620016e3565b600060405180830381600087803b1580156200066257600080fd5b505af115801562000677573d6000803e3d6000fd5b50959c9b505050505050505050505050565b6000806200070983620006cd604051806040016040528060198152602001784f726269744c3247617465776179526f7574657250726f787960381b81525062000304565b620003ea604051806040016040528060198152602001784f726269744c3247617465776179526f757465724c6f67696360381b81525062000304565b905060006200074f600062000438604051806040016040528060198152602001784f726269744c3247617465776179526f757465724c6f67696360381b81525062000304565b60405163266a23b160e21b81529091506001600160a01b038516906399a88ec490620007829085908590600401620016c9565b600060405180830381600087803b1580156200079d57600080fd5b505af1158015620007b2573d6000803e3d6000fd5b505060405163485cc95560e01b81526001600160a01b038416925063485cc9559150620007e89061dead908190600401620016c9565b600060405180830381600087803b1580156200080357600080fd5b505af115801562000818573d6000803e3d6000fd5b505060405163485cc95560e01b81526001600160a01b038516925063485cc95591506200084c9089908990600401620016c9565b600060405180830381600087803b1580156200086757600080fd5b505af11580156200087c573d6000803e3d6000fd5b50939a9950505050505050505050565b60006200091183620008d36040518060400160405280601b81526020017f4f726269744c325374616e646172644761746577617950726f7879000000000081525062000304565b620003ea6040518060400160405280601b81526020017a4f726269744c325374616e64617264476174657761794c6f67696360281b81525062000304565b90506000620009596000620004386040518060400160405280601b81526020017a4f726269744c325374616e64617264476174657761794c6f67696360281b81525062000304565b60405163266a23b160e21b81529091506001600160a01b038516906399a88ec4906200098c9085908590600401620016c9565b600060405180830381600087803b158015620009a757600080fd5b505af1158015620009bc573d6000803e3d6000fd5b505060405163c0c53b8b60e01b81526001600160a01b038416925063c0c53b8b9150620009f49061dead908190819060040162001757565b600060405180830381600087803b15801562000a0f57600080fd5b505af115801562000a24573d6000803e3d6000fd5b50505050600062000a626040518060400160405280601581526020017404f726269745374616e64617264417262455243323605c1b81525062000304565b60405162000a7090620014c6565b8190604051809103906000f590508015801562000a91573d6000803e3d6000fd5b509050600062000acf6040518060400160405280601681526020017527b93134ba2ab833b930b232b0b13632a132b0b1b7b760511b81525062000304565b8260405162000ade90620014d4565b62000aea919062001654565b8190604051809103906000f590508015801562000b0b573d6000803e3d6000fd5b509050600062000b4a604051806040016040528060178152602001764f72626974426561636f6e50726f7879466163746f727960481b81525062000304565b60405162000b5890620014e2565b8190604051809103906000f590508015801562000b79573d6000803e3d6000fd5b5060405163189acdbd60e31b81529091506001600160a01b0382169063c4d66de89062000bab90859060040162001654565b600060405180830381600087803b15801562000bc657600080fd5b505af115801562000bdb573d6000803e3d6000fd5b505060405163c0c53b8b60e01b81526001600160a01b038816925063c0c53b8b915062000c11908c908c90869060040162001757565b600060405180830381600087803b15801562000c2c57600080fd5b505af115801562000c41573d6000803e3d6000fd5b505060405163f2fde38b60e01b81526001600160a01b038516925063f2fde38b915062000c7390899060040162001654565b600060405180830381600087803b15801562000c8e57600080fd5b505af115801562000ca3573d6000803e3d6000fd5b505050505050505050505050505050565b600062000d338262000cf7604051806040016040528060198152602001784f726269744c32437573746f6d4761746577617950726f787960381b81525062000304565b620003ea604051806040016040528060198152602001784f726269744c32437573746f6d476174657761794c6f67696360381b81525062000304565b9050600062000dba600062000d79604051806040016040528060198152602001784f726269744c32437573746f6d476174657761794c6f67696360381b81525062000304565b6200028d8a8a8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250620012ee92505050565b60405163266a23b160e21b81529091506001600160a01b038416906399a88ec49062000ded9085908590600401620016c9565b600060405180830381600087803b15801562000e0857600080fd5b505af115801562000e1d573d6000803e3d6000fd5b505060405163485cc95560e01b81526001600160a01b038416925063485cc955915062000e539061dead908190600401620016c9565b600060405180830381600087803b15801562000e6e57600080fd5b505af115801562000e83573d6000803e3d6000fd5b505060405163485cc95560e01b81526001600160a01b038516925063485cc955915062000eb79088908890600401620016c9565b600060405180830381600087803b15801562000ed257600080fd5b505af115801562000ee7573d6000803e3d6000fd5b5050505050505050505050565b600062000f5c8262000f2e6040518060400160405280601081526020016f4f726269744c325745544850726f787960801b81525062000304565b620003ea6040518060400160405280600b81526020016a09ee4c4d2e89864ae8aa8960ab1b81525062000304565b9050600062000f946000620004386040518060400160405280600b81526020016a09ee4c4d2e89864ae8aa8960ab1b81525062000304565b60405163266a23b160e21b81529091506001600160a01b038416906399a88ec49062000fc79085908590600401620016c9565b600060405180830381600087803b15801562000fe257600080fd5b505af115801562000ff7573d6000803e3d6000fd5b50505050600062001076846200103c604051806040016040528060178152602001764f726269744c32576574684761746577617950726f787960481b81525062000304565b620003ea604051806040016040528060178152602001764f726269744c3257657468476174657761794c6f67696360481b81525062000304565b90506000620010fb6000620010ba604051806040016040528060178152602001764f726269744c3257657468476174657761794c6f67696360481b81525062000304565b6200028d8f8f8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250620012ee92505050565b60405163266a23b160e21b81529091506001600160a01b038616906399a88ec4906200112e9085908590600401620016c9565b600060405180830381600087803b1580156200114957600080fd5b505af11580156200115e573d6000803e3d6000fd5b5050604051637c643b2f60e11b81526001600160a01b038416925063f8c8765e9150620011989061dead908190819081906004016200177a565b600060405180830381600087803b158015620011b357600080fd5b505af1158015620011c8573d6000803e3d6000fd5b5050604051637c643b2f60e11b81526001600160a01b038516925063f8c8765e915062001200908b908a908c908a906004016200177a565b600060405180830381600087803b1580156200121b57600080fd5b505af115801562001230573d6000803e3d6000fd5b505060405163641078a360e11b815260a06004820152600060a4820181905260c0602483015260c48201819052604482015261dead6064820181905260848201526001600160a01b038616925063c820f146915060e401600060405180830381600087803b158015620012a257600080fd5b505af1158015620012b7573d6000803e3d6000fd5b505060405163641078a360e11b81526001600160a01b038716925063c820f1469150620002c29060129086908c90600401620017bc565b6060815182604051602001620013069291906200180b565b6040516020818303038152906040529050919050565b600083471015620013745760405162461bcd60e51b815260206004820152601d60248201527f437265617465323a20696e73756666696369656e742062616c616e636500000060448201526064015b60405180910390fd5b8151600003620013c75760405162461bcd60e51b815260206004820181905260248201527f437265617465323a2062797465636f6465206c656e677468206973207a65726f60448201526064016200136b565b8282516020840186f590506001600160a01b038116620014265760405162461bcd60e51b8152602060048201526019602482015278437265617465323a204661696c6564206f6e206465706c6f7960381b60448201526064016200136b565b9392505050565b600082826040516200143f90620014f0565b8190604051809103906000f590508015801562001460573d6000803e3d6000fd5b5085604051806020016040528060008152506040516200148090620014fd565b6200148e939291906200187b565b8190604051809103906000f5905080158015620014af573d6000803e3d6000fd5b50949350505050565b61070f80620018cb83390190565b611e7e8062001fda83390190565b6104e28062003e5883390190565b610cb4806200433a83390190565b605c8062004fee83390190565b610ebb806200504a83390190565b80356001600160a01b03811681146200152357600080fd5b919050565b60008060008060008060008060006101208a8c0312156200154857600080fd5b893567ffffffffffffffff8111156200156057600080fd5b8a0160e0818d0312156200157357600080fd5b98506200158360208b016200150b565b97506200159360408b016200150b565b9650620015a360608b016200150b565b9550620015b360808b016200150b565b9450620015c360a08b016200150b565b9350620015d360c08b016200150b565b9250620015e360e08b016200150b565b9150620015f46101008b016200150b565b90509295985092959850929598565b6000808335601e198436030181126200161b57600080fd5b83018035915067ffffffffffffffff8211156200163757600080fd5b6020019150368190038213156200164d57600080fd5b9250929050565b6001600160a01b0391909116815260200190565b60005b83811015620016855781810151838201526020016200166b565b50506000910152565b60008451620016a281846020890162001668565b919091019283525060601b6bffffffffffffffffffffffff19166020820152603401919050565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b038381168252604060208084018290528451918401829052600092858201929091906060860190855b818110156200173357855185168352948301949183019160010162001713565b509098975050505050505050565b634e487b7160e01b600052603260045260246000fd5b6001600160a01b0393841681529183166020830152909116604082015260600190565b6001600160a01b03948516815292841660208401529083166040830152909116606082015260800190565b60048152630ae8aa8960e31b602082015260400190565b60a081526000620017d060a08301620017a5565b8281036020840152620017e381620017a5565b60ff96909616604084015250506001600160a01b039283166060820152911660809091015290565b710608060405234801561001057600080fd5b560741b8152606160f81b601282015260f083901b6001600160f01b03191660138201526a4030801030001cb00079ff60a91b601582015281516000906200186d81602080860190870162001668565b919091016020019392505050565b600060018060a01b038086168352808516602084015250606060408301528251806060840152620018b481608085016020870162001668565b601f01601f19169190910160800194935050505056fe608060405234801561001057600080fd5b5061001a3361001f565b61006f565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6106918061007e6000396000f3fe60806040526004361061006b5760003560e01c8063204e1c7a14610070578063715018a6146100a65780637eff275e146100bd5780638da5cb5b146100dd5780639623609d146100fb57806399a88ec41461010e578063f2fde38b1461012e578063f3b7dead1461014e575b600080fd5b34801561007c57600080fd5b5061009061008b366004610483565b61016e565b60405161009d91906104a7565b60405180910390f35b3480156100b257600080fd5b506100bb6101ff565b005b3480156100c957600080fd5b506100bb6100d83660046104bb565b610213565b3480156100e957600080fd5b506000546001600160a01b0316610090565b6100bb61010936600461050a565b61027d565b34801561011a57600080fd5b506100bb6101293660046104bb565b6102ec565b34801561013a57600080fd5b506100bb610149366004610483565b610320565b34801561015a57600080fd5b50610090610169366004610483565b61039e565b6000806000836001600160a01b031660405161019490635c60da1b60e01b815260040190565b600060405180830381855afa9150503d80600081146101cf576040519150601f19603f3d011682016040523d82523d6000602084013e6101d4565b606091505b5091509150816101e357600080fd5b808060200190518101906101f791906105e0565b949350505050565b6102076103c4565b610211600061041e565b565b61021b6103c4565b6040516308f2839760e41b81526001600160a01b03831690638f283970906102479084906004016104a7565b600060405180830381600087803b15801561026157600080fd5b505af1158015610275573d6000803e3d6000fd5b505050505050565b6102856103c4565b60405163278f794360e11b81526001600160a01b03841690634f1ef2869034906102b590869086906004016105fd565b6000604051808303818588803b1580156102ce57600080fd5b505af11580156102e2573d6000803e3d6000fd5b5050505050505050565b6102f46103c4565b604051631b2ce7f360e11b81526001600160a01b03831690633659cfe6906102479084906004016104a7565b6103286103c4565b6001600160a01b0381166103925760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61039b8161041e565b50565b6000806000836001600160a01b0316604051610194906303e1469160e61b815260040190565b6000546001600160a01b031633146102115760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610389565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b038116811461039b57600080fd5b60006020828403121561049557600080fd5b81356104a08161046e565b9392505050565b6001600160a01b0391909116815260200190565b600080604083850312156104ce57600080fd5b82356104d98161046e565b915060208301356104e98161046e565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b60008060006060848603121561051f57600080fd5b833561052a8161046e565b9250602084013561053a8161046e565b9150604084013567ffffffffffffffff8082111561055757600080fd5b818601915086601f83011261056b57600080fd5b81358181111561057d5761057d6104f4565b604051601f8201601f19908116603f011681019083821181831017156105a5576105a56104f4565b816040528281528960208487010111156105be57600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b6000602082840312156105f257600080fd5b81516104a08161046e565b60018060a01b038316815260006020604081840152835180604085015260005b818110156106395785810183015185820160600152820161061d565b506000606082860101526060601f19601f83011685010192505050939250505056fea26469706673582212208a2d9d6b4833462246845f0898b3da66a9086b54eb9fb2d870a9780af5c177f664736f6c6343000810003360806040523480156200001157600080fd5b50600054610100900460ff1615808015620000335750600054600160ff909116105b8062000063575062000050306200015060201b620007b71760201c565b15801562000063575060005460ff166001145b620000cb5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840160405180910390fd5b6000805460ff191660011790558015620000ef576000805461ff0019166101001790555b801562000136576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5060cd805460ff60a01b1916600160a01b1790556200015f565b6001600160a01b03163b151590565b611d0f806200016f6000396000f3fe608060405234801561001057600080fd5b50600436106101375760003560e01c806370a08231116100b857806395d89b411161007c57806395d89b411461029e578063a457c2d7146102a6578063a9059cbb146102b9578063c2eeeebd146102cc578063d505accf146102df578063dd62ed3e146102f257600080fd5b806370a082311461021157806374f4f5471461023a5780637ecebe001461024d5780638c2a993e146102605780638fa74a0e1461027357600080fd5b8063313ce567116100ff578063313ce567146101b75780633644e515146101d157806339509351146101d95780634000aea0146101ec5780636f791d29146101ff57600080fd5b806306fdde031461013c578063095ea7b31461015a57806318160ddd1461017d578063189db7d21461018f57806323b872dd146101a4575b600080fd5b610144610305565b604051610151919061162e565b60405180910390f35b61016d61016836600461165d565b61032a565b6040519015158152602001610151565b6035545b604051908152602001610151565b6101a261019d36600461174a565b610344565b005b61016d6101b2366004611797565b6103fb565b6101bf610421565b60405160ff9091168152602001610151565b61018161043e565b61016d6101e736600461165d565b610448565b61016d6101fa3660046117d3565b61046a565b60cd54600160a01b900460ff1661016d565b61018161021f366004611829565b6001600160a01b031660009081526033602052604090205490565b6101a261024836600461165d565b6104e0565b61018161025b366004611829565b610521565b6101a261026e36600461165d565b61053f565b60cc54610286906001600160a01b031681565b6040516001600160a01b039091168152602001610151565b610144610573565b61016d6102b436600461165d565b610594565b61016d6102c736600461165d565b61061a565b60cd54610286906001600160a01b031681565b6101a26102ed366004611844565b610628565b6101816103003660046118b7565b61078c565b60ce54606090610100900460ff161561031d57600080fd5b6103256107c6565b905090565b600033610338818585610858565b60019150505b92915050565b60008060008380602001905181019061035d919061193a565b92509250925060008061036f8561097d565b9150915060008061037f8661097d565b9150915060008061038f87610b06565b915091506103a0858483338f610b55565b506040805160608101825291158083529515602083018190529315910181905260ce805461ffff191661ff0019909616959095176101009093029290921762ff00001916620100009092029190911790925550505050505050565b600033610409858285610c27565b610414858585610ca1565b60019150505b9392505050565b60ce5460009060ff161561043457600080fd5b5060385460ff1690565b6000610325610e3a565b60003361033881858561045b838361078c565b61046591906119cd565b610858565b6000610476848461061a565b50836001600160a01b0316336001600160a01b03167fe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c1685856040516104bc9291906119e0565b60405180910390a3833b156104d6576104d6848484610eb5565b5060019392505050565b60cc546001600160a01b031633146105135760405162461bcd60e51b815260040161050a90611a01565b60405180910390fd5b61051d8282610f1f565b5050565b6001600160a01b03811660009081526099602052604081205461033e565b60cc546001600160a01b031633146105695760405162461bcd60e51b815260040161050a90611a01565b61051d828261103e565b60ce5460609062010000900460ff161561058c57600080fd5b6103256110ed565b600033816105a2828661078c565b9050838110156106025760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b606482015260840161050a565b61060f8286868403610858565b506001949350505050565b600033610338818585610ca1565b834211156106785760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e65000000604482015260640161050a565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886106a78c6110fc565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e001604051602081830303815290604052805190602001209050600061070282611124565b9050600061071282878787611172565b9050896001600160a01b0316816001600160a01b0316146107755760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e61747572650000604482015260640161050a565b6107808a8a8a610858565b50505050505050505050565b6001600160a01b03918216600090815260346020908152604080832093909416825291909152205490565b6001600160a01b03163b151590565b6060603680546107d590611a27565b80601f016020809104026020016040519081016040528092919081815260200182805461080190611a27565b801561084e5780601f106108235761010080835404028352916020019161084e565b820191906000526020600020905b81548152906001019060200180831161083157829003601f168201915b5050505050905090565b6001600160a01b0383166108ba5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b606482015260840161050a565b6001600160a01b03821661091b5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b606482015260840161050a565b6001600160a01b0383811660008181526034602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000606082516000036109935760009150915091565b8251602003610ae65782516000908490601f9081106109b4576109b4611a5b565b01602001516001600160f81b031916146109d15760009150915091565b6001915060205b600081118015610a1257506000846109f1600184611a71565b81518110610a0157610a01611a5b565b01602001516001600160f81b031916145b15610a295780610a2181611a84565b9150506109d8565b6000816001600160401b03811115610a4357610a43611687565b6040519080825280601f01601f191660200182016040528015610a6d576020820181803683370190505b50905060005b828160ff161015610adc57858160ff1681518110610a9357610a93611a5b565b602001015160f81c60f81b828260ff1681518110610ab357610ab3611a5b565b60200101906001600160f81b031916908160001a90535080610ad481611a9b565b915050610a73565b509150610b019050565b6001915082806020019051810190610afe9190611aba565b90505b915091565b6000808251602014610b1d57506000928392509050565b600083806020019051810190610b339190611b02565b905060ff811115610b4a5750600093849350915050565b600194909350915050565b6001600160a01b038216610b9d5760405162461bcd60e51b815260206004820152600f60248201526e494e56414c49445f4741544557415960881b604482015260640161050a565b60cc546001600160a01b031615610be55760405162461bcd60e51b815260206004820152600c60248201526b1053149150511657d253925560a21b604482015260640161050a565b60cc80546001600160a01b038085166001600160a01b03199283161790925560cd805492841692909116919091179055610c2085858561119a565b5050505050565b6000610c33848461078c565b90506000198114610c9b5781811015610c8e5760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000604482015260640161050a565b610c9b8484848403610858565b50505050565b6001600160a01b038316610d055760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b606482015260840161050a565b6001600160a01b038216610d675760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b606482015260840161050a565b6001600160a01b03831660009081526033602052604090205481811015610ddf5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b606482015260840161050a565b6001600160a01b038085166000818152603360205260408082208686039055928616808252908390208054860190559151600080516020611cba83398151915290610e2d9086815260200190565b60405180910390a3610c9b565b60006103257f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f610e6960655490565b6066546040805160208101859052908101839052606081018290524660808201523060a082015260009060c0016040516020818303038152906040528051906020012090509392505050565b604051635260769b60e11b815283906001600160a01b0382169063a4c0ed3690610ee790339087908790600401611b1b565b600060405180830381600087803b158015610f0157600080fd5b505af1158015610f15573d6000803e3d6000fd5b5050505050505050565b6001600160a01b038216610f7f5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b606482015260840161050a565b6001600160a01b03821660009081526033602052604090205481811015610ff35760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b606482015260840161050a565b6001600160a01b0383166000818152603360209081526040808320868603905560358054879003905551858152919291600080516020611cba8339815191529101610970565b505050565b6001600160a01b0382166110945760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015260640161050a565b80603560008282546110a691906119cd565b90915550506001600160a01b038216600081815260336020908152604080832080548601905551848152600080516020611cba833981519152910160405180910390a35050565b6060603780546107d590611a27565b6001600160a01b03811660009081526099602052604090208054600181018255905b50919050565b600061033e611131610e3a565b8360405161190160f01b6020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b6000806000611183878787876112ce565b9150915061119081611388565b5095945050505050565b600054610100900460ff16158080156111ba5750600054600160ff909116105b806111db57506111c9306107b7565b1580156111db575060005460ff166001145b61123e5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840161050a565b6000805460ff191660011790558015611261576000805461ff0019166101001790555b61126a846114d0565b611274848461151a565b6038805460ff191660ff84161790558015610c9b576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150505050565b6000806fa2a8918ca85bafe22016d0b997e4df60600160ff1b038311156112fb575060009050600361137f565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa15801561134f573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166113785760006001925092505061137f565b9150600090505b94509492505050565b600081600481111561139c5761139c611b4b565b036113a45750565b60018160048111156113b8576113b8611b4b565b036114005760405162461bcd60e51b815260206004820152601860248201527745434453413a20696e76616c6964207369676e617475726560401b604482015260640161050a565b600281600481111561141457611414611b4b565b036114615760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161050a565b600381600481111561147557611475611b4b565b036114cd5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b606482015260840161050a565b50565b600054610100900460ff166114f75760405162461bcd60e51b815260040161050a90611b61565b6114cd81604051806040016040528060018152602001603160f81b81525061154b565b600054610100900460ff166115415760405162461bcd60e51b815260040161050a90611b61565b61051d828261158c565b600054610100900460ff166115725760405162461bcd60e51b815260040161050a90611b61565b815160209283012081519190920120606591909155606655565b600054610100900460ff166115b35760405162461bcd60e51b815260040161050a90611b61565b60366115bf8382611bfa565b5060376115cc8282611bfa565b50506038805460ff1916601217905550565b60005b838110156115f95781810151838201526020016115e1565b50506000910152565b6000815180845261161a8160208601602086016115de565b601f01601f19169290920160200192915050565b60208152600061041a6020830184611602565b80356001600160a01b038116811461165857600080fd5b919050565b6000806040838503121561167057600080fd5b61167983611641565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156116c5576116c5611687565b604052919050565b60006001600160401b038211156116e6576116e6611687565b50601f01601f191660200190565b600082601f83011261170557600080fd5b8135611718611713826116cd565b61169d565b81815284602083860101111561172d57600080fd5b816020850160208301376000918101602001919091529392505050565b6000806040838503121561175d57600080fd5b61176683611641565b915060208301356001600160401b0381111561178157600080fd5b61178d858286016116f4565b9150509250929050565b6000806000606084860312156117ac57600080fd5b6117b584611641565b92506117c360208501611641565b9150604084013590509250925092565b6000806000606084860312156117e857600080fd5b6117f184611641565b92506020840135915060408401356001600160401b0381111561181357600080fd5b61181f868287016116f4565b9150509250925092565b60006020828403121561183b57600080fd5b61041a82611641565b600080600080600080600060e0888a03121561185f57600080fd5b61186888611641565b965061187660208901611641565b95506040880135945060608801359350608088013560ff8116811461189a57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156118ca57600080fd5b6118d383611641565b91506118e160208401611641565b90509250929050565b60006118f8611713846116cd565b905082815283838301111561190c57600080fd5b61041a8360208301846115de565b600082601f83011261192b57600080fd5b61041a838351602085016118ea565b60008060006060848603121561194f57600080fd5b83516001600160401b038082111561196657600080fd5b6119728783880161191a565b9450602086015191508082111561198857600080fd5b6119948783880161191a565b935060408601519150808211156119aa57600080fd5b5061181f8682870161191a565b634e487b7160e01b600052601160045260246000fd5b8082018082111561033e5761033e6119b7565b8281526040602082015260006119f96040830184611602565b949350505050565b6020808252600c908201526b4f4e4c595f4741544557415960a01b604082015260600190565b600181811c90821680611a3b57607f821691505b60208210810361111e57634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052603260045260246000fd5b8181038181111561033e5761033e6119b7565b600081611a9357611a936119b7565b506000190190565b600060ff821660ff8103611ab157611ab16119b7565b60010192915050565b600060208284031215611acc57600080fd5b81516001600160401b03811115611ae257600080fd5b8201601f81018413611af357600080fd5b6119f9848251602084016118ea565b600060208284031215611b1457600080fd5b5051919050565b60018060a01b0384168152826020820152606060408201526000611b426060830184611602565b95945050505050565b634e487b7160e01b600052602160045260246000fd5b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b601f82111561103957600081815260208120601f850160051c81016020861015611bd35750805b601f850160051c820191505b81811015611bf257828155600101611bdf565b505050505050565b81516001600160401b03811115611c1357611c13611687565b611c2781611c218454611a27565b84611bac565b602080601f831160018114611c5c5760008415611c445750858301515b600019600386901b1c1916600185901b178555611bf2565b600085815260208120601f198616915b82811015611c8b57888601518255948401946001909101908401611c6c565b5085821015611ca95787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122070845537e703bebbfe17ccc1f6f77686f42e5101a18b732d87a34447165dfe1164736f6c63430008100033608060405234801561001057600080fd5b506040516104e23803806104e283398101604081905261002f91610151565b61003833610047565b61004181610097565b50610181565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6100aa8161014260201b6101a01760201c565b6101205760405162461bcd60e51b815260206004820152603360248201527f5570677261646561626c65426561636f6e3a20696d706c656d656e746174696f60448201527f6e206973206e6f74206120636f6e747261637400000000000000000000000000606482015260840160405180910390fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b03163b151590565b60006020828403121561016357600080fd5b81516001600160a01b038116811461017a57600080fd5b9392505050565b610352806101906000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80633659cfe61461005c5780635c60da1b14610071578063715018a61461009a5780638da5cb5b146100a2578063f2fde38b146100b3575b600080fd5b61006f61006a3660046102ec565b6100c6565b005b6001546001600160a01b03165b6040516001600160a01b03909116815260200160405180910390f35b61006f61010e565b6000546001600160a01b031661007e565b61006f6100c13660046102ec565b610122565b6100ce6101af565b6100d781610209565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6101166101af565b610120600061029c565b565b61012a6101af565b6001600160a01b0381166101945760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61019d8161029c565b50565b6001600160a01b03163b151590565b6000546001600160a01b031633146101205760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161018b565b610212816101a0565b61027a5760405162461bcd60e51b815260206004820152603360248201527f5570677261646561626c65426561636f6e3a20696d706c656d656e746174696f6044820152721b881a5cc81b9bdd08184818dbdb9d1c9858dd606a1b606482015260840161018b565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156102fe57600080fd5b81356001600160a01b038116811461031557600080fd5b939250505056fea264697066735822122059e4b83111825434711a238db6203ec67d74a40640c8c22ba30c6fc5fc18964a64736f6c63430008100033608060405234801561001057600080fd5b50610c94806100206000396000f3fe60806040523480156200001157600080fd5b5060043610620000765760003560e01c806329a5c5cf146200007b578063396a5f9514620000af57806359659e9014620000c657806397881f8d14620000da578063b3e3bf4214620000f3578063c4d66de8146200010a578063e75b21411462000123575b600080fd5b620000926200008c36600462000354565b6200013a565b6040516001600160a01b0390911681526020015b60405180910390f35b62000092620000c036600462000354565b62000186565b60005462000092906001600160a01b031681565b620000e4620001c7565b604051908152602001620000a6565b620000e4620001043660046200038b565b620001f6565b620001216200011b366004620003b8565b62000233565b005b62000092620001343660046200038b565b620002ec565b600080620001493384620001f6565b90506000816040516200015c9062000346565b8190604051809103906000f59050801580156200017d573d6000803e3d6000fd5b50949350505050565b6000620001c182604051806020016200019f9062000346565b6020820181038252601f19601f8201166040525080519060200120306200031c565b92915050565b604051620001d86020820162000346565b6020820181038252601f19601f820116604052508051906020012081565b604080516001600160a01b038416602082015290810182905260009060600160405160208183030381529060405280519060200120905092915050565b6001600160a01b038116620002805760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa122a0a1a7a760911b60448201526064015b60405180910390fd5b6000546001600160a01b031615620002ca5760405162461bcd60e51b815260206004820152600c60248201526b1053149150511657d253925560a21b604482015260640162000277565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b600080620002fb8484620001f6565b90506200031481604051806020016200019f9062000346565b949350505050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b61088180620003de83390190565b6000602082840312156200036757600080fd5b5035919050565b80356001600160a01b03811681146200038657600080fd5b919050565b600080604083850312156200039f57600080fd5b620003aa836200036e565b946020939093013593505050565b600060208284031215620003cb57600080fd5b620003d6826200036e565b939250505056fe608060405234801561001057600080fd5b50336001600160a01b03166359659e906040518163ffffffff1660e01b8152600401602060405180830381865afa15801561004f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610073919061046c565b604051806020016040528060008152506100958282600061009c60201b60201c565b5050610508565b6100a583610167565b6040516001600160a01b038416907f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e90600090a26000825111806100e65750805b1561016257610160836001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561012c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610150919061046c565b8361030a60201b6100291760201c565b505b505050565b61017a8161033660201b6100551760201c565b6101d95760405162461bcd60e51b815260206004820152602560248201527f455243313936373a206e657720626561636f6e206973206e6f74206120636f6e6044820152641d1c9858dd60da1b60648201526084015b60405180910390fd5b61024d816001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561021a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061023e919061046c565b61033660201b6100551760201c565b6102b25760405162461bcd60e51b815260206004820152603060248201527f455243313936373a20626561636f6e20696d706c656d656e746174696f6e206960448201526f1cc81b9bdd08184818dbdb9d1c9858dd60821b60648201526084016101d0565b806102e97fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5060001b61034560201b6100641760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b606061032f838360405180606001604052806027815260200161085a60279139610348565b9392505050565b6001600160a01b03163b151590565b90565b6060600080856001600160a01b03168560405161036591906104b9565b600060405180830381855af49150503d80600081146103a0576040519150601f19603f3d011682016040523d82523d6000602084013e6103a5565b606091505b5090925090506103b7868383876103c1565b9695505050505050565b60608315610430578251600003610429576001600160a01b0385163b6104295760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016101d0565b508161043a565b61043a8383610442565b949350505050565b8151156104525781518083602001fd5b8060405162461bcd60e51b81526004016101d091906104d5565b60006020828403121561047e57600080fd5b81516001600160a01b038116811461032f57600080fd5b60005b838110156104b0578181015183820152602001610498565b50506000910152565b600082516104cb818460208701610495565b9190910192915050565b60208152600082518060208401526104f4816040850160208701610495565b601f01601f19169190910160400192915050565b610343806105176000396000f3fe60806040523661001357610011610017565b005b6100115b610027610022610067565b610100565b565b606061004e83836040518060600160405280602781526020016102e760279139610124565b9392505050565b6001600160a01b03163b151590565b90565b600061009a7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50546001600160a01b031690565b6001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100fb919061024a565b905090565b3660008037600080366000845af43d6000803e80801561011f573d6000f35b3d6000fd5b6060600080856001600160a01b0316856040516101419190610297565b600060405180830381855af49150503d806000811461017c576040519150601f19603f3d011682016040523d82523d6000602084013e610181565b606091505b50915091506101928683838761019c565b9695505050505050565b6060831561020e578251600003610207576101b685610055565b6102075760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064015b60405180910390fd5b5081610218565b6102188383610220565b949350505050565b8151156102305781518083602001fd5b8060405162461bcd60e51b81526004016101fe91906102b3565b60006020828403121561025c57600080fd5b81516001600160a01b038116811461004e57600080fd5b60005b8381101561028e578181015183820152602001610276565b50506000910152565b600082516102a9818460208701610273565b9190910192915050565b60208152600082518060208401526102d2816040850160208701610273565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e9bed491ce4cc7495def60dc616a13f39ccd912637e0c8ba02d45400506de9c064736f6c63430008100033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220237fb73aef7b871fb5f0c1ef15f58a63a1b7502356cc319c2d83ad0717d2cd7264736f6c634300081000336080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea26469706673582212203fc1d73d12f631c8d0a3a1e5ebc667c0e6e4c09698b94f0ed53d99f55382279e64736f6c63430008100033608060405260405162000ebb38038062000ebb833981016040819052620000269162000497565b828162000036828260006200004d565b50620000449050826200008a565b505050620005ca565b6200005883620000e5565b600082511180620000665750805b1562000085576200008383836200012760201b620001691760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f620000b562000156565b604080516001600160a01b03928316815291841660208301520160405180910390a1620000e2816200018f565b50565b620000f08162000244565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606200014f838360405180606001604052806027815260200162000e9460279139620002f8565b9392505050565b60006200018060008051602062000e7483398151915260001b6200037760201b620001951760201c565b546001600160a01b0316919050565b6001600160a01b038116620001fa5760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b806200022360008051602062000e7483398151915260001b6200037760201b620001951760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6200025a816200037a60201b620001981760201c565b620002be5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401620001f1565b80620002237f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b6200037760201b620001951760201c565b6060600080856001600160a01b03168560405162000317919062000577565b600060405180830381855af49150503d806000811462000354576040519150601f19603f3d011682016040523d82523d6000602084013e62000359565b606091505b5090925090506200036d8683838762000389565b9695505050505050565b90565b6001600160a01b03163b151590565b60608315620003fd578251600003620003f5576001600160a01b0385163b620003f55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401620001f1565b508162000409565b62000409838362000411565b949350505050565b815115620004225781518083602001fd5b8060405162461bcd60e51b8152600401620001f1919062000595565b80516001600160a01b03811681146200045657600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156200048e57818101518382015260200162000474565b50506000910152565b600080600060608486031215620004ad57600080fd5b620004b8846200043e565b9250620004c8602085016200043e565b60408501519092506001600160401b0380821115620004e657600080fd5b818601915086601f830112620004fb57600080fd5b8151818111156200051057620005106200045b565b604051601f8201601f19908116603f011681019083821181831017156200053b576200053b6200045b565b816040528281528960208487010111156200055557600080fd5b6200056883602083016020880162000471565b80955050505050509250925092565b600082516200058b81846020870162000471565b9190910192915050565b6020815260008251806020840152620005b681604085016020870162000471565b601f01601f19169190910160400192915050565b61089a80620005da6000396000f3fe60806040523661001357610011610017565b005b6100115b61001f6101a7565b6001600160a01b0316330361015f5760606001600160e01b0319600035166364d3180d60e11b810161005a576100536101da565b9150610157565b63587086bd60e11b6001600160e01b031982160161007a57610053610231565b63070d7c6960e41b6001600160e01b031982160161009a57610053610277565b621eb96f60e61b6001600160e01b03198216016100b9576100536102a8565b63a39f25e560e01b6001600160e01b03198216016100d9576100536102e8565b60405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b815160208301f35b6101676102fc565b565b606061018e838360405180606001604052806027815260200161083e6027913961030c565b9392505050565b90565b6001600160a01b03163b151590565b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b60606101e4610384565b60006101f33660048184610691565b81019061020091906106d7565b905061021d8160405180602001604052806000815250600061038f565b505060408051602081019091526000815290565b60606000806102433660048184610691565b8101906102509190610708565b915091506102608282600161038f565b604051806020016040528060008152509250505090565b6060610281610384565b60006102903660048184610691565b81019061029d91906106d7565b905061021d816103bb565b60606102b2610384565b60006102bc6101a7565b604080516001600160a01b03831660208201529192500160405160208183030381529060405291505090565b60606102f2610384565b60006102bc610412565b610167610307610412565b610421565b6060600080856001600160a01b03168560405161032991906107ee565b600060405180830381855af49150503d8060008114610364576040519150601f19603f3d011682016040523d82523d6000602084013e610369565b606091505b509150915061037a86838387610445565b9695505050505050565b341561016757600080fd5b610398836104c4565b6000825111806103a55750805b156103b6576103b48383610169565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6103e46101a7565b604080516001600160a01b03928316815291841660208301520160405180910390a161040f81610504565b50565b600061041c6105ad565b905090565b3660008037600080366000845af43d6000803e808015610440573d6000f35b3d6000fd5b606083156104b25782516000036104ab5761045f85610198565b6104ab5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161014e565b50816104bc565b6104bc83836105d5565b949350505050565b6104cd816105ff565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b0381166105695760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b606482015260840161014e565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80546001600160a01b0319166001600160a01b039290921691909117905550565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6101cb565b8151156105e55781518083602001fd5b8060405162461bcd60e51b815260040161014e919061080a565b61060881610198565b61066a5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b606482015260840161014e565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61058c565b600080858511156106a157600080fd5b838611156106ae57600080fd5b5050820193919092039150565b80356001600160a01b03811681146106d257600080fd5b919050565b6000602082840312156106e957600080fd5b61018e826106bb565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561071b57600080fd5b610724836106bb565b9150602083013567ffffffffffffffff8082111561074157600080fd5b818501915085601f83011261075557600080fd5b813581811115610767576107676106f2565b604051601f8201601f19908116603f0116810190838211818310171561078f5761078f6106f2565b816040528281528860208487010111156107a857600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60005b838110156107e55781810151838201526020016107cd565b50506000910152565b600082516108008184602087016107ca565b9190910192915050565b60208152600082518060208401526108298160408501602087016107ca565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122072007c277cc5d8471be1434c6b2d5b70fb7c2f6f77a0697617733c4d4ae5b76964736f6c63430008100033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220377d031a5f294a74596aa9acc30ca418bd32dd3f9cbae6ed71ce8cc1f17439cc64736f6c63430008100033000000000000", "nonce": "0x0", "to": "0x000000000000000000000000000000000000006e", "transactionIndex": "0x1", "value": "0x0", "type": "0x69", "chainId": "0xda8c8b77", "v": "0x0", "r": "0x0", "s": "0x0", "requestId": "0x0000000000000000000000000000000000000000000000000000000000000001", "refundTo": "0xbee947aec820389c1560c87bd96e2723bad05b61", "l1BaseFee": "0x5f5e100", "depositValue": "0x22f2505249400", "retryValue": "0x0", "retryData": "0x608060405234801561001057600080fd5b50615f3a806100206000396000f3fe60806040523480156200001157600080fd5b50600436106200002e5760003560e01c8063b1c7a8701462000033575b600080fd5b6200004a6200004436600462001528565b6200004c565b005b6000620000b9620000866040518060400160405280601181526020017027b93134ba2619283937bc3ca0b236b4b760791b81525062000304565b6040516200009760208201620014b8565b6020820181038252601f19601f8201166040525080519060200120306200033a565b90506001600160a01b0381163b15620000e5576040516377e0068560e11b815260040160405180910390fd5b5060006200011c6040518060400160405280601181526020017027b93134ba2619283937bc3ca0b236b4b760791b81525062000304565b6040516200012a90620014b8565b8190604051809103906000f59050801580156200014b573d6000803e3d6000fd5b50905060006200016c6200016360a08d018d62001603565b86858762000364565b9050600062000189620001808d8062001603565b8d898762000689565b9050620001a86200019e60208e018e62001603565b8c8487876200088c565b620001c4620001bb60408e018e62001603565b8b848762000cb4565b6001600160a01b03881615620002035762000203620001e760608e018e62001603565b8e8060800190620001f9919062001603565b8c8c878a62000ef4565b6200029360006200023c6040518060400160405280601081526020016f13dc989a5d130c935d5b1d1a58d85b1b60821b81525062000304565b6200028d8f8060c0019062000252919062001603565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250620012ee92505050565b6200131c565b5060405163f2fde38b60e01b81526001600160a01b0384169063f2fde38b90620002c290859060040162001654565b600060405180830381600087803b158015620002dd57600080fd5b505af1158015620002f2573d6000803e3d6000fd5b50505050505050505050505050505050565b60008146336040516020016200031d939291906200168e565b604051602081830303815290604052805190602001209050919050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b600080620003f084620003ac6040518060400160405280601b81526020017f4f726269744c32557067726164654578656375746f7250726f7879000000000081525062000304565b620003ea6040518060400160405280601b81526020017a4f726269744c32557067726164654578656375746f724c6f67696360281b81525062000304565b6200142d565b90506000620004796000620004386040518060400160405280601b81526020017a4f726269744c32557067726164654578656375746f724c6f67696360281b81525062000304565b6200028d8b8b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250620012ee92505050565b60405163266a23b160e21b81529091506001600160a01b038616906399a88ec490620004ac9085908590600401620016c9565b600060405180830381600087803b158015620004c757600080fd5b505af1158015620004dc573d6000803e3d6000fd5b5060009250829150620004ec9050565b60405190808252806020026020018201604052801562000516578160200160208202803683370190505b5060405163251b648160e21b81529091506001600160a01b0383169063946d9204906200054c9061dead908590600401620016e3565b600060405180830381600087803b1580156200056757600080fd5b505af11580156200057c573d6000803e3d6000fd5b5060009250600291506200058d9050565b604051908082528060200260200182016040528015620005b7578160200160208202803683370190505b5090508781600081518110620005d157620005d162001741565b60200260200101906001600160a01b031690816001600160a01b031681525050858160018151811062000608576200060862001741565b6001600160a01b03928316602091820292909201015260405163251b648160e21b81529085169063946d920490620006479087908590600401620016e3565b600060405180830381600087803b1580156200066257600080fd5b505af115801562000677573d6000803e3d6000fd5b50959c9b505050505050505050505050565b6000806200070983620006cd604051806040016040528060198152602001784f726269744c3247617465776179526f7574657250726f787960381b81525062000304565b620003ea604051806040016040528060198152602001784f726269744c3247617465776179526f757465724c6f67696360381b81525062000304565b905060006200074f600062000438604051806040016040528060198152602001784f726269744c3247617465776179526f757465724c6f67696360381b81525062000304565b60405163266a23b160e21b81529091506001600160a01b038516906399a88ec490620007829085908590600401620016c9565b600060405180830381600087803b1580156200079d57600080fd5b505af1158015620007b2573d6000803e3d6000fd5b505060405163485cc95560e01b81526001600160a01b038416925063485cc9559150620007e89061dead908190600401620016c9565b600060405180830381600087803b1580156200080357600080fd5b505af115801562000818573d6000803e3d6000fd5b505060405163485cc95560e01b81526001600160a01b038516925063485cc95591506200084c9089908990600401620016c9565b600060405180830381600087803b1580156200086757600080fd5b505af11580156200087c573d6000803e3d6000fd5b50939a9950505050505050505050565b60006200091183620008d36040518060400160405280601b81526020017f4f726269744c325374616e646172644761746577617950726f7879000000000081525062000304565b620003ea6040518060400160405280601b81526020017a4f726269744c325374616e64617264476174657761794c6f67696360281b81525062000304565b90506000620009596000620004386040518060400160405280601b81526020017a4f726269744c325374616e64617264476174657761794c6f67696360281b81525062000304565b60405163266a23b160e21b81529091506001600160a01b038516906399a88ec4906200098c9085908590600401620016c9565b600060405180830381600087803b158015620009a757600080fd5b505af1158015620009bc573d6000803e3d6000fd5b505060405163c0c53b8b60e01b81526001600160a01b038416925063c0c53b8b9150620009f49061dead908190819060040162001757565b600060405180830381600087803b15801562000a0f57600080fd5b505af115801562000a24573d6000803e3d6000fd5b50505050600062000a626040518060400160405280601581526020017404f726269745374616e64617264417262455243323605c1b81525062000304565b60405162000a7090620014c6565b8190604051809103906000f590508015801562000a91573d6000803e3d6000fd5b509050600062000acf6040518060400160405280601681526020017527b93134ba2ab833b930b232b0b13632a132b0b1b7b760511b81525062000304565b8260405162000ade90620014d4565b62000aea919062001654565b8190604051809103906000f590508015801562000b0b573d6000803e3d6000fd5b509050600062000b4a604051806040016040528060178152602001764f72626974426561636f6e50726f7879466163746f727960481b81525062000304565b60405162000b5890620014e2565b8190604051809103906000f590508015801562000b79573d6000803e3d6000fd5b5060405163189acdbd60e31b81529091506001600160a01b0382169063c4d66de89062000bab90859060040162001654565b600060405180830381600087803b15801562000bc657600080fd5b505af115801562000bdb573d6000803e3d6000fd5b505060405163c0c53b8b60e01b81526001600160a01b038816925063c0c53b8b915062000c11908c908c90869060040162001757565b600060405180830381600087803b15801562000c2c57600080fd5b505af115801562000c41573d6000803e3d6000fd5b505060405163f2fde38b60e01b81526001600160a01b038516925063f2fde38b915062000c7390899060040162001654565b600060405180830381600087803b15801562000c8e57600080fd5b505af115801562000ca3573d6000803e3d6000fd5b505050505050505050505050505050565b600062000d338262000cf7604051806040016040528060198152602001784f726269744c32437573746f6d4761746577617950726f787960381b81525062000304565b620003ea604051806040016040528060198152602001784f726269744c32437573746f6d476174657761794c6f67696360381b81525062000304565b9050600062000dba600062000d79604051806040016040528060198152602001784f726269744c32437573746f6d476174657761794c6f67696360381b81525062000304565b6200028d8a8a8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250620012ee92505050565b60405163266a23b160e21b81529091506001600160a01b038416906399a88ec49062000ded9085908590600401620016c9565b600060405180830381600087803b15801562000e0857600080fd5b505af115801562000e1d573d6000803e3d6000fd5b505060405163485cc95560e01b81526001600160a01b038416925063485cc955915062000e539061dead908190600401620016c9565b600060405180830381600087803b15801562000e6e57600080fd5b505af115801562000e83573d6000803e3d6000fd5b505060405163485cc95560e01b81526001600160a01b038516925063485cc955915062000eb79088908890600401620016c9565b600060405180830381600087803b15801562000ed257600080fd5b505af115801562000ee7573d6000803e3d6000fd5b5050505050505050505050565b600062000f5c8262000f2e6040518060400160405280601081526020016f4f726269744c325745544850726f787960801b81525062000304565b620003ea6040518060400160405280600b81526020016a09ee4c4d2e89864ae8aa8960ab1b81525062000304565b9050600062000f946000620004386040518060400160405280600b81526020016a09ee4c4d2e89864ae8aa8960ab1b81525062000304565b60405163266a23b160e21b81529091506001600160a01b038416906399a88ec49062000fc79085908590600401620016c9565b600060405180830381600087803b15801562000fe257600080fd5b505af115801562000ff7573d6000803e3d6000fd5b50505050600062001076846200103c604051806040016040528060178152602001764f726269744c32576574684761746577617950726f787960481b81525062000304565b620003ea604051806040016040528060178152602001764f726269744c3257657468476174657761794c6f67696360481b81525062000304565b90506000620010fb6000620010ba604051806040016040528060178152602001764f726269744c3257657468476174657761794c6f67696360481b81525062000304565b6200028d8f8f8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250620012ee92505050565b60405163266a23b160e21b81529091506001600160a01b038616906399a88ec4906200112e9085908590600401620016c9565b600060405180830381600087803b1580156200114957600080fd5b505af11580156200115e573d6000803e3d6000fd5b5050604051637c643b2f60e11b81526001600160a01b038416925063f8c8765e9150620011989061dead908190819081906004016200177a565b600060405180830381600087803b158015620011b357600080fd5b505af1158015620011c8573d6000803e3d6000fd5b5050604051637c643b2f60e11b81526001600160a01b038516925063f8c8765e915062001200908b908a908c908a906004016200177a565b600060405180830381600087803b1580156200121b57600080fd5b505af115801562001230573d6000803e3d6000fd5b505060405163641078a360e11b815260a06004820152600060a4820181905260c0602483015260c48201819052604482015261dead6064820181905260848201526001600160a01b038616925063c820f146915060e401600060405180830381600087803b158015620012a257600080fd5b505af1158015620012b7573d6000803e3d6000fd5b505060405163641078a360e11b81526001600160a01b038716925063c820f1469150620002c29060129086908c90600401620017bc565b6060815182604051602001620013069291906200180b565b6040516020818303038152906040529050919050565b600083471015620013745760405162461bcd60e51b815260206004820152601d60248201527f437265617465323a20696e73756666696369656e742062616c616e636500000060448201526064015b60405180910390fd5b8151600003620013c75760405162461bcd60e51b815260206004820181905260248201527f437265617465323a2062797465636f6465206c656e677468206973207a65726f60448201526064016200136b565b8282516020840186f590506001600160a01b038116620014265760405162461bcd60e51b8152602060048201526019602482015278437265617465323a204661696c6564206f6e206465706c6f7960381b60448201526064016200136b565b9392505050565b600082826040516200143f90620014f0565b8190604051809103906000f590508015801562001460573d6000803e3d6000fd5b5085604051806020016040528060008152506040516200148090620014fd565b6200148e939291906200187b565b8190604051809103906000f5905080158015620014af573d6000803e3d6000fd5b50949350505050565b61070f80620018cb83390190565b611e7e8062001fda83390190565b6104e28062003e5883390190565b610cb4806200433a83390190565b605c8062004fee83390190565b610ebb806200504a83390190565b80356001600160a01b03811681146200152357600080fd5b919050565b60008060008060008060008060006101208a8c0312156200154857600080fd5b893567ffffffffffffffff8111156200156057600080fd5b8a0160e0818d0312156200157357600080fd5b98506200158360208b016200150b565b97506200159360408b016200150b565b9650620015a360608b016200150b565b9550620015b360808b016200150b565b9450620015c360a08b016200150b565b9350620015d360c08b016200150b565b9250620015e360e08b016200150b565b9150620015f46101008b016200150b565b90509295985092959850929598565b6000808335601e198436030181126200161b57600080fd5b83018035915067ffffffffffffffff8211156200163757600080fd5b6020019150368190038213156200164d57600080fd5b9250929050565b6001600160a01b0391909116815260200190565b60005b83811015620016855781810151838201526020016200166b565b50506000910152565b60008451620016a281846020890162001668565b919091019283525060601b6bffffffffffffffffffffffff19166020820152603401919050565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b038381168252604060208084018290528451918401829052600092858201929091906060860190855b818110156200173357855185168352948301949183019160010162001713565b509098975050505050505050565b634e487b7160e01b600052603260045260246000fd5b6001600160a01b0393841681529183166020830152909116604082015260600190565b6001600160a01b03948516815292841660208401529083166040830152909116606082015260800190565b60048152630ae8aa8960e31b602082015260400190565b60a081526000620017d060a08301620017a5565b8281036020840152620017e381620017a5565b60ff96909616604084015250506001600160a01b039283166060820152911660809091015290565b710608060405234801561001057600080fd5b560741b8152606160f81b601282015260f083901b6001600160f01b03191660138201526a4030801030001cb00079ff60a91b601582015281516000906200186d81602080860190870162001668565b919091016020019392505050565b600060018060a01b038086168352808516602084015250606060408301528251806060840152620018b481608085016020870162001668565b601f01601f19169190910160800194935050505056fe608060405234801561001057600080fd5b5061001a3361001f565b61006f565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6106918061007e6000396000f3fe60806040526004361061006b5760003560e01c8063204e1c7a14610070578063715018a6146100a65780637eff275e146100bd5780638da5cb5b146100dd5780639623609d146100fb57806399a88ec41461010e578063f2fde38b1461012e578063f3b7dead1461014e575b600080fd5b34801561007c57600080fd5b5061009061008b366004610483565b61016e565b60405161009d91906104a7565b60405180910390f35b3480156100b257600080fd5b506100bb6101ff565b005b3480156100c957600080fd5b506100bb6100d83660046104bb565b610213565b3480156100e957600080fd5b506000546001600160a01b0316610090565b6100bb61010936600461050a565b61027d565b34801561011a57600080fd5b506100bb6101293660046104bb565b6102ec565b34801561013a57600080fd5b506100bb610149366004610483565b610320565b34801561015a57600080fd5b50610090610169366004610483565b61039e565b6000806000836001600160a01b031660405161019490635c60da1b60e01b815260040190565b600060405180830381855afa9150503d80600081146101cf576040519150601f19603f3d011682016040523d82523d6000602084013e6101d4565b606091505b5091509150816101e357600080fd5b808060200190518101906101f791906105e0565b949350505050565b6102076103c4565b610211600061041e565b565b61021b6103c4565b6040516308f2839760e41b81526001600160a01b03831690638f283970906102479084906004016104a7565b600060405180830381600087803b15801561026157600080fd5b505af1158015610275573d6000803e3d6000fd5b505050505050565b6102856103c4565b60405163278f794360e11b81526001600160a01b03841690634f1ef2869034906102b590869086906004016105fd565b6000604051808303818588803b1580156102ce57600080fd5b505af11580156102e2573d6000803e3d6000fd5b5050505050505050565b6102f46103c4565b604051631b2ce7f360e11b81526001600160a01b03831690633659cfe6906102479084906004016104a7565b6103286103c4565b6001600160a01b0381166103925760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61039b8161041e565b50565b6000806000836001600160a01b0316604051610194906303e1469160e61b815260040190565b6000546001600160a01b031633146102115760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610389565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b038116811461039b57600080fd5b60006020828403121561049557600080fd5b81356104a08161046e565b9392505050565b6001600160a01b0391909116815260200190565b600080604083850312156104ce57600080fd5b82356104d98161046e565b915060208301356104e98161046e565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b60008060006060848603121561051f57600080fd5b833561052a8161046e565b9250602084013561053a8161046e565b9150604084013567ffffffffffffffff8082111561055757600080fd5b818601915086601f83011261056b57600080fd5b81358181111561057d5761057d6104f4565b604051601f8201601f19908116603f011681019083821181831017156105a5576105a56104f4565b816040528281528960208487010111156105be57600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b6000602082840312156105f257600080fd5b81516104a08161046e565b60018060a01b038316815260006020604081840152835180604085015260005b818110156106395785810183015185820160600152820161061d565b506000606082860101526060601f19601f83011685010192505050939250505056fea26469706673582212208a2d9d6b4833462246845f0898b3da66a9086b54eb9fb2d870a9780af5c177f664736f6c6343000810003360806040523480156200001157600080fd5b50600054610100900460ff1615808015620000335750600054600160ff909116105b8062000063575062000050306200015060201b620007b71760201c565b15801562000063575060005460ff166001145b620000cb5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840160405180910390fd5b6000805460ff191660011790558015620000ef576000805461ff0019166101001790555b801562000136576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5060cd805460ff60a01b1916600160a01b1790556200015f565b6001600160a01b03163b151590565b611d0f806200016f6000396000f3fe608060405234801561001057600080fd5b50600436106101375760003560e01c806370a08231116100b857806395d89b411161007c57806395d89b411461029e578063a457c2d7146102a6578063a9059cbb146102b9578063c2eeeebd146102cc578063d505accf146102df578063dd62ed3e146102f257600080fd5b806370a082311461021157806374f4f5471461023a5780637ecebe001461024d5780638c2a993e146102605780638fa74a0e1461027357600080fd5b8063313ce567116100ff578063313ce567146101b75780633644e515146101d157806339509351146101d95780634000aea0146101ec5780636f791d29146101ff57600080fd5b806306fdde031461013c578063095ea7b31461015a57806318160ddd1461017d578063189db7d21461018f57806323b872dd146101a4575b600080fd5b610144610305565b604051610151919061162e565b60405180910390f35b61016d61016836600461165d565b61032a565b6040519015158152602001610151565b6035545b604051908152602001610151565b6101a261019d36600461174a565b610344565b005b61016d6101b2366004611797565b6103fb565b6101bf610421565b60405160ff9091168152602001610151565b61018161043e565b61016d6101e736600461165d565b610448565b61016d6101fa3660046117d3565b61046a565b60cd54600160a01b900460ff1661016d565b61018161021f366004611829565b6001600160a01b031660009081526033602052604090205490565b6101a261024836600461165d565b6104e0565b61018161025b366004611829565b610521565b6101a261026e36600461165d565b61053f565b60cc54610286906001600160a01b031681565b6040516001600160a01b039091168152602001610151565b610144610573565b61016d6102b436600461165d565b610594565b61016d6102c736600461165d565b61061a565b60cd54610286906001600160a01b031681565b6101a26102ed366004611844565b610628565b6101816103003660046118b7565b61078c565b60ce54606090610100900460ff161561031d57600080fd5b6103256107c6565b905090565b600033610338818585610858565b60019150505b92915050565b60008060008380602001905181019061035d919061193a565b92509250925060008061036f8561097d565b9150915060008061037f8661097d565b9150915060008061038f87610b06565b915091506103a0858483338f610b55565b506040805160608101825291158083529515602083018190529315910181905260ce805461ffff191661ff0019909616959095176101009093029290921762ff00001916620100009092029190911790925550505050505050565b600033610409858285610c27565b610414858585610ca1565b60019150505b9392505050565b60ce5460009060ff161561043457600080fd5b5060385460ff1690565b6000610325610e3a565b60003361033881858561045b838361078c565b61046591906119cd565b610858565b6000610476848461061a565b50836001600160a01b0316336001600160a01b03167fe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c1685856040516104bc9291906119e0565b60405180910390a3833b156104d6576104d6848484610eb5565b5060019392505050565b60cc546001600160a01b031633146105135760405162461bcd60e51b815260040161050a90611a01565b60405180910390fd5b61051d8282610f1f565b5050565b6001600160a01b03811660009081526099602052604081205461033e565b60cc546001600160a01b031633146105695760405162461bcd60e51b815260040161050a90611a01565b61051d828261103e565b60ce5460609062010000900460ff161561058c57600080fd5b6103256110ed565b600033816105a2828661078c565b9050838110156106025760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b606482015260840161050a565b61060f8286868403610858565b506001949350505050565b600033610338818585610ca1565b834211156106785760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e65000000604482015260640161050a565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886106a78c6110fc565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e001604051602081830303815290604052805190602001209050600061070282611124565b9050600061071282878787611172565b9050896001600160a01b0316816001600160a01b0316146107755760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e61747572650000604482015260640161050a565b6107808a8a8a610858565b50505050505050505050565b6001600160a01b03918216600090815260346020908152604080832093909416825291909152205490565b6001600160a01b03163b151590565b6060603680546107d590611a27565b80601f016020809104026020016040519081016040528092919081815260200182805461080190611a27565b801561084e5780601f106108235761010080835404028352916020019161084e565b820191906000526020600020905b81548152906001019060200180831161083157829003601f168201915b5050505050905090565b6001600160a01b0383166108ba5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b606482015260840161050a565b6001600160a01b03821661091b5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b606482015260840161050a565b6001600160a01b0383811660008181526034602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000606082516000036109935760009150915091565b8251602003610ae65782516000908490601f9081106109b4576109b4611a5b565b01602001516001600160f81b031916146109d15760009150915091565b6001915060205b600081118015610a1257506000846109f1600184611a71565b81518110610a0157610a01611a5b565b01602001516001600160f81b031916145b15610a295780610a2181611a84565b9150506109d8565b6000816001600160401b03811115610a4357610a43611687565b6040519080825280601f01601f191660200182016040528015610a6d576020820181803683370190505b50905060005b828160ff161015610adc57858160ff1681518110610a9357610a93611a5b565b602001015160f81c60f81b828260ff1681518110610ab357610ab3611a5b565b60200101906001600160f81b031916908160001a90535080610ad481611a9b565b915050610a73565b509150610b019050565b6001915082806020019051810190610afe9190611aba565b90505b915091565b6000808251602014610b1d57506000928392509050565b600083806020019051810190610b339190611b02565b905060ff811115610b4a5750600093849350915050565b600194909350915050565b6001600160a01b038216610b9d5760405162461bcd60e51b815260206004820152600f60248201526e494e56414c49445f4741544557415960881b604482015260640161050a565b60cc546001600160a01b031615610be55760405162461bcd60e51b815260206004820152600c60248201526b1053149150511657d253925560a21b604482015260640161050a565b60cc80546001600160a01b038085166001600160a01b03199283161790925560cd805492841692909116919091179055610c2085858561119a565b5050505050565b6000610c33848461078c565b90506000198114610c9b5781811015610c8e5760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000604482015260640161050a565b610c9b8484848403610858565b50505050565b6001600160a01b038316610d055760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b606482015260840161050a565b6001600160a01b038216610d675760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b606482015260840161050a565b6001600160a01b03831660009081526033602052604090205481811015610ddf5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b606482015260840161050a565b6001600160a01b038085166000818152603360205260408082208686039055928616808252908390208054860190559151600080516020611cba83398151915290610e2d9086815260200190565b60405180910390a3610c9b565b60006103257f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f610e6960655490565b6066546040805160208101859052908101839052606081018290524660808201523060a082015260009060c0016040516020818303038152906040528051906020012090509392505050565b604051635260769b60e11b815283906001600160a01b0382169063a4c0ed3690610ee790339087908790600401611b1b565b600060405180830381600087803b158015610f0157600080fd5b505af1158015610f15573d6000803e3d6000fd5b5050505050505050565b6001600160a01b038216610f7f5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b606482015260840161050a565b6001600160a01b03821660009081526033602052604090205481811015610ff35760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b606482015260840161050a565b6001600160a01b0383166000818152603360209081526040808320868603905560358054879003905551858152919291600080516020611cba8339815191529101610970565b505050565b6001600160a01b0382166110945760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015260640161050a565b80603560008282546110a691906119cd565b90915550506001600160a01b038216600081815260336020908152604080832080548601905551848152600080516020611cba833981519152910160405180910390a35050565b6060603780546107d590611a27565b6001600160a01b03811660009081526099602052604090208054600181018255905b50919050565b600061033e611131610e3a565b8360405161190160f01b6020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b6000806000611183878787876112ce565b9150915061119081611388565b5095945050505050565b600054610100900460ff16158080156111ba5750600054600160ff909116105b806111db57506111c9306107b7565b1580156111db575060005460ff166001145b61123e5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840161050a565b6000805460ff191660011790558015611261576000805461ff0019166101001790555b61126a846114d0565b611274848461151a565b6038805460ff191660ff84161790558015610c9b576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150505050565b6000806fa2a8918ca85bafe22016d0b997e4df60600160ff1b038311156112fb575060009050600361137f565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa15801561134f573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166113785760006001925092505061137f565b9150600090505b94509492505050565b600081600481111561139c5761139c611b4b565b036113a45750565b60018160048111156113b8576113b8611b4b565b036114005760405162461bcd60e51b815260206004820152601860248201527745434453413a20696e76616c6964207369676e617475726560401b604482015260640161050a565b600281600481111561141457611414611b4b565b036114615760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161050a565b600381600481111561147557611475611b4b565b036114cd5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b606482015260840161050a565b50565b600054610100900460ff166114f75760405162461bcd60e51b815260040161050a90611b61565b6114cd81604051806040016040528060018152602001603160f81b81525061154b565b600054610100900460ff166115415760405162461bcd60e51b815260040161050a90611b61565b61051d828261158c565b600054610100900460ff166115725760405162461bcd60e51b815260040161050a90611b61565b815160209283012081519190920120606591909155606655565b600054610100900460ff166115b35760405162461bcd60e51b815260040161050a90611b61565b60366115bf8382611bfa565b5060376115cc8282611bfa565b50506038805460ff1916601217905550565b60005b838110156115f95781810151838201526020016115e1565b50506000910152565b6000815180845261161a8160208601602086016115de565b601f01601f19169290920160200192915050565b60208152600061041a6020830184611602565b80356001600160a01b038116811461165857600080fd5b919050565b6000806040838503121561167057600080fd5b61167983611641565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156116c5576116c5611687565b604052919050565b60006001600160401b038211156116e6576116e6611687565b50601f01601f191660200190565b600082601f83011261170557600080fd5b8135611718611713826116cd565b61169d565b81815284602083860101111561172d57600080fd5b816020850160208301376000918101602001919091529392505050565b6000806040838503121561175d57600080fd5b61176683611641565b915060208301356001600160401b0381111561178157600080fd5b61178d858286016116f4565b9150509250929050565b6000806000606084860312156117ac57600080fd5b6117b584611641565b92506117c360208501611641565b9150604084013590509250925092565b6000806000606084860312156117e857600080fd5b6117f184611641565b92506020840135915060408401356001600160401b0381111561181357600080fd5b61181f868287016116f4565b9150509250925092565b60006020828403121561183b57600080fd5b61041a82611641565b600080600080600080600060e0888a03121561185f57600080fd5b61186888611641565b965061187660208901611641565b95506040880135945060608801359350608088013560ff8116811461189a57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156118ca57600080fd5b6118d383611641565b91506118e160208401611641565b90509250929050565b60006118f8611713846116cd565b905082815283838301111561190c57600080fd5b61041a8360208301846115de565b600082601f83011261192b57600080fd5b61041a838351602085016118ea565b60008060006060848603121561194f57600080fd5b83516001600160401b038082111561196657600080fd5b6119728783880161191a565b9450602086015191508082111561198857600080fd5b6119948783880161191a565b935060408601519150808211156119aa57600080fd5b5061181f8682870161191a565b634e487b7160e01b600052601160045260246000fd5b8082018082111561033e5761033e6119b7565b8281526040602082015260006119f96040830184611602565b949350505050565b6020808252600c908201526b4f4e4c595f4741544557415960a01b604082015260600190565b600181811c90821680611a3b57607f821691505b60208210810361111e57634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052603260045260246000fd5b8181038181111561033e5761033e6119b7565b600081611a9357611a936119b7565b506000190190565b600060ff821660ff8103611ab157611ab16119b7565b60010192915050565b600060208284031215611acc57600080fd5b81516001600160401b03811115611ae257600080fd5b8201601f81018413611af357600080fd5b6119f9848251602084016118ea565b600060208284031215611b1457600080fd5b5051919050565b60018060a01b0384168152826020820152606060408201526000611b426060830184611602565b95945050505050565b634e487b7160e01b600052602160045260246000fd5b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b601f82111561103957600081815260208120601f850160051c81016020861015611bd35750805b601f850160051c820191505b81811015611bf257828155600101611bdf565b505050505050565b81516001600160401b03811115611c1357611c13611687565b611c2781611c218454611a27565b84611bac565b602080601f831160018114611c5c5760008415611c445750858301515b600019600386901b1c1916600185901b178555611bf2565b600085815260208120601f198616915b82811015611c8b57888601518255948401946001909101908401611c6c565b5085821015611ca95787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122070845537e703bebbfe17ccc1f6f77686f42e5101a18b732d87a34447165dfe1164736f6c63430008100033608060405234801561001057600080fd5b506040516104e23803806104e283398101604081905261002f91610151565b61003833610047565b61004181610097565b50610181565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6100aa8161014260201b6101a01760201c565b6101205760405162461bcd60e51b815260206004820152603360248201527f5570677261646561626c65426561636f6e3a20696d706c656d656e746174696f60448201527f6e206973206e6f74206120636f6e747261637400000000000000000000000000606482015260840160405180910390fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b03163b151590565b60006020828403121561016357600080fd5b81516001600160a01b038116811461017a57600080fd5b9392505050565b610352806101906000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80633659cfe61461005c5780635c60da1b14610071578063715018a61461009a5780638da5cb5b146100a2578063f2fde38b146100b3575b600080fd5b61006f61006a3660046102ec565b6100c6565b005b6001546001600160a01b03165b6040516001600160a01b03909116815260200160405180910390f35b61006f61010e565b6000546001600160a01b031661007e565b61006f6100c13660046102ec565b610122565b6100ce6101af565b6100d781610209565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6101166101af565b610120600061029c565b565b61012a6101af565b6001600160a01b0381166101945760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61019d8161029c565b50565b6001600160a01b03163b151590565b6000546001600160a01b031633146101205760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161018b565b610212816101a0565b61027a5760405162461bcd60e51b815260206004820152603360248201527f5570677261646561626c65426561636f6e3a20696d706c656d656e746174696f6044820152721b881a5cc81b9bdd08184818dbdb9d1c9858dd606a1b606482015260840161018b565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156102fe57600080fd5b81356001600160a01b038116811461031557600080fd5b939250505056fea264697066735822122059e4b83111825434711a238db6203ec67d74a40640c8c22ba30c6fc5fc18964a64736f6c63430008100033608060405234801561001057600080fd5b50610c94806100206000396000f3fe60806040523480156200001157600080fd5b5060043610620000765760003560e01c806329a5c5cf146200007b578063396a5f9514620000af57806359659e9014620000c657806397881f8d14620000da578063b3e3bf4214620000f3578063c4d66de8146200010a578063e75b21411462000123575b600080fd5b620000926200008c36600462000354565b6200013a565b6040516001600160a01b0390911681526020015b60405180910390f35b62000092620000c036600462000354565b62000186565b60005462000092906001600160a01b031681565b620000e4620001c7565b604051908152602001620000a6565b620000e4620001043660046200038b565b620001f6565b620001216200011b366004620003b8565b62000233565b005b62000092620001343660046200038b565b620002ec565b600080620001493384620001f6565b90506000816040516200015c9062000346565b8190604051809103906000f59050801580156200017d573d6000803e3d6000fd5b50949350505050565b6000620001c182604051806020016200019f9062000346565b6020820181038252601f19601f8201166040525080519060200120306200031c565b92915050565b604051620001d86020820162000346565b6020820181038252601f19601f820116604052508051906020012081565b604080516001600160a01b038416602082015290810182905260009060600160405160208183030381529060405280519060200120905092915050565b6001600160a01b038116620002805760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa122a0a1a7a760911b60448201526064015b60405180910390fd5b6000546001600160a01b031615620002ca5760405162461bcd60e51b815260206004820152600c60248201526b1053149150511657d253925560a21b604482015260640162000277565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b600080620002fb8484620001f6565b90506200031481604051806020016200019f9062000346565b949350505050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b61088180620003de83390190565b6000602082840312156200036757600080fd5b5035919050565b80356001600160a01b03811681146200038657600080fd5b919050565b600080604083850312156200039f57600080fd5b620003aa836200036e565b946020939093013593505050565b600060208284031215620003cb57600080fd5b620003d6826200036e565b939250505056fe608060405234801561001057600080fd5b50336001600160a01b03166359659e906040518163ffffffff1660e01b8152600401602060405180830381865afa15801561004f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610073919061046c565b604051806020016040528060008152506100958282600061009c60201b60201c565b5050610508565b6100a583610167565b6040516001600160a01b038416907f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e90600090a26000825111806100e65750805b1561016257610160836001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561012c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610150919061046c565b8361030a60201b6100291760201c565b505b505050565b61017a8161033660201b6100551760201c565b6101d95760405162461bcd60e51b815260206004820152602560248201527f455243313936373a206e657720626561636f6e206973206e6f74206120636f6e6044820152641d1c9858dd60da1b60648201526084015b60405180910390fd5b61024d816001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561021a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061023e919061046c565b61033660201b6100551760201c565b6102b25760405162461bcd60e51b815260206004820152603060248201527f455243313936373a20626561636f6e20696d706c656d656e746174696f6e206960448201526f1cc81b9bdd08184818dbdb9d1c9858dd60821b60648201526084016101d0565b806102e97fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5060001b61034560201b6100641760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b606061032f838360405180606001604052806027815260200161085a60279139610348565b9392505050565b6001600160a01b03163b151590565b90565b6060600080856001600160a01b03168560405161036591906104b9565b600060405180830381855af49150503d80600081146103a0576040519150601f19603f3d011682016040523d82523d6000602084013e6103a5565b606091505b5090925090506103b7868383876103c1565b9695505050505050565b60608315610430578251600003610429576001600160a01b0385163b6104295760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016101d0565b508161043a565b61043a8383610442565b949350505050565b8151156104525781518083602001fd5b8060405162461bcd60e51b81526004016101d091906104d5565b60006020828403121561047e57600080fd5b81516001600160a01b038116811461032f57600080fd5b60005b838110156104b0578181015183820152602001610498565b50506000910152565b600082516104cb818460208701610495565b9190910192915050565b60208152600082518060208401526104f4816040850160208701610495565b601f01601f19169190910160400192915050565b610343806105176000396000f3fe60806040523661001357610011610017565b005b6100115b610027610022610067565b610100565b565b606061004e83836040518060600160405280602781526020016102e760279139610124565b9392505050565b6001600160a01b03163b151590565b90565b600061009a7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50546001600160a01b031690565b6001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100fb919061024a565b905090565b3660008037600080366000845af43d6000803e80801561011f573d6000f35b3d6000fd5b6060600080856001600160a01b0316856040516101419190610297565b600060405180830381855af49150503d806000811461017c576040519150601f19603f3d011682016040523d82523d6000602084013e610181565b606091505b50915091506101928683838761019c565b9695505050505050565b6060831561020e578251600003610207576101b685610055565b6102075760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064015b60405180910390fd5b5081610218565b6102188383610220565b949350505050565b8151156102305781518083602001fd5b8060405162461bcd60e51b81526004016101fe91906102b3565b60006020828403121561025c57600080fd5b81516001600160a01b038116811461004e57600080fd5b60005b8381101561028e578181015183820152602001610276565b50506000910152565b600082516102a9818460208701610273565b9190910192915050565b60208152600082518060208401526102d2816040850160208701610273565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e9bed491ce4cc7495def60dc616a13f39ccd912637e0c8ba02d45400506de9c064736f6c63430008100033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220237fb73aef7b871fb5f0c1ef15f58a63a1b7502356cc319c2d83ad0717d2cd7264736f6c634300081000336080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea26469706673582212203fc1d73d12f631c8d0a3a1e5ebc667c0e6e4c09698b94f0ed53d99f55382279e64736f6c63430008100033608060405260405162000ebb38038062000ebb833981016040819052620000269162000497565b828162000036828260006200004d565b50620000449050826200008a565b505050620005ca565b6200005883620000e5565b600082511180620000665750805b1562000085576200008383836200012760201b620001691760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f620000b562000156565b604080516001600160a01b03928316815291841660208301520160405180910390a1620000e2816200018f565b50565b620000f08162000244565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606200014f838360405180606001604052806027815260200162000e9460279139620002f8565b9392505050565b60006200018060008051602062000e7483398151915260001b6200037760201b620001951760201c565b546001600160a01b0316919050565b6001600160a01b038116620001fa5760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b806200022360008051602062000e7483398151915260001b6200037760201b620001951760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6200025a816200037a60201b620001981760201c565b620002be5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401620001f1565b80620002237f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b6200037760201b620001951760201c565b6060600080856001600160a01b03168560405162000317919062000577565b600060405180830381855af49150503d806000811462000354576040519150601f19603f3d011682016040523d82523d6000602084013e62000359565b606091505b5090925090506200036d8683838762000389565b9695505050505050565b90565b6001600160a01b03163b151590565b60608315620003fd578251600003620003f5576001600160a01b0385163b620003f55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401620001f1565b508162000409565b62000409838362000411565b949350505050565b815115620004225781518083602001fd5b8060405162461bcd60e51b8152600401620001f1919062000595565b80516001600160a01b03811681146200045657600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156200048e57818101518382015260200162000474565b50506000910152565b600080600060608486031215620004ad57600080fd5b620004b8846200043e565b9250620004c8602085016200043e565b60408501519092506001600160401b0380821115620004e657600080fd5b818601915086601f830112620004fb57600080fd5b8151818111156200051057620005106200045b565b604051601f8201601f19908116603f011681019083821181831017156200053b576200053b6200045b565b816040528281528960208487010111156200055557600080fd5b6200056883602083016020880162000471565b80955050505050509250925092565b600082516200058b81846020870162000471565b9190910192915050565b6020815260008251806020840152620005b681604085016020870162000471565b601f01601f19169190910160400192915050565b61089a80620005da6000396000f3fe60806040523661001357610011610017565b005b6100115b61001f6101a7565b6001600160a01b0316330361015f5760606001600160e01b0319600035166364d3180d60e11b810161005a576100536101da565b9150610157565b63587086bd60e11b6001600160e01b031982160161007a57610053610231565b63070d7c6960e41b6001600160e01b031982160161009a57610053610277565b621eb96f60e61b6001600160e01b03198216016100b9576100536102a8565b63a39f25e560e01b6001600160e01b03198216016100d9576100536102e8565b60405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b815160208301f35b6101676102fc565b565b606061018e838360405180606001604052806027815260200161083e6027913961030c565b9392505050565b90565b6001600160a01b03163b151590565b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b60606101e4610384565b60006101f33660048184610691565b81019061020091906106d7565b905061021d8160405180602001604052806000815250600061038f565b505060408051602081019091526000815290565b60606000806102433660048184610691565b8101906102509190610708565b915091506102608282600161038f565b604051806020016040528060008152509250505090565b6060610281610384565b60006102903660048184610691565b81019061029d91906106d7565b905061021d816103bb565b60606102b2610384565b60006102bc6101a7565b604080516001600160a01b03831660208201529192500160405160208183030381529060405291505090565b60606102f2610384565b60006102bc610412565b610167610307610412565b610421565b6060600080856001600160a01b03168560405161032991906107ee565b600060405180830381855af49150503d8060008114610364576040519150601f19603f3d011682016040523d82523d6000602084013e610369565b606091505b509150915061037a86838387610445565b9695505050505050565b341561016757600080fd5b610398836104c4565b6000825111806103a55750805b156103b6576103b48383610169565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6103e46101a7565b604080516001600160a01b03928316815291841660208301520160405180910390a161040f81610504565b50565b600061041c6105ad565b905090565b3660008037600080366000845af43d6000803e808015610440573d6000f35b3d6000fd5b606083156104b25782516000036104ab5761045f85610198565b6104ab5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161014e565b50816104bc565b6104bc83836105d5565b949350505050565b6104cd816105ff565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b0381166105695760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b606482015260840161014e565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80546001600160a01b0319166001600160a01b039290921691909117905550565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6101cb565b8151156105e55781518083602001fd5b8060405162461bcd60e51b815260040161014e919061080a565b61060881610198565b61066a5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b606482015260840161014e565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61058c565b600080858511156106a157600080fd5b838611156106ae57600080fd5b5050820193919092039150565b80356001600160a01b03811681146106d257600080fd5b919050565b6000602082840312156106e957600080fd5b61018e826106bb565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561071b57600080fd5b610724836106bb565b9150602083013567ffffffffffffffff8082111561074157600080fd5b818501915085601f83011261075557600080fd5b813581811115610767576107676106f2565b604051601f8201601f19908116603f0116810190838211818310171561078f5761078f6106f2565b816040528281528860208487010111156107a857600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60005b838110156107e55781810151838201526020016107cd565b50506000910152565b600082516108008184602087016107ca565b9190910192915050565b60208152600082518060208401526108298160408501602087016107ca565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122072007c277cc5d8471be1434c6b2d5b70fb7c2f6f77a0697617733c4d4ae5b76964736f6c63430008100033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220377d031a5f294a74596aa9acc30ca418bd32dd3f9cbae6ed71ce8cc1f17439cc64736f6c63430008100033", "beneficiary": "0xbee947aec820389c1560c87bd96e2723bad05b61", "maxSubmissionFee": "0xd72a2471400" }, { "blockHash": "0xc4cfe190b18fa6cfc6f80e5e0df16fdc78c054e750b98caf952443e653cc5049", "blockNumber": "0x1", "from": "0xc573c69f8f638d2954c9618b03765fc17701a1e0", "gas": "0x5b8d80", "gasPrice": "0x5f5e100", "maxFeePerGas": "0x5f5e100", "hash": "0x2f9350314c5271853749c318c9621e57c30d22836f5023e0052004bdbc1e1492", "input": "0x608060405234801561001057600080fd5b50615f3a806100206000396000f3fe60806040523480156200001157600080fd5b50600436106200002e5760003560e01c8063b1c7a8701462000033575b600080fd5b6200004a6200004436600462001528565b6200004c565b005b6000620000b9620000866040518060400160405280601181526020017027b93134ba2619283937bc3ca0b236b4b760791b81525062000304565b6040516200009760208201620014b8565b6020820181038252601f19601f8201166040525080519060200120306200033a565b90506001600160a01b0381163b15620000e5576040516377e0068560e11b815260040160405180910390fd5b5060006200011c6040518060400160405280601181526020017027b93134ba2619283937bc3ca0b236b4b760791b81525062000304565b6040516200012a90620014b8565b8190604051809103906000f59050801580156200014b573d6000803e3d6000fd5b50905060006200016c6200016360a08d018d62001603565b86858762000364565b9050600062000189620001808d8062001603565b8d898762000689565b9050620001a86200019e60208e018e62001603565b8c8487876200088c565b620001c4620001bb60408e018e62001603565b8b848762000cb4565b6001600160a01b03881615620002035762000203620001e760608e018e62001603565b8e8060800190620001f9919062001603565b8c8c878a62000ef4565b6200029360006200023c6040518060400160405280601081526020016f13dc989a5d130c935d5b1d1a58d85b1b60821b81525062000304565b6200028d8f8060c0019062000252919062001603565b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250620012ee92505050565b6200131c565b5060405163f2fde38b60e01b81526001600160a01b0384169063f2fde38b90620002c290859060040162001654565b600060405180830381600087803b158015620002dd57600080fd5b505af1158015620002f2573d6000803e3d6000fd5b50505050505050505050505050505050565b60008146336040516020016200031d939291906200168e565b604051602081830303815290604052805190602001209050919050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b600080620003f084620003ac6040518060400160405280601b81526020017f4f726269744c32557067726164654578656375746f7250726f7879000000000081525062000304565b620003ea6040518060400160405280601b81526020017a4f726269744c32557067726164654578656375746f724c6f67696360281b81525062000304565b6200142d565b90506000620004796000620004386040518060400160405280601b81526020017a4f726269744c32557067726164654578656375746f724c6f67696360281b81525062000304565b6200028d8b8b8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250620012ee92505050565b60405163266a23b160e21b81529091506001600160a01b038616906399a88ec490620004ac9085908590600401620016c9565b600060405180830381600087803b158015620004c757600080fd5b505af1158015620004dc573d6000803e3d6000fd5b5060009250829150620004ec9050565b60405190808252806020026020018201604052801562000516578160200160208202803683370190505b5060405163251b648160e21b81529091506001600160a01b0383169063946d9204906200054c9061dead908590600401620016e3565b600060405180830381600087803b1580156200056757600080fd5b505af11580156200057c573d6000803e3d6000fd5b5060009250600291506200058d9050565b604051908082528060200260200182016040528015620005b7578160200160208202803683370190505b5090508781600081518110620005d157620005d162001741565b60200260200101906001600160a01b031690816001600160a01b031681525050858160018151811062000608576200060862001741565b6001600160a01b03928316602091820292909201015260405163251b648160e21b81529085169063946d920490620006479087908590600401620016e3565b600060405180830381600087803b1580156200066257600080fd5b505af115801562000677573d6000803e3d6000fd5b50959c9b505050505050505050505050565b6000806200070983620006cd604051806040016040528060198152602001784f726269744c3247617465776179526f7574657250726f787960381b81525062000304565b620003ea604051806040016040528060198152602001784f726269744c3247617465776179526f757465724c6f67696360381b81525062000304565b905060006200074f600062000438604051806040016040528060198152602001784f726269744c3247617465776179526f757465724c6f67696360381b81525062000304565b60405163266a23b160e21b81529091506001600160a01b038516906399a88ec490620007829085908590600401620016c9565b600060405180830381600087803b1580156200079d57600080fd5b505af1158015620007b2573d6000803e3d6000fd5b505060405163485cc95560e01b81526001600160a01b038416925063485cc9559150620007e89061dead908190600401620016c9565b600060405180830381600087803b1580156200080357600080fd5b505af115801562000818573d6000803e3d6000fd5b505060405163485cc95560e01b81526001600160a01b038516925063485cc95591506200084c9089908990600401620016c9565b600060405180830381600087803b1580156200086757600080fd5b505af11580156200087c573d6000803e3d6000fd5b50939a9950505050505050505050565b60006200091183620008d36040518060400160405280601b81526020017f4f726269744c325374616e646172644761746577617950726f7879000000000081525062000304565b620003ea6040518060400160405280601b81526020017a4f726269744c325374616e64617264476174657761794c6f67696360281b81525062000304565b90506000620009596000620004386040518060400160405280601b81526020017a4f726269744c325374616e64617264476174657761794c6f67696360281b81525062000304565b60405163266a23b160e21b81529091506001600160a01b038516906399a88ec4906200098c9085908590600401620016c9565b600060405180830381600087803b158015620009a757600080fd5b505af1158015620009bc573d6000803e3d6000fd5b505060405163c0c53b8b60e01b81526001600160a01b038416925063c0c53b8b9150620009f49061dead908190819060040162001757565b600060405180830381600087803b15801562000a0f57600080fd5b505af115801562000a24573d6000803e3d6000fd5b50505050600062000a626040518060400160405280601581526020017404f726269745374616e64617264417262455243323605c1b81525062000304565b60405162000a7090620014c6565b8190604051809103906000f590508015801562000a91573d6000803e3d6000fd5b509050600062000acf6040518060400160405280601681526020017527b93134ba2ab833b930b232b0b13632a132b0b1b7b760511b81525062000304565b8260405162000ade90620014d4565b62000aea919062001654565b8190604051809103906000f590508015801562000b0b573d6000803e3d6000fd5b509050600062000b4a604051806040016040528060178152602001764f72626974426561636f6e50726f7879466163746f727960481b81525062000304565b60405162000b5890620014e2565b8190604051809103906000f590508015801562000b79573d6000803e3d6000fd5b5060405163189acdbd60e31b81529091506001600160a01b0382169063c4d66de89062000bab90859060040162001654565b600060405180830381600087803b15801562000bc657600080fd5b505af115801562000bdb573d6000803e3d6000fd5b505060405163c0c53b8b60e01b81526001600160a01b038816925063c0c53b8b915062000c11908c908c90869060040162001757565b600060405180830381600087803b15801562000c2c57600080fd5b505af115801562000c41573d6000803e3d6000fd5b505060405163f2fde38b60e01b81526001600160a01b038516925063f2fde38b915062000c7390899060040162001654565b600060405180830381600087803b15801562000c8e57600080fd5b505af115801562000ca3573d6000803e3d6000fd5b505050505050505050505050505050565b600062000d338262000cf7604051806040016040528060198152602001784f726269744c32437573746f6d4761746577617950726f787960381b81525062000304565b620003ea604051806040016040528060198152602001784f726269744c32437573746f6d476174657761794c6f67696360381b81525062000304565b9050600062000dba600062000d79604051806040016040528060198152602001784f726269744c32437573746f6d476174657761794c6f67696360381b81525062000304565b6200028d8a8a8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250620012ee92505050565b60405163266a23b160e21b81529091506001600160a01b038416906399a88ec49062000ded9085908590600401620016c9565b600060405180830381600087803b15801562000e0857600080fd5b505af115801562000e1d573d6000803e3d6000fd5b505060405163485cc95560e01b81526001600160a01b038416925063485cc955915062000e539061dead908190600401620016c9565b600060405180830381600087803b15801562000e6e57600080fd5b505af115801562000e83573d6000803e3d6000fd5b505060405163485cc95560e01b81526001600160a01b038516925063485cc955915062000eb79088908890600401620016c9565b600060405180830381600087803b15801562000ed257600080fd5b505af115801562000ee7573d6000803e3d6000fd5b5050505050505050505050565b600062000f5c8262000f2e6040518060400160405280601081526020016f4f726269744c325745544850726f787960801b81525062000304565b620003ea6040518060400160405280600b81526020016a09ee4c4d2e89864ae8aa8960ab1b81525062000304565b9050600062000f946000620004386040518060400160405280600b81526020016a09ee4c4d2e89864ae8aa8960ab1b81525062000304565b60405163266a23b160e21b81529091506001600160a01b038416906399a88ec49062000fc79085908590600401620016c9565b600060405180830381600087803b15801562000fe257600080fd5b505af115801562000ff7573d6000803e3d6000fd5b50505050600062001076846200103c604051806040016040528060178152602001764f726269744c32576574684761746577617950726f787960481b81525062000304565b620003ea604051806040016040528060178152602001764f726269744c3257657468476174657761794c6f67696360481b81525062000304565b90506000620010fb6000620010ba604051806040016040528060178152602001764f726269744c3257657468476174657761794c6f67696360481b81525062000304565b6200028d8f8f8080601f016020809104026020016040519081016040528093929190818152602001838380828437600092019190915250620012ee92505050565b60405163266a23b160e21b81529091506001600160a01b038616906399a88ec4906200112e9085908590600401620016c9565b600060405180830381600087803b1580156200114957600080fd5b505af11580156200115e573d6000803e3d6000fd5b5050604051637c643b2f60e11b81526001600160a01b038416925063f8c8765e9150620011989061dead908190819081906004016200177a565b600060405180830381600087803b158015620011b357600080fd5b505af1158015620011c8573d6000803e3d6000fd5b5050604051637c643b2f60e11b81526001600160a01b038516925063f8c8765e915062001200908b908a908c908a906004016200177a565b600060405180830381600087803b1580156200121b57600080fd5b505af115801562001230573d6000803e3d6000fd5b505060405163641078a360e11b815260a06004820152600060a4820181905260c0602483015260c48201819052604482015261dead6064820181905260848201526001600160a01b038616925063c820f146915060e401600060405180830381600087803b158015620012a257600080fd5b505af1158015620012b7573d6000803e3d6000fd5b505060405163641078a360e11b81526001600160a01b038716925063c820f1469150620002c29060129086908c90600401620017bc565b6060815182604051602001620013069291906200180b565b6040516020818303038152906040529050919050565b600083471015620013745760405162461bcd60e51b815260206004820152601d60248201527f437265617465323a20696e73756666696369656e742062616c616e636500000060448201526064015b60405180910390fd5b8151600003620013c75760405162461bcd60e51b815260206004820181905260248201527f437265617465323a2062797465636f6465206c656e677468206973207a65726f60448201526064016200136b565b8282516020840186f590506001600160a01b038116620014265760405162461bcd60e51b8152602060048201526019602482015278437265617465323a204661696c6564206f6e206465706c6f7960381b60448201526064016200136b565b9392505050565b600082826040516200143f90620014f0565b8190604051809103906000f590508015801562001460573d6000803e3d6000fd5b5085604051806020016040528060008152506040516200148090620014fd565b6200148e939291906200187b565b8190604051809103906000f5905080158015620014af573d6000803e3d6000fd5b50949350505050565b61070f80620018cb83390190565b611e7e8062001fda83390190565b6104e28062003e5883390190565b610cb4806200433a83390190565b605c8062004fee83390190565b610ebb806200504a83390190565b80356001600160a01b03811681146200152357600080fd5b919050565b60008060008060008060008060006101208a8c0312156200154857600080fd5b893567ffffffffffffffff8111156200156057600080fd5b8a0160e0818d0312156200157357600080fd5b98506200158360208b016200150b565b97506200159360408b016200150b565b9650620015a360608b016200150b565b9550620015b360808b016200150b565b9450620015c360a08b016200150b565b9350620015d360c08b016200150b565b9250620015e360e08b016200150b565b9150620015f46101008b016200150b565b90509295985092959850929598565b6000808335601e198436030181126200161b57600080fd5b83018035915067ffffffffffffffff8211156200163757600080fd5b6020019150368190038213156200164d57600080fd5b9250929050565b6001600160a01b0391909116815260200190565b60005b83811015620016855781810151838201526020016200166b565b50506000910152565b60008451620016a281846020890162001668565b919091019283525060601b6bffffffffffffffffffffffff19166020820152603401919050565b6001600160a01b0392831681529116602082015260400190565b6001600160a01b038381168252604060208084018290528451918401829052600092858201929091906060860190855b818110156200173357855185168352948301949183019160010162001713565b509098975050505050505050565b634e487b7160e01b600052603260045260246000fd5b6001600160a01b0393841681529183166020830152909116604082015260600190565b6001600160a01b03948516815292841660208401529083166040830152909116606082015260800190565b60048152630ae8aa8960e31b602082015260400190565b60a081526000620017d060a08301620017a5565b8281036020840152620017e381620017a5565b60ff96909616604084015250506001600160a01b039283166060820152911660809091015290565b710608060405234801561001057600080fd5b560741b8152606160f81b601282015260f083901b6001600160f01b03191660138201526a4030801030001cb00079ff60a91b601582015281516000906200186d81602080860190870162001668565b919091016020019392505050565b600060018060a01b038086168352808516602084015250606060408301528251806060840152620018b481608085016020870162001668565b601f01601f19169190910160800194935050505056fe608060405234801561001057600080fd5b5061001a3361001f565b61006f565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6106918061007e6000396000f3fe60806040526004361061006b5760003560e01c8063204e1c7a14610070578063715018a6146100a65780637eff275e146100bd5780638da5cb5b146100dd5780639623609d146100fb57806399a88ec41461010e578063f2fde38b1461012e578063f3b7dead1461014e575b600080fd5b34801561007c57600080fd5b5061009061008b366004610483565b61016e565b60405161009d91906104a7565b60405180910390f35b3480156100b257600080fd5b506100bb6101ff565b005b3480156100c957600080fd5b506100bb6100d83660046104bb565b610213565b3480156100e957600080fd5b506000546001600160a01b0316610090565b6100bb61010936600461050a565b61027d565b34801561011a57600080fd5b506100bb6101293660046104bb565b6102ec565b34801561013a57600080fd5b506100bb610149366004610483565b610320565b34801561015a57600080fd5b50610090610169366004610483565b61039e565b6000806000836001600160a01b031660405161019490635c60da1b60e01b815260040190565b600060405180830381855afa9150503d80600081146101cf576040519150601f19603f3d011682016040523d82523d6000602084013e6101d4565b606091505b5091509150816101e357600080fd5b808060200190518101906101f791906105e0565b949350505050565b6102076103c4565b610211600061041e565b565b61021b6103c4565b6040516308f2839760e41b81526001600160a01b03831690638f283970906102479084906004016104a7565b600060405180830381600087803b15801561026157600080fd5b505af1158015610275573d6000803e3d6000fd5b505050505050565b6102856103c4565b60405163278f794360e11b81526001600160a01b03841690634f1ef2869034906102b590869086906004016105fd565b6000604051808303818588803b1580156102ce57600080fd5b505af11580156102e2573d6000803e3d6000fd5b5050505050505050565b6102f46103c4565b604051631b2ce7f360e11b81526001600160a01b03831690633659cfe6906102479084906004016104a7565b6103286103c4565b6001600160a01b0381166103925760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61039b8161041e565b50565b6000806000836001600160a01b0316604051610194906303e1469160e61b815260040190565b6000546001600160a01b031633146102115760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610389565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6001600160a01b038116811461039b57600080fd5b60006020828403121561049557600080fd5b81356104a08161046e565b9392505050565b6001600160a01b0391909116815260200190565b600080604083850312156104ce57600080fd5b82356104d98161046e565b915060208301356104e98161046e565b809150509250929050565b634e487b7160e01b600052604160045260246000fd5b60008060006060848603121561051f57600080fd5b833561052a8161046e565b9250602084013561053a8161046e565b9150604084013567ffffffffffffffff8082111561055757600080fd5b818601915086601f83011261056b57600080fd5b81358181111561057d5761057d6104f4565b604051601f8201601f19908116603f011681019083821181831017156105a5576105a56104f4565b816040528281528960208487010111156105be57600080fd5b8260208601602083013760006020848301015280955050505050509250925092565b6000602082840312156105f257600080fd5b81516104a08161046e565b60018060a01b038316815260006020604081840152835180604085015260005b818110156106395785810183015185820160600152820161061d565b506000606082860101526060601f19601f83011685010192505050939250505056fea26469706673582212208a2d9d6b4833462246845f0898b3da66a9086b54eb9fb2d870a9780af5c177f664736f6c6343000810003360806040523480156200001157600080fd5b50600054610100900460ff1615808015620000335750600054600160ff909116105b8062000063575062000050306200015060201b620007b71760201c565b15801562000063575060005460ff166001145b620000cb5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840160405180910390fd5b6000805460ff191660011790558015620000ef576000805461ff0019166101001790555b801562000136576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b5060cd805460ff60a01b1916600160a01b1790556200015f565b6001600160a01b03163b151590565b611d0f806200016f6000396000f3fe608060405234801561001057600080fd5b50600436106101375760003560e01c806370a08231116100b857806395d89b411161007c57806395d89b411461029e578063a457c2d7146102a6578063a9059cbb146102b9578063c2eeeebd146102cc578063d505accf146102df578063dd62ed3e146102f257600080fd5b806370a082311461021157806374f4f5471461023a5780637ecebe001461024d5780638c2a993e146102605780638fa74a0e1461027357600080fd5b8063313ce567116100ff578063313ce567146101b75780633644e515146101d157806339509351146101d95780634000aea0146101ec5780636f791d29146101ff57600080fd5b806306fdde031461013c578063095ea7b31461015a57806318160ddd1461017d578063189db7d21461018f57806323b872dd146101a4575b600080fd5b610144610305565b604051610151919061162e565b60405180910390f35b61016d61016836600461165d565b61032a565b6040519015158152602001610151565b6035545b604051908152602001610151565b6101a261019d36600461174a565b610344565b005b61016d6101b2366004611797565b6103fb565b6101bf610421565b60405160ff9091168152602001610151565b61018161043e565b61016d6101e736600461165d565b610448565b61016d6101fa3660046117d3565b61046a565b60cd54600160a01b900460ff1661016d565b61018161021f366004611829565b6001600160a01b031660009081526033602052604090205490565b6101a261024836600461165d565b6104e0565b61018161025b366004611829565b610521565b6101a261026e36600461165d565b61053f565b60cc54610286906001600160a01b031681565b6040516001600160a01b039091168152602001610151565b610144610573565b61016d6102b436600461165d565b610594565b61016d6102c736600461165d565b61061a565b60cd54610286906001600160a01b031681565b6101a26102ed366004611844565b610628565b6101816103003660046118b7565b61078c565b60ce54606090610100900460ff161561031d57600080fd5b6103256107c6565b905090565b600033610338818585610858565b60019150505b92915050565b60008060008380602001905181019061035d919061193a565b92509250925060008061036f8561097d565b9150915060008061037f8661097d565b9150915060008061038f87610b06565b915091506103a0858483338f610b55565b506040805160608101825291158083529515602083018190529315910181905260ce805461ffff191661ff0019909616959095176101009093029290921762ff00001916620100009092029190911790925550505050505050565b600033610409858285610c27565b610414858585610ca1565b60019150505b9392505050565b60ce5460009060ff161561043457600080fd5b5060385460ff1690565b6000610325610e3a565b60003361033881858561045b838361078c565b61046591906119cd565b610858565b6000610476848461061a565b50836001600160a01b0316336001600160a01b03167fe19260aff97b920c7df27010903aeb9c8d2be5d310a2c67824cf3f15396e4c1685856040516104bc9291906119e0565b60405180910390a3833b156104d6576104d6848484610eb5565b5060019392505050565b60cc546001600160a01b031633146105135760405162461bcd60e51b815260040161050a90611a01565b60405180910390fd5b61051d8282610f1f565b5050565b6001600160a01b03811660009081526099602052604081205461033e565b60cc546001600160a01b031633146105695760405162461bcd60e51b815260040161050a90611a01565b61051d828261103e565b60ce5460609062010000900460ff161561058c57600080fd5b6103256110ed565b600033816105a2828661078c565b9050838110156106025760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b606482015260840161050a565b61060f8286868403610858565b506001949350505050565b600033610338818585610ca1565b834211156106785760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e65000000604482015260640161050a565b60007f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886106a78c6110fc565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e001604051602081830303815290604052805190602001209050600061070282611124565b9050600061071282878787611172565b9050896001600160a01b0316816001600160a01b0316146107755760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e61747572650000604482015260640161050a565b6107808a8a8a610858565b50505050505050505050565b6001600160a01b03918216600090815260346020908152604080832093909416825291909152205490565b6001600160a01b03163b151590565b6060603680546107d590611a27565b80601f016020809104026020016040519081016040528092919081815260200182805461080190611a27565b801561084e5780601f106108235761010080835404028352916020019161084e565b820191906000526020600020905b81548152906001019060200180831161083157829003601f168201915b5050505050905090565b6001600160a01b0383166108ba5760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b606482015260840161050a565b6001600160a01b03821661091b5760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b606482015260840161050a565b6001600160a01b0383811660008181526034602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92591015b60405180910390a3505050565b6000606082516000036109935760009150915091565b8251602003610ae65782516000908490601f9081106109b4576109b4611a5b565b01602001516001600160f81b031916146109d15760009150915091565b6001915060205b600081118015610a1257506000846109f1600184611a71565b81518110610a0157610a01611a5b565b01602001516001600160f81b031916145b15610a295780610a2181611a84565b9150506109d8565b6000816001600160401b03811115610a4357610a43611687565b6040519080825280601f01601f191660200182016040528015610a6d576020820181803683370190505b50905060005b828160ff161015610adc57858160ff1681518110610a9357610a93611a5b565b602001015160f81c60f81b828260ff1681518110610ab357610ab3611a5b565b60200101906001600160f81b031916908160001a90535080610ad481611a9b565b915050610a73565b509150610b019050565b6001915082806020019051810190610afe9190611aba565b90505b915091565b6000808251602014610b1d57506000928392509050565b600083806020019051810190610b339190611b02565b905060ff811115610b4a5750600093849350915050565b600194909350915050565b6001600160a01b038216610b9d5760405162461bcd60e51b815260206004820152600f60248201526e494e56414c49445f4741544557415960881b604482015260640161050a565b60cc546001600160a01b031615610be55760405162461bcd60e51b815260206004820152600c60248201526b1053149150511657d253925560a21b604482015260640161050a565b60cc80546001600160a01b038085166001600160a01b03199283161790925560cd805492841692909116919091179055610c2085858561119a565b5050505050565b6000610c33848461078c565b90506000198114610c9b5781811015610c8e5760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e6365000000604482015260640161050a565b610c9b8484848403610858565b50505050565b6001600160a01b038316610d055760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b606482015260840161050a565b6001600160a01b038216610d675760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b606482015260840161050a565b6001600160a01b03831660009081526033602052604090205481811015610ddf5760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b606482015260840161050a565b6001600160a01b038085166000818152603360205260408082208686039055928616808252908390208054860190559151600080516020611cba83398151915290610e2d9086815260200190565b60405180910390a3610c9b565b60006103257f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f610e6960655490565b6066546040805160208101859052908101839052606081018290524660808201523060a082015260009060c0016040516020818303038152906040528051906020012090509392505050565b604051635260769b60e11b815283906001600160a01b0382169063a4c0ed3690610ee790339087908790600401611b1b565b600060405180830381600087803b158015610f0157600080fd5b505af1158015610f15573d6000803e3d6000fd5b5050505050505050565b6001600160a01b038216610f7f5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b606482015260840161050a565b6001600160a01b03821660009081526033602052604090205481811015610ff35760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b606482015260840161050a565b6001600160a01b0383166000818152603360209081526040808320868603905560358054879003905551858152919291600080516020611cba8339815191529101610970565b505050565b6001600160a01b0382166110945760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f206164647265737300604482015260640161050a565b80603560008282546110a691906119cd565b90915550506001600160a01b038216600081815260336020908152604080832080548601905551848152600080516020611cba833981519152910160405180910390a35050565b6060603780546107d590611a27565b6001600160a01b03811660009081526099602052604090208054600181018255905b50919050565b600061033e611131610e3a565b8360405161190160f01b6020820152602281018390526042810182905260009060620160405160208183030381529060405280519060200120905092915050565b6000806000611183878787876112ce565b9150915061119081611388565b5095945050505050565b600054610100900460ff16158080156111ba5750600054600160ff909116105b806111db57506111c9306107b7565b1580156111db575060005460ff166001145b61123e5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b606482015260840161050a565b6000805460ff191660011790558015611261576000805461ff0019166101001790555b61126a846114d0565b611274848461151a565b6038805460ff191660ff84161790558015610c9b576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150505050565b6000806fa2a8918ca85bafe22016d0b997e4df60600160ff1b038311156112fb575060009050600361137f565b6040805160008082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa15801561134f573d6000803e3d6000fd5b5050604051601f1901519150506001600160a01b0381166113785760006001925092505061137f565b9150600090505b94509492505050565b600081600481111561139c5761139c611b4b565b036113a45750565b60018160048111156113b8576113b8611b4b565b036114005760405162461bcd60e51b815260206004820152601860248201527745434453413a20696e76616c6964207369676e617475726560401b604482015260640161050a565b600281600481111561141457611414611b4b565b036114615760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e67746800604482015260640161050a565b600381600481111561147557611475611b4b565b036114cd5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b606482015260840161050a565b50565b600054610100900460ff166114f75760405162461bcd60e51b815260040161050a90611b61565b6114cd81604051806040016040528060018152602001603160f81b81525061154b565b600054610100900460ff166115415760405162461bcd60e51b815260040161050a90611b61565b61051d828261158c565b600054610100900460ff166115725760405162461bcd60e51b815260040161050a90611b61565b815160209283012081519190920120606591909155606655565b600054610100900460ff166115b35760405162461bcd60e51b815260040161050a90611b61565b60366115bf8382611bfa565b5060376115cc8282611bfa565b50506038805460ff1916601217905550565b60005b838110156115f95781810151838201526020016115e1565b50506000910152565b6000815180845261161a8160208601602086016115de565b601f01601f19169290920160200192915050565b60208152600061041a6020830184611602565b80356001600160a01b038116811461165857600080fd5b919050565b6000806040838503121561167057600080fd5b61167983611641565b946020939093013593505050565b634e487b7160e01b600052604160045260246000fd5b604051601f8201601f191681016001600160401b03811182821017156116c5576116c5611687565b604052919050565b60006001600160401b038211156116e6576116e6611687565b50601f01601f191660200190565b600082601f83011261170557600080fd5b8135611718611713826116cd565b61169d565b81815284602083860101111561172d57600080fd5b816020850160208301376000918101602001919091529392505050565b6000806040838503121561175d57600080fd5b61176683611641565b915060208301356001600160401b0381111561178157600080fd5b61178d858286016116f4565b9150509250929050565b6000806000606084860312156117ac57600080fd5b6117b584611641565b92506117c360208501611641565b9150604084013590509250925092565b6000806000606084860312156117e857600080fd5b6117f184611641565b92506020840135915060408401356001600160401b0381111561181357600080fd5b61181f868287016116f4565b9150509250925092565b60006020828403121561183b57600080fd5b61041a82611641565b600080600080600080600060e0888a03121561185f57600080fd5b61186888611641565b965061187660208901611641565b95506040880135945060608801359350608088013560ff8116811461189a57600080fd5b9699959850939692959460a0840135945060c09093013592915050565b600080604083850312156118ca57600080fd5b6118d383611641565b91506118e160208401611641565b90509250929050565b60006118f8611713846116cd565b905082815283838301111561190c57600080fd5b61041a8360208301846115de565b600082601f83011261192b57600080fd5b61041a838351602085016118ea565b60008060006060848603121561194f57600080fd5b83516001600160401b038082111561196657600080fd5b6119728783880161191a565b9450602086015191508082111561198857600080fd5b6119948783880161191a565b935060408601519150808211156119aa57600080fd5b5061181f8682870161191a565b634e487b7160e01b600052601160045260246000fd5b8082018082111561033e5761033e6119b7565b8281526040602082015260006119f96040830184611602565b949350505050565b6020808252600c908201526b4f4e4c595f4741544557415960a01b604082015260600190565b600181811c90821680611a3b57607f821691505b60208210810361111e57634e487b7160e01b600052602260045260246000fd5b634e487b7160e01b600052603260045260246000fd5b8181038181111561033e5761033e6119b7565b600081611a9357611a936119b7565b506000190190565b600060ff821660ff8103611ab157611ab16119b7565b60010192915050565b600060208284031215611acc57600080fd5b81516001600160401b03811115611ae257600080fd5b8201601f81018413611af357600080fd5b6119f9848251602084016118ea565b600060208284031215611b1457600080fd5b5051919050565b60018060a01b0384168152826020820152606060408201526000611b426060830184611602565b95945050505050565b634e487b7160e01b600052602160045260246000fd5b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b601f82111561103957600081815260208120601f850160051c81016020861015611bd35750805b601f850160051c820191505b81811015611bf257828155600101611bdf565b505050505050565b81516001600160401b03811115611c1357611c13611687565b611c2781611c218454611a27565b84611bac565b602080601f831160018114611c5c5760008415611c445750858301515b600019600386901b1c1916600185901b178555611bf2565b600085815260208120601f198616915b82811015611c8b57888601518255948401946001909101908401611c6c565b5085821015611ca95787850151600019600388901b60f8161c191681555b5050505050600190811b0190555056feddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3efa264697066735822122070845537e703bebbfe17ccc1f6f77686f42e5101a18b732d87a34447165dfe1164736f6c63430008100033608060405234801561001057600080fd5b506040516104e23803806104e283398101604081905261002f91610151565b61003833610047565b61004181610097565b50610181565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6100aa8161014260201b6101a01760201c565b6101205760405162461bcd60e51b815260206004820152603360248201527f5570677261646561626c65426561636f6e3a20696d706c656d656e746174696f60448201527f6e206973206e6f74206120636f6e747261637400000000000000000000000000606482015260840160405180910390fd5b600180546001600160a01b0319166001600160a01b0392909216919091179055565b6001600160a01b03163b151590565b60006020828403121561016357600080fd5b81516001600160a01b038116811461017a57600080fd5b9392505050565b610352806101906000396000f3fe608060405234801561001057600080fd5b50600436106100575760003560e01c80633659cfe61461005c5780635c60da1b14610071578063715018a61461009a5780638da5cb5b146100a2578063f2fde38b146100b3575b600080fd5b61006f61006a3660046102ec565b6100c6565b005b6001546001600160a01b03165b6040516001600160a01b03909116815260200160405180910390f35b61006f61010e565b6000546001600160a01b031661007e565b61006f6100c13660046102ec565b610122565b6100ce6101af565b6100d781610209565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6101166101af565b610120600061029c565b565b61012a6101af565b6001600160a01b0381166101945760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b61019d8161029c565b50565b6001600160a01b03163b151590565b6000546001600160a01b031633146101205760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e6572604482015260640161018b565b610212816101a0565b61027a5760405162461bcd60e51b815260206004820152603360248201527f5570677261646561626c65426561636f6e3a20696d706c656d656e746174696f6044820152721b881a5cc81b9bdd08184818dbdb9d1c9858dd606a1b606482015260840161018b565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b600080546001600160a01b038381166001600160a01b0319831681178455604051919092169283917f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e09190a35050565b6000602082840312156102fe57600080fd5b81356001600160a01b038116811461031557600080fd5b939250505056fea264697066735822122059e4b83111825434711a238db6203ec67d74a40640c8c22ba30c6fc5fc18964a64736f6c63430008100033608060405234801561001057600080fd5b50610c94806100206000396000f3fe60806040523480156200001157600080fd5b5060043610620000765760003560e01c806329a5c5cf146200007b578063396a5f9514620000af57806359659e9014620000c657806397881f8d14620000da578063b3e3bf4214620000f3578063c4d66de8146200010a578063e75b21411462000123575b600080fd5b620000926200008c36600462000354565b6200013a565b6040516001600160a01b0390911681526020015b60405180910390f35b62000092620000c036600462000354565b62000186565b60005462000092906001600160a01b031681565b620000e4620001c7565b604051908152602001620000a6565b620000e4620001043660046200038b565b620001f6565b620001216200011b366004620003b8565b62000233565b005b62000092620001343660046200038b565b620002ec565b600080620001493384620001f6565b90506000816040516200015c9062000346565b8190604051809103906000f59050801580156200017d573d6000803e3d6000fd5b50949350505050565b6000620001c182604051806020016200019f9062000346565b6020820181038252601f19601f8201166040525080519060200120306200031c565b92915050565b604051620001d86020820162000346565b6020820181038252601f19601f820116604052508051906020012081565b604080516001600160a01b038416602082015290810182905260009060600160405160208183030381529060405280519060200120905092915050565b6001600160a01b038116620002805760405162461bcd60e51b815260206004820152600e60248201526d24a72b20a624a22fa122a0a1a7a760911b60448201526064015b60405180910390fd5b6000546001600160a01b031615620002ca5760405162461bcd60e51b815260206004820152600c60248201526b1053149150511657d253925560a21b604482015260640162000277565b600080546001600160a01b0319166001600160a01b0392909216919091179055565b600080620002fb8484620001f6565b90506200031481604051806020016200019f9062000346565b949350505050565b6000604051836040820152846020820152828152600b8101905060ff815360559020949350505050565b61088180620003de83390190565b6000602082840312156200036757600080fd5b5035919050565b80356001600160a01b03811681146200038657600080fd5b919050565b600080604083850312156200039f57600080fd5b620003aa836200036e565b946020939093013593505050565b600060208284031215620003cb57600080fd5b620003d6826200036e565b939250505056fe608060405234801561001057600080fd5b50336001600160a01b03166359659e906040518163ffffffff1660e01b8152600401602060405180830381865afa15801561004f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610073919061046c565b604051806020016040528060008152506100958282600061009c60201b60201c565b5050610508565b6100a583610167565b6040516001600160a01b038416907f1cf3b03a6cf19fa2baba4df148e9dcabedea7f8a5c07840e207e5c089be95d3e90600090a26000825111806100e65750805b1561016257610160836001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561012c573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610150919061046c565b8361030a60201b6100291760201c565b505b505050565b61017a8161033660201b6100551760201c565b6101d95760405162461bcd60e51b815260206004820152602560248201527f455243313936373a206e657720626561636f6e206973206e6f74206120636f6e6044820152641d1c9858dd60da1b60648201526084015b60405180910390fd5b61024d816001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561021a573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061023e919061046c565b61033660201b6100551760201c565b6102b25760405162461bcd60e51b815260206004820152603060248201527f455243313936373a20626561636f6e20696d706c656d656e746174696f6e206960448201526f1cc81b9bdd08184818dbdb9d1c9858dd60821b60648201526084016101d0565b806102e97fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d5060001b61034560201b6100641760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b606061032f838360405180606001604052806027815260200161085a60279139610348565b9392505050565b6001600160a01b03163b151590565b90565b6060600080856001600160a01b03168560405161036591906104b9565b600060405180830381855af49150503d80600081146103a0576040519150601f19603f3d011682016040523d82523d6000602084013e6103a5565b606091505b5090925090506103b7868383876103c1565b9695505050505050565b60608315610430578251600003610429576001600160a01b0385163b6104295760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016101d0565b508161043a565b61043a8383610442565b949350505050565b8151156104525781518083602001fd5b8060405162461bcd60e51b81526004016101d091906104d5565b60006020828403121561047e57600080fd5b81516001600160a01b038116811461032f57600080fd5b60005b838110156104b0578181015183820152602001610498565b50506000910152565b600082516104cb818460208701610495565b9190910192915050565b60208152600082518060208401526104f4816040850160208701610495565b601f01601f19169190910160400192915050565b610343806105176000396000f3fe60806040523661001357610011610017565b005b6100115b610027610022610067565b610100565b565b606061004e83836040518060600160405280602781526020016102e760279139610124565b9392505050565b6001600160a01b03163b151590565b90565b600061009a7fa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50546001600160a01b031690565b6001600160a01b0316635c60da1b6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156100d7573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906100fb919061024a565b905090565b3660008037600080366000845af43d6000803e80801561011f573d6000f35b3d6000fd5b6060600080856001600160a01b0316856040516101419190610297565b600060405180830381855af49150503d806000811461017c576040519150601f19603f3d011682016040523d82523d6000602084013e610181565b606091505b50915091506101928683838761019c565b9695505050505050565b6060831561020e578251600003610207576101b685610055565b6102075760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064015b60405180910390fd5b5081610218565b6102188383610220565b949350505050565b8151156102305781518083602001fd5b8060405162461bcd60e51b81526004016101fe91906102b3565b60006020828403121561025c57600080fd5b81516001600160a01b038116811461004e57600080fd5b60005b8381101561028e578181015183820152602001610276565b50506000910152565b600082516102a9818460208701610273565b9190910192915050565b60208152600082518060208401526102d2816040850160208701610273565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220e9bed491ce4cc7495def60dc616a13f39ccd912637e0c8ba02d45400506de9c064736f6c63430008100033416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220237fb73aef7b871fb5f0c1ef15f58a63a1b7502356cc319c2d83ad0717d2cd7264736f6c634300081000336080604052348015600f57600080fd5b50603f80601d6000396000f3fe6080604052600080fdfea26469706673582212203fc1d73d12f631c8d0a3a1e5ebc667c0e6e4c09698b94f0ed53d99f55382279e64736f6c63430008100033608060405260405162000ebb38038062000ebb833981016040819052620000269162000497565b828162000036828260006200004d565b50620000449050826200008a565b505050620005ca565b6200005883620000e5565b600082511180620000665750805b1562000085576200008383836200012760201b620001691760201c565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f620000b562000156565b604080516001600160a01b03928316815291841660208301520160405180910390a1620000e2816200018f565b50565b620000f08162000244565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b60606200014f838360405180606001604052806027815260200162000e9460279139620002f8565b9392505050565b60006200018060008051602062000e7483398151915260001b6200037760201b620001951760201c565b546001600160a01b0316919050565b6001600160a01b038116620001fa5760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b60648201526084015b60405180910390fd5b806200022360008051602062000e7483398151915260001b6200037760201b620001951760201c565b80546001600160a01b0319166001600160a01b039290921691909117905550565b6200025a816200037a60201b620001981760201c565b620002be5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b6064820152608401620001f1565b80620002237f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc60001b6200037760201b620001951760201c565b6060600080856001600160a01b03168560405162000317919062000577565b600060405180830381855af49150503d806000811462000354576040519150601f19603f3d011682016040523d82523d6000602084013e62000359565b606091505b5090925090506200036d8683838762000389565b9695505050505050565b90565b6001600160a01b03163b151590565b60608315620003fd578251600003620003f5576001600160a01b0385163b620003f55760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401620001f1565b508162000409565b62000409838362000411565b949350505050565b815115620004225781518083602001fd5b8060405162461bcd60e51b8152600401620001f1919062000595565b80516001600160a01b03811681146200045657600080fd5b919050565b634e487b7160e01b600052604160045260246000fd5b60005b838110156200048e57818101518382015260200162000474565b50506000910152565b600080600060608486031215620004ad57600080fd5b620004b8846200043e565b9250620004c8602085016200043e565b60408501519092506001600160401b0380821115620004e657600080fd5b818601915086601f830112620004fb57600080fd5b8151818111156200051057620005106200045b565b604051601f8201601f19908116603f011681019083821181831017156200053b576200053b6200045b565b816040528281528960208487010111156200055557600080fd5b6200056883602083016020880162000471565b80955050505050509250925092565b600082516200058b81846020870162000471565b9190910192915050565b6020815260008251806020840152620005b681604085016020870162000471565b601f01601f19169190910160400192915050565b61089a80620005da6000396000f3fe60806040523661001357610011610017565b005b6100115b61001f6101a7565b6001600160a01b0316330361015f5760606001600160e01b0319600035166364d3180d60e11b810161005a576100536101da565b9150610157565b63587086bd60e11b6001600160e01b031982160161007a57610053610231565b63070d7c6960e41b6001600160e01b031982160161009a57610053610277565b621eb96f60e61b6001600160e01b03198216016100b9576100536102a8565b63a39f25e560e01b6001600160e01b03198216016100d9576100536102e8565b60405162461bcd60e51b815260206004820152604260248201527f5472616e73706172656e745570677261646561626c6550726f78793a2061646d60448201527f696e2063616e6e6f742066616c6c6261636b20746f2070726f78792074617267606482015261195d60f21b608482015260a4015b60405180910390fd5b815160208301f35b6101676102fc565b565b606061018e838360405180606001604052806027815260200161083e6027913961030c565b9392505050565b90565b6001600160a01b03163b151590565b60007fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b546001600160a01b0316919050565b60606101e4610384565b60006101f33660048184610691565b81019061020091906106d7565b905061021d8160405180602001604052806000815250600061038f565b505060408051602081019091526000815290565b60606000806102433660048184610691565b8101906102509190610708565b915091506102608282600161038f565b604051806020016040528060008152509250505090565b6060610281610384565b60006102903660048184610691565b81019061029d91906106d7565b905061021d816103bb565b60606102b2610384565b60006102bc6101a7565b604080516001600160a01b03831660208201529192500160405160208183030381529060405291505090565b60606102f2610384565b60006102bc610412565b610167610307610412565b610421565b6060600080856001600160a01b03168560405161032991906107ee565b600060405180830381855af49150503d8060008114610364576040519150601f19603f3d011682016040523d82523d6000602084013e610369565b606091505b509150915061037a86838387610445565b9695505050505050565b341561016757600080fd5b610398836104c4565b6000825111806103a55750805b156103b6576103b48383610169565b505b505050565b7f7e644d79422f17c01e4894b5f4f588d331ebfa28653d42ae832dc59e38c9798f6103e46101a7565b604080516001600160a01b03928316815291841660208301520160405180910390a161040f81610504565b50565b600061041c6105ad565b905090565b3660008037600080366000845af43d6000803e808015610440573d6000f35b3d6000fd5b606083156104b25782516000036104ab5761045f85610198565b6104ab5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e7472616374000000604482015260640161014e565b50816104bc565b6104bc83836105d5565b949350505050565b6104cd816105ff565b6040516001600160a01b038216907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a250565b6001600160a01b0381166105695760405162461bcd60e51b815260206004820152602660248201527f455243313936373a206e65772061646d696e20697320746865207a65726f206160448201526564647265737360d01b606482015260840161014e565b807fb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d61035b80546001600160a01b0319166001600160a01b039290921691909117905550565b60007f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc6101cb565b8151156105e55781518083602001fd5b8060405162461bcd60e51b815260040161014e919061080a565b61060881610198565b61066a5760405162461bcd60e51b815260206004820152602d60248201527f455243313936373a206e657720696d706c656d656e746174696f6e206973206e60448201526c1bdd08184818dbdb9d1c9858dd609a1b606482015260840161014e565b807f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc61058c565b600080858511156106a157600080fd5b838611156106ae57600080fd5b5050820193919092039150565b80356001600160a01b03811681146106d257600080fd5b919050565b6000602082840312156106e957600080fd5b61018e826106bb565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561071b57600080fd5b610724836106bb565b9150602083013567ffffffffffffffff8082111561074157600080fd5b818501915085601f83011261075557600080fd5b813581811115610767576107676106f2565b604051601f8201601f19908116603f0116810190838211818310171561078f5761078f6106f2565b816040528281528860208487010111156107a857600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b60005b838110156107e55781810151838201526020016107cd565b50506000910152565b600082516108008184602087016107ca565b9190910192915050565b60208152600082518060208401526108298160408501602087016107ca565b601f01601f1916919091016040019291505056fe416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a264697066735822122072007c277cc5d8471be1434c6b2d5b70fb7c2f6f77a0697617733c4d4ae5b76964736f6c63430008100033b53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103416464726573733a206c6f772d6c6576656c2064656c65676174652063616c6c206661696c6564a2646970667358221220377d031a5f294a74596aa9acc30ca418bd32dd3f9cbae6ed71ce8cc1f17439cc64736f6c63430008100033", "nonce": "0x0", "to": null, "transactionIndex": "0x2", "value": "0x0", "type": "0x68", "chainId": "0xda8c8b77", "v": "0x0", "r": "0x0", "s": "0x0", "ticketId": "0x07fcc2df6293de1451ca79c4299ff16845d2572a41985a0426806aeb09b22d23", "maxRefund": "0x22f2505249400", "submissionFeeRefund": "0xd72a2471400", "refundTo": "0xbee947aec820389c1560c87bd96e2723bad05b61" } ], "transactionsRoot": "0xe44e2d243f72d352be0157632b1fc84149b77914c0e429faac99bdca4acc66b0", "uncles": [] } ================================================ FILE: testsuite/block-full.json ================================================ { "number": "0x1", "hash": "0x0000000000000000000000000000000000000000000000000000000000000001", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000002", "sha3Uncles": "0x0000000000000000000000000000000000000000000000000000000000000003", "transactionsRoot": "0x0000000000000000000000000000000000000000000000000000000000000001", "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000003", "receiptsRoot": "0x0000000000000000000000000000000000000000000000000000000000000002", "miner": "0x0000000000000000000000000000000000000001", "gasLimit": "0x2", "gasUsed": "0x3", "timestamp": "0x4", "difficulty": "0x5", "extraData": "0x01", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000003", "nonce": "0x0a00000000000000", "uncles": [ "0x0000000000000000000000000000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000000000000000000000000000002" ], "transactions": [ { "type": "0x0", "hash": "0x0000000000000000000000000000000000000000000000000000000000000001", "from": "0x0000000000000000000000000000000000000001", "input": "0x00", "value": "0x0", "gasPrice": "0x0", "gas": "0x10", "nonce": "0x10", "to": "0x0000000000000000000000000000000000000001", "v": "0x25", "r": "0x0000000000000000000000000000000000000000000000000000000000000001", "s": "0x0000000000000000000000000000000000000000000000000000000000000001", "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000001", "blockNumber": "0x0", "transactionIndex": "0x0" } ] } ================================================ FILE: testsuite/block-txn-hashes.json ================================================ { "number": "0x1", "hash": "0x0000000000000000000000000000000000000000000000000000000000000001", "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000002", "sha3Uncles": "0x0000000000000000000000000000000000000000000000000000000000000003", "transactionsRoot": "0x0000000000000000000000000000000000000000000000000000000000000001", "stateRoot": "0x0000000000000000000000000000000000000000000000000000000000000003", "receiptsRoot": "0x0000000000000000000000000000000000000000000000000000000000000002", "miner": "0x0000000000000000000000000000000000000001", "gasLimit": "0x2", "gasUsed": "0x3", "timestamp": "0x4", "difficulty": "0x5", "extraData": "0x01", "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000003", "nonce": "0x0a00000000000000", "uncles": [ "0x0000000000000000000000000000000000000000000000000000000000000001", "0x0000000000000000000000000000000000000000000000000000000000000002" ], "transactions": [ "0x0000000000000000000000000000000000000000000000000000000000000001" ] } ================================================ FILE: testsuite/receipts.json ================================================ [ { "blockHash": "0x1ee40a01cdb865600eef166d175eade70ab51f215c5663a7fcc5ec1f960f4e40", "blockNumber": "0x10482a", "contractAddress": "0xd63d7145f125b16bb36fe73f2128bb77dcc8ccf1", "cumulativeGasUsed": "0x3765f", "effectiveGasPrice": "0xba43b7400", "from": "0x20e021a751cb6bcab2eaf7b9db570ec10c8ad0a2", "gasUsed": "0x32457", "logs": [], "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "root": "0xe41f2551706d287917eb795bb81348d68ca6f9300e1394e4c8d3288ae16865dc", "to": null, "transactionHash": "0xcbb8db2cec3fa38e2ff221462708252bbbe32abb57e5a43f402155880d2883e0", "transactionIndex": "0x1", "type": "0x0" }, { "blockHash": "0x7ba787b8371397a05412121bf915373ebbcdfa6e4117f6076911a34ee9bca4a8", "blockNumber": "0xee76d0", "contractAddress": null, "cumulativeGasUsed": "0x114db42", "effectiveGasPrice": "0x222359b14", "from": "0xdafea492d9c6733ae3d56b7ed1adb60692c98bc5", "gasUsed": "0x523f", "logs": [], "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "status": "0x1", "to": "0xebec795c9c8bbd61ffc14a6662944748f299cacf", "transactionHash": "0xdd002538bd3165c8aed8a7435f965420bca4406951e490190f4271302a4c5254", "transactionIndex": "0x90", "type": "0x0" } ] ================================================ FILE: testsuite/transaction-call.json ================================================ { "type": "0x0", "hash": "0x0000000000000000000000000000000000000000000000000000000000000001", "from": "0x0000000000000000000000000000000000000001", "input": "0x00", "value": "0x0", "gasPrice": "0x0", "gas": "0x10", "nonce": "0x10", "to": "0x0000000000000000000000000000000000000001", "v": "0x25", "r": "0x0000000000000000000000000000000000000000000000000000000000000001", "s": "0x0000000000000000000000000000000000000000000000000000000000000001", "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000001", "blockNumber": "0x0", "transactionIndex": "0x0" } ================================================ FILE: testsuite/transaction-contract-creation.json ================================================ { "type": "0x0", "hash": "0x0000000000000000000000000000000000000000000000000000000000000001", "from": "0x0000000000000000000000000000000000000001", "input": "0x00", "value": "0x0", "gasPrice": "0x0", "gas": "0x10", "nonce": "0x10", "to": null, "v": "0x25", "r": "0x0000000000000000000000000000000000000000000000000000000000000001", "s": "0x0000000000000000000000000000000000000000000000000000000000000001", "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000001", "blockNumber": "0x0", "transactionIndex": "0x0" } ================================================ FILE: testsuite/transaction-eip1159.json ================================================ { "type": "0x2", "hash": "0x0000000000000000000000000000000000000000000000000000000000000001", "from": "0x0000000000000000000000000000000000000001", "input": "0x00", "value": "0x0", "maxPriorityFeePerGas": "0x10", "maxFeePerGas": "0x10", "gas": "0x10", "nonce": "0x10", "to": null, "v": "0x25", "r": "0x0000000000000000000000000000000000000000000000000000000000000001", "s": "0x0000000000000000000000000000000000000000000000000000000000000001", "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000001", "blockNumber": "0x0", "transactionIndex": "0x0", "chainId": "0x1", "accessList": [ { "address": "0x0000000000000000000000000000000000000001", "storageKeys": [ "0x0000000000000000000000000000000000000000000000000000000000000001" ] } ] } ================================================ FILE: testsuite/transaction-eip1559-notype.json ================================================ { "hash": "0x0000000000000000000000000000000000000000000000000000000000000001", "from": "0x0000000000000000000000000000000000000001", "input": "0x00", "value": "0x0", "maxPriorityFeePerGas": "0x10", "maxFeePerGas": "0x10", "gas": "0x10", "nonce": "0x10", "to": null, "v": "0x25", "r": "0x0000000000000000000000000000000000000000000000000000000000000001", "s": "0x0000000000000000000000000000000000000000000000000000000000000001", "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000001", "blockNumber": "0x0", "transactionIndex": "0x0", "chainId": "0x1", "accessList": [ { "address": "0x0000000000000000000000000000000000000001", "storageKeys": [ "0x0000000000000000000000000000000000000000000000000000000000000001" ] } ] } ================================================ FILE: testsuite/transaction-eip2930.json ================================================ { "type": "0x1", "hash": "0x0000000000000000000000000000000000000000000000000000000000000001", "from": "0x0000000000000000000000000000000000000001", "input": "0x00", "value": "0x0", "gasPrice": "0x0", "gas": "0x10", "nonce": "0x10", "to": null, "v": "0x25", "r": "0x0000000000000000000000000000000000000000000000000000000000000001", "s": "0x0000000000000000000000000000000000000000000000000000000000000001", "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000001", "blockNumber": "0x0", "transactionIndex": "0x0", "chainId": "0x1", "accessList": [ { "address": "0x0000000000000000000000000000000000000001", "storageKeys": [ "0x0000000000000000000000000000000000000000000000000000000000000001" ] } ] } ================================================ FILE: testsuite/transaction-pending.json ================================================ { "type": "0x0", "hash": "0x0000000000000000000000000000000000000000000000000000000000000001", "from": "0x0000000000000000000000000000000000000001", "input": "0x00", "value": "0x0", "gasPrice": "0x0", "gas": "0x10", "nonce": "0x10", "to": "0x0000000000000000000000000000000000000001", "v": "0x25", "r": "0x0000000000000000000000000000000000000000000000000000000000000001", "s": "0x0000000000000000000000000000000000000000000000000000000000000001", "blockHash": null, "blockNumber": null, "transactionIndex": null } ================================================ FILE: testutil/contract.go ================================================ package testutil import ( "encoding/hex" "fmt" "strconv" "strings" "github.com/umbracle/ethgo/compiler" "golang.org/x/crypto/sha3" ) // Contract is a test contract type Contract struct { events []*Event callbacks []func() string } // AddEvent adds a new event to the contract func (c *Contract) AddEvent(e *Event) { if c.events == nil { c.events = []*Event{} } c.AddCallback(e.printDecl) c.AddCallback(e.printSetter) c.events = append(c.events, e) } // GetEvent returns the event with the given name func (c *Contract) GetEvent(name string) *Event { for _, i := range c.events { if i.name == name { return i } } return nil } // Print prints the contract func (c *Contract) Print() string { str := "pragma solidity ^0.5.5;\n" str += "pragma experimental ABIEncoderV2;\n" str += "\n" str += "contract Sample {\n" for _, f := range c.callbacks { str += f() + "\n" } str += "}" return str } func (c *Contract) AddCallback(f func() string) { if c.callbacks == nil { c.callbacks = []func() string{} } c.callbacks = append(c.callbacks, f) } // Compile compiles the contract func (c *Contract) Compile() (*compiler.Artifact, error) { output, err := compiler.NewSolidityCompiler("solc").CompileCode(c.Print()) if err != nil { return nil, err } solcContract, ok := output.Contracts[":Sample"] if !ok { return nil, fmt.Errorf("Expected the contract to be called Sample") } return solcContract, nil } // AddConstructor creates a constructor with args that set contract variables func (c *Contract) AddConstructor(args ...string) { // add variables c.AddCallback(func() string { str := "" input := []string{} body := "" for indx, arg := range args { str += fmt.Sprintf("%s public val_%d;\n", arg, indx) input = append(input, fmt.Sprintf("%s local_%d", arg, indx)) body += fmt.Sprintf("val_%d = local_%d;\n", indx, indx) } str += "constructor(" + strings.Join(input, ",") + ") public {\n" str += body str += "}" return str }) } // AddDualCaller adds a call function that returns the same values that takes func (c *Contract) AddDualCaller(funcName string, args ...string) { c.AddCallback(func() string { var params, rets, body []string for indx, i := range args { name := "val_" + strconv.Itoa(indx) // function params params = append(params, i+" "+name) // function returns rets = append(rets, i) // function body body = append(body, name) } str := "function " + funcName + "(" + strings.Join(params, ",") + ") public view returns (" + strings.Join(rets, ",") + ") {\n" str += "return (" + strings.Join(body, ",") + ");\n" str += "}" return str }) } // AddOutputCaller adsd a view function that does not take any input func (c *Contract) AddOutputCaller(funcName string) { c.AddCallback(func() string { return `function ` + funcName + ` () public view returns (uint256) { return 1; }` }) } // EmitEvent emits a specific event func (c *Contract) EmitEvent(funcName string, name string, args ...string) { exists := false for _, i := range c.events { if i.name == name { exists = true } } if !exists { panic(fmt.Errorf("event %s does not exists", name)) } c.AddCallback(func() string { str := "function " + funcName + "() public payable {\n" str += fmt.Sprintf("emit %s(%s)", name, strings.Join(args, ", ")) + ";\n" str += "}" return str }) } type eventField struct { indexed bool typ string } // Event is a test event type Event struct { name string fields []*eventField } func (e *Event) printDecl() string { args := []string{} for indx, i := range e.fields { arg := i.typ if i.indexed { arg += " indexed" } arg += " val_" + strconv.Itoa(indx) args = append(args, arg) } return fmt.Sprintf("event %s(%s);", e.name, strings.Join(args, ", ")) } func (e *Event) printSetter() string { params := []string{} body := []string{} for indx, i := range e.fields { // function params // Some params are in memory typ := i.typ if typ == "string" || strings.Contains(typ, "[") { typ = typ + " memory" } params = append(params, fmt.Sprintf("%s val_%d", typ, indx)) // function body body = append(body, fmt.Sprintf("val_%d", indx)) } str := "function setter" + e.name + "(" + strings.Join(params, ", ") + ") public payable {\n" str += "emit " + e.name + "(" + strings.Join(body, ", ") + ");\n" str += "}" return str } // Add adds a new field to the event func (e *Event) Add(typStr string, indexed bool) *Event { if e.fields == nil { e.fields = []*eventField{} } e.fields = append(e.fields, &eventField{indexed, typStr}) return e } // Sig returns the signature of the event func (e *Event) Sig() string { args := []string{} for _, i := range e.fields { args = append(args, i.typ) } signature := e.name + "(" + strings.Join(args, ",") + ")" h := sha3.NewLegacyKeccak256() h.Write([]byte(signature)) b := h.Sum(nil) return "0x" + hex.EncodeToString(b) } // NewEvent creates a new contract event func NewEvent(name string, args ...interface{}) *Event { if len(args)%2 != 0 { panic("it should be even") } e := &Event{name: name} for i := 0; i < len(args); i++ { e.Add(args[i].(string), args[i+1].(bool)) } return e } ================================================ FILE: testutil/mock.go ================================================ package testutil import ( "encoding/hex" "fmt" "math/big" "strconv" "strings" "sync" "github.com/umbracle/ethgo" ) type mockCall int const ( blockByNumberCall mockCall = iota blockByHashCall blockNumberCall getLogsCall ) type MockClient struct { lock sync.Mutex num uint64 blockNum map[uint64]ethgo.Hash blocks map[ethgo.Hash]*ethgo.Block logs map[ethgo.Hash][]*ethgo.Log chainID *big.Int } func (m *MockClient) SetChainID(id *big.Int) { m.chainID = id } func (d *MockClient) ChainID() (*big.Int, error) { if d.chainID == nil { d.chainID = big.NewInt(1337) } return d.chainID, nil } func (d *MockClient) GetLastBlocks(n uint64) (res []*ethgo.Block) { if d.num == 0 { return } num := n if d.num < num { num = d.num + 1 } for i := int(num - 1); i >= 0; i-- { res = append(res, d.blocks[d.blockNum[d.num-uint64(i)]]) } return } func (d *MockClient) GetAllLogs() (res []*ethgo.Log) { if d.num == 0 { return } for i := uint64(0); i <= d.num; i++ { res = append(res, d.logs[d.blocks[d.blockNum[i]].Hash]...) } return } func (d *MockClient) AddScenario(m MockList) { d.lock.Lock() defer d.lock.Unlock() // add the logs for _, b := range m { block := ðgo.Block{ Hash: b.Hash(), Number: uint64(b.num), } if b.num != 0 { bb, err := d.blockByNumberLock(uint64(b.num) - 1) if err != nil { // This happens during reconcile tests because we include only partial blocks block.ParentHash = encodeHash(strconv.Itoa(b.num - 1)) } else { block.ParentHash = bb.Hash } } // add history block d.addBlocks(block) // add logs // remove any other logs for this block in case there are any if _, ok := d.logs[block.Hash]; ok { delete(d.logs, block.Hash) } d.AddLogs(b.GetLogs()) } } func (d *MockClient) AddLogs(logs []*ethgo.Log) { if d.logs == nil { d.logs = map[ethgo.Hash][]*ethgo.Log{} } for _, log := range logs { entry, ok := d.logs[log.BlockHash] if ok { entry = append(entry, log) } else { entry = []*ethgo.Log{log} } d.logs[log.BlockHash] = entry } } func (d *MockClient) addBlocks(bb ...*ethgo.Block) { if d.blocks == nil { d.blocks = map[ethgo.Hash]*ethgo.Block{} } if d.blockNum == nil { d.blockNum = map[uint64]ethgo.Hash{} } for _, b := range bb { if b.Number > d.num { d.num = b.Number } d.blocks[b.Hash] = b d.blockNum[b.Number] = b.Hash } } func (d *MockClient) BlockNumber() (uint64, error) { d.lock.Lock() defer d.lock.Unlock() return d.num, nil } func (d *MockClient) GetBlockByHash(hash ethgo.Hash, full bool) (*ethgo.Block, error) { d.lock.Lock() defer d.lock.Unlock() b := d.blocks[hash] if b == nil { return nil, fmt.Errorf("hash %s not found", hash) } return b, nil } func (d *MockClient) blockByNumberLock(i uint64) (*ethgo.Block, error) { hash, ok := d.blockNum[i] if !ok { return nil, fmt.Errorf("number %d not found", i) } return d.blocks[hash], nil } func (d *MockClient) GetBlockByNumber(i ethgo.BlockNumber, full bool) (*ethgo.Block, error) { d.lock.Lock() defer d.lock.Unlock() if i < 0 { switch i { case ethgo.Latest: if d.num == 0 { return ðgo.Block{Number: 0}, nil } return d.blockByNumberLock(d.num) default: return nil, fmt.Errorf("getBlockByNumber query not supported") } } return d.blockByNumberLock(uint64(i)) } func (d *MockClient) GetLogs(filter *ethgo.LogFilter) ([]*ethgo.Log, error) { d.lock.Lock() defer d.lock.Unlock() if filter.BlockHash != nil { return d.logs[*filter.BlockHash], nil } from, to := uint64(*filter.From), uint64(*filter.To) if from > to { return nil, fmt.Errorf("from higher than to") } if int(to) > len(d.blocks) { return nil, fmt.Errorf("out of bounds") } logs := []*ethgo.Log{} for i := from; i <= to; i++ { b, err := d.blockByNumberLock(i) if err != nil { return nil, err } elems, ok := d.logs[b.Hash] if ok { logs = append(logs, elems...) } } return logs, nil } type MockLog struct { data string } type MockBlock struct { hash string extra string parent string num int logs []*MockLog } func mustDecodeHash(str string) []byte { if strings.HasPrefix(str, "0x") { str = str[2:] } if len(str)%2 == 1 { str = str + "0" } buf, err := hex.DecodeString(str) if err != nil { panic(err) } return buf } func (m *MockBlock) Extra(data string) *MockBlock { m.extra = data return m } func (m *MockBlock) GetLogs() (logs []*ethgo.Log) { for _, log := range m.logs { logs = append(logs, ðgo.Log{Data: mustDecodeHash(log.data), BlockNumber: uint64(m.num), BlockHash: m.Hash()}) } return } func (m *MockBlock) Log(data string) *MockBlock { m.logs = append(m.logs, &MockLog{data}) return m } func (m *MockBlock) GetNum() int { return m.num } func (m *MockBlock) Num(i int) *MockBlock { m.num = i return m } func (m *MockBlock) Parent(i int) *MockBlock { m.parent = strconv.Itoa(i) m.num = i + 1 return m } func encodeHash(str string) (h ethgo.Hash) { tmp := "" for i := 0; i < 64-len(str); i++ { tmp += "0" } str = "0x" + tmp + str if err := h.UnmarshalText([]byte(str)); err != nil { panic(err) } return } func (m *MockBlock) Hash() ethgo.Hash { return encodeHash(m.extra + m.hash) } func (m *MockBlock) Block() *ethgo.Block { b := ðgo.Block{ Hash: m.Hash(), Number: uint64(m.num), } if m.num != 0 { b.ParentHash = encodeHash(m.parent) } return b } func Mock(number int) *MockBlock { return &MockBlock{hash: strconv.Itoa(number), num: number, parent: strconv.Itoa(number - 1)} } type MockList []*MockBlock func (m *MockList) Create(from, to int, callback func(b *MockBlock)) { for i := from; i < to; i++ { b := Mock(i) callback(b) *m = append(*m, b) } } func (m *MockList) GetLogs() (res []*ethgo.Log) { for _, log := range *m { res = append(res, log.GetLogs()...) } return } func (m *MockList) ToBlocks() []*ethgo.Block { e := []*ethgo.Block{} for _, i := range *m { e = append(e, i.Block()) } return e } ================================================ FILE: testutil/server.go ================================================ package testutil import ( "bytes" "encoding/hex" "encoding/json" "fmt" "io/ioutil" "math/big" "math/rand" "net" "net/http" "os" "strconv" "testing" "time" "github.com/ory/dockertest" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/compiler" "golang.org/x/crypto/sha3" ) var ( DefaultGasPrice = uint64(1879048192) // 0x70000000 DefaultGasLimit = uint64(5242880) // 0x500000 ) var ( DummyAddr = ethgo.HexToAddress("0x015f68893a39b3ba0681584387670ff8b00f4db2") ) func getOpenPort() string { rand.Seed(time.Now().UnixNano()) min, max := 12000, 15000 for { port := strconv.Itoa(rand.Intn(max-min) + min) server, err := net.Listen("tcp", ":"+port) if err == nil { server.Close() return port } } } // MultiAddr creates new servers to test different addresses func MultiAddr(t *testing.T, c func(s *TestServer, addr string)) { s := NewTestServer(t) // http addr c(s, s.HTTPAddr()) // ws addr // c(s, s.WSAddr()) // ip addr // c(s, s.IPCPath()) // s.Close() } // TestServerConfig is the configuration of the server type TestServerConfig struct { Period int } // ServerConfigCallback is the callback to modify the config type ServerConfigCallback func(c *TestServerConfig) // TestServer is a Geth test server type TestServer struct { addr string accounts []ethgo.Address client *ethClient } // DeployTestServer creates a new Geth test server func DeployTestServer(t *testing.T, cb ServerConfigCallback) *TestServer { tmpDir, err := ioutil.TempDir("/tmp", "geth-") if err != nil { t.Fatalf("err: %s", err) } config := &TestServerConfig{} if cb != nil { cb(config) } args := []string{"--dev"} // periodic mining if config.Period != 0 { args = append(args, "--dev.period", strconv.Itoa(config.Period)) } // add data dir args = append(args, "--datadir", "/eth1data") // add ipcpath args = append(args, "--ipcpath", "/eth1data/geth.ipc") // enable rpc args = append(args, "--http", "--http.addr", "0.0.0.0", "--http.api", "eth,net,web3,debug") // enable ws args = append(args, "--ws", "--ws.addr", "0.0.0.0") // enable debug verbosity args = append(args, "--verbosity", "4") opts := &dockertest.RunOptions{ Repository: "ethereum/client-go", Tag: "v1.10.15", Cmd: args, Mounts: []string{ tmpDir + ":/eth1data", }, } pool, err := dockertest.NewPool("") if err != nil { t.Fatalf("Could not connect to docker: %s", err) } resource, err := pool.RunWithOptions(opts) if err != nil { t.Fatalf("Could not start go-ethereum: %s", err) } closeFn := func() { if err := pool.Purge(resource); err != nil { t.Fatalf("Could not purge geth: %s", err) } } ipAddr := resource.Container.NetworkSettings.IPAddress addr := fmt.Sprintf("http://%s:8545", ipAddr) if err := pool.Retry(func() error { return testHTTPEndpoint(addr) }); err != nil { closeFn() } t.Cleanup(func() { closeFn() }) return NewTestServer(t, addr) } func NewTestServer(t *testing.T, addrs ...string) *TestServer { var addr string if len(addrs) != 0 { addr = addrs[0] } else { // default address addr = "http://127.0.0.1:8545" } server := &TestServer{ addr: addr, } server.client = ðClient{addr} if err := server.client.call("eth_accounts", &server.accounts); err != nil { t.Fatal(err) } return server } // Account returns a specific account func (t *TestServer) Account(i int) ethgo.Address { return t.accounts[i] } // IPCPath returns the ipc endpoint func (t *TestServer) IPCPath() string { return "" // return t.tmpDir + "/geth.ipc" } // WSAddr returns the websocket endpoint func (t *TestServer) WSAddr() string { return fmt.Sprintf("ws://localhost:8546") } // HTTPAddr returns the http endpoint func (t *TestServer) HTTPAddr() string { return fmt.Sprintf(t.addr) } // ProcessBlock processes a new block func (t *TestServer) ProcessBlockWithReceipt() (*ethgo.Receipt, error) { receipt, err := t.SendTxn(ðgo.Transaction{ From: t.accounts[0], To: &DummyAddr, Value: big.NewInt(10), }) return receipt, err } func (t *TestServer) ProcessBlock() error { _, err := t.ProcessBlockWithReceipt() return err } var emptyAddr ethgo.Address func isEmptyAddr(w ethgo.Address) bool { return bytes.Equal(w[:], emptyAddr[:]) } // Call sends a contract call func (t *TestServer) Call(msg *ethgo.CallMsg) (string, error) { if isEmptyAddr(msg.From) { msg.From = t.Account(0) } var resp string if err := t.client.call("eth_call", &resp, msg, "latest"); err != nil { return "", err } return resp, nil } func (t *TestServer) Fund(address ethgo.Address) (*ethgo.Receipt, error) { return t.Transfer(address, big.NewInt(1000000000000000000)) } func (t *TestServer) Transfer(address ethgo.Address, value *big.Int) (*ethgo.Receipt, error) { return t.SendTxn(ðgo.Transaction{ From: t.accounts[0], To: &address, Value: value, }) } // TxnTo sends a transaction to a given method without any arguments func (t *TestServer) TxnTo(address ethgo.Address, method string) (*ethgo.Receipt, error) { return t.SendTxn(ðgo.Transaction{ To: &address, Input: MethodSig(method), }) } // SendTxn sends a transaction func (t *TestServer) SendTxn(txn *ethgo.Transaction) (*ethgo.Receipt, error) { if isEmptyAddr(txn.From) { txn.From = t.Account(0) } if txn.Type == ethgo.TransactionDynamicFee { if txn.MaxPriorityFeePerGas == nil || txn.MaxPriorityFeePerGas.Cmp(big.NewInt(0)) == 0 { txn.MaxPriorityFeePerGas = new(big.Int).SetUint64(DefaultGasPrice) } } else { if txn.GasPrice == 0 { txn.GasPrice = DefaultGasPrice } } if txn.Gas == 0 { txn.Gas = DefaultGasLimit } var hash ethgo.Hash if err := t.client.call("eth_sendTransaction", &hash, txn); err != nil { return nil, err } return t.WaitForReceipt(hash) } // WaitForReceipt waits for the receipt func (t *TestServer) WaitForReceipt(hash ethgo.Hash) (*ethgo.Receipt, error) { var receipt *ethgo.Receipt var count uint64 for { err := t.client.call("eth_getTransactionReceipt", &receipt, hash) if err != nil { if err.Error() != "not found" { return nil, err } } if receipt != nil { break } if count > 300 { return nil, fmt.Errorf("timeout waiting for receipt") } time.Sleep(500 * time.Millisecond) count++ } return receipt, nil } // DeployContract deploys a contract with account 0 and returns the address func (t *TestServer) DeployContract(c *Contract) (*compiler.Artifact, ethgo.Address, error) { // solcContract := compile(c.Print()) solcContract, err := c.Compile() if err != nil { return nil, ethgo.Address{}, err } buf, err := hex.DecodeString(solcContract.Bin) if err != nil { return nil, ethgo.Address{}, err } receipt, err := t.SendTxn(ðgo.Transaction{ Input: buf, }) if err != nil { return nil, ethgo.Address{}, err } return solcContract, receipt.ContractAddress, nil } // Simple jsonrpc client to avoid cycle dependencies type jsonRPCRequest struct { ID int `json:"id"` Method string `json:"method"` Params json.RawMessage `json:"params"` } type jsonRPCResponse struct { ID int `json:"id"` Result json.RawMessage `json:"result"` Error *jsonRPCErrorObject `json:"error,omitempty"` } type jsonRPCErrorObject struct { Code int `json:"code"` Message string `json:"message"` Data interface{} `json:"data,omitempty"` } type ethClient struct { url string } var errNotFound = fmt.Errorf("not found") func (e *ethClient) call(method string, out interface{}, params ...interface{}) error { if e.url == "" { e.url = "http://127.0.0.1:8545" } var err error jsonReq := &jsonRPCRequest{ Method: method, } if len(params) > 0 { jsonReq.Params, err = json.Marshal(params) if err != nil { return err } } raw, err := json.Marshal(jsonReq) if err != nil { return err } resp, err := http.Post(e.url, "application/json", bytes.NewBuffer(raw)) if err != nil { return err } defer resp.Body.Close() var jsonResp jsonRPCResponse d := json.NewDecoder(resp.Body) if err := d.Decode(&jsonResp); err != nil { return err } if jsonResp.Error != nil { return fmt.Errorf(jsonResp.Error.Message) } if bytes.Equal(jsonResp.Result, []byte("null")) { return errNotFound } if err := json.Unmarshal(jsonResp.Result, out); err != nil { return err } return nil } // MethodSig returns the signature of a non-parametrized function func MethodSig(name string) []byte { h := sha3.NewLegacyKeccak256() h.Write([]byte(name + "()")) b := h.Sum(nil) return b[:4] } // TestInfuraEndpoint returns the testing infura endpoint to make testing requests func TestInfuraEndpoint(t *testing.T) string { url := os.Getenv("INFURA_URL") if url == "" { t.Skip("Infura url not set") } return url } func testHTTPEndpoint(endpoint string) error { resp, err := http.Post(endpoint, "application/json", nil) if err != nil { return err } defer resp.Body.Close() return nil } ================================================ FILE: testutil/server_test.go ================================================ package testutil import ( "testing" "github.com/stretchr/testify/require" "github.com/umbracle/ethgo" ) func TestDeployServer(t *testing.T) { srv := DeployTestServer(t, nil) require.NotEmpty(t, srv.accounts) clt := ðClient{srv.HTTPAddr()} account := []ethgo.Address{} err := clt.call("eth_accounts", &account) require.NoError(t, err) } ================================================ FILE: testutil/util.go ================================================ package testutil import ( "math/big" "reflect" "github.com/umbracle/ethgo" ) func CompareLogs(one, two []*ethgo.Log) bool { if len(one) != len(two) { return false } if len(one) == 0 { return true } return reflect.DeepEqual(one, two) } func CompareBlocks(one, two []*ethgo.Block) bool { if len(one) != len(two) { return false } if len(one) == 0 { return true } // difficulty is hard to check, set the values to zero for _, i := range one { if i.Transactions == nil { i.Transactions = []*ethgo.Transaction{} } i.Difficulty = big.NewInt(0) } for _, i := range two { if i.Transactions == nil { i.Transactions = []*ethgo.Transaction{} } i.Difficulty = big.NewInt(0) } return reflect.DeepEqual(one, two) } ================================================ FILE: tracker/README.md ================================================ # Tracker ``` package main import ( "context" "encoding/binary" "flag" "fmt" "os" "os/signal" "syscall" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/abi" "github.com/umbracle/ethgo/jsonrpc" "github.com/umbracle/ethgo/tracker" boltdbStore "github.com/umbracle/ethgo/tracker/store/boltdb" ) var depositEvent = abi.MustNewEvent(`DepositEvent( bytes pubkey, bytes whitdrawalcred, bytes amount, bytes signature, bytes index )`) func main() { var endpoint string var target string flag.StringVar(&endpoint, "endpoint", "", "") flag.StringVar(&target, "target", "", "") flag.Parse() provider, err := jsonrpc.NewClient(endpoint) if err != nil { fmt.Printf("[ERR]: %v", err) os.Exit(1) } store, err := boltdbStore.New("deposit.db") if err != nil { fmt.Printf("[ERR]: failted to start store %v", err) os.Exit(1) } tt, err := tracker.NewTracker(provider.Eth(), tracker.WithBatchSize(20000), tracker.WithStore(store), tracker.WithEtherscan(os.Getenv("ETHERSCAN_APIKEY")), tracker.WithFilter(&tracker.FilterConfig{ Async: true, Address: []ethgo.Address{ ethgo.HexToAddress(target), }, }), ) if err != nil { fmt.Printf("[ERR]: failed to create the tracker %v", err) os.Exit(1) } lastBlock, err := tt.GetLastBlock() if err != nil { fmt.Printf("[ERR]: failed to get last block %v", err) os.Exit(1) } if lastBlock != nil { fmt.Printf("Last block processed: %d\n", lastBlock.Number) } ctx, cancelFn := context.WithCancel(context.Background()) go func() { go func() { if err := tt.Sync(ctx); err != nil { fmt.Printf("[ERR]: %v", err) } }() go func() { for { select { case evnt := <-tt.EventCh: for _, log := range evnt.Added { if depositEvent.Match(log) { vals, err := depositEvent.ParseLog(log) if err != nil { panic(err) } index := binary.LittleEndian.Uint64(vals["index"].([]byte)) amount := binary.LittleEndian.Uint64(vals["amount"].([]byte)) fmt.Printf("Deposit: Block %d Index %d Amount %d\n", log.BlockNumber, index, amount) } } case <-tt.DoneCh: fmt.Println("historical sync done") } } }() }() handleSignals(cancelFn) } func handleSignals(cancelFn context.CancelFunc) int { signalCh := make(chan os.Signal, 4) signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM, syscall.SIGHUP) <-signalCh gracefulCh := make(chan struct{}) go func() { cancelFn() close(gracefulCh) }() select { case <-signalCh: return 1 case <-gracefulCh: return 0 } } ``` You can query the ETH2.0 Deposit contract like so: ``` go run main.go --endpoint https://mainnet.infura.io/v3/... --target 0x00000000219ab540356cbb839cbe05303d7705fa ``` ================================================ FILE: tracker/store/boltdb/bolt_store.go ================================================ package trackerboltdb import ( "bytes" "encoding/binary" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/tracker/store" bolt "go.etcd.io/bbolt" ) var _ store.Store = (*BoltStore)(nil) var ( dbLogs = []byte("logs") dbConf = []byte("conf") ) // BoltStore is a tracker store implementation. type BoltStore struct { conn *bolt.DB } // New creates a new boltdbstore func New(path string) (*BoltStore, error) { db, err := bolt.Open(path, 0600, nil) if err != nil { return nil, err } store := &BoltStore{ conn: db, } if err := store.setupDB(); err != nil { store.Close() return nil, err } return store, nil } func (b *BoltStore) setupDB() error { txn, err := b.conn.Begin(true) if err != nil { return err } defer txn.Rollback() if _, err := txn.CreateBucketIfNotExists(dbConf); err != nil { return err } return txn.Commit() } // Close implements the store interface func (b *BoltStore) Close() error { return b.conn.Close() } // Get implements the store interface func (b *BoltStore) Get(k string) (string, error) { txn, err := b.conn.Begin(false) if err != nil { return "", err } defer txn.Rollback() bucket := txn.Bucket(dbConf) val := bucket.Get([]byte(k)) return string(val), nil } // ListPrefix implements the store interface func (b *BoltStore) ListPrefix(prefix string) ([]string, error) { txn, err := b.conn.Begin(false) if err != nil { return nil, err } defer txn.Rollback() res := []string{} c := txn.Bucket(dbConf).Cursor() for k, v := c.Seek([]byte(prefix)); k != nil && bytes.HasPrefix(k, []byte(prefix)); k, v = c.Next() { res = append(res, string(v)) } return res, nil } // Set implements the store interface func (b *BoltStore) Set(k, v string) error { txn, err := b.conn.Begin(true) if err != nil { return err } defer txn.Rollback() bucket := txn.Bucket(dbConf) if err := bucket.Put([]byte(k), []byte(v)); err != nil { return err } return txn.Commit() } // GetEntry implements the store interface func (b *BoltStore) GetEntry(hash string) (store.Entry, error) { txn, err := b.conn.Begin(true) if err != nil { return nil, err } defer txn.Rollback() bucketName := append(dbLogs, []byte(hash)...) if _, err := txn.CreateBucketIfNotExists(bucketName); err != nil { return nil, err } if err := txn.Commit(); err != nil { return nil, err } e := &Entry{ conn: b.conn, bucket: bucketName, } return e, nil } // Entry is an store.Entry implementation type Entry struct { conn *bolt.DB bucket []byte } // LastIndex implements the store interface func (e *Entry) LastIndex() (uint64, error) { tx, err := e.conn.Begin(false) if err != nil { return 0, err } defer tx.Rollback() curs := tx.Bucket(e.bucket).Cursor() if last, _ := curs.Last(); last != nil { return bytesToUint64(last) + 1, nil } return 0, nil } // StoreLog implements the store interface func (e *Entry) StoreLog(log *ethgo.Log) error { return e.StoreLogs([]*ethgo.Log{log}) } // StoreLogs implements the store interface func (e *Entry) StoreLogs(logs []*ethgo.Log) error { tx, err := e.conn.Begin(true) if err != nil { return err } defer tx.Rollback() indx, err := e.LastIndex() if err != nil { return err } bucket := tx.Bucket(e.bucket) for logIndx, log := range logs { key := uint64ToBytes(indx + uint64(logIndx)) val, err := log.MarshalJSON() if err != nil { return err } if err := bucket.Put(key, val); err != nil { return err } } return tx.Commit() } // RemoveLogs implements the store interface func (e *Entry) RemoveLogs(indx uint64) error { indxKey := uint64ToBytes(indx) tx, err := e.conn.Begin(true) if err != nil { return err } defer tx.Rollback() curs := tx.Bucket(e.bucket).Cursor() for k, _ := curs.Seek(indxKey); k != nil; k, _ = curs.Next() { if err := curs.Delete(); err != nil { return err } } return tx.Commit() } // GetLog implements the store interface func (e *Entry) GetLog(indx uint64, log *ethgo.Log) error { txn, err := e.conn.Begin(false) if err != nil { return err } defer txn.Rollback() bucket := txn.Bucket(e.bucket) val := bucket.Get(uint64ToBytes(indx)) if err := log.UnmarshalJSON(val); err != nil { return err } return nil } func bytesToUint64(b []byte) uint64 { return binary.BigEndian.Uint64(b) } func uint64ToBytes(u uint64) []byte { buf := make([]byte, 8) binary.BigEndian.PutUint64(buf, u) return buf } ================================================ FILE: tracker/store/boltdb/bolt_store_test.go ================================================ package trackerboltdb import ( "io/ioutil" "os" "path/filepath" "testing" "github.com/umbracle/ethgo/tracker/store" ) func setupDB(t *testing.T) (store.Store, func()) { dir, err := ioutil.TempDir("/tmp", "boltdb-test") if err != nil { t.Fatal(err) } path := filepath.Join(dir, "test.db") store, err := New(path) if err != nil { t.Fatal(err) } close := func() { if err := os.RemoveAll(dir); err != nil { t.Fatal(err) } } return store, close } func TestBoltDBStore(t *testing.T) { store.TestStore(t, setupDB) } ================================================ FILE: tracker/store/inmem/inmem_store.go ================================================ package inmem import ( "strings" "sync" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/tracker/store" ) var _ store.Store = (*InmemStore)(nil) // InmemStore implements the Store interface. type InmemStore struct { l sync.RWMutex entries map[string]*Entry kv map[string]string } // NewInmemStore returns a new in-memory store. func NewInmemStore() *InmemStore { return &InmemStore{ entries: map[string]*Entry{}, kv: map[string]string{}, } } // Close implements the store interface func (i *InmemStore) Close() error { return nil } // Get implements the store interface func (i *InmemStore) Get(k string) (string, error) { i.l.Lock() defer i.l.Unlock() return i.kv[string(k)], nil } // ListPrefix implements the store interface func (i *InmemStore) ListPrefix(prefix string) ([]string, error) { i.l.Lock() defer i.l.Unlock() res := []string{} for k, v := range i.kv { if strings.HasPrefix(k, prefix) { res = append(res, v) } } return res, nil } // Set implements the store interface func (i *InmemStore) Set(k, v string) error { i.l.Lock() defer i.l.Unlock() i.kv[string(k)] = v return nil } // GetEntry implements the store interface func (i *InmemStore) GetEntry(hash string) (store.Entry, error) { i.l.Lock() defer i.l.Unlock() e, ok := i.entries[hash] if ok { return e, nil } e = &Entry{ logs: []*ethgo.Log{}, } i.entries[hash] = e return e, nil } // Entry is a store.Entry implementation type Entry struct { l sync.RWMutex logs []*ethgo.Log } // LastIndex implements the store interface func (e *Entry) LastIndex() (uint64, error) { e.l.Lock() defer e.l.Unlock() return uint64(len(e.logs)), nil } // Logs returns the logs of the inmemory store func (e *Entry) Logs() []*ethgo.Log { return e.logs } // StoreLogs implements the store interface func (e *Entry) StoreLogs(logs []*ethgo.Log) error { e.l.Lock() defer e.l.Unlock() for _, log := range logs { e.logs = append(e.logs, log) } return nil } // RemoveLogs implements the store interface func (e *Entry) RemoveLogs(indx uint64) error { e.l.Lock() defer e.l.Unlock() e.logs = e.logs[:indx] return nil } // GetLog implements the store interface func (e *Entry) GetLog(indx uint64, log *ethgo.Log) error { *log = *e.logs[indx] return nil } ================================================ FILE: tracker/store/inmem/inmem_store_test.go ================================================ package inmem import ( "testing" "github.com/umbracle/ethgo/tracker/store" ) func TestInMemoryStore(t *testing.T) { store.TestStore(t, func(t *testing.T) (store.Store, func()) { return NewInmemStore(), func() {} }) } ================================================ FILE: tracker/store/postgresql/postgresql_store.go ================================================ package trackerpostgresql import ( "database/sql" "encoding/hex" "fmt" "strings" "github.com/jmoiron/sqlx" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/tracker/store" // Enable postgres for sqlx _ "github.com/lib/pq" ) var _ store.Store = (*PostgreSQLStore)(nil) // PostgreSQLStore is a tracker store implementation that uses PostgreSQL as a backend. type PostgreSQLStore struct { db *sqlx.DB } // NewPostgreSQLStore creates a new PostgreSQL store func NewPostgreSQLStore(endpoint string) (*PostgreSQLStore, error) { db, err := sql.Open("postgres", endpoint) if err != nil { return nil, err } return NewSQLStore(db, "postgres") } // NewSQLStore creates a new store with an sql driver func NewSQLStore(db *sql.DB, driver string) (*PostgreSQLStore, error) { sqlxDB := sqlx.NewDb(db, driver) // create the kv database if it does not exists if _, err := db.Exec(kvSQLSchema); err != nil { return nil, err } return &PostgreSQLStore{db: sqlxDB}, nil } // Close implements the store interface func (p *PostgreSQLStore) Close() error { return p.db.Close() } // Get implements the store interface func (p *PostgreSQLStore) Get(k string) (string, error) { var out string if err := p.db.Get(&out, "SELECT val FROM kv WHERE key=$1", string(k)); err != nil { if err == sql.ErrNoRows { return "", nil } return "", err } return out, nil } // ListPrefix implements the store interface func (p *PostgreSQLStore) ListPrefix(prefix string) ([]string, error) { var out []string if err := p.db.Select(&out, "SELECT val FROM kv WHERE key LIKE $1", string(prefix)+"%"); err != nil { return nil, err } return out, nil } // Set implements the store interface func (p *PostgreSQLStore) Set(k, v string) error { if _, err := p.db.Exec("INSERT INTO kv (key, val) VALUES ($1, $2) ON CONFLICT (key) DO UPDATE SET val = $2", k, v); err != nil { return err } return nil } // GetEntry implements the store interface func (p *PostgreSQLStore) GetEntry(hash string) (store.Entry, error) { tableName := "logs_" + hash if _, err := p.db.Exec(logSQLSchema(tableName)); err != nil { return nil, err } e := &Entry{ table: tableName, db: p.db, } return e, nil } // Entry is an store.Entry implementation type Entry struct { table string db *sqlx.DB } // LastIndex implements the store interface func (e *Entry) LastIndex() (uint64, error) { var index uint64 if err := e.db.Get(&index, "SELECT indx FROM "+e.table+" ORDER BY indx DESC LIMIT 1"); err != nil { if err == sql.ErrNoRows { return 0, nil } return 0, err } return index + 1, nil } // StoreLogs implements the store interface func (e *Entry) StoreLogs(logs []*ethgo.Log) error { lastIndex, err := e.LastIndex() if err != nil { return err } tx, err := e.db.Beginx() if err != nil { return err } defer tx.Rollback() query := "INSERT INTO " + e.table + " (indx, tx_index, tx_hash, block_num, block_hash, address, data, topics) VALUES (:indx, :tx_index, :tx_hash, :block_num, :block_hash, :address, :data, :topics)" for indx, log := range logs { topics := []string{} for _, topic := range log.Topics { topics = append(topics, topic.String()) } obj := &logObj{ Index: lastIndex + uint64(indx), TxIndex: log.TransactionIndex, TxHash: log.TransactionHash.String(), BlockNum: log.BlockNumber, BlockHash: log.BlockHash.String(), Address: log.Address.String(), Topics: strings.Join(topics, ","), } if log.Data != nil { obj.Data = "0x" + hex.EncodeToString(log.Data) } if _, err := tx.NamedExec(query, obj); err != nil { return err } } if err := tx.Commit(); err != nil { return err } return nil } // RemoveLogs implements the store interface func (e *Entry) RemoveLogs(indx uint64) error { if _, err := e.db.Exec("DELETE FROM "+e.table+" WHERE indx >= $1", indx); err != nil { return err } return nil } // GetLog implements the store interface func (e *Entry) GetLog(indx uint64, log *ethgo.Log) error { obj := logObj{} if err := e.db.Get(&obj, "SELECT * FROM "+e.table+" WHERE indx=$1", indx); err != nil { return err } log.TransactionIndex = obj.TxIndex if err := log.TransactionHash.UnmarshalText([]byte(obj.TxHash)); err != nil { return err } log.BlockNumber = obj.BlockNum if err := log.BlockHash.UnmarshalText([]byte(obj.BlockHash)); err != nil { return err } if err := log.Address.UnmarshalText([]byte(obj.Address)); err != nil { return err } if obj.Topics != "" { log.Topics = []ethgo.Hash{} for _, item := range strings.Split(obj.Topics, ",") { var topic ethgo.Hash if err := topic.UnmarshalText([]byte(item)); err != nil { return err } log.Topics = append(log.Topics, topic) } } else { log.Topics = nil } if obj.Data != "" { if !strings.HasPrefix(obj.Data, "0x") { return fmt.Errorf("0x prefix not found in data") } buf, err := hex.DecodeString(obj.Data[2:]) if err != nil { return err } log.Data = buf } else { log.Data = nil } return nil } type logObj struct { Index uint64 `db:"indx"` TxIndex uint64 `db:"tx_index"` TxHash string `db:"tx_hash"` BlockNum uint64 `db:"block_num"` BlockHash string `db:"block_hash"` Address string `db:"address"` Topics string `db:"topics"` Data string `db:"data"` } var kvSQLSchema = ` CREATE TABLE IF NOT EXISTS kv ( key text unique, val text ); ` func logSQLSchema(name string) string { return ` CREATE TABLE IF NOT EXISTS ` + name + ` ( indx numeric, tx_index numeric, tx_hash text, block_num numeric, block_hash text, address text, topics text, data text ); ` } ================================================ FILE: tracker/store/postgresql/postgresql_store_test.go ================================================ package trackerpostgresql import ( "database/sql" "fmt" "testing" "github.com/ory/dockertest" "github.com/umbracle/ethgo/tracker/store" ) func setupDB(t *testing.T) (store.Store, func()) { pool, err := dockertest.NewPool("") if err != nil { t.Fatalf("Could not connect to docker: %s", err) } resource, err := pool.Run("postgres", "latest", []string{"POSTGRES_HOST_AUTH_METHOD=trust"}) if err != nil { t.Fatalf("Could not start resource: %s", err) } endpoint := fmt.Sprintf("postgres://postgres@localhost:%s/postgres?sslmode=disable", resource.GetPort("5432/tcp")) // wait for the db to be running if err := pool.Retry(func() error { db, err := sql.Open("postgres", endpoint) if err != nil { return err } return db.Ping() }); err != nil { t.Fatal(err) } cleanup := func() { if err := pool.Purge(resource); err != nil { t.Fatalf("Could not purge resource: %s", err) } } store, err := NewPostgreSQLStore(endpoint) if err != nil { t.Fatal(err) } return store, cleanup } func TestPostgreSQLStore(t *testing.T) { store.TestStore(t, setupDB) } ================================================ FILE: tracker/store/store.go ================================================ package store import "github.com/umbracle/ethgo" // Store is a datastore for the tracker type Store interface { // Get gets a value Get(k string) (string, error) // ListPrefix lists values by prefix ListPrefix(prefix string) ([]string, error) // Set sets a value Set(k, v string) error // Close closes the store Close() error // GetEntry returns a specific entry GetEntry(hash string) (Entry, error) } // Entry is a filter entry in the store type Entry interface { // LastIndex returns index of the last stored event LastIndex() (uint64, error) // StoreLogs stores the web3 logs of the event StoreLogs(logs []*ethgo.Log) error // RemoveLogs all the logs starting at index 'indx' RemoveLogs(indx uint64) error // GetLog returns the log at indx GetLog(indx uint64, log *ethgo.Log) error } ================================================ FILE: tracker/store/testing.go ================================================ package store import ( "reflect" "testing" "github.com/umbracle/ethgo" ) // SetupDB is a function that creates a backend type SetupDB func(t *testing.T) (Store, func()) // TestStore tests a tracker store func TestStore(t *testing.T, setup SetupDB) { testMultipleStores(t, setup) testGetSet(t, setup) testRemoveLogs(t, setup) testStoreLogs(t, setup) testPrefix(t, setup) } func testMultipleStores(t *testing.T, setup SetupDB) { store, close := setup(t) defer close() entry0, err := store.GetEntry("0") if err != nil { t.Fatal(err) } log := ethgo.Log{ BlockNumber: 10, } if err := entry0.StoreLogs([]*ethgo.Log{&log}); err != nil { t.Fatal(err) } entry1, err := store.GetEntry("1") if err != nil { t.Fatal(err) } log = ethgo.Log{ BlockNumber: 15, } if err := entry1.StoreLogs([]*ethgo.Log{&log}); err != nil { t.Fatal(err) } index0, err := entry0.LastIndex() if err != nil { t.Fatal(err) } if index0 != 1 { t.Fatal("bad") } index1, err := entry1.LastIndex() if err != nil { t.Fatal(err) } if index1 != 1 { t.Fatal("bad") } } func testPrefix(t *testing.T, setup SetupDB) { store, close := setup(t) defer close() v1 := "val1" v2 := "val2" v3 := "val3" // add same prefix values if err := store.Set(v1, v1); err != nil { t.Fatal(err) } if err := store.Set(v2, v2); err != nil { t.Fatal(err) } if err := store.Set(v3, v3); err != nil { t.Fatal(err) } // add distinct value if err := store.Set("a", "b"); err != nil { t.Fatal(err) } checkPrefix := func(prefix string, expected int) { res, err := store.ListPrefix(prefix) if err != nil { t.Fatal(err) } if len(res) != expected { t.Fatalf("%d values expected for prefix '%s' but %d found", expected, string(prefix), len(res)) } } checkPrefix("val", 3) checkPrefix("a", 1) checkPrefix("b", 0) } func testGetSet(t *testing.T, setup SetupDB) { store, close := setup(t) defer close() k1 := "k1" v1 := "v1" v2 := "v2" res, err := store.Get(k1) if err != nil { t.Fatal(err) } if len(res) != 0 { t.Fatal("expected empty") } // set the entry if err := store.Set(k1, v1); err != nil { t.Fatal(err) } res, err = store.Get(k1) if err != nil { t.Fatal(err) } if res != v1 { t.Fatal("bad") } // update the entry if err := store.Set(k1, v2); err != nil { t.Fatal(err) } res, err = store.Get(k1) if err != nil { t.Fatal(err) } if res != v2 { t.Fatal("bad") } } func testStoreLogs(t *testing.T, setup SetupDB) { store, close := setup(t) defer close() entry, err := store.GetEntry("1") if err != nil { t.Fatal(err) } indx, err := entry.LastIndex() if err != nil { t.Fatal(err) } if indx != 0 { t.Fatal("index should be zero") } log := ethgo.Log{ BlockNumber: 10, } if err := entry.StoreLogs([]*ethgo.Log{&log}); err != nil { t.Fatal(err) } indx, err = entry.LastIndex() if err != nil { t.Fatal(err) } if indx != 1 { t.Fatal("index should be one") } var log2 ethgo.Log if err := entry.GetLog(0, &log2); err != nil { t.Fatal(err) } if !reflect.DeepEqual(log, log2) { t.Fatal("bad") } // retrieve entry again entry1, err := store.GetEntry("1") if err != nil { t.Fatal(err) } indx1, err := entry1.LastIndex() if err != nil { t.Fatal(err) } if indx1 != indx { t.Fatal("bad last index") } } func testRemoveLogs(t *testing.T, setup SetupDB) { store, close := setup(t) defer close() logs := []*ethgo.Log{} for i := uint64(0); i < 10; i++ { logs = append(logs, ðgo.Log{ BlockNumber: i, }) } entry, err := store.GetEntry("1") if err != nil { t.Fatal(err) } if err := entry.StoreLogs(logs); err != nil { t.Fatal(err) } if err := entry.RemoveLogs(5); err != nil { t.Fatal(err) } indx, err := entry.LastIndex() if err != nil { t.Fatal(err) } if indx != 5 { t.Fatal("bad") } // add again the values if err := entry.StoreLogs(logs[5:]); err != nil { t.Fatal(err) } indx, err = entry.LastIndex() if err != nil { t.Fatal(err) } if indx != 10 { t.Fatal("bad") } } ================================================ FILE: tracker/tracker.go ================================================ package tracker import ( "context" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "io/ioutil" "log" "math/big" "strconv" "strings" "sync" "sync/atomic" "time" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/blocktracker" "github.com/umbracle/ethgo/etherscan" "github.com/umbracle/ethgo/jsonrpc/codec" "github.com/umbracle/ethgo/tracker/store" "github.com/umbracle/ethgo/tracker/store/inmem" ) var ( dbGenesis = "genesis" dbChainID = "chainID" dbLastBlock = "lastBlock" dbFilter = "filter" ) const ( defaultBatchSize = 100 ) // BlockTracking defines interface for block tracker implementations type BlockTracking interface { BlocksBlocked() []*ethgo.Block AddBlockLocked(block *ethgo.Block) error MaxBlockBacklog() uint64 Init() error Start() error Close() error Subscribe() chan *blocktracker.BlockEvent AcquireLock() blocktracker.Lock LastBlocked() *ethgo.Block HandleBlockEvent(block *ethgo.Block) (*blocktracker.BlockEvent, error) Len() int } // FilterConfig is a tracker filter configuration type FilterConfig struct { Address []ethgo.Address `json:"address"` Topics [][]*ethgo.Hash `json:"topics"` Start uint64 Hash string Async bool } func (f *FilterConfig) buildHash() { h := sha256.New() for _, i := range f.Address { h.Write([]byte(i.String())) } for _, topics := range f.Topics { if topics == nil { h.Write([]byte("empty")) continue } for _, innerTopic := range topics { if innerTopic == nil { h.Write([]byte("empty")) continue } h.Write([]byte(innerTopic.String())) } } f.Hash = hex.EncodeToString(h.Sum(nil)) } func (f *FilterConfig) getFilterSearch() *ethgo.LogFilter { filter := ðgo.LogFilter{} if len(f.Address) != 0 { filter.Address = f.Address } if len(f.Topics) != 0 { filter.Topics = f.Topics } return filter } // Config is the configuration of the tracker type Config struct { BatchSize uint64 BlockTracker BlockTracking EtherscanAPIKey string Filter *FilterConfig Store store.Store } type ConfigOption func(*Config) func WithBatchSize(b uint64) ConfigOption { return func(c *Config) { c.BatchSize = b } } func WithBlockTracker(b BlockTracking) ConfigOption { return func(c *Config) { c.BlockTracker = b } } func WithStore(s store.Store) ConfigOption { return func(c *Config) { c.Store = s } } func WithFilter(f *FilterConfig) ConfigOption { return func(c *Config) { c.Filter = f } } func WithEtherscan(k string) ConfigOption { return func(c *Config) { c.EtherscanAPIKey = k } } // DefaultConfig returns the default tracker config func DefaultConfig() *Config { return &Config{ BatchSize: defaultBatchSize, Store: inmem.NewInmemStore(), Filter: &FilterConfig{}, EtherscanAPIKey: "", } } // Provider are the eth1x methods required by the tracker type Provider interface { BlockNumber() (uint64, error) GetBlockByHash(hash ethgo.Hash, full bool) (*ethgo.Block, error) GetBlockByNumber(i ethgo.BlockNumber, full bool) (*ethgo.Block, error) GetLogs(filter *ethgo.LogFilter) ([]*ethgo.Log, error) ChainID() (*big.Int, error) } // Tracker is a contract event tracker type Tracker struct { logger *log.Logger provider Provider config *Config store store.Store entry store.Entry preSyncOnce sync.Once blockTracker BlockTracking synced int32 BlockCh chan *blocktracker.BlockEvent ReadyCh chan struct{} SyncCh chan uint64 EventCh chan *Event DoneCh chan struct{} } // NewTracker creates a new tracker func NewTracker(provider Provider, opts ...ConfigOption) (*Tracker, error) { config := DefaultConfig() for _, opt := range opts { opt(config) } t := &Tracker{ provider: provider, config: config, BlockCh: make(chan *blocktracker.BlockEvent, 1), logger: log.New(ioutil.Discard, "", log.LstdFlags), ReadyCh: make(chan struct{}), store: config.Store, blockTracker: config.BlockTracker, DoneCh: make(chan struct{}, 1), EventCh: make(chan *Event), SyncCh: make(chan uint64, 1), synced: 0, } if err := t.setupFilter(); err != nil { return nil, err } return t, nil } // NewFilter creates a new log filter func (t *Tracker) setupFilter() error { if t.config.Filter == nil { // generic config t.config.Filter = &FilterConfig{} } // generate a random hash if not provided if t.config.Filter.Hash == "" { t.config.Filter.buildHash() } entry, err := t.store.GetEntry(t.config.Filter.Hash) if err != nil { return err } t.entry = entry // insert the filter config in the db filterKey := dbFilter + "_" + t.config.Filter.Hash data, err := t.store.Get(filterKey) if err != nil { return err } if data == "" { raw, err := json.Marshal(t.config.Filter) if err != nil { return err } rawStr := hex.EncodeToString(raw) if err := t.store.Set(filterKey, rawStr); err != nil { return err } } return nil } func (t *Tracker) Entry() store.Entry { return t.entry } // GetLastBlock returns the last block processed for this filter func (t *Tracker) GetLastBlock() (*ethgo.Block, error) { buf, err := t.store.Get(dbLastBlock + "_" + t.config.Filter.Hash) if err != nil { return nil, err } if len(buf) == 0 { return nil, nil } raw, err := hex.DecodeString(buf) if err != nil { return nil, err } b := ðgo.Block{} if err := b.UnmarshalJSON(raw); err != nil { return nil, err } return b, nil } func (t *Tracker) storeLastBlock(b *ethgo.Block) error { if b.Difficulty == nil { b.Difficulty = big.NewInt(0) } buf, err := b.MarshalJSON() if err != nil { return err } raw := hex.EncodeToString(buf) return t.store.Set(dbLastBlock+"_"+t.config.Filter.Hash, raw) } func (t *Tracker) emitEvent(evnt *Event) { if evnt == nil { return } if t.config.Filter.Async { select { case t.EventCh <- evnt: default: } } else { t.EventCh <- evnt } } // IsSynced returns true if the filter is synced to head func (t *Tracker) IsSynced() bool { return atomic.LoadInt32(&t.synced) != 0 } // Wait waits the filter to finish func (t *Tracker) Wait() { t.WaitDuration(0) } // WaitDuration waits for the filter to finish up to duration func (t *Tracker) WaitDuration(dur time.Duration) error { if t.IsSynced() { return nil } var waitCh <-chan time.Time if dur == 0 { waitCh = time.After(dur) } select { case <-waitCh: return fmt.Errorf("timeout") case <-t.DoneCh: } return nil } func (t *Tracker) findAncestor(block, pivot *ethgo.Block) (uint64, error) { // block is part of a fork that is not the current head, find a common ancestor // both block and pivot are at the same height var err error for i := uint64(0); i < t.blockTracker.MaxBlockBacklog(); i++ { if block.Number != pivot.Number { return 0, fmt.Errorf("block numbers do not match") } if block.Hash == pivot.Hash { // this is the common ancestor in both return block.Number, nil } block, err = t.provider.GetBlockByHash(block.ParentHash, false) if err != nil { return 0, err } else if block == nil { // if block does not exist (for example reorg happened) GetBlockByNumber will return nil, nil return 0, fmt.Errorf("block with hash %s not found", block.ParentHash) } pivot, err = t.provider.GetBlockByHash(pivot.ParentHash, false) if err != nil { return 0, err } else if pivot == nil { // if block does not exist (for example reorg happened) GetBlockByNumber will return nil, nil return 0, fmt.Errorf("block/pivot with hash %s not found", pivot.ParentHash) } } return 0, fmt.Errorf("the reorg is bigger than maxBlockBacklog %d", t.blockTracker.MaxBlockBacklog()) } func (t *Tracker) emitLogs(typ EventType, logs []*ethgo.Log) { evnt := &Event{} if typ == EventAdd { evnt.Added = logs } if typ == EventDel { evnt.Removed = logs } t.emitEvent(evnt) } func tooMuchDataRequestedError(err error) bool { obj, ok := err.(*codec.ErrorObject) if !ok { return false } if obj.Message == "query returned more than 10000 results" { return true } return false } func (t *Tracker) syncBatch(ctx context.Context, from, to uint64) error { query := t.config.Filter.getFilterSearch() batchSize := t.config.BatchSize additiveFactor := uint64(float64(batchSize) * 0.10) i := from START: dst := min(to, i+batchSize) query.SetFromUint64(i) query.SetToUint64(dst) logs, err := t.provider.GetLogs(query) if err != nil { if tooMuchDataRequestedError(err) { // multiplicative decrease batchSize = batchSize / 2 goto START } return err } if t.SyncCh != nil { select { case t.SyncCh <- dst: default: } } // add logs to the store if err := t.entry.StoreLogs(logs); err != nil { return err } t.emitLogs(EventAdd, logs) // update the last block entry block, err := t.getBlockByNumber(dst) if err != nil { return err } if err := t.storeLastBlock(block); err != nil { return err } // check if the execution is over after each query batch if err := ctx.Err(); err != nil { return err } i += batchSize + 1 // update the batchSize with additive increase if batchSize < t.config.BatchSize { batchSize = min(t.config.BatchSize, batchSize+additiveFactor) } if i <= to { goto START } return nil } func (t *Tracker) preSyncCheck() error { var err error t.preSyncOnce.Do(func() { err = t.preSyncCheckImpl() }) return err } func (t *Tracker) preSyncCheckImpl() error { rGenesis, err := t.getBlockByNumber(0) if err != nil { return err } rChainID, err := t.provider.ChainID() if err != nil { return err } genesis, err := t.store.Get(dbGenesis) if err != nil { return err } chainID, err := t.store.Get(dbChainID) if err != nil { return err } if len(genesis) != 0 { if genesis != rGenesis.Hash.String() { return fmt.Errorf("bad genesis") } if chainID != rChainID.String() { return fmt.Errorf("bad genesis") } } else { if err := t.store.Set(dbGenesis, rGenesis.Hash.String()); err != nil { return err } if err := t.store.Set(dbChainID, rChainID.String()); err != nil { return err } } return nil } func (t *Tracker) fastTrack(filterConfig *FilterConfig) (*ethgo.Block, error) { // Try to use first the user provided block if any if filterConfig.Start != 0 { bb, err := t.getBlockByNumber(filterConfig.Start) if err != nil { return nil, err } return bb, nil } // Only possible if we filter addresses if len(filterConfig.Address) == 0 { return nil, nil } if t.config.EtherscanAPIKey != "" { chainID, err := t.provider.ChainID() if err != nil { return nil, err } // get the etherscan instance for this chainID e, err := etherscan.NewEtherscanFromNetwork(ethgo.Network(chainID.Uint64()), t.config.EtherscanAPIKey) if err != nil { // there is no etherscan api for this specific chainid return nil, nil } getAddress := func(addr ethgo.Address) (uint64, error) { params := map[string]string{ "address": addr.String(), "fromBlock": "0", "toBlock": "latest", } var out []map[string]interface{} if err := e.Query("logs", "getLogs", &out, params); err != nil { return 0, err } if len(out) == 0 { return 0, nil } cc, ok := out[0]["blockNumber"].(string) if !ok { return 0, fmt.Errorf("failed to cast blocknumber") } num, err := parseUint64orHex(cc) if err != nil { return 0, err } return num, nil } minBlock := ^uint64(0) // max uint64 for _, addr := range filterConfig.Address { num, err := getAddress(addr) if err != nil { return nil, err } if num < minBlock { minBlock = num } } bb, err := t.getBlockByNumber(minBlock - 1) if err != nil { return nil, err } return bb, nil } return nil, nil } func (t *Tracker) BatchSync(ctx context.Context) error { if err := t.preSyncCheck(); err != nil { return err } if t.blockTracker == nil { // run a specfic block tracker t.blockTracker = blocktracker.NewBlockTracker(t.provider) if err := t.blockTracker.Init(); err != nil { return err } go t.blockTracker.Start() go func() { // track our stop <-ctx.Done() t.blockTracker.Close() }() } else { // just try to init if err := t.blockTracker.Init(); err != nil { return err } } close(t.ReadyCh) if err := t.syncImpl(ctx); err != nil { return err } select { case t.DoneCh <- struct{}{}: default: } atomic.StoreInt32(&t.synced, 1) return nil } // Sync syncs a specific filter. // This can take a long time so should be run concurrently. func (t *Tracker) Sync(ctx context.Context) error { if err := t.BatchSync(ctx); err != nil { return err } // subscribe and sync sub := t.blockTracker.Subscribe() for { select { case evnt := <-sub: if err := t.handleBlockEvnt(evnt); err != nil { return err } case <-ctx.Done(): return ctx.Err() } } } func (t *Tracker) syncImpl(ctx context.Context) error { if err := t.preSyncCheck(); err != nil { return err } lock := t.blockTracker.AcquireLock() defer func() { if lock.Locked { lock.Unlock() } }() // We only hold the lock when we sync the head (last MaxBackLogs) // because we want to avoid changes in the head while we sync. // We will only release the lock if we do a bulk sync since it can // move the target block for the sync. lock.Lock() if t.blockTracker.Len() == 0 { return nil } // get the current target target := t.blockTracker.LastBlocked() if target == nil { return nil } targetNum := target.Number last, err := t.GetLastBlock() if err != nil { return err } if last == nil { // Try to fast track to the valid block (if possible) last, err = t.fastTrack(t.config.Filter) if err != nil { return fmt.Errorf("failed to fasttrack: %v", err) } if last != nil { if err := t.storeLastBlock(last); err != nil { return err } } } else { if last.Hash == target.Hash { return nil } } // There might been a reorg when we stopped syncing last time, // check that our 'beacon' block matches the one in the chain. // If that is not the case, we consider beacon-maxBackLog our // real origin point and remove any logs ahead of that point. var origin uint64 if last != nil { if last.Number > targetNum { return fmt.Errorf("store is more advanced than the chain") } pivot, err := t.getBlockByNumber(last.Number) if err != nil { return err } if last.Number == targetNum { origin = last.Number } else { origin = last.Number + 1 } if pivot.Hash != last.Hash { ancestor, err := t.findAncestor(last, pivot) if err != nil { return err } origin = ancestor + 1 logs, err := t.removeLogs(ancestor+1, nil) if err != nil { return err } t.emitLogs(EventDel, logs) last, err = t.getBlockByNumber(ancestor) if err != nil { return err } } } step := targetNum - origin + 1 if step > t.blockTracker.MaxBlockBacklog() { // we are far (more than maxBackLog) from the target block // Do a bulk sync with the eth_getLogs endpoint and get closer // to the target block. for { if origin > targetNum { return fmt.Errorf("from (%d) higher than to (%d)", origin, targetNum) } if targetNum-origin+1 <= t.blockTracker.MaxBlockBacklog() { break } // release the lock lock.Unlock() limit := targetNum - t.blockTracker.MaxBlockBacklog() if err := t.syncBatch(ctx, origin, limit); err != nil { return err } origin = limit + 1 // lock again to reset the target block lock.Lock() targetNum = t.blockTracker.LastBlocked().Number } } // we are still holding the lock on the blocksLock so that we are sure // that the targetNum has not changed trackerBlocks := t.blockTracker.BlocksBlocked() added := trackerBlocks[uint64(len(trackerBlocks))-1-(targetNum-origin):] evnt, err := t.doFilter(added, nil) if err != nil { return err } if evnt != nil { t.emitEvent(evnt) } // release the lock on the blocks lock.Unlock() return nil } func (t *Tracker) removeLogs(number uint64, hash *ethgo.Hash) ([]*ethgo.Log, error) { index, err := t.entry.LastIndex() if err != nil { return nil, err } if index == 0 { return nil, nil } var remove []*ethgo.Log for { elemIndex := index - 1 var log ethgo.Log if err := t.entry.GetLog(elemIndex, &log); err != nil { return nil, err } if log.BlockNumber == number { if hash != nil && log.BlockHash != *hash { break } } if log.BlockNumber < number { break } remove = append(remove, &log) if elemIndex == 0 { index = 0 break } index = elemIndex } if err := t.entry.RemoveLogs(index); err != nil { return nil, err } return remove, nil } func revertLogs(in []*ethgo.Log) (out []*ethgo.Log) { for i := len(in) - 1; i >= 0; i-- { out = append(out, in[i]) } return } func (t *Tracker) handleBlockEvnt(blockEvnt *blocktracker.BlockEvent) error { if blockEvnt == nil { return nil } // emit the block event select { case t.BlockCh <- blockEvnt: default: } if t.IsSynced() { evnt, err := t.doFilter(blockEvnt.Added, blockEvnt.Removed) if err != nil { return err } if evnt != nil { t.emitEvent(evnt) } } return nil } func (t *Tracker) doFilter(added []*ethgo.Block, removed []*ethgo.Block) (*Event, error) { evnt := &Event{} if len(removed) != 0 { pivot := removed[0] logs, err := t.removeLogs(pivot.Number, &pivot.Hash) if err != nil { return nil, err } evnt.Removed = append(evnt.Removed, revertLogs(logs)...) } for _, block := range added { // check logs for this blocks query := t.config.Filter.getFilterSearch() query.BlockHash = &block.Hash // We check the hash, we need to do a retry to let unsynced nodes get the block var logs []*ethgo.Log var err error for i := 0; i < 5; i++ { logs, err = t.provider.GetLogs(query) if err == nil { break } time.Sleep(500 * time.Millisecond) } if err != nil { return nil, err } // add logs to the store if err := t.entry.StoreLogs(logs); err != nil { return nil, err } evnt.Added = append(evnt.Added, logs...) } // store the last block as the new index if err := t.storeLastBlock(added[len(added)-1]); err != nil { return nil, err } return evnt, nil } func (t *Tracker) getBlockByNumber(blockNumber uint64) (*ethgo.Block, error) { block, err := t.provider.GetBlockByNumber(ethgo.BlockNumber(blockNumber), false) if err != nil { return nil, err } else if block == nil { // if block does not exist (for example reorg happened) GetBlockByNumber will return nil, nil return nil, fmt.Errorf("block with number %d not found", blockNumber) } return block, nil } // EventType is the type of the event type EventType int const ( // EventAdd happens when a new event is included in the chain EventAdd EventType = iota // EventDel may happen when there is a reorg and a past event is deleted EventDel ) // Event is an event emitted when a new log is included type Event struct { Type EventType Added []*ethgo.Log Removed []*ethgo.Log } // BlockEvent is an event emitted when a new block is included type BlockEvent struct { Type EventType Added []*ethgo.Block Removed []*ethgo.Block } func min(i, j uint64) uint64 { if i < j { return i } return j } func parseUint64orHex(str string) (uint64, error) { base := 10 if strings.HasPrefix(str, "0x") { str = str[2:] base = 16 } return strconv.ParseUint(str, base, 64) } ================================================ FILE: tracker/tracker_test.go ================================================ package tracker import ( "context" "fmt" "math/big" "math/rand" "reflect" "strconv" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/abi" "github.com/umbracle/ethgo/blocktracker" "github.com/umbracle/ethgo/jsonrpc" "github.com/umbracle/ethgo/jsonrpc/codec" "github.com/umbracle/ethgo/testutil" "github.com/umbracle/ethgo/tracker/store/inmem" ) func testConfig() ConfigOption { return func(c *Config) { c.BatchSize = 10 } } func testFilter(t *testing.T, provider Provider, filterConfig *FilterConfig) []*ethgo.Log { filterConfig.Async = true tt, _ := NewTracker(provider, WithFilter(filterConfig)) ctx, cancelFn := context.WithCancel(context.Background()) defer cancelFn() if err := tt.BatchSync(ctx); err != nil { require.NoError(t, err) } return tt.entry.(*inmem.Entry).Logs() } func TestPolling(t *testing.T) { t.Skip() s := testutil.NewTestServer(t) client, _ := jsonrpc.NewClient(s.HTTPAddr()) c0 := &testutil.Contract{} c0.AddEvent(testutil.NewEvent("A").Add("uint256", true).Add("uint256", true)) c0.EmitEvent("setA1", "A", "1", "2") _, addr0, err := s.DeployContract(c0) require.NoError(t, err) // send 5 txns for i := 0; i < 5; i++ { s.TxnTo(addr0, "setA1") } tt, err := NewTracker(client.Eth()) assert.NoError(t, err) ctx, cancelFn := context.WithCancel(context.Background()) defer cancelFn() go func() { if err := tt.Sync(ctx); err != nil { panic(err) } }() // wait for the bulk sync to finish for { select { case <-tt.EventCh: case <-tt.DoneCh: goto EXIT case <-time.After(1 * time.Second): t.Fatal("timeout to sync") } } EXIT: // send another 5 transactions, we have to have another log each time for i := 0; i < 5; i++ { receipt, err := s.TxnTo(addr0, "setA1") require.NoError(t, err) select { case evnt := <-tt.EventCh: if !reflect.DeepEqual(evnt.Added, receipt.Logs) { t.Fatal("bad") } case <-time.After(2 * time.Second): // wait at least the polling interval t.Fatal("event expected") } } } func TestFilterIntegration(t *testing.T) { s := testutil.NewTestServer(t) client, _ := jsonrpc.NewClient(s.HTTPAddr()) c0 := &testutil.Contract{} c0.AddEvent(testutil.NewEvent("A").Add("uint256", true).Add("uint256", true)) c0.EmitEvent("setA1", "A", "1", "2") _, addr0, err := s.DeployContract(c0) require.NoError(t, err) _, addr1, err := s.DeployContract(c0) require.NoError(t, err) for i := 0; i < 20; i++ { if i%2 == 0 { _, err := s.TxnTo(addr0, "setA1") require.NoError(t, err) } else { _, err := s.TxnTo(addr1, "setA1") require.NoError(t, err) } } // filter by address logs := testFilter(t, client.Eth(), &FilterConfig{Address: []ethgo.Address{addr0}}) require.NotEmpty(t, logs) // filter by value typ, _ := abi.NewType("uint256") topic, _ := abi.EncodeTopic(typ, 1) logs = testFilter(t, client.Eth(), &FilterConfig{Topics: [][]*ethgo.Hash{nil, {&topic}}}) require.NotEmpty(t, logs) } func TestPreflight(t *testing.T) { store := inmem.NewInmemStore() l := testutil.MockList{} l.Create(0, 100, func(b *testutil.MockBlock) {}) m := &testutil.MockClient{} m.AddScenario(l) tt0, _ := NewTracker(m, testConfig(), WithStore(store)) if err := tt0.preSyncCheckImpl(); err != nil { t.Fatal(err) } // change the genesis hash l0 := testutil.MockList{} l0.Create(0, 100, func(b *testutil.MockBlock) { b = b.Extra("1") }) m.AddScenario(l0) tt1, _ := NewTracker(m, testConfig(), WithStore(store)) if err := tt1.preSyncCheckImpl(); err == nil { t.Fatal("it should fail") } // change the chainID m.AddScenario(l) m.SetChainID(big.NewInt(1)) tt2, _ := NewTracker(m, testConfig(), WithStore(store)) if err := tt2.preSyncCheckImpl(); err == nil { t.Fatal("it should fail") } } func TestTrackerSyncerRestarts(t *testing.T) { store := inmem.NewInmemStore() m := &testutil.MockClient{} l := testutil.MockList{} advance := func(first, last int, void ...bool) { if len(void) == 0 { l.Create(first, last, func(b *testutil.MockBlock) { if b.GetNum()%5 == 0 { b = b.Log("0x1") } }) m.AddScenario(l) } tt, err := NewTracker(m, testConfig(), WithStore(store), WithFilter(&FilterConfig{Async: true}), ) assert.NoError(t, err) go func() { if err := tt.Sync(context.Background()); err != nil { panic(err) } }() if err := tt.WaitDuration(2 * time.Second); err != nil { t.Fatal(err) } if tt.blockTracker.BlocksBlocked()[0].Number != uint64(last-10) { t.Fatal("bad") } if tt.blockTracker.BlocksBlocked()[9].Number != uint64(last-1) { t.Fatal("bad") } if !testutil.CompareLogs(l.GetLogs(), tt.entry.(*inmem.Entry).Logs()) { t.Fatal("bad") } } // initial range advance(0, 100) // dont advance advance(0, 100, true) // advance less than backlog advance(100, 105) // advance more than backlog advance(105, 150) } func testSyncerReconcile(t *testing.T, iniLen, forkNum, endLen int) { // test that the syncer can reconcile if there is a fork in the saved state l := testutil.MockList{} l.Create(0, iniLen, func(b *testutil.MockBlock) { b = b.Log("0x01") }) m := &testutil.MockClient{} m.AddScenario(l) store := inmem.NewInmemStore() tt0, err := NewTracker(m, testConfig(), WithStore(store), WithFilter(&FilterConfig{Async: true}), ) assert.NoError(t, err) go func() { if err := tt0.Sync(context.Background()); err != nil { panic(err) } }() tt0.WaitDuration(2 * time.Second) // create a fork at 'forkNum' and continue to 'endLen' l1 := testutil.MockList{} l1.Create(0, endLen, func(b *testutil.MockBlock) { if b.GetNum() < forkNum { b = b.Log("0x01") // old fork } else { if b.GetNum() == forkNum { b = b.Log("0x02") } else { b = b.Log("0x03") } b = b.Extra("123") // used to set the new fork } }) m1 := &testutil.MockClient{} m1.AddScenario(l) m1.AddScenario(l1) tt1, _ := NewTracker(m1, testConfig(), WithStore(store), WithFilter(&FilterConfig{Async: true}), ) go func() { if err := tt1.Sync(context.Background()); err != nil { panic(err) } }() tt1.WaitDuration(2 * time.Second) logs := tt1.entry.(*inmem.Entry).Logs() if !testutil.CompareLogs(l1.GetLogs(), logs) { t.Fatal("bad") } // check the content of the logs // first half for i := 0; i < forkNum; i++ { if logs[i].Data[0] != 0x1 { t.Fatal("bad") } } // fork point if logs[forkNum].Data[0] != 0x2 { t.Fatal("bad") } // second half for i := forkNum + 1; i < endLen; i++ { if logs[i].Data[0] != 0x3 { t.Fatal("bad") } } } func TestTrackerSyncerReconcile(t *testing.T) { t.Run("Backlog", func(t *testing.T) { testSyncerReconcile(t, 50, 45, 55) }) t.Run("Historical", func(t *testing.T) { testSyncerReconcile(t, 50, 45, 100) }) } func randomInt(min, max int) int { return min + rand.Intn(max-min) } func testTrackerSyncerRandom(t *testing.T, n int, backlog uint64) { m := &testutil.MockClient{} c := 0 // current block f := 0 // current fork store := inmem.NewInmemStore() for i := 0; i < n; i++ { // fmt.Println("########################################") // create the new batch of blocks var forkSize int if randomInt(0, 10) < 3 && c > 10 { // add a fork, go back at most maxBacklogSize forkSize = randomInt(1, int(backlog)) c = c - forkSize f++ } forkID := strconv.Itoa(f) // add new blocks l := testutil.MockList{} // we have to create at least the blocks removed by the fork, otherwise // we may end up going backwards if the forks remove more data than the // advance includes start := forkSize if start == 0 && i == 0 { start = 1 // at least advance one block on the first iteration } num := randomInt(start, 20) count := 0 for j := c; j < c+num; j++ { bb := testutil.Mock(j).Extra(forkID) if j != 0 { count++ bb = bb.Log(forkID) } l = append(l, bb) } m.AddScenario(l) // use a custom block tracker to add specific backlog tracker := blocktracker.NewBlockTracker(m, blocktracker.WithBlockMaxBacklog(backlog)) tt, _ := NewTracker(m, testConfig(), WithStore(store), WithBlockTracker(tracker), ) go func() { if err := tt.Sync(context.Background()); err != nil { panic(err) } }() var added, removed []*ethgo.Log for { select { case evnt := <-tt.EventCh: added = append(added, evnt.Added...) removed = append(removed, evnt.Removed...) case <-tt.DoneCh: // no more events to read goto EXIT } } EXIT: // validate the included logs if len(added) != count { t.Fatal("bad added logs") } // validate the removed logs if len(removed) != forkSize { t.Fatal("bad removed logs") } // validate blocks if blocks := m.GetLastBlocks(backlog); !testutil.CompareBlocks(tt.blockTracker.BlocksBlocked(), blocks) { // tracker does not consider block 0 but getLastBlocks does return it, this is only a problem // with syncs on chains lower than maxBacklog if !testutil.CompareBlocks(blocks[1:], tt.blockTracker.BlocksBlocked()) { t.Fatal("bad blocks") } } // validate logs if logs := m.GetAllLogs(); !testutil.CompareLogs(tt.entry.(*inmem.Entry).Logs(), logs) { t.Fatal("bad logs") } c += num } } func TestTrackerSyncerRandom(t *testing.T) { rand.Seed(time.Now().UTC().UnixNano()) for i := 0; i < 100; i++ { t.Run("", func(t *testing.T) { testTrackerSyncerRandom(t, 100, uint64(randomInt(2, 10))) }) } } func TestTrackerReconcile(t *testing.T) { type TestEvent struct { Added testutil.MockList Removed testutil.MockList } type Reconcile struct { block *testutil.MockBlock event *TestEvent } cases := []struct { Name string Scenario testutil.MockList History testutil.MockList Reconcile []Reconcile Expected testutil.MockList }{ { Name: "Empty history", Reconcile: []Reconcile{ { block: testutil.Mock(0x1).Log("0x1"), event: &TestEvent{ Added: testutil.MockList{ testutil.Mock(0x1).Log("0x1"), }, }, }, }, Expected: []*testutil.MockBlock{ testutil.Mock(1).Log("0x1"), }, }, { Name: "Repeated header", History: []*testutil.MockBlock{ testutil.Mock(0x1), }, Reconcile: []Reconcile{ { block: testutil.Mock(0x1), }, }, Expected: []*testutil.MockBlock{ testutil.Mock(0x1), }, }, { Name: "New head", History: testutil.MockList{ testutil.Mock(0x1), }, Reconcile: []Reconcile{ { block: testutil.Mock(0x2), event: &TestEvent{ Added: testutil.MockList{ testutil.Mock(0x2), }, }, }, }, Expected: testutil.MockList{ testutil.Mock(0x1), testutil.Mock(0x2), }, }, { Name: "Ignore block already on history", History: testutil.MockList{ testutil.Mock(0x1), testutil.Mock(0x2), testutil.Mock(0x3), }, Reconcile: []Reconcile{ { block: testutil.Mock(0x2), }, }, Expected: testutil.MockList{ testutil.Mock(0x1), testutil.Mock(0x2), testutil.Mock(0x3), }, }, { Name: "Multi Roll back", History: testutil.MockList{ testutil.Mock(0x1), testutil.Mock(0x2), testutil.Mock(0x3).Log("0x3"), testutil.Mock(0x4).Log("0x4"), }, Reconcile: []Reconcile{ { block: testutil.Mock(0x30).Parent(0x2).Log("0x30"), event: &TestEvent{ Added: testutil.MockList{ testutil.Mock(0x30).Parent(0x2).Log("0x30"), }, Removed: testutil.MockList{ testutil.Mock(0x3).Log("0x3"), testutil.Mock(0x4).Log("0x4"), }, }, }, }, Expected: testutil.MockList{ testutil.Mock(0x1), testutil.Mock(0x2), testutil.Mock(0x30).Parent(0x2).Log("0x30"), }, }, { Name: "Backfills missing blocks", Scenario: testutil.MockList{ testutil.Mock(0x3), testutil.Mock(0x4).Log("0x2"), }, History: testutil.MockList{ testutil.Mock(0x1).Log("0x1"), testutil.Mock(0x2), }, Reconcile: []Reconcile{ { block: testutil.Mock(0x5).Log("0x3"), event: &TestEvent{ Added: testutil.MockList{ testutil.Mock(0x3), testutil.Mock(0x4).Log("0x2"), testutil.Mock(0x5).Log("0x3"), }, }, }, }, Expected: testutil.MockList{ testutil.Mock(0x1).Log("0x1"), testutil.Mock(0x2), testutil.Mock(0x3), testutil.Mock(0x4).Log("0x2"), testutil.Mock(0x5).Log("0x3"), }, }, { Name: "Rolls back and backfills", Scenario: testutil.MockList{ testutil.Mock(0x30).Parent(0x2).Num(3).Log("0x5"), testutil.Mock(0x40).Parent(0x30).Num(4), }, History: testutil.MockList{ testutil.Mock(0x1), testutil.Mock(0x2).Log("0x3"), testutil.Mock(0x3).Log("0x2"), testutil.Mock(0x4).Log("0x1"), }, Reconcile: []Reconcile{ { block: testutil.Mock(0x50).Parent(0x40).Num(5), event: &TestEvent{ Added: testutil.MockList{ testutil.Mock(0x30).Parent(0x2).Num(3).Log("0x5"), testutil.Mock(0x40).Parent(0x30).Num(4), testutil.Mock(0x50).Parent(0x40).Num(5), }, Removed: testutil.MockList{ testutil.Mock(0x3).Log("0x2"), testutil.Mock(0x4).Log("0x1"), }, }, }, }, Expected: testutil.MockList{ testutil.Mock(0x1), testutil.Mock(0x2).Log("0x3"), testutil.Mock(0x30).Parent(0x2).Num(3).Log("0x5"), testutil.Mock(0x40).Parent(0x30).Num(4), testutil.Mock(0x50).Parent(0x40).Num(5), }, }, } for _, c := range cases { t.Run(c.Name, func(t *testing.T) { // safe check for now, we ma need to restart the tracker and mock client for every reconcile scenario? if len(c.Reconcile) != 1 { t.Fatal("only one reconcile supported so far") } m := &testutil.MockClient{} // add the full scenario with the logs m.AddScenario(c.Scenario) // add the logs of the reconcile block because those are also unknown for the tracker m.AddLogs(c.Reconcile[0].block.GetLogs()) store := inmem.NewInmemStore() btracker := blocktracker.NewBlockTracker(m) tt, err := NewTracker(m, WithStore(store), WithBlockTracker(btracker)) if err != nil { t.Fatal(err) } // important to set a buffer here, otherwise everything is blocked tt.EventCh = make(chan *Event, 1) // set the filter as synced since we only want to // try reconciliation tt.synced = 1 // build past block history for _, b := range c.History.ToBlocks() { tt.blockTracker.AddBlockLocked(b) } // add the history to the store for _, b := range c.History { tt.entry.StoreLogs(b.GetLogs()) } for _, b := range c.Reconcile { aux, err := tt.blockTracker.HandleBlockEvent(b.block.Block()) if err != nil { t.Fatal(err) } if aux == nil { continue } if err := tt.handleBlockEvnt(aux); err != nil { t.Fatal(err) } var evnt *Event select { case evnt = <-tt.EventCh: case <-time.After(1 * time.Second): t.Fatal("log event timeout") } // check logs if !testutil.CompareLogs(b.event.Added.GetLogs(), evnt.Added) { t.Fatal("err") } if !testutil.CompareLogs(b.event.Removed.GetLogs(), evnt.Removed) { t.Fatal("err") } var blockEvnt *blocktracker.BlockEvent select { case blockEvnt = <-tt.BlockCh: case <-time.After(1 * time.Second): t.Fatal("block event timeout") } // check blocks if !testutil.CompareBlocks(b.event.Added.ToBlocks(), blockEvnt.Added) { t.Fatal("err") } if !testutil.CompareBlocks(b.event.Removed.ToBlocks(), blockEvnt.Removed) { t.Fatal("err") } } // check the post state (logs and blocks) after all the reconcile events if !testutil.CompareLogs(tt.entry.(*inmem.Entry).Logs(), c.Expected.GetLogs()) { t.Fatal("bad3") } if !testutil.CompareBlocks(tt.blockTracker.BlocksBlocked(), c.Expected.ToBlocks()) { t.Fatal("bad") } }) } } type mockClientWithLimit struct { limit uint64 testutil.MockClient } func (m *mockClientWithLimit) GetLogs(filter *ethgo.LogFilter) ([]*ethgo.Log, error) { if filter.BlockHash != nil { return m.MockClient.GetLogs(filter) } from, to := uint64(*filter.From), uint64(*filter.To) if from > to { return nil, fmt.Errorf("from higher than to") } if to-from > m.limit { return nil, &codec.ErrorObject{Message: "query returned more than 10000 results"} } // fallback to the client return m.MockClient.GetLogs(filter) } func TestTooMuchDataRequested(t *testing.T) { count := 0 // create 100 blocks with 2 (even) or 5 (odd) logs each l := testutil.MockList{} l.Create(0, 100, func(b *testutil.MockBlock) { var numLogs int if b.GetNum()%2 == 0 { numLogs = 2 } else { numLogs = 5 } for i := 0; i < numLogs; i++ { count++ b.Log("0x1") } }) m := &testutil.MockClient{} m.AddScenario(l) mm := &mockClientWithLimit{ limit: 3, MockClient: *m, } config := DefaultConfig() config.BatchSize = 11 tt, _ := NewTracker(mm, WithFilter(&FilterConfig{Async: true}), ) if err := tt.BatchSync(context.Background()); err != nil { t.Fatal(err) } if count != len(tt.entry.(*inmem.Entry).Logs()) { t.Fatal("not the same count") } } ================================================ FILE: units.go ================================================ package ethgo import "math/big" func convert(val uint64, decimals int64) *big.Int { v := big.NewInt(int64(val)) exp := new(big.Int).Exp(big.NewInt(10), big.NewInt(decimals), nil) return v.Mul(v, exp) } // Ether converts a value to the ether unit with 18 decimals func Ether(i uint64) *big.Int { return convert(i, 18) } // Gwei converts a value to the gwei unit with 9 decimals func Gwei(i uint64) *big.Int { return convert(i, 9) } ================================================ FILE: wallet/fixtures/wallet_json.json ================================================ [ { "wallet": { "crypto": { "cipher": "aes-128-ctr", "cipherparams": { "iv": "6087dab2f9fdbbfaddc31a909735c1e6" }, "ciphertext": "5318b4d5bcd28de64ee5559e671353e16f075ecae9f99c7a79a38af5f869aa46", "kdf": "pbkdf2", "kdfparams": { "c": 262144, "dklen": 32, "prf": "hmac-sha256", "salt": "ae3cd4e7013836a3df6bd7241b12db061dbe2c6785853cce422d148a624ce0bd" }, "mac": "517ead924a9d0dc3124507e3393d175ce3ff7c1e96529c6c555ce9e51205e9b2" }, "id": "3198bc9c-6672-5ab3-d995-4942343ae5b6", "version": 3 }, "password": "testpassword", "address": "0x008AeEda4D805471dF9b2A5B0f38A0C3bCBA786b" }, { "wallet": { "crypto" : { "cipher" : "aes-128-ctr", "cipherparams" : { "iv" : "83dbcc02d8ccb40e466191a123791e0e" }, "ciphertext" : "d172bf743a674da9cdad04534d56926ef8358534d458fffccd4e6ad2fbde479c", "kdf" : "scrypt", "kdfparams" : { "dklen" : 32, "n" : 262144, "p" : 8, "r" : 1, "salt" : "ab0c7876052600dd703518d6fc3fe8984592145b591fc8fb5c6d43190334ba19" }, "mac" : "2103ac29920d71da29f15d75b4a16dbe95cfd7ff8faea1056c33131d846e3097" }, "id" : "3198bc9c-6672-5ab3-d995-4942343ae5b6", "version" : 3 }, "password": "testpassword", "address": "0x008AeEda4D805471dF9b2A5B0f38A0C3bCBA786b" } ] ================================================ FILE: wallet/key.go ================================================ package wallet import ( "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "fmt" "github.com/btcsuite/btcd/btcec/v2" btcecdsa "github.com/btcsuite/btcd/btcec/v2/ecdsa" "github.com/umbracle/ethgo" ) // S256 is the secp256k1 elliptic curve var S256 = btcec.S256() var _ ethgo.Key = &Key{} // Key is an implementation of the Key interface with a private key type Key struct { priv *btcec.PrivateKey pub *btcec.PublicKey addr ethgo.Address } func (k *Key) Address() ethgo.Address { return k.addr } func (k *Key) MarshallPrivateKey() ([]byte, error) { return (*btcec.PrivateKey)(k.priv).Serialize(), nil } func (k *Key) SignMsg(msg []byte) ([]byte, error) { return k.Sign(ethgo.Keccak256(msg)) } func (k *Key) Sign(hash []byte) ([]byte, error) { sig, err := btcecdsa.SignCompact(k.priv, hash, false) if err != nil { return nil, err } term := byte(0) if sig[0] == 28 { term = 1 } return append(sig, term)[1:], nil } // NewKey creates a new key with a private key func NewKey(prv *ecdsa.PrivateKey) (*Key, error) { var priv btcec.PrivateKey if overflow := priv.Key.SetByteSlice(prv.D.Bytes()); overflow || priv.Key.IsZero() { return nil, fmt.Errorf("invalid key: overflow") } k := &Key{ priv: &priv, pub: priv.PubKey(), addr: pubKeyToAddress(priv.PubKey().ToECDSA()), } return k, nil } func pubKeyToAddress(pub *ecdsa.PublicKey) (addr ethgo.Address) { b := ethgo.Keccak256(elliptic.Marshal(S256, pub.X, pub.Y)[1:]) copy(addr[:], b[12:]) return } // GenerateKey generates a new key based on the secp256k1 elliptic curve. func GenerateKey() (*Key, error) { priv, err := ecdsa.GenerateKey(S256, rand.Reader) if err != nil { return nil, err } return NewKey(priv) } func EcrecoverMsg(msg, signature []byte) (ethgo.Address, error) { return Ecrecover(ethgo.Keccak256(msg), signature) } func Ecrecover(hash, signature []byte) (ethgo.Address, error) { pub, err := RecoverPubkey(signature, hash) if err != nil { return ethgo.Address{}, err } return pubKeyToAddress(pub), nil } func RecoverPubkey(signature, hash []byte) (*ecdsa.PublicKey, error) { size := len(signature) term := byte(27) if signature[size-1] == 1 { term = 28 } sig := append([]byte{term}, signature[:size-1]...) pub, _, err := btcecdsa.RecoverCompact(sig, hash) if err != nil { return nil, err } return pub.ToECDSA(), nil } ================================================ FILE: wallet/key_test.go ================================================ package wallet import ( "testing" "github.com/stretchr/testify/assert" ) func TestKeySign(t *testing.T) { key, err := GenerateKey() assert.NoError(t, err) msg := []byte("hello world") signature, err := key.SignMsg(msg) assert.NoError(t, err) addr, err := EcrecoverMsg(msg, signature) assert.NoError(t, err) assert.Equal(t, addr, key.addr) } ================================================ FILE: wallet/signer.go ================================================ package wallet import ( "math/big" "github.com/umbracle/ethgo" "github.com/umbracle/fastrlp" ) type Signer interface { // RecoverSender returns the sender to the transaction RecoverSender(tx *ethgo.Transaction) (ethgo.Address, error) // SignTx signs a transaction SignTx(tx *ethgo.Transaction, key ethgo.Key) (*ethgo.Transaction, error) } type EIP1155Signer struct { chainID uint64 } func NewEIP155Signer(chainID uint64) *EIP1155Signer { return &EIP1155Signer{chainID: chainID} } func (e *EIP1155Signer) RecoverSender(tx *ethgo.Transaction) (ethgo.Address, error) { v := new(big.Int).SetBytes(tx.V).Uint64() if v > 1 { v -= 27 if v > 1 { v -= e.chainID * 2 v -= 8 } } sig, err := encodeSignature(tx.R, tx.S, byte(v)) if err != nil { return ethgo.Address{}, err } addr, err := Ecrecover(signHash(tx, e.chainID), sig) if err != nil { return ethgo.Address{}, err } return addr, nil } func trimBytesZeros(b []byte) []byte { var i int for i = 0; i < len(b); i++ { if b[i] != 0x0 { break } } return b[i:] } func (e *EIP1155Signer) SignTx(tx *ethgo.Transaction, key ethgo.Key) (*ethgo.Transaction, error) { hash := signHash(tx, e.chainID) sig, err := key.Sign(hash) if err != nil { return nil, err } vv := uint64(sig[64]) if tx.Type == 0 { vv = vv + 35 + e.chainID*2 } tx.R = trimBytesZeros(sig[:32]) tx.S = trimBytesZeros(sig[32:64]) tx.V = new(big.Int).SetUint64(vv).Bytes() return tx, nil } func signHash(tx *ethgo.Transaction, chainID uint64) []byte { a := fastrlp.DefaultArenaPool.Get() defer fastrlp.DefaultArenaPool.Put(a) v := a.NewArray() if tx.Type != ethgo.TransactionLegacy { // either dynamic and access type v.Set(a.NewBigInt(new(big.Int).SetUint64(chainID))) } v.Set(a.NewUint(tx.Nonce)) if tx.Type == ethgo.TransactionDynamicFee { // dynamic fee uses v.Set(a.NewBigInt(tx.MaxPriorityFeePerGas)) v.Set(a.NewBigInt(tx.MaxFeePerGas)) } else { // legacy and access type use gas price v.Set(a.NewUint(tx.GasPrice)) } v.Set(a.NewUint(tx.Gas)) if tx.To == nil { v.Set(a.NewNull()) } else { v.Set(a.NewCopyBytes((*tx.To)[:])) } v.Set(a.NewBigInt(tx.Value)) v.Set(a.NewCopyBytes(tx.Input)) if tx.Type != ethgo.TransactionLegacy { // either dynamic and access type accessList, err := tx.AccessList.MarshalRLPWith(a) if err != nil { panic(err) } v.Set(accessList) } // EIP155 if chainID != 0 && tx.Type == ethgo.TransactionLegacy { v.Set(a.NewUint(chainID)) v.Set(a.NewUint(0)) v.Set(a.NewUint(0)) } dst := v.MarshalTo(nil) // append the tx type byte if tx.Type != ethgo.TransactionLegacy { dst = append([]byte{byte(tx.Type)}, dst...) } return ethgo.Keccak256(dst) } func encodeSignature(R, S []byte, V byte) ([]byte, error) { sig := make([]byte, 65) copy(sig[32-len(R):32], R) copy(sig[64-len(S):64], S) sig[64] = V return sig, nil } ================================================ FILE: wallet/signer_test.go ================================================ package wallet import ( "math/big" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/umbracle/ethgo" "pgregory.net/rapid" ) func TestSigner_SignAndRecover(t *testing.T) { rapid.Check(t, func(t *rapid.T) { // fill in common types for a transaction txn := ðgo.Transaction{} if rapid.Bool().Draw(t, "to") { to := ethgo.BytesToAddress(rapid.SliceOf(rapid.Byte()).Draw(t, "to_addr")) txn.To = &to } txType := rapid.IntRange(0, 2).Draw(t, "tx type") // fill in specific fields depending on the type // of the transaction. txn.Type = ethgo.TransactionType(txType) if txn.Type == ethgo.TransactionDynamicFee { maxFeePerGas := rapid.Int64Range(1, 1000000000).Draw(t, "maxFeePerGas") txn.MaxFeePerGas = big.NewInt(maxFeePerGas) maxPriorityFeePerGas := rapid.Int64Range(1, 1000000000).Draw(t, "maxPriorityFeePerGas") txn.MaxPriorityFeePerGas = big.NewInt(maxPriorityFeePerGas) } else { gasPrice := rapid.Uint64Range(1, 1000000000).Draw(t, "gasPrice") txn.GasPrice = gasPrice } // signer is from a random chain chainId := rapid.Uint64().Draw(t, "chainId") signer := NewEIP155Signer(chainId) key, err := GenerateKey() require.NoError(t, err) signedTxn, err := signer.SignTx(txn, key) require.NoError(t, err) // recover the sender sender, err := signer.RecoverSender(signedTxn) require.NoError(t, err) require.Equal(t, sender, key.Address()) }) } func TestSigner_EIP1155(t *testing.T) { signer1 := NewEIP155Signer(1337) addr0 := ethgo.Address{0x1} key, err := GenerateKey() assert.NoError(t, err) txn := ðgo.Transaction{ To: &addr0, Value: big.NewInt(10), GasPrice: 0, } txn, err = signer1.SignTx(txn, key) assert.NoError(t, err) from, err := signer1.RecoverSender(txn) assert.NoError(t, err) assert.Equal(t, from, key.addr) } func TestTrimBytesZeros(t *testing.T) { assert.Equal(t, trimBytesZeros([]byte{0x1, 0x2}), []byte{0x1, 0x2}) assert.Equal(t, trimBytesZeros([]byte{0x0, 0x1}), []byte{0x1}) assert.Equal(t, trimBytesZeros([]byte{0x0, 0x0}), []byte{}) } ================================================ FILE: wallet/wallet_hd.go ================================================ package wallet import ( "crypto/ecdsa" "fmt" "math/big" "strings" "github.com/btcsuite/btcd/btcutil/hdkeychain" "github.com/btcsuite/btcd/chaincfg" "github.com/tyler-smith/go-bip39" ) type DerivationPath []uint32 // 0x800000 var decVal = big.NewInt(2147483648) // DefaultDerivationPath is the default derivation path for Ethereum addresses var DefaultDerivationPath = DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0, 0} func (d *DerivationPath) Derive(master *hdkeychain.ExtendedKey) (*ecdsa.PrivateKey, error) { var err error key := master for _, n := range *d { key, err = key.Derive(n) if err != nil { return nil, err } } priv, err := key.ECPrivKey() if err != nil { return nil, err } return priv.ToECDSA(), nil } func parseDerivationPath(path string) (*DerivationPath, error) { parts := strings.Split(path, "/") if len(parts) == 0 { return nil, fmt.Errorf("no derivation path") } // clean all the parts of any trim spaces for indx := range parts { parts[indx] = strings.TrimSpace(parts[indx]) } // first part has to be an 'm' if parts[0] != "m" { return nil, fmt.Errorf("first has to be m") } result := DerivationPath{} for _, p := range parts[1:] { val := new(big.Int) if strings.HasSuffix(p, "'") { p = strings.TrimSuffix(p, "'") val.Add(val, decVal) } bigVal, ok := new(big.Int).SetString(p, 0) if !ok { return nil, fmt.Errorf("invalid path") } val.Add(val, bigVal) // TODO, limit to uint32 if !val.IsUint64() { return nil, fmt.Errorf("bad") } result = append(result, uint32(val.Uint64())) } return &result, nil } func NewWalletFromMnemonic(mnemonic string) (*Key, error) { seed, err := bip39.NewSeedWithErrorChecking(mnemonic, "") if err != nil { return nil, err } masterKey, err := hdkeychain.NewMaster(seed, &chaincfg.MainNetParams) if err != nil { return nil, err } priv, err := DefaultDerivationPath.Derive(masterKey) if err != nil { return nil, err } return NewKey(priv) } ================================================ FILE: wallet/wallet_hd_test.go ================================================ package wallet import ( "testing" "github.com/stretchr/testify/assert" ) func TestWallet_Mnemonic(t *testing.T) { _, err := NewWalletFromMnemonic("sound practice disease erupt basket pumpkin truck file gorilla behave find exchange napkin boy congress address city net prosper crop chair marine chase seven") assert.NoError(t, err) } func TestWallet_MnemonicDerivationPath(t *testing.T) { cases := []struct { path string derivation DerivationPath }{ {"m/44'/60'/0'/0", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 0}}, {"m/44'/60'/0'/128", DerivationPath{0x80000000 + 44, 0x80000000 + 60, 0x80000000 + 0, 128}}, } for _, c := range cases { path, err := parseDerivationPath(c.path) assert.NoError(t, err) assert.Equal(t, *path, c.derivation) } } ================================================ FILE: wallet/wallet_json.go ================================================ package wallet import ( "io/ioutil" "github.com/umbracle/ethgo/keystore" ) func NewJSONWalletFromFile(path string, password string) (*Key, error) { data, err := ioutil.ReadFile(path) if err != nil { return nil, err } return NewJSONWalletFromContent(data, password) } func NewJSONWalletFromContent(content []byte, password string) (*Key, error) { dst, err := keystore.DecryptV3(content, password) if err != nil { return nil, err } key, err := NewWalletFromPrivKey(dst) if err != nil { return nil, err } return key, nil } ================================================ FILE: wallet/wallet_json_test.go ================================================ package wallet import ( "encoding/json" "io/ioutil" "testing" "github.com/stretchr/testify/assert" ) func TestWallet_JSON(t *testing.T) { raw, err := ioutil.ReadFile("./fixtures/wallet_json.json") assert.NoError(t, err) var cases []struct { Wallet json.RawMessage Password string Address string } assert.NoError(t, json.Unmarshal(raw, &cases)) for _, c := range cases { key, err := NewJSONWalletFromContent(c.Wallet, c.Password) assert.NoError(t, err) assert.Equal(t, key.Address().String(), c.Address) } } ================================================ FILE: wallet/wallet_priv.go ================================================ package wallet import ( "crypto/ecdsa" "github.com/btcsuite/btcd/btcec/v2" ) func ParsePrivateKey(buf []byte) (*ecdsa.PrivateKey, error) { prv, _ := btcec.PrivKeyFromBytes(buf) return prv.ToECDSA(), nil } func NewWalletFromPrivKey(p []byte) (*Key, error) { priv, err := ParsePrivateKey(p) if err != nil { return nil, err } return NewKey(priv) } ================================================ FILE: wallet/wallet_priv_test.go ================================================ package wallet import ( "testing" "github.com/stretchr/testify/assert" ) func TestWallet_Priv(t *testing.T) { key, err := GenerateKey() assert.NoError(t, err) raw, err := key.MarshallPrivateKey() assert.NoError(t, err) key1, err := NewWalletFromPrivKey(raw) assert.NoError(t, err) assert.Equal(t, key.addr, key1.addr) } ================================================ FILE: website/README.md ================================================ # Website ## Usage ``` $ npm install $ npm run dev ``` ================================================ FILE: website/components/eip.jsx ================================================ import Link from 'next/link' export default function EIPLink({children, num}) { return {children} } ================================================ FILE: website/components/godoc.jsx ================================================ import Link from 'next/link' const goDocRef = "https://pkg.go.dev/github.com/umbracle/ethgo/" export default function GodocLink({children, href}) { return {children} } ================================================ FILE: website/components/primitives.jsx ================================================ import Link from 'next/link' import GodocLink from "./godoc" function Address({text='Address'}) { return {`(${text})`} } function Hash({text='Hash'}) { return {`(${text})`} } function Block() { return {'(Block)'} } function Blocktag() { return {'(BlockTag)'} } function ABI() { return {'(ABI)'} } function Transaction() { return {'(Transaction)'} } function Receipt() { return {'(Receipt)'} } function LogFilter() { return {'(LogFilter)'} } function Log({text='Log'}) { return {`(${text})`} } export { Address, Hash, Block, Blocktag, Transaction, Receipt, ABI, Log, LogFilter, } ================================================ FILE: website/next.config.js ================================================ // next.config.js const withNextra = require('nextra')({ theme: 'nextra-theme-docs', themeConfig: './theme.config.js', }) module.exports = withNextra() ================================================ FILE: website/package.json ================================================ { "dependencies": { "next": "^12.0.9", "nextra": "^2.0.0-alpha.23", "nextra-theme-docs": "^2.0.0-alpha.23", "prism-react-renderer": "^1.2.1", "prismjs": "^1.26.0", "react": "^17.0.2", "react-dom": "^17.0.2" }, "scripts": { "dev": "next dev", "build": "next build", "start": "next start" } } ================================================ FILE: website/pages/_app.js ================================================ import 'nextra-theme-docs/style.css' import Prism from 'prism-react-renderer/prism' (typeof global !== "undefined" ? global : window).Prism = Prism require("prismjs/components/prism-go") import Head from 'next/head'; const prod = process.env.NODE_ENV === 'production' export default function Nextra({ Component, pageProps }) { return ( <> {prod && } ) } ================================================ FILE: website/pages/abi.mdx ================================================ # Application Binary interface To use the library import: ```go "github.com/umbracle/ethgo/abi" ``` Declare basic objects: ```go typ, err := abi.NewType("uint256") ``` or ```go typ = abi.MustNewType("uint256") ``` and use it to encode/decode the data: ```go num := big.NewInt(1) encoded, err := typ.Encode(num) if err != nil { panic(err) } decoded, err := typ.Decode(encoded) // decoded as interface if err != nil { panic(err) } num2 := decoded.(*big.Int) fmt.Println(num.Cmp(num2) == 0) // num == num2 ``` You can also codify structs as Solidity tuples: ```go import ( "fmt" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/abi" "math/big" ) func main() { typ := abi.MustNewType("tuple(address a, uint256 b)") type Obj struct { A ethgo.Address B *big.Int } obj := &Obj{ A: ethgo.Address{0x1}, B: big.NewInt(1), } // Encode encoded, err := typ.Encode(obj) if err != nil { panic(err) } // Decode output into a map res, err := typ.Decode(encoded) if err != nil { panic(err) } // Decode into a struct var obj2 Obj if err := typ.DecodeStruct(encoded, &obj2); err != nil { panic(err) } fmt.Println(res) fmt.Println(obj) } ``` ## Testing The ABI codifier uses randomized tests with e2e integration tests with a real Geth client to ensure that the codification is correct and provides the same results as the AbiEncoder from Solidity. ================================================ FILE: website/pages/cli/4byte.mdx ================================================ # 4byte The `4byte` command resolve a function or event bytes signature to its full name using the [4byte](https://www.4byte.directory/) API. ## Usage ```shell $ ethgo 4byte 0xddf252ad ``` ================================================ FILE: website/pages/cli/abigen.mdx ================================================ # Abigen The `abigen` command generates bindings to interact with smart contracts using Go. It requires as input the ABI specification of the smart contract in JSON format. ## Usage ```shell $ ethgo abigen --source ./erc20.json --package erc20 ``` ## Options - `source`: Path of the ABI contract file. - `package`: Name of the Go package. - `output`: Output directory. It defaults to the current location. ## Output It generates two output files. - `[name].go`: It contains the bindings for the contract. - `[name]_artifacts.go`. It contains the artifacts for the contract (ABI and deploy binary). ================================================ FILE: website/pages/cli/ens_resolve.mdx ================================================ # Ens resolve The `ens resolve ` command resolves an ENS name to its Ethereum address. ## Usage ```shell $ ethgo ens resolve umbracle.eth [--provider https://mainnet.infura.io...] ``` ## Options - `provider`: URL of the JSON-RPC endpoint to resolve queries (default=`http://localhost:8545`). It can also be set with the `JSONRPC_PROVIDER` environment variable. ================================================ FILE: website/pages/cli/meta.json ================================================ { "abigen": "Abigen", "version": "Version", "ens_resolve": "Ens Resolve" } ================================================ FILE: website/pages/cli/version.mdx ================================================ # Version The `version` command outputs the version of the Ethgo binary. ## Usage ```shell $ ethgo version ``` ================================================ FILE: website/pages/contract.mdx ================================================ import GoDocLink from '../components/godoc' import EIPLink from '../components/eip' import {Address, ABI} from '../components/primitives' # Contract The Contract struct represents a deployed Solidity contract. To instantiate a `Contract` object use: ```go contract.NewContract(addr, abi) ``` with: - `addr`
: Address of the contract. - `abi` : ABI of the contract. By default, it connects to the `https://localhost:8545` JsonRPC endpoint. ## Options Besides `addr` and `abi`, you can use the option pattern to parametrize the contract, the available options are: - WithJsonRPCEndpoint: JsonRPC url of the endpoint to connect with. - WithJsonRPCClient: [`JsonRPC`](/jsonrpc) object to make rpc calls. It takes preference over an address from `WithAddress`. - WithSigner: [`Signer`](/signers/signer) object to send transactions or use a custom `from` address. - WithProvider: Custom NodeProvider implementation to resolve calls and transactions. - WithEIP1559: Send transactions with EIP-1559 pricing. ## Examples Check [examples](https://github.com/umbracle/ethgo/tree/master/examples) for a list of examples on how to interact with a smart contract. ### Call a contract ```go package main import ( "fmt" "math/big" "github.com/umbracle/ethgo" "github.com/umbracle/ethgo/abi" "github.com/umbracle/ethgo/contract" "github.com/umbracle/ethgo/jsonrpc" ) func handleErr(err error) { if err != nil { panic(err) } } // call a contract func main() { var functions = []string{ "function totalSupply() view returns (uint256)", } abiContract, err := abi.NewABIFromList(functions) handleErr(err) // Matic token addr := ethgo.HexToAddress("0x7d1afa7b718fb893db30a3abc0cfc608aacfebb0") client, err := jsonrpc.NewClient("https://mainnet.infura.io") handleErr(err) c := contract.NewContract(addr, abiContract, contract.WithJsonRPC(client.Eth())) res, err := c.Call("totalSupply", ethgo.Latest) handleErr(err) fmt.Printf("TotalSupply: %s", res["totalSupply"].(*big.Int)) } ``` ## Abigen One small limitation of `Contract` is that works with `interface` objects since the input and outputs of a smart contract are arbitrary. As an alternative, you can use [Abigen](./cli/abigen) to generate Go bindings that wrap the `Contract` object and provide native and typed Go functions to interact with the contracts. By default, `ethgo` includes builtin `abigen` contracts for `ens` and `erc20` tokens. ================================================ FILE: website/pages/index.mdx ================================================ # Introduction `Ethgo` is a lightweight SDK in Go to interact with Ethereum compatible blockchains. ================================================ FILE: website/pages/integrations/4byte.mdx ================================================ import GoDocLink from '../../components/godoc' # 4Byte [4Byte](https://www.4byte.directory/) is a public directory that indexes signatures for Ethereum functions and events. Run Resolve to resolve a signature in string format to its name: ```go package main import ( fourbyte "github.com/umbracle/ethgo/4byte" ) func main() { found, err := fourbyte.Resolve("0xddf252ad") if err != nil { panic(err) } // Transfer(address,address,uint256) } ``` ================================================ FILE: website/pages/integrations/ens.mdx ================================================ import GoDocLink from '../../components/godoc' import {Address, Hash, Blocktag, Block, Transaction, Receipt} from '../../components/primitives' # Ethereum Name Service (ENS) The Ethereum Name Service ([ENS](https://ens.domains/)) is a distributed, open, and extensible naming system based on the Ethereum blockchain. The ENS object on the `ens` package is a module that abstracts the interaction with the ENS registry. ```go package main import ( "github.com/umbracle/ethgo/ens" ) func main() { ensMod, err := ens.NewENS(ens.WithAddress("https://mainnet.infura.io")) if err != nil { panic(err) } } ``` It will default to `0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e` as the address for the ENS registry when connecting with one of the official Ethereum networks. However, this can be parametrized at creation time. This module also requires a JsonRPC connection to make the calls to the ENS registry contract. ## Options - WithAddress: JsonRPC url of the endpoint to connect with. - WithClient: [`JsonRPC`](/jsonrpc) object to make rpc calls. It takes preference over an address from `WithAddress`. - WithResolver: Custom resolver address to use rather than the default one. ## Resolve Resolve resolves an ENS name to its registered address. ```go ensMod.Resolve("umbracle.eth") ``` Input: - `name` (string): ENS name to resolve. Output: - `address`
: Ethereum address that resolves to the input name. ================================================ FILE: website/pages/integrations/etherscan.mdx ================================================ import GoDocLink from '../../components/godoc' import {Log, Address, Hash, Blocktag, Block, Transaction, Receipt, LogFilter} from '../../components/primitives' # Etherscan [Etherscan](https://etherscan.io/) is a block explorer and it implements some read compatible endpoint with the [JsonRPC](/jsonrpc) spec. It requires an [apiKey](https://docs.etherscan.io/getting-started/viewing-api-usage-statistics) to use the service and not be rate limited. Create an instance of Etherscan from a network id: ```go package main import ( "github.com/umbracle/ethgo/etherscan" "github.com/umbracle/ethgo" ) func main() { ethscan, err := etherscan.NewEtherscanFromNetwork(ethgo.Mainnet, "apiKey") } ``` The package will resolve the name of the network to the specific endpoint in Etherscan, at this point it only works for `Mainnet`, `Ropsten`, `Rinkeby` and `Goerli`. For a custom url use: ```go ethscan, err := etherscan.NewEtherscan("https://api.polygonscan.com", "apiKey") ``` ## BlockNumber BlockNumber returns the current block number. ```go num, err := ethscan.BlockNumber() ``` Output - `num` (`uint64`): Last block number. ## GetBlockByNumber GetBlockByNumber returns a specific block by its number. ```go block, err := ethscan.GetBlockByNumber(ethgo.BlockNumber(100), true) ``` Input: - `block number` : Block number selection - `full` (`bool`): Whether to return the full block with transactions. Output: - `block`: (): Block object ## GetContractCode GetContractCode returns the contract of a given address (if any). ```go code, err := ethscan.GetContractCode(address) ``` Input: - `address`
: Address of the contract. Output: - `code` (`[]byte`): Code of the contract. ## GetLogs GetLogs returns the logs given a log filter. ```go filter := ðgo.LogFilter{ Address: []ethgo.Address{ ethgo.HexToAddress("..."), }, } logs, err := ethscan.GetLogs(filter) ``` Input: - `filter` : Filter for the logs to return. Output: - `logs` : List of logs that match the filter. ## GasPrice GasPrice returns the gas price of the latest block. Output: - `gasPrice` (`uint64`): Gas price of the latest block. ================================================ FILE: website/pages/integrations/meta.json ================================================ { "ens": "Ethereum Name Service", "etherscan": "Etherscan" } ================================================ FILE: website/pages/jsonrpc/eth.mdx ================================================ import GoDocLink from '../../components/godoc' import {Address, Hash, Blocktag, Block, Transaction, Receipt} from '../../components/primitives' # Ethereum ## GetCode GetCode returns code at a given address. ```go code, err := client.Eth().GetCode(address, block) ``` Params: - `address`
: Address of the contract to query. - `block` : Block reference to query the data. Output: - `code` `([]byte)`: Bytecode of the contract. ## Accounts Accounts returns a list of addresses owned by client. This endpoint is not enabled in infrastructure providers. ```go accounts, err := client.Eth().Accounts() ``` Output: - `accounts`
: List of addresses registered in the client. ## GetStorageAt GetStorageAt returns the value from a storage position at a given address. ```go storage, err := client.Eth().GetStorageAt(addr, slot, block) ``` Params: - `addr`
: Address to query. - `slot` : Slot storage for the address. - `block` : Block reference to query the data. Output: - `storage` `([]byte)`: Content of the storage. ## BlockNumber BlockNumber returns the number of most recent block. ```go number, err := client.Eth().BlockNumber() ``` Output: - `number` (`uint`): Number of the most recent block. ## GetBlockByNumber GetBlockByNumber returns information about a block by block number. ```go block, err := client.Eth().GetBlockByNumber(num, full) ``` Params: - `number` (`uint`): Number of the block to query. - `full` (`bool`): Whether the ouput block should include transactions. Output: - `block` : Block queried. ## GetBlockByHash GetBlockByHash returns information about a block by hash. ```go block, err := client.Eth().GetBlockByHash(hash, full) ``` Params: - `hash` : Hash of the block to query. - `full` (`bool`): Whether the ouput block should include transactions. Output: - `block` : Block queried. ## GetTransactionByHash GetTransactionByHash returns a transaction by his hash ```go transaction, err := client.Eth().GetTransactionByHash(hash) ``` Params: - `hash` : Hash of the transaction to query. Output: - `transaction` : Transaction being queried. ## SendRawTransaction SendRawTransaction creates new message call transaction or a contract creation for signed transactions. ```go hash, err := client.Eth().SendRawTransaction(transaction) ``` Params: - `transaction` `([]byte)`: Signed transaction and encoded in RLP format. Output: - `hash` : Hash of the transaction created. ## GetTransactionReceipt GetTransactionReceipt returns the receipt of a transaction by transaction hash. ```go receipt, err := client.Eth().GetTransactionReceipt(hash) ``` Params: - `hash` : Hash of the transaction being queried. Output: - `receipt` : receipt being queried. ## GetNonce GetNonce returns the number of transactions sent from an address. ```go nonce, err := client.Eth().GetNonce(address) ``` Params: - `address`
: address of the account to query. Output: - `nonce` `(uint64)`: next valid nonce for the account. ## GetBalance GetBalance returns the balance of the account of given address. ```go balance, err := client.Eth().GetBalance(address) ``` Params: - `address`
: address of the account to query. Output: - `balance` `(big.Int)`: balance of the account in big format. ## GasPrice GasPrice returns the current price per gas in wei. ```go price, err := client.Eth().GasPrice() ``` Output: - `price` `(big.Int)`: gas price in wei. ================================================ FILE: website/pages/jsonrpc/index.mdx ================================================ import GoDocLink from '../../components/godoc' import {Address, Hash, Blocktag, Block, Transaction, Receipt} from '../../components/primitives' # JsonRPC Ethereum uses `JsonRPC` as the main interface to interact with the client and the network. ## Overview ```go package main import ( "github.com/umbracle/ethgo/jsonrpc" ) func main() { client, err := jsonrpc.NewClient("https://mainnet.infura.io") if err != nil { panic(err) } } ``` `Ethgo` supports different transport protocols besides `http` depending on the endpoint: Use the endpoint with `wss://` prefix to connect with [`websockets`](https://en.wikipedia.org/wiki/WebSocket): ```go client, err := jsonrpc.NewClient("wss://mainnet.infura.io") ``` or the endpoint with `ipc://` prefix to use [`ipc`](https://en.wikipedia.org/wiki/Inter-process_communication): ```go client, err := jsonrpc.NewClient("ipc://path/geth.ipc") ``` ## Endpoints Once the JsonRPC client has been created, the endpoints are available on different namespaces following the spec: ```go eth := client.Eth() ``` The available namespaces are: - [Eth](./jsonrpc/eth): Ethereum network endpoints. - [Net](./jsonrpc/net): Client information. ## Block tag Some endpoints of the `eth` namespace can be queried at a specific block. There exists three ways to specify this block: - `number` or `tag` (BlockNumber): integer block number or the tag `latest`, `pending` or `earliest`. - `hash` : hash of the block. ================================================ FILE: website/pages/jsonrpc/meta.json ================================================ { "index": "Overview", "eth": "Eth", "net": "Net" } ================================================ FILE: website/pages/jsonrpc/net.mdx ================================================ import GoDocLink from '../../components/godoc' import {Address, Hash, Blocktag, Block, Transaction, Receipt} from '../../components/primitives' # Net ## PeerCount PeerCount returns number of peers currently connected to the client. ```go count, err := client.Net().PeerCount() ``` Output: - `count` `(uint64)`: Number of peers connected. ================================================ FILE: website/pages/meta.json ================================================ { "index": "Introduction", "jsonrpc": "JsonRPC", "abi": "Application Binary Interface", "signers": "Signers", "contract": "Contract", "integrations": "Integrations", "cli": "Command Line Interface" } ================================================ FILE: website/pages/signers/signer.mdx ================================================ # Signers The `Signer` represents an abstract entity that can sign arbitrary messages. ```go type Key interface { Address() Address Sign(hash []byte) ([]byte, error) } ``` ================================================ FILE: website/pages/signers/wallet.mdx ================================================ import GoDocLink from '../../components/godoc' # Wallet The `wallet` package is an implementation of the [Signer](./signer) interface for an ECDSA private key and it represents an Ethereum account. It supports loading the private key from different mediums. ## Random Create a random key: ```go key, err := wallet.GenerateKey() ``` ## Mnemonic Create the key from a mnemonic phrase: ```go mnemonic := "" key, err := wallet.NewWalletFromMnemonic(mnemonic) ``` ## PrivateKey Create the key from a private key (in bytes): ```go key, err := wallet.NewWalletFromPrivKey([]byte{}) ``` ## Hardware file Create the key from an encrypted JSON wallet following the [keystore](https://github.com/ethereum/wiki/wiki/Web3-Secret-Storage-Definition) format. ```go key, err := wallet.NewJSONWalletFromFile("./file.json") ``` ================================================ FILE: website/theme.config.js ================================================ export default { projectLink: 'https://github.com/umbracle/ethgo', // GitHub link in the navbar docsRepositoryBase: 'https://github.com/umbracle/ethgo/tree/master/website/pages', // base URL for the docs repository titleSuffix: ' – Ethgo', nextLinks: true, prevLinks: true, search: false, customSearch: null, // customizable, you can use algolia for example darkMode: true, footer: true, footerText: ( <> Powered by Umbracle ), footerEditLink: `Edit this page on GitHub`, floatTOC: true, logo: ( <> Ethgo Go Ethereum SDK ), head: ( <> ), }