├── .gitignore ├── .golangci.yaml ├── LICENSE ├── Makefile ├── README.md ├── assets └── img │ └── Ethereum-icon-purple.svg ├── client ├── client_test.go ├── convert.go ├── convert_test.go ├── evm.go ├── evm_test.go ├── interface.go ├── pool.go ├── pool_test.go ├── solana.go └── solana_test.go ├── consts ├── abis.go └── chain.go ├── erc └── erc20 │ ├── erc20.go │ └── erc20.json ├── go.mod ├── go.sum ├── model ├── client │ ├── conf.go │ └── default.go └── solana │ └── rpc.go └── pkg ├── otel └── metrics.go ├── pk └── signer.go └── wjson └── json.go /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/go,intellij+all,visualstudiocode 3 | # Edit at https://www.gitignore.io/?templates=go,intellij+all,visualstudiocode 4 | 5 | ### Go ### 6 | # Binaries for programs and plugins 7 | *.exe 8 | *.exe~ 9 | *.dll 10 | *.so 11 | *.dylib 12 | # MacOS data file 13 | .DS_store 14 | # Test binary, built with `go test -c` 15 | *.test 16 | 17 | # Output of the go coverage tool, specifically when used with LiteIDE 18 | *.out 19 | 20 | # Dependency directories (remove the comment below to include it) 21 | # vendor/ 22 | 23 | ### Go Patch ### 24 | /vendor/ 25 | /Godeps/ 26 | 27 | ### Intellij+all ### 28 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm 29 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 30 | 31 | # User-specific stuff 32 | .idea/**/workspace.xml 33 | .idea/**/tasks.xml 34 | .idea/**/usage.statistics.xml 35 | .idea/**/dictionaries 36 | .idea/**/shelf 37 | 38 | # Generated files 39 | .idea/**/contentModel.xml 40 | 41 | # Sensitive or high-churn files 42 | .idea/**/dataSources/ 43 | .idea/**/dataSources.ids 44 | .idea/**/dataSources.local.xml 45 | .idea/**/sqlDataSources.xml 46 | .idea/**/dynamic.xml 47 | .idea/**/uiDesigner.xml 48 | .idea/**/dbnavigator.xml 49 | 50 | # Gradle 51 | .idea/**/gradle.xml 52 | .idea/**/libraries 53 | 54 | # Gradle and Maven with auto-import 55 | # When using Gradle or Maven with auto-import, you should exclude module files, 56 | # since they will be recreated, and may cause churn. Uncomment if using 57 | # auto-import. 58 | # .idea/modules.xml 59 | # .idea/*.iml 60 | # .idea/modules 61 | # *.iml 62 | # *.ipr 63 | 64 | # CMake 65 | cmake-build-*/ 66 | 67 | # Mongo Explorer plugin 68 | .idea/**/mongoSettings.xml 69 | 70 | # File-based project format 71 | *.iws 72 | 73 | # IntelliJ 74 | out/ 75 | 76 | # mpeltonen/sbt-idea plugin 77 | .idea_modules/ 78 | 79 | # JIRA plugin 80 | atlassian-ide-plugin.xml 81 | 82 | # Cursive Clojure plugin 83 | .idea/replstate.xml 84 | 85 | # Crashlytics plugin (for Android Studio and IntelliJ) 86 | com_crashlytics_export_strings.xml 87 | crashlytics.properties 88 | crashlytics-build.properties 89 | fabric.properties 90 | 91 | # Editor-based Rest Client 92 | .idea/httpRequests 93 | 94 | # Android studio 3.1+ serialized cache file 95 | .idea/caches/build_file_checksums.ser 96 | 97 | ### Intellij+all Patch ### 98 | # Ignores the whole .idea folder and all .iml files 99 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360 100 | 101 | .idea/ 102 | 103 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023 104 | 105 | *.iml 106 | modules.xml 107 | .idea/misc.xml 108 | *.ipr 109 | 110 | # Sonarlint plugin 111 | .idea/sonarlint 112 | 113 | ### VisualStudioCode ### 114 | .vscode/* 115 | !.vscode/settings.json 116 | !.vscode/tasks.json 117 | !.vscode/launch.json 118 | !.vscode/extensions.json 119 | 120 | ### VisualStudioCode Patch ### 121 | # Ignore all local history of files 122 | .history 123 | 124 | # End of https://www.gitignore.io/api/go,intellij+all,visualstudiocode 125 | 126 | *.pem 127 | .agollo 128 | *.log 129 | **/kube-config 130 | 131 | # Test 132 | test/geth/data/* 133 | test/geth/log/* 134 | -------------------------------------------------------------------------------- /.golangci.yaml: -------------------------------------------------------------------------------- 1 | # This file configures github.com/golangci/golangci-lint. 2 | 3 | run: 4 | timeout: 20m 5 | tests: true 6 | # default is true. Enables skipping of directories: 7 | # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ 8 | skip-dirs-use-default: true 9 | skip-files: 10 | - core/genesis_alloc.go 11 | 12 | linters: 13 | disable-all: true 14 | enable: 15 | - goconst 16 | - goimports 17 | - gosimple 18 | - govet 19 | - ineffassign 20 | - misspell 21 | - unconvert 22 | - typecheck 23 | - unused 24 | - staticcheck 25 | - bidichk 26 | - durationcheck 27 | - exportloopref 28 | - whitespace 29 | 30 | # - structcheck # lots of false positives 31 | # - errcheck #lot of false positives 32 | # - contextcheck 33 | # - errchkjson # lots of false positives 34 | # - errorlint # this check crashes 35 | # - exhaustive # silly check 36 | # - makezero # false positives 37 | # - nilerr # several intentional 38 | 39 | linters-settings: 40 | gofmt: 41 | simplify: true 42 | goconst: 43 | min-len: 3 # minimum length of string constant 44 | min-occurrences: 6 # minimum number of occurrences 45 | 46 | issues: 47 | exclude-rules: 48 | - path: crypto/bn256/cloudflare/optate.go 49 | linters: 50 | - deadcode 51 | - staticcheck 52 | - path: internal/build/pgp.go 53 | text: 'SA1019: "golang.org/x/crypto/openpgp" is deprecated: this package is unmaintained except for security fixes.' 54 | - path: core/vm/contracts.go 55 | text: 'SA1019: "golang.org/x/crypto/ripemd160" is deprecated: RIPEMD-160 is a legacy hash and should not be used for new applications.' 56 | - path: accounts/usbwallet/trezor.go 57 | text: 'SA1019: "github.com/golang/protobuf/proto" is deprecated: Use the "google.golang.org/protobuf/proto" package instead.' 58 | - path: accounts/usbwallet/trezor/ 59 | text: 'SA1019: "github.com/golang/protobuf/proto" is deprecated: Use the "google.golang.org/protobuf/proto" package instead.' 60 | exclude: 61 | - 'SA1019: event.TypeMux is deprecated: use Feed' 62 | - 'SA1019: strings.Title is deprecated' 63 | - 'SA1019: strings.Title has been deprecated since Go 1.18 and an alternative has been available since Go 1.0: The rule Title uses for word boundaries does not handle Unicode punctuation properly. Use golang.org/x/text/cases instead.' 64 | - 'SA1029: should not use built-in type string as key for value' 65 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | all: node 2 | @echo "all" 3 | 4 | node: 5 | geth \ 6 | -datadir test/geth/data \ 7 | --dev \ 8 | --ws \ 9 | --http \ 10 | --mine \ 11 | --metrics \ 12 | --pprof \ 13 | --graphql \ 14 | --http.api admin,eth,debug,miner,net,txpool,personal,web3 \ 15 | console 2 >> test/geth/log/geth.log 16 | 17 | console: 18 | geth attach test/geth/data/geth.ipc 19 | lint: 20 | golangci-lint run ./... 21 | clean: 22 | rm -rf test/geth/data 23 | init: 24 | geth \ 25 | -datadir test/geth/data \ 26 | --dev \ 27 | --ws \ 28 | --http \ 29 | --mine \ 30 | --http.api admin,eth,debug,miner,net,txpool,personal,web3 \ 31 | init test/geth/genesis.json 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 |
3 |

4 | 5 | Logo 6 | 7 | 8 |

Web3 Go

9 | 10 |

11 | Ethereum Dapp Go API, inspired by 12 | web3.js. 13 |
14 | Report Bug 15 | · 16 | Pull Request 17 |

18 |

19 | 20 | [![WEBSITE](https://img.shields.io/badge/Web3-Go-brightgreen)](https://github.com/kylesliu/web3-go) 21 | [![LISTENSE](https://img.shields.io/github/license/6boris/web3-go)](https://github.com/kylesliu/web3-go/blob/main/LICENSE) 22 | 23 | ## Introduction 24 | 25 | This is the Ethereum [Golang API](https://github.com/kylesliu/web3-go) which connects to the Generic [JSON-RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) spec. 26 | 27 | You need to run a local or remote Ethereum node to use this library. 28 | 29 | Here is an open source case [Web3 Studio](https://web3-studio.leek.dev/d/demo/web3-studio) reference under. 30 | 31 | 32 | Logo 33 | 34 | 35 | 36 | ### Client 37 | 38 | 39 | ```bash 40 | export WEB3_GO_DEV_KEY_1="YOU_EVM_PRIVATE_KEY 1" 41 | export WEB3_GO_DEV_KEY_2="YOU_EVM_PRIVATE_KEY 1" 42 | ``` 43 | 44 | ```bash 45 | go get github.com/6boris/web3-go 46 | ``` 47 | 48 | 49 | ```go 50 | package main 51 | 52 | import ( 53 | "context" 54 | "fmt" 55 | "github.com/6boris/web3-go/client" 56 | clientModel "github.com/6boris/web3-go/model/client" 57 | "github.com/6boris/web3-go/pkg/pk" 58 | "github.com/ethereum/go-ethereum/common" 59 | "github.com/shopspring/decimal" 60 | "math/big" 61 | "os" 62 | ) 63 | 64 | func main() { 65 | ctx := context.Background() 66 | evmSigners := make([]*clientModel.ConfEvmChainSigner, 0) 67 | for _, v := range []string{"WEB3_GO_DEV_KEY_1", "WEB3_GO_DEV_KEY_1"} { 68 | signer, loopErr := pk.TransformPkToEvmSigner(os.Getenv(v)) 69 | if loopErr != nil { 70 | continue 71 | } 72 | evmSigners = append(evmSigners, signer) 73 | } 74 | ec, err := client.NewEvmClient(&clientModel.ConfEvmChainClient{ 75 | TransportURL: "https://1rpc.io/matic", 76 | GasFeeRate: decimal.NewFromFloat(2), 77 | GasLimitRate: decimal.NewFromFloat(1.5), 78 | Signers: evmSigners, 79 | }) 80 | 81 | nativeTx, err := ec.SendTransactionSimple( 82 | ctx, 83 | ec.GetAllSinners()[0], ec.GetAllSinners()[1], 84 | big.NewInt(1), 85 | ) 86 | if err != nil { 87 | panic(err) 88 | } 89 | fmt.Println(fmt.Sprintf("Native Token Tx: %s", nativeTx.Hash())) 90 | 91 | erc20Tx, err := ec.ERC20Transfer( 92 | ctx, 93 | common.HexToAddress("0xc2132D05D31c914a87C6611C10748AEb04B58e8F"), 94 | ec.GetAllSinners()[0], ec.GetAllSinners()[1], 95 | decimal.NewFromFloat(0.000001).Mul(decimal.New(1, int32(6))).BigInt(), 96 | ) 97 | if err != nil { 98 | panic(err) 99 | } 100 | fmt.Println(fmt.Sprintf("USDT Token Tx: %s", erc20Tx.Hash())) 101 | } 102 | /* 103 | Output: 104 | Native Token Tx: 0xf3aa0e634357a222c39e8.......8f1a7e7d313db71827c3 105 | USDT Token Tx: 0xedd54d9e6bd3738880cd55........21aa69f06dba1e011625 106 | */ 107 | ``` 108 | 109 | ## Development Trips 110 | - [X] Client 111 | - [ ] Base Method 112 | - [X] eth_chainId 113 | - [X] web3_clientVersion 114 | - [X] eth_gasPrice 115 | - [X] eth_blockNumber 116 | - [X] eth_getBalance 117 | - [ ] ... 118 | - [ ] Middleware 119 | - [X] LoadBalance 120 | - [X] Metrics 121 | - [ ] Grafana 122 | - [ ] CircuitBreaker 123 | - [ ] Business Cases 124 | - [ ] Web3 Studio 125 | - [ ] Other ... 126 | 127 | 128 | 129 | ## Community 130 | 131 | - [web3.js](https://github.com/ChainSafe/web3.js) Ethereum JavaScript API. 132 | - [Web3j](https://github.com/web3j/web3j) Web3 Java Ethereum Ðapp API. 133 | - [Web3.py](https://github.com/ethereum/web3.py) A Python library for interacting with Ethereum. 134 | 135 | ## Provider 136 | - https://public.blockpi.io/ 137 | 138 | 139 | ## Dev tool 140 | 141 | - [JSON RPC](https://www.jsonrpc.org/specification) Defining the JSON RPC specification. 142 | - [Go Ethereum](https://github.com/ethereum/go-ethereum) Official Golang implementation of the Ethereum protocol. 143 | - [Ethereum 1.0 API](https://github.com/ethereum/eth1.0-apis) Ethereum JSON-RPC Specification. 144 | -------------------------------------------------------------------------------- /assets/img/Ethereum-icon-purple.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /client/client_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "testing" 7 | 8 | clientModel "github.com/6boris/web3-go/model/client" 9 | "github.com/6boris/web3-go/pkg/pk" 10 | "github.com/shopspring/decimal" 11 | ) 12 | 13 | var ( 14 | testCtx context.Context 15 | testPool *Pool 16 | testEvmEthClient *EvmClient 17 | testEvmPolygonClient *EvmClient 18 | testSolanaClient *SolanaClient 19 | ) 20 | 21 | func TestMain(m *testing.M) { 22 | var err error 23 | evmSigners := make([]*clientModel.ConfEvmChainSigner, 0) 24 | for _, v := range []string{"WEB3_GO_DEV_KEY_1", "WEB3_GO_DEV_KEY_1"} { 25 | signer, loopErr := pk.TransformPkToEvmSigner(os.Getenv(v)) 26 | if loopErr != nil { 27 | continue 28 | } 29 | evmSigners = append(evmSigners, signer) 30 | } 31 | testEvmEthClient, err = NewEvmClient(&clientModel.ConfEvmChainClient{TransportURL: "https://1rpc.io/eth"}) 32 | if err != nil { 33 | panic(err) 34 | } 35 | testEvmPolygonClient, err = NewEvmClient(&clientModel.ConfEvmChainClient{ 36 | TransportURL: "https://1rpc.io/matic", 37 | GasFeeRate: decimal.NewFromFloat(2), 38 | GasLimitMax: decimal.NewFromInt(30000000), 39 | GasLimitRate: decimal.NewFromFloat(1.5), 40 | Signers: evmSigners, 41 | }) 42 | if err != nil { 43 | panic(err) 44 | } 45 | testSolanaClient, err = NewSolanaClient(&clientModel.ConfSolanaClient{ 46 | TransportURL: "https://api.mainnet-beta.solana.com", 47 | IsDev: true, 48 | }) 49 | if err != nil { 50 | panic(err) 51 | } 52 | testPool = NewPool(clientModel.GetDefaultConfPool()) 53 | testCtx = context.TODO() 54 | // Before Test 55 | code := m.Run() 56 | // After Test 57 | os.Exit(code) 58 | } 59 | -------------------------------------------------------------------------------- /client/convert.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "fmt" 5 | "math/big" 6 | "net/http" 7 | "strconv" 8 | 9 | "github.com/6boris/web3-go/consts" 10 | clientModel "github.com/6boris/web3-go/model/client" 11 | "github.com/6boris/web3-go/model/solana" 12 | "github.com/ethereum/go-ethereum/common" 13 | "github.com/gin-gonic/gin" 14 | ) 15 | 16 | type GinMethodConvert struct { 17 | pool *Pool 18 | } 19 | 20 | func PromHandler(handler http.Handler) gin.HandlerFunc { 21 | return func(c *gin.Context) { 22 | handler.ServeHTTP(c.Writer, c.Request) 23 | } 24 | } 25 | 26 | func NewGinMethodConvert(conf *clientModel.ConfPool) *GinMethodConvert { 27 | g := &GinMethodConvert{} 28 | g.pool = NewPool(conf) 29 | return g 30 | } 31 | 32 | func (g *GinMethodConvert) _convertGinHandler(ctx *gin.Context) { 33 | req := &clientModel.EvmCallProxyRequest{} 34 | err := ctx.BindJSON(req) 35 | if err != nil { 36 | ctx.JSON(400, clientModel.ErrReply{Code: 400, Reason: "PARAMS_ERR", Message: err.Error()}) 37 | return 38 | } 39 | evmClient := g.pool.GetEvmClient(req.ChainID) 40 | if req.ChainID > 0 && evmClient == nil { 41 | ctx.JSON(400, clientModel.ErrReply{Code: 400, Reason: "PARAMS_ERR", Message: "Not have enable client"}) 42 | return 43 | } 44 | switch req.Method { 45 | case consts.EvmMethodChainID: 46 | g._convertEvmChainID(ctx, req, evmClient) 47 | case consts.EvmMethodSuggestGasTipCap: 48 | g._convertEvmGasPrice(ctx, req, evmClient) 49 | case consts.EvmMethodBlockNumber: 50 | g._convertEvmBlockNumber(ctx, req, evmClient) 51 | case consts.EvmMethodBalanceAt: 52 | g._convertEvmGetBalance(ctx, req, evmClient) 53 | default: 54 | ctx.JSON(400, clientModel.ErrReply{Code: 400, Reason: "PARAMS_ERR", Message: fmt.Sprintf("Not support this method:%s", req.Method)}) 55 | return 56 | } 57 | } 58 | 59 | // eth_chainId https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_chainId 60 | func (g *GinMethodConvert) _convertEvmChainID(ctx *gin.Context, req *clientModel.EvmCallProxyRequest, client *EvmClient) { 61 | resp := &clientModel.EvmCallProxyReply{ 62 | ID: req.ID, 63 | JsonRpc: req.JsonRpc, 64 | } 65 | ethResp, err := client.ChainID(ctx) 66 | if err != nil { 67 | ctx.JSON(500, &clientModel.ErrReply{Code: 500, Reason: "ETH_ERR", Message: err.Error(), Metadata: map[string]string{"transport_url": client.GetTransportURL()}}) 68 | return 69 | } 70 | resp.Result = fmt.Sprintf("0x%s", ethResp.Text(16)) 71 | ctx.JSON(http.StatusOK, resp) 72 | } 73 | 74 | // eth_gasPrice https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_gasprice 75 | func (g *GinMethodConvert) _convertEvmGasPrice(ctx *gin.Context, req *clientModel.EvmCallProxyRequest, client *EvmClient) { 76 | resp := &clientModel.EvmCallProxyReply{ 77 | ID: req.ID, 78 | JsonRpc: req.JsonRpc, 79 | } 80 | ethResp, err := client.SuggestGasPrice(ctx) 81 | if err != nil { 82 | ctx.JSON(500, &clientModel.ErrReply{Code: 500, Reason: "ETH_ERR", Message: err.Error(), Metadata: map[string]string{"transport_url": client.GetTransportURL()}}) 83 | return 84 | } 85 | 86 | resp.Result = ethResp 87 | ctx.JSON(http.StatusOK, resp) 88 | } 89 | 90 | // eth_blockNumber https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_blocknumber 91 | func (g *GinMethodConvert) _convertEvmBlockNumber(ctx *gin.Context, req *clientModel.EvmCallProxyRequest, client *EvmClient) { 92 | resp := &clientModel.EvmCallProxyReply{ 93 | ID: req.ID, 94 | JsonRpc: req.JsonRpc, 95 | } 96 | ethResp, err := client.BlockNumber(ctx) 97 | if err != nil { 98 | ctx.JSON(500, &clientModel.ErrReply{Code: 500, Reason: "ETH_ERR", Message: err.Error(), Metadata: map[string]string{"transport_url": client.GetTransportURL()}}) 99 | return 100 | } 101 | resp.Result = fmt.Sprintf("0x%s", big.NewInt(int64(ethResp)).Text(16)) 102 | ctx.JSON(http.StatusOK, resp) 103 | } 104 | 105 | // eth_getBalance https://ethereum.org/en/developers/docs/apis/json-rpc/#eth_getbalance 106 | func (g *GinMethodConvert) _convertEvmGetBalance(ctx *gin.Context, req *clientModel.EvmCallProxyRequest, client *EvmClient) { 107 | resp := &clientModel.EvmCallProxyReply{ 108 | ID: req.ID, 109 | JsonRpc: req.JsonRpc, 110 | } 111 | 112 | account := req.Params[0].(string) 113 | var blockNumber *big.Int 114 | if len(req.Params) > 2 { 115 | blockStr, ok := req.Params[1].(string) 116 | if ok { 117 | block, err := strconv.ParseInt(blockStr, 10, 64) 118 | if err == nil { 119 | blockNumber = big.NewInt(block) 120 | } 121 | } 122 | } 123 | ethResp, err := client.BalanceAt(ctx, common.HexToAddress(account), blockNumber) 124 | if err != nil { 125 | ctx.JSON(500, &clientModel.ErrReply{Code: 500, Reason: "ETH_ERR", Message: err.Error(), Metadata: map[string]string{"transport_url": client.GetTransportURL()}}) 126 | return 127 | } 128 | resp.Result = fmt.Sprintf("0x%s", ethResp.Text(16)) 129 | ctx.JSON(http.StatusOK, resp) 130 | } 131 | 132 | func (g *GinMethodConvert) _convertSolanaHandler(ctx *gin.Context) { 133 | req := &clientModel.SolanaCallProxyRequest{} 134 | err := ctx.BindJSON(req) 135 | if err != nil { 136 | ctx.JSON(400, clientModel.ErrReply{Code: 400, Reason: "PARAMS_ERR", Message: err.Error()}) 137 | return 138 | } 139 | solanaClient := g.pool.GetSolanaClient(req.ChainEnv) 140 | if solanaClient == nil { 141 | ctx.JSON(400, clientModel.ErrReply{Code: 400, Reason: "PARAMS_ERR", Message: "Not have enable client"}) 142 | return 143 | } 144 | switch req.Method { 145 | case consts.SolanaMethodGetBalance: 146 | g._convertSolanaGetBalance(ctx, req, solanaClient) 147 | default: 148 | ctx.JSON(400, clientModel.ErrReply{Code: 400, Reason: "PARAMS_ERR", Message: fmt.Sprintf("Not support this method:%s", req.Method)}) 149 | return 150 | } 151 | } 152 | 153 | func (g *GinMethodConvert) _convertSolanaGetBalance(ctx *gin.Context, req *clientModel.SolanaCallProxyRequest, client *SolanaClient) { 154 | resp := &clientModel.EvmCallProxyReply{ 155 | ID: req.ID, 156 | JsonRpc: req.JsonRpc, 157 | } 158 | 159 | account := req.Params[0].(string) 160 | callResp, err := client.GetBalance(ctx, &solana.GetBalanceRequest{ 161 | Account: account, 162 | }) 163 | if err != nil { 164 | ctx.JSON(500, &clientModel.ErrReply{Code: 500, Reason: "ETH_ERR", Message: err.Error(), Metadata: map[string]string{"transport_url": client.TransportURL}}) 165 | return 166 | } 167 | resp.Result = callResp.Value 168 | ctx.JSON(http.StatusOK, resp) 169 | } 170 | -------------------------------------------------------------------------------- /client/convert_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | 7 | "github.com/6boris/web3-go/consts" 8 | clientModel "github.com/6boris/web3-go/model/client" 9 | "github.com/ethereum/go-ethereum/common" 10 | "github.com/gin-gonic/gin" 11 | "github.com/imroc/req/v3" 12 | "github.com/prometheus/client_golang/prometheus/promhttp" 13 | "github.com/stretchr/testify/assert" 14 | ) 15 | 16 | func Test_Convert_Server(t *testing.T) { 17 | t.Run("Start Server", func(t *testing.T) { 18 | app := gin.New() 19 | app.GET("/metrics", PromHandler(promhttp.Handler())) 20 | app.POST("/evm", NewGinMethodConvert(clientModel.GetDefaultConfPool())._convertGinHandler) 21 | app.POST("/solana", NewGinMethodConvert(clientModel.GetDefaultConfPool())._convertSolanaHandler) 22 | _ = app.Run(":8545") 23 | }) 24 | } 25 | 26 | func Test_Convert_Unite(t *testing.T) { 27 | cases := []struct { 28 | name string 29 | url string 30 | params interface{} 31 | expect string 32 | }{ 33 | { 34 | "EVM", "http://127.0.0.1:8545/evm", 35 | map[string]interface{}{"chain_id": 1, "method": consts.EvmMethodChainID, "params": []interface{}{}}, 36 | "EVM", 37 | }, 38 | { 39 | "EVM", "http://127.0.0.1:8545/evm", 40 | map[string]interface{}{"chain_id": 1, "method": consts.EvmMethodSuggestGasTipCap, "params": []interface{}{}}, 41 | "", 42 | }, 43 | { 44 | "EVM", "http://127.0.0.1:8545/evm", 45 | map[string]interface{}{"chain_id": 1, "method": consts.EvmMethodBlockNumber, "params": []interface{}{}}, 46 | "", 47 | }, 48 | { 49 | "EVM", "http://127.0.0.1:8545/evm", 50 | map[string]interface{}{"chain_id": 1, "method": consts.EvmMethodBalanceAt, "params": []interface{}{common.HexToAddress("0xcDd37Ada79F589c15bD4f8fD2083dc88E34A2af2")}}, 51 | "", 52 | }, 53 | { 54 | "SOLANA", "http://127.0.0.1:8545/solana", 55 | map[string]interface{}{"chain_env": consts.ChainEnvMainnet, "method": consts.SolanaMethodGetBalance, "params": []interface{}{"5EhGYUyQNrxgUbuYF4vbL2SZDT6RMfhq3yjeyevvULeC"}}, 56 | "", 57 | }, 58 | } 59 | t.Run("Unite Case", func(t *testing.T) { 60 | for i, c := range cases { 61 | t.Run(c.name+" "+strconv.Itoa(i+1), func(t *testing.T) { 62 | _, err := req.C().DevMode().R().SetBody(c.params).Post(c.url) 63 | assert.Nil(t, err) 64 | }) 65 | } 66 | }) 67 | } 68 | -------------------------------------------------------------------------------- /client/evm.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "crypto/ecdsa" 6 | "errors" 7 | "math/big" 8 | "strings" 9 | "time" 10 | 11 | "github.com/6boris/web3-go/consts" 12 | "github.com/6boris/web3-go/erc/erc20" 13 | clientModel "github.com/6boris/web3-go/model/client" 14 | "github.com/6boris/web3-go/pkg/otel" 15 | "github.com/ethereum/go-ethereum" 16 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 17 | "github.com/ethereum/go-ethereum/common" 18 | "github.com/ethereum/go-ethereum/core/types" 19 | "github.com/ethereum/go-ethereum/ethclient" 20 | "github.com/ethereum/go-ethereum/rpc" 21 | "github.com/google/uuid" 22 | "github.com/shopspring/decimal" 23 | "go.opentelemetry.io/otel/attribute" 24 | "go.opentelemetry.io/otel/metric" 25 | ) 26 | 27 | type EvmClient struct { 28 | ethClient *ethclient.Client 29 | rpcClient *rpc.Client 30 | _signers []*clientModel.ConfEvmChainSigner 31 | _gasLimitMax decimal.Decimal 32 | _gasFeeRate decimal.Decimal 33 | _gasLimitRate decimal.Decimal 34 | _clientID string 35 | _appID string 36 | _zone string 37 | _cluster string 38 | _ethChainID int64 39 | _ethChainName string 40 | _ethChainEnv string 41 | _provider string 42 | _transportURL string 43 | } 44 | 45 | func NewEvmClient(conf *clientModel.ConfEvmChainClient) (*EvmClient, error) { 46 | var err error 47 | ec := &EvmClient{ 48 | _clientID: strings.ReplaceAll(uuid.NewString(), "-", ""), 49 | _provider: conf.Provider, 50 | _transportURL: conf.TransportURL, 51 | _gasFeeRate: conf.GasFeeRate, 52 | _gasLimitRate: conf.GasLimitRate, 53 | _gasLimitMax: conf.GasLimitMax, 54 | } 55 | 56 | if ec._gasFeeRate == decimal.Zero { 57 | ec._gasFeeRate = decimal.NewFromFloat(1.1) 58 | } 59 | if ec._gasLimitRate == decimal.Zero { 60 | ec._gasLimitRate = decimal.NewFromFloat(2) 61 | } 62 | if ec._gasLimitMax == decimal.Zero { 63 | ec._gasLimitMax = decimal.NewFromFloat(30000000) 64 | } 65 | ec._signers = conf.Signers 66 | 67 | ec.ethClient, err = ethclient.Dial(conf.TransportURL) 68 | if err != nil { 69 | return nil, err 70 | } 71 | ec.rpcClient, err = rpc.Dial(conf.TransportURL) 72 | if err != nil { 73 | return nil, err 74 | } 75 | 76 | return ec, nil 77 | } 78 | 79 | func (ec *EvmClient) _getSinnerPrivateKey(account common.Address) (*ecdsa.PrivateKey, error) { 80 | for _, v := range ec._signers { 81 | if v.PublicAddress.String() == account.String() { 82 | return v.PrivateKey, nil 83 | } 84 | } 85 | return nil, errors.New("signer not config") 86 | } 87 | func (ec *EvmClient) _beforeHooks(ctx context.Context, meta *clientModel.Metadata) { 88 | _ = ctx 89 | meta.StartAt = time.Now() 90 | } 91 | func (ec *EvmClient) _afterHooks(ctx context.Context, meta *clientModel.Metadata) { 92 | otel.MetricsWeb3RequestCounter.Add(ctx, 1, metric.WithAttributes( 93 | attribute.Key("client_id").String(ec._clientID), 94 | attribute.Key("app_id").String(ec._appID), 95 | attribute.Key("zone").String(ec._appID), 96 | attribute.Key("cluster").String(ec._cluster), 97 | attribute.Key("chain_id").Int64(ec._ethChainID), 98 | attribute.Key("chain_name").String(ec._ethChainName), 99 | attribute.Key("chain_env").String(ec._ethChainEnv), 100 | attribute.Key("provider").String(ec._provider), 101 | attribute.Key("abi_method").String(meta.CallMethod), 102 | attribute.Key("status").String(meta.Status), 103 | )) 104 | otel.MetricsWeb3RequestHistogram.Record(ctx, time.Since(meta.StartAt).Milliseconds(), metric.WithAttributes( 105 | attribute.Key("client_id").String(ec._clientID), 106 | attribute.Key("app_id").String(ec._appID), 107 | attribute.Key("zone").String(ec._appID), 108 | attribute.Key("cluster").String(ec._cluster), 109 | attribute.Key("chain_id").Int64(ec._ethChainID), 110 | attribute.Key("chain_env").String(ec._ethChainEnv), 111 | attribute.Key("chain_name").String(ec._ethChainName), 112 | attribute.Key("provider").String(ec._provider), 113 | attribute.Key("abi_method").String(meta.CallMethod), 114 | )) 115 | } 116 | func (ec *EvmClient) _getTransactOpts(ctx context.Context, signer common.Address, to common.Address, dataHex string) (*bind.TransactOpts, error) { 117 | msgSignerPk, err := ec._getSinnerPrivateKey(signer) 118 | if err != nil { 119 | return nil, err 120 | } 121 | nonce, err := ec.PendingNonceAt(context.Background(), signer) 122 | if err != nil { 123 | return nil, err 124 | } 125 | gasPrice, err := ec.SuggestGasPrice(context.Background()) 126 | if err != nil { 127 | return nil, err 128 | } 129 | chainID, err := ec.ChainID(context.Background()) 130 | if err != nil { 131 | return nil, err 132 | } 133 | estimateGas, err := ec.ethClient.EstimateGas(ctx, ethereum.CallMsg{ 134 | From: signer, 135 | To: &to, 136 | Gas: uint64(ec._gasLimitMax.BigInt().Int64()), 137 | Value: big.NewInt(0), 138 | Data: common.Hex2Bytes(dataHex), 139 | }) 140 | if err != nil { 141 | return nil, err 142 | } 143 | opts, err := bind.NewKeyedTransactorWithChainID(msgSignerPk, chainID) 144 | if err != nil { 145 | return nil, err 146 | } 147 | opts.Nonce = big.NewInt(int64(nonce)) 148 | opts.Value = big.NewInt(0) 149 | opts.GasLimit = 0 150 | opts.GasPrice = decimal.NewFromBigInt(gasPrice, 0).Mul(ec._gasFeeRate).BigInt() 151 | opts.Context = ctx 152 | if estimateGas > 0 && !ec._gasLimitRate.IsZero() { 153 | opts.GasLimit = decimal.NewFromInt(int64(estimateGas)).Mul(ec._gasLimitRate).BigInt().Uint64() 154 | } 155 | if !ec._gasLimitMax.IsZero() && opts.GasLimit > uint64(ec._gasLimitMax.BigInt().Int64()) { 156 | opts.GasLimit = uint64(ec._gasLimitMax.BigInt().Int64()) 157 | } 158 | return opts, nil 159 | } 160 | func (ec *EvmClient) _getCallOpts(ctx context.Context) (*bind.CallOpts, error) { 161 | opts := &bind.CallOpts{ 162 | Context: ctx, 163 | } 164 | return opts, nil 165 | } 166 | 167 | func (ec *EvmClient) Close() { 168 | ec.ethClient.Close() 169 | ec.rpcClient.Close() 170 | } 171 | func (ec *EvmClient) GetAllSinners() []common.Address { 172 | data := make([]common.Address, 0) 173 | for _, v := range ec._signers { 174 | data = append(data, v.PublicAddress) 175 | } 176 | return data 177 | } 178 | func (ec *EvmClient) GetTransportURL() string { 179 | return ec._transportURL 180 | } 181 | 182 | func (ec *EvmClient) BlockByHash(ctx context.Context, blockHash common.Hash) (*types.Block, error) { 183 | abiMethod := consts.EvmMethodBlockByHash 184 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 185 | ec._beforeHooks(ctx, meta) 186 | defer func() { 187 | ec._afterHooks(ctx, meta) 188 | }() 189 | result, err := ec.ethClient.BlockByHash(ctx, blockHash) 190 | if err != nil { 191 | meta.Status = consts.AbiCallStatusFail 192 | } 193 | return result, err 194 | } 195 | func (ec *EvmClient) BlockByNumber(ctx context.Context, blockNumber *big.Int) (*types.Block, error) { 196 | abiMethod := consts.EvmMethodBlockByNumber 197 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 198 | ec._beforeHooks(ctx, meta) 199 | defer func() { 200 | ec._afterHooks(ctx, meta) 201 | }() 202 | result, err := ec.ethClient.BlockByNumber(ctx, blockNumber) 203 | if err != nil { 204 | meta.Status = consts.AbiCallStatusFail 205 | } 206 | return result, err 207 | } 208 | func (ec *EvmClient) HeaderByHash(ctx context.Context, blockHash common.Hash) (*types.Header, error) { 209 | abiMethod := consts.EvmMethodHeaderByHash 210 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 211 | ec._beforeHooks(ctx, meta) 212 | defer func() { 213 | ec._afterHooks(ctx, meta) 214 | }() 215 | result, err := ec.ethClient.HeaderByHash(ctx, blockHash) 216 | if err != nil { 217 | meta.Status = consts.AbiCallStatusFail 218 | } 219 | return result, err 220 | } 221 | func (ec *EvmClient) HeaderByNumber(ctx context.Context, blockNumber *big.Int) (*types.Header, error) { 222 | abiMethod := consts.EvmMethodHeaderByNumber 223 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 224 | ec._beforeHooks(ctx, meta) 225 | defer func() { 226 | ec._afterHooks(ctx, meta) 227 | }() 228 | result, err := ec.ethClient.HeaderByNumber(ctx, blockNumber) 229 | if err != nil { 230 | meta.Status = consts.AbiCallStatusFail 231 | } 232 | return result, err 233 | } 234 | func (ec *EvmClient) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) { 235 | abiMethod := consts.EvmMethodTransactionCount 236 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 237 | ec._beforeHooks(ctx, meta) 238 | defer func() { 239 | ec._afterHooks(ctx, meta) 240 | }() 241 | result, err := ec.ethClient.TransactionCount(ctx, blockHash) 242 | if err != nil { 243 | meta.Status = consts.AbiCallStatusFail 244 | } 245 | return result, err 246 | } 247 | func (ec *EvmClient) TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) { 248 | abiMethod := consts.EvmMethodTransactionInBlock 249 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 250 | ec._beforeHooks(ctx, meta) 251 | defer func() { 252 | ec._afterHooks(ctx, meta) 253 | }() 254 | result, err := ec.ethClient.TransactionInBlock(ctx, blockHash, index) 255 | if err != nil { 256 | meta.Status = consts.AbiCallStatusFail 257 | } 258 | return result, err 259 | } 260 | func (ec *EvmClient) TransactionByHash(ctx context.Context, blockHash common.Hash) (*types.Transaction, bool, error) { 261 | abiMethod := consts.EvmMethodTransactionByHash 262 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 263 | ec._beforeHooks(ctx, meta) 264 | defer func() { 265 | ec._afterHooks(ctx, meta) 266 | }() 267 | result, isPending, err := ec.ethClient.TransactionByHash(ctx, blockHash) 268 | if err != nil { 269 | meta.Status = consts.AbiCallStatusFail 270 | } 271 | return result, isPending, err 272 | } 273 | func (ec *EvmClient) TransactionReceipt(ctx context.Context, blockHash common.Hash) (*types.Receipt, error) { 274 | abiMethod := consts.EvmMethodTransactionReceipt 275 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 276 | ec._beforeHooks(ctx, meta) 277 | defer func() { 278 | ec._afterHooks(ctx, meta) 279 | }() 280 | result, err := ec.ethClient.TransactionReceipt(ctx, blockHash) 281 | if err != nil { 282 | meta.Status = consts.AbiCallStatusFail 283 | } 284 | return result, err 285 | } 286 | 287 | func (ec *EvmClient) SendTransaction(ctx context.Context, tx *types.Transaction) error { 288 | abiMethod := consts.EvmMethodSendTransaction 289 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 290 | ec._beforeHooks(ctx, meta) 291 | defer func() { 292 | ec._afterHooks(ctx, meta) 293 | }() 294 | err := ec.ethClient.SendTransaction(ctx, tx) 295 | if err != nil { 296 | meta.Status = consts.AbiCallStatusFail 297 | } 298 | return err 299 | } 300 | func (ec *EvmClient) SendTransactionSimple(ctx context.Context, signer common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { 301 | abiMethod := consts.EvmMethodSendTransaction 302 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 303 | ec._beforeHooks(ctx, meta) 304 | defer func() { 305 | ec._afterHooks(ctx, meta) 306 | }() 307 | msgSignerPk, err := ec._getSinnerPrivateKey(signer) 308 | if err != nil { 309 | return nil, err 310 | } 311 | opts, err := ec._getTransactOpts(ctx, signer, to, "0x") 312 | if err != nil { 313 | return nil, err 314 | } 315 | chainID, err := ec.ChainID(context.Background()) 316 | if err != nil { 317 | return nil, err 318 | } 319 | signedTx, err := types.SignNewTx(msgSignerPk, types.NewEIP155Signer(chainID), &types.LegacyTx{ 320 | To: &to, 321 | Nonce: opts.Nonce.Uint64(), 322 | Value: value, 323 | Gas: opts.GasLimit, 324 | GasPrice: opts.GasPrice, 325 | }) 326 | if err != nil { 327 | return nil, err 328 | } 329 | err = ec.ethClient.SendTransaction(ctx, signedTx) 330 | if err != nil { 331 | meta.Status = consts.AbiCallStatusFail 332 | } 333 | return signedTx, err 334 | } 335 | 336 | func (ec *EvmClient) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) { 337 | abiMethod := consts.EvmMethodBalanceAt 338 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 339 | ec._beforeHooks(ctx, meta) 340 | defer func() { 341 | ec._afterHooks(ctx, meta) 342 | }() 343 | result, err := ec.ethClient.BalanceAt(ctx, account, blockNumber) 344 | if err != nil { 345 | meta.Status = consts.AbiCallStatusFail 346 | } 347 | return result, err 348 | } 349 | func (ec *EvmClient) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) { 350 | abiMethod := consts.EvmMethodStorageAt 351 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 352 | ec._beforeHooks(ctx, meta) 353 | defer func() { 354 | ec._afterHooks(ctx, meta) 355 | }() 356 | result, err := ec.ethClient.StorageAt(ctx, account, key, blockNumber) 357 | if err != nil { 358 | meta.Status = consts.AbiCallStatusFail 359 | } 360 | return result, err 361 | } 362 | func (ec *EvmClient) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) { 363 | abiMethod := consts.EvmMethodCodeAt 364 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 365 | ec._beforeHooks(ctx, meta) 366 | defer func() { 367 | ec._afterHooks(ctx, meta) 368 | }() 369 | result, err := ec.ethClient.CodeAt(ctx, account, blockNumber) 370 | if err != nil { 371 | meta.Status = consts.AbiCallStatusFail 372 | } 373 | return result, err 374 | } 375 | func (ec *EvmClient) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) { 376 | abiMethod := consts.EvmMethodNonceAt 377 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 378 | ec._beforeHooks(ctx, meta) 379 | defer func() { 380 | ec._afterHooks(ctx, meta) 381 | }() 382 | result, err := ec.ethClient.NonceAt(ctx, account, blockNumber) 383 | if err != nil { 384 | meta.Status = consts.AbiCallStatusFail 385 | } 386 | return result, err 387 | } 388 | 389 | func (ec *EvmClient) SuggestGasPrice(ctx context.Context) (*big.Int, error) { 390 | abiMethod := consts.EvmMethodSuggestGasPrice 391 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 392 | ec._beforeHooks(ctx, meta) 393 | defer func() { 394 | ec._afterHooks(ctx, meta) 395 | }() 396 | result, err := ec.ethClient.SuggestGasPrice(ctx) 397 | if err != nil { 398 | meta.Status = consts.AbiCallStatusFail 399 | } 400 | return result, err 401 | } 402 | func (ec *EvmClient) SuggestGasTipCap(ctx context.Context) (*big.Int, error) { 403 | abiMethod := consts.EvmMethodSuggestGasTipCap 404 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 405 | ec._beforeHooks(ctx, meta) 406 | defer func() { 407 | ec._afterHooks(ctx, meta) 408 | }() 409 | result, err := ec.ethClient.SuggestGasTipCap(ctx) 410 | if err != nil { 411 | meta.Status = consts.AbiCallStatusFail 412 | } 413 | return result, err 414 | } 415 | func (ec *EvmClient) FeeHistory(ctx context.Context, blockCount uint64, lastBlock *big.Int, rewardPercentiles []float64) (*ethereum.FeeHistory, error) { 416 | abiMethod := consts.EvmMethodFeeHistory 417 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 418 | ec._beforeHooks(ctx, meta) 419 | defer func() { 420 | ec._afterHooks(ctx, meta) 421 | }() 422 | result, err := ec.ethClient.FeeHistory(ctx, blockCount, lastBlock, rewardPercentiles) 423 | if err != nil { 424 | meta.Status = consts.AbiCallStatusFail 425 | } 426 | return result, err 427 | } 428 | func (ec *EvmClient) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (uint64, error) { 429 | abiMethod := consts.EvmMethodEstimateGas 430 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 431 | ec._beforeHooks(ctx, meta) 432 | defer func() { 433 | ec._afterHooks(ctx, meta) 434 | }() 435 | result, err := ec.ethClient.EstimateGas(ctx, msg) 436 | if err != nil { 437 | meta.Status = consts.AbiCallStatusFail 438 | } 439 | return result, err 440 | } 441 | 442 | func (ec *EvmClient) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) { 443 | abiMethod := consts.EvmMethodPendingBalanceAtp 444 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 445 | ec._beforeHooks(ctx, meta) 446 | defer func() { 447 | ec._afterHooks(ctx, meta) 448 | }() 449 | result, err := ec.ethClient.PendingBalanceAt(ctx, account) 450 | if err != nil { 451 | meta.Status = consts.AbiCallStatusFail 452 | } 453 | return result, err 454 | } 455 | func (ec *EvmClient) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) { 456 | abiMethod := consts.EvmMethodPendingStorageAt 457 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 458 | ec._beforeHooks(ctx, meta) 459 | defer func() { 460 | ec._afterHooks(ctx, meta) 461 | }() 462 | result, err := ec.ethClient.PendingStorageAt(ctx, account, key) 463 | if err != nil { 464 | meta.Status = consts.AbiCallStatusFail 465 | } 466 | return result, err 467 | } 468 | func (ec *EvmClient) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) { 469 | abiMethod := consts.EvmMethodPendingCodeAt 470 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 471 | ec._beforeHooks(ctx, meta) 472 | defer func() { 473 | ec._afterHooks(ctx, meta) 474 | }() 475 | result, err := ec.ethClient.PendingCodeAt(ctx, account) 476 | if err != nil { 477 | meta.Status = consts.AbiCallStatusFail 478 | } 479 | return result, err 480 | } 481 | func (ec *EvmClient) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) { 482 | abiMethod := consts.EvmMethodPendingNonceAt 483 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 484 | ec._beforeHooks(ctx, meta) 485 | defer func() { 486 | ec._afterHooks(ctx, meta) 487 | }() 488 | result, err := ec.ethClient.PendingNonceAt(ctx, account) 489 | if err != nil { 490 | meta.Status = consts.AbiCallStatusFail 491 | } 492 | return result, err 493 | } 494 | func (ec *EvmClient) PendingTransactionCount(ctx context.Context) (uint, error) { 495 | abiMethod := consts.EvmMethodPendingTransactionCount 496 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 497 | ec._beforeHooks(ctx, meta) 498 | defer func() { 499 | ec._afterHooks(ctx, meta) 500 | }() 501 | result, err := ec.ethClient.PendingTransactionCount(ctx) 502 | if err != nil { 503 | meta.Status = consts.AbiCallStatusFail 504 | } 505 | return result, err 506 | } 507 | 508 | func (ec *EvmClient) BlockNumber(ctx context.Context) (uint64, error) { 509 | abiMethod := consts.EvmMethodBlockNumber 510 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 511 | ec._beforeHooks(ctx, meta) 512 | defer func() { 513 | ec._afterHooks(ctx, meta) 514 | }() 515 | result, err := ec.ethClient.BlockNumber(ctx) 516 | if err != nil { 517 | meta.Status = consts.AbiCallStatusFail 518 | } 519 | return result, err 520 | } 521 | func (ec *EvmClient) ChainID(ctx context.Context) (*big.Int, error) { 522 | abiMethod := consts.EvmMethodChainID 523 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 524 | ec._beforeHooks(ctx, meta) 525 | defer func() { 526 | ec._afterHooks(ctx, meta) 527 | }() 528 | result, err := ec.ethClient.ChainID(ctx) 529 | if err != nil { 530 | meta.Status = consts.AbiCallStatusFail 531 | } 532 | return result, err 533 | } 534 | func (ec *EvmClient) NetworkID(ctx context.Context) (*big.Int, error) { 535 | abiMethod := consts.EvmMethodNetworkID 536 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 537 | ec._beforeHooks(ctx, meta) 538 | defer func() { 539 | ec._afterHooks(ctx, meta) 540 | }() 541 | result, err := ec.ethClient.NetworkID(ctx) 542 | if err != nil { 543 | meta.Status = consts.AbiCallStatusFail 544 | } 545 | return result, err 546 | } 547 | 548 | func (ec *EvmClient) ERC20Name(ctx context.Context, token common.Address) (string, error) { 549 | abiMethod := consts.EvmErc20MethodName 550 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 551 | ec._beforeHooks(ctx, meta) 552 | defer func() { 553 | ec._afterHooks(ctx, meta) 554 | }() 555 | inst, err := erc20.NewERC20(token, ec.ethClient) 556 | if err != nil { 557 | return "", err 558 | } 559 | opts, err := ec._getCallOpts(ctx) 560 | if err != nil { 561 | return "", err 562 | } 563 | callResp, err := inst.Name(opts) 564 | if err != nil { 565 | return "", err 566 | } 567 | return callResp, nil 568 | } 569 | func (ec *EvmClient) ERC20Symbol(ctx context.Context, token common.Address) (string, error) { 570 | abiMethod := consts.EvmErc20MethodSymbol 571 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 572 | ec._beforeHooks(ctx, meta) 573 | defer func() { 574 | ec._afterHooks(ctx, meta) 575 | }() 576 | inst, err := erc20.NewERC20(token, ec.ethClient) 577 | if err != nil { 578 | return "", err 579 | } 580 | opts, err := ec._getCallOpts(ctx) 581 | if err != nil { 582 | return "", err 583 | } 584 | callResp, err := inst.Symbol(opts) 585 | if err != nil { 586 | return "", err 587 | } 588 | return callResp, nil 589 | } 590 | func (ec *EvmClient) ERC20Decimals(ctx context.Context, token common.Address) (uint8, error) { 591 | abiMethod := consts.EvmErc20MethodDecimals 592 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 593 | ec._beforeHooks(ctx, meta) 594 | defer func() { 595 | ec._afterHooks(ctx, meta) 596 | }() 597 | inst, err := erc20.NewERC20(token, ec.ethClient) 598 | if err != nil { 599 | return 0, err 600 | } 601 | opts, err := ec._getCallOpts(ctx) 602 | if err != nil { 603 | return 0, err 604 | } 605 | callResp, err := inst.Decimals(opts) 606 | if err != nil { 607 | return 0, err 608 | } 609 | return callResp, nil 610 | } 611 | func (ec *EvmClient) ERC20BalanceOf(ctx context.Context, token common.Address, account common.Address) (*big.Int, error) { 612 | abiMethod := consts.EvmErc20MethodBalanceOf 613 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 614 | ec._beforeHooks(ctx, meta) 615 | defer func() { 616 | ec._afterHooks(ctx, meta) 617 | }() 618 | inst, err := erc20.NewERC20(token, ec.ethClient) 619 | if err != nil { 620 | return big.NewInt(0), err 621 | } 622 | opts, err := ec._getCallOpts(ctx) 623 | if err != nil { 624 | return big.NewInt(0), err 625 | } 626 | callResp, err := inst.BalanceOf(opts, account) 627 | if err != nil { 628 | return big.NewInt(0), err 629 | } 630 | return callResp, nil 631 | } 632 | func (ec *EvmClient) ERC20TotalSupply(ctx context.Context, token common.Address) (*big.Int, error) { 633 | abiMethod := consts.EvmErc20MethodTotalSupply 634 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 635 | ec._beforeHooks(ctx, meta) 636 | defer func() { 637 | ec._afterHooks(ctx, meta) 638 | }() 639 | inst, err := erc20.NewERC20(token, ec.ethClient) 640 | if err != nil { 641 | return big.NewInt(0), err 642 | } 643 | opts, err := ec._getCallOpts(ctx) 644 | if err != nil { 645 | return big.NewInt(0), err 646 | } 647 | callResp, err := inst.TotalSupply(opts) 648 | if err != nil { 649 | return big.NewInt(0), err 650 | } 651 | return callResp, nil 652 | } 653 | func (ec *EvmClient) ERC20Transfer(ctx context.Context, token common.Address, signer common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { 654 | abiMethod := consts.EvmErc20MethodTransfer 655 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 656 | ec._beforeHooks(ctx, meta) 657 | defer func() { 658 | ec._afterHooks(ctx, meta) 659 | }() 660 | inst, err := erc20.NewERC20(token, ec.ethClient) 661 | if err != nil { 662 | return nil, err 663 | } 664 | abi, err := erc20.ERC20MetaData.GetAbi() 665 | if err != nil { 666 | return nil, err 667 | } 668 | abiData, err := abi.Pack("transfer", to, value) 669 | if err != nil { 670 | return nil, err 671 | } 672 | 673 | opts, err := ec._getTransactOpts(ctx, signer, token, common.Bytes2Hex(abiData)) 674 | if err != nil { 675 | return nil, err 676 | } 677 | callResp, err := inst.Transfer(opts, to, value) 678 | if err != nil { 679 | return nil, err 680 | } 681 | 682 | return callResp, nil 683 | } 684 | func (ec *EvmClient) ERC20Approve(ctx context.Context, token common.Address, signer common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { 685 | abiMethod := consts.EvmErc20MethodApprove 686 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 687 | ec._beforeHooks(ctx, meta) 688 | defer func() { 689 | ec._afterHooks(ctx, meta) 690 | }() 691 | inst, err := erc20.NewERC20(token, ec.ethClient) 692 | if err != nil { 693 | return nil, err 694 | } 695 | abi, err := erc20.ERC20MetaData.GetAbi() 696 | if err != nil { 697 | return nil, err 698 | } 699 | abiData, err := abi.Pack("transfer", to, value) 700 | if err != nil { 701 | return nil, err 702 | } 703 | opts, err := ec._getTransactOpts(ctx, signer, token, common.Bytes2Hex(abiData)) 704 | if err != nil { 705 | return nil, err 706 | } 707 | callResp, err := inst.Approve(opts, to, value) 708 | if err != nil { 709 | return nil, err 710 | } 711 | return callResp, nil 712 | } 713 | func (ec *EvmClient) ERC20IncreaseAllowance(ctx context.Context, token common.Address, signer common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { 714 | abiMethod := consts.EvmErc20MethodIncreaseAllowance 715 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 716 | ec._beforeHooks(ctx, meta) 717 | defer func() { 718 | ec._afterHooks(ctx, meta) 719 | }() 720 | inst, err := erc20.NewERC20(token, ec.ethClient) 721 | if err != nil { 722 | return nil, err 723 | } 724 | abi, err := erc20.ERC20MetaData.GetAbi() 725 | if err != nil { 726 | return nil, err 727 | } 728 | abiData, err := abi.Pack("transfer", to, value) 729 | if err != nil { 730 | return nil, err 731 | } 732 | opts, err := ec._getTransactOpts(ctx, signer, token, common.Bytes2Hex(abiData)) 733 | if err != nil { 734 | return nil, err 735 | } 736 | callResp, err := inst.IncreaseAllowance(opts, to, value) 737 | if err != nil { 738 | return nil, err 739 | } 740 | return callResp, nil 741 | } 742 | func (ec *EvmClient) ERC20DecreaseAllowance(ctx context.Context, token common.Address, signer common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { 743 | abiMethod := consts.EvmErc20MethodDecreaseAllowance 744 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 745 | ec._beforeHooks(ctx, meta) 746 | defer func() { 747 | ec._afterHooks(ctx, meta) 748 | }() 749 | inst, err := erc20.NewERC20(token, ec.ethClient) 750 | if err != nil { 751 | return nil, err 752 | } 753 | abi, err := erc20.ERC20MetaData.GetAbi() 754 | if err != nil { 755 | return nil, err 756 | } 757 | abiData, err := abi.Pack("transfer", to, value) 758 | if err != nil { 759 | return nil, err 760 | } 761 | opts, err := ec._getTransactOpts(ctx, signer, token, common.Bytes2Hex(abiData)) 762 | if err != nil { 763 | return nil, err 764 | } 765 | callResp, err := inst.DecreaseAllowance(opts, to, value) 766 | if err != nil { 767 | return nil, err 768 | } 769 | return callResp, nil 770 | } 771 | func (ec *EvmClient) ERC20Allowance(ctx context.Context, token common.Address, owner common.Address, spender common.Address) (*big.Int, error) { 772 | abiMethod := consts.EvmErc20MethodAllowance 773 | meta := &clientModel.Metadata{CallMethod: abiMethod, Status: consts.AbiCallStatusSuccess} 774 | ec._beforeHooks(ctx, meta) 775 | defer func() { 776 | ec._afterHooks(ctx, meta) 777 | }() 778 | inst, err := erc20.NewERC20(token, ec.ethClient) 779 | if err != nil { 780 | return big.NewInt(0), err 781 | } 782 | opts, err := ec._getCallOpts(ctx) 783 | if err != nil { 784 | return big.NewInt(0), err 785 | } 786 | callResp, err := inst.Allowance(opts, owner, spender) 787 | if err != nil { 788 | return big.NewInt(0), err 789 | } 790 | return callResp, nil 791 | } 792 | -------------------------------------------------------------------------------- /client/evm_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "crypto/ecdsa" 6 | "fmt" 7 | "math/big" 8 | "os" 9 | "testing" 10 | 11 | "github.com/6boris/web3-go/pkg/wjson" 12 | "github.com/davecgh/go-spew/spew" 13 | "github.com/ethereum/go-ethereum" 14 | "github.com/ethereum/go-ethereum/common" 15 | "github.com/ethereum/go-ethereum/core/types" 16 | "github.com/ethereum/go-ethereum/crypto" 17 | "github.com/shopspring/decimal" 18 | "github.com/stretchr/testify/assert" 19 | ) 20 | 21 | func TestNewEvmClient(t *testing.T) { 22 | testAccount := common.HexToAddress("0xf15689636571dba322b48E9EC9bA6cFB3DF818e1") 23 | testBlockHash := common.HexToHash("0x21e0118bd8618a14632f82e1445c1f178cfab5bbd2debf1eb3338886ced75e15") 24 | testBlockNumber := big.NewInt(19454574) 25 | testTxHash := common.HexToHash("0xb382be540032c6751b97cb623ff2bbcd0f1da2a386c4a75f7e561ce4ebefb787") 26 | testEvmPolygonUSDTAddress := common.HexToAddress("0xc2132D05D31c914a87C6611C10748AEb04B58e8F") 27 | t.Run("BlockByHash", func(t *testing.T) { 28 | blockInfo, err := testEvmEthClient.BlockByHash(testCtx, testBlockHash) 29 | assert.Nil(t, err) 30 | spew.Dump(blockInfo) 31 | }) 32 | t.Run("BlockByNumber", func(t *testing.T) { 33 | blockInfo, err := testEvmEthClient.BlockByNumber(testCtx, testBlockNumber) 34 | assert.Nil(t, err) 35 | spew.Dump(blockInfo) 36 | }) 37 | t.Run("HeaderByHash", func(t *testing.T) { 38 | headerInfo, err := testEvmEthClient.HeaderByHash(testCtx, testBlockHash) 39 | assert.Nil(t, err) 40 | spew.Dump(headerInfo) 41 | }) 42 | t.Run("HeaderByNumber", func(t *testing.T) { 43 | headerInfo, err := testEvmEthClient.HeaderByNumber(testCtx, testBlockNumber) 44 | assert.Nil(t, err) 45 | spew.Dump(headerInfo) 46 | }) 47 | t.Run("TransactionCount", func(t *testing.T) { 48 | txCount, err := testEvmEthClient.TransactionCount(testCtx, testBlockHash) 49 | assert.Nil(t, err) 50 | spew.Dump(txCount) 51 | }) 52 | t.Run("TransactionInBlock", func(t *testing.T) { 53 | txCount, err := testEvmEthClient.TransactionInBlock(testCtx, testBlockHash, 1) 54 | assert.Nil(t, err) 55 | spew.Dump(txCount) 56 | }) 57 | t.Run("TransactionByHash", func(t *testing.T) { 58 | txInfo, isPending, err := testEvmEthClient.TransactionByHash(testCtx, testTxHash) 59 | assert.Nil(t, err) 60 | spew.Dump(isPending) 61 | spew.Dump(txInfo) 62 | }) 63 | t.Run("TransactionReceipt", func(t *testing.T) { 64 | txInfo, err := testEvmEthClient.TransactionReceipt(testCtx, testTxHash) 65 | assert.Nil(t, err) 66 | spew.Dump(txInfo) 67 | }) 68 | t.Run("SendTransaction_Native_Token", func(t *testing.T) { 69 | privateKey, err := crypto.HexToECDSA(os.Getenv("WEB3_GO_DEV_KEY_2")) 70 | assert.Nil(t, err) 71 | publicKey := privateKey.Public() 72 | publicKeyECDSA, _ := publicKey.(*ecdsa.PublicKey) 73 | assert.Nil(t, err) 74 | sendAccount := crypto.PubkeyToAddress(*publicKeyECDSA) 75 | 76 | chainID, err := testEvmPolygonClient.ChainID(context.Background()) 77 | assert.Nil(t, err) 78 | 79 | nonce, err := testEvmPolygonClient.PendingNonceAt(context.Background(), sendAccount) 80 | assert.Nil(t, err) 81 | 82 | gasLimit := uint64(21000) 83 | value := decimal.New(1, 0).BigInt() 84 | gasPrice, err := testEvmPolygonClient.SuggestGasPrice(testCtx) 85 | gasPrice = decimal.NewFromBigInt(gasPrice, 0). 86 | Mul(decimal.NewFromFloat(2)).BigInt() 87 | assert.Nil(t, err) 88 | signedTx, err := types.SignNewTx(privateKey, types.NewEIP155Signer(chainID), &types.LegacyTx{ 89 | To: &sendAccount, 90 | Nonce: nonce, 91 | Value: value, 92 | Gas: gasLimit, 93 | GasPrice: gasPrice, 94 | }) 95 | assert.Nil(t, err) 96 | 97 | err = testEvmPolygonClient.SendTransaction(context.Background(), signedTx) 98 | assert.Nil(t, err) 99 | fmt.Println(wjson.StructToJsonStringWithIndent(map[string]interface{}{ 100 | "chain_id": chainID, 101 | "sendAccount": sendAccount, 102 | "toAccount": sendAccount, 103 | "nonce": nonce, 104 | "gas_limit": gasLimit, 105 | "value": value, 106 | "gas_price": decimal.NewFromBigInt(gasPrice, -9), 107 | "tx_hash": signedTx.Hash().String(), 108 | }, "", " ")) 109 | }) 110 | t.Run("SendTransactionSimple", func(t *testing.T) { 111 | tx, err := testEvmPolygonClient.SendTransactionSimple( 112 | testCtx, 113 | testEvmPolygonClient.GetAllSinners()[0], 114 | testEvmPolygonClient.GetAllSinners()[1], 115 | decimal.NewFromFloat(0.00001).Mul(decimal.New(1, 18)).BigInt(), 116 | ) 117 | assert.Nil(t, err) 118 | spew.Dump(tx.Hash()) 119 | }) 120 | t.Run("BalanceAt", func(t *testing.T) { 121 | accountBalance, err := testEvmEthClient.BalanceAt(testCtx, testAccount, nil) 122 | assert.Nil(t, err) 123 | spew.Dump(decimal.NewFromBigInt(accountBalance, -18)) 124 | }) 125 | t.Run("StorageAt", func(t *testing.T) { 126 | storageBytes, err := testEvmEthClient.StorageAt( 127 | testCtx, testAccount, 128 | common.HexToHash(""), big.NewInt(19454700), 129 | ) 130 | assert.Nil(t, err) 131 | spew.Dump(storageBytes) 132 | }) 133 | t.Run("CodeAt", func(t *testing.T) { 134 | codeBytes, err := testEvmEthClient.CodeAt( 135 | testCtx, common.HexToAddress("0xdAC17F958D2ee523a2206206994597C13D831ec7"), 136 | big.NewInt(19454700), 137 | ) 138 | assert.Nil(t, err) 139 | spew.Dump(codeBytes) 140 | }) 141 | t.Run("NonceAt", func(t *testing.T) { 142 | nonceAt, err := testEvmEthClient.NonceAt( 143 | testCtx, testAccount, 144 | big.NewInt(19454700), 145 | ) 146 | assert.Nil(t, err) 147 | spew.Dump(nonceAt) 148 | }) 149 | t.Run("SuggestGasPrice", func(t *testing.T) { 150 | gasPrice, err := testEvmEthClient.SuggestGasPrice(testCtx) 151 | assert.Nil(t, err) 152 | spew.Dump(decimal.NewFromBigInt(gasPrice, -18)) 153 | }) 154 | t.Run("SuggestGasTipCap", func(t *testing.T) { 155 | gasPrice, err := testEvmEthClient.SuggestGasTipCap(testCtx) 156 | assert.Nil(t, err) 157 | spew.Dump(decimal.NewFromBigInt(gasPrice, -18)) 158 | }) 159 | t.Run("FeeHistory", func(t *testing.T) { 160 | feeHistory, err := testEvmEthClient.FeeHistory(testCtx, 161 | 2, 162 | testBlockNumber, 163 | []float64{0.008912678667376286}, 164 | ) 165 | assert.Nil(t, err) 166 | spew.Dump(feeHistory) 167 | }) 168 | t.Run("EstimateGas", func(t *testing.T) { 169 | msg := ethereum.CallMsg{ 170 | From: testAccount, 171 | To: &common.Address{}, 172 | Gas: 21000, 173 | Value: big.NewInt(1), 174 | } 175 | gasInfo, err := testEvmEthClient.EstimateGas(testCtx, msg) 176 | assert.Nil(t, err) 177 | spew.Dump(gasInfo) 178 | }) 179 | t.Run("PendingBalanceAt", func(t *testing.T) { 180 | accountInfo, err := testEvmEthClient.PendingBalanceAt(testCtx, testAccount) 181 | assert.Nil(t, err) 182 | spew.Dump(decimal.NewFromBigInt(accountInfo, -18)) 183 | }) 184 | t.Run("PendingStorageAt", func(t *testing.T) { 185 | storageBytes, err := testEvmEthClient.PendingStorageAt( 186 | testCtx, testAccount, 187 | common.HexToHash(""), 188 | ) 189 | assert.Nil(t, err) 190 | spew.Dump(storageBytes) 191 | }) 192 | t.Run("PendingCodeAt", func(t *testing.T) { 193 | codeBytes, err := testEvmEthClient.PendingCodeAt( 194 | testCtx, common.HexToAddress("0xdAC17F958D2ee523a2206206994597C13D831ec7"), 195 | ) 196 | assert.Nil(t, err) 197 | spew.Dump(codeBytes) 198 | }) 199 | t.Run("PendingNonceAt", func(t *testing.T) { 200 | nonceAt, err := testEvmEthClient.PendingNonceAt( 201 | testCtx, testAccount, 202 | ) 203 | assert.Nil(t, err) 204 | spew.Dump(nonceAt) 205 | }) 206 | t.Run("PendingTransactionCount", func(t *testing.T) { 207 | pendingTxCount, err := testEvmEthClient.PendingTransactionCount(testCtx) 208 | assert.Nil(t, err) 209 | spew.Dump(pendingTxCount) 210 | }) 211 | t.Run("BlockNumber", func(t *testing.T) { 212 | blockNumber, err := testEvmEthClient.BlockNumber(testCtx) 213 | assert.Nil(t, err) 214 | spew.Dump(blockNumber) 215 | }) 216 | t.Run("ChainID", func(t *testing.T) { 217 | chanID, err := testEvmEthClient.ChainID(testCtx) 218 | assert.Nil(t, err) 219 | spew.Dump(chanID) 220 | }) 221 | t.Run("ERC20Name", func(t *testing.T) { 222 | tokenName, err := testEvmPolygonClient.ERC20Name(testCtx, testEvmPolygonUSDTAddress) 223 | assert.Nil(t, err) 224 | spew.Dump(tokenName) 225 | }) 226 | t.Run("ERC20Symbol", func(t *testing.T) { 227 | tokenSymbol, err := testEvmPolygonClient.ERC20Symbol(testCtx, testEvmPolygonUSDTAddress) 228 | assert.Nil(t, err) 229 | spew.Dump(tokenSymbol) 230 | }) 231 | t.Run("ERC20Decimals", func(t *testing.T) { 232 | tokenDecimals, err := testEvmPolygonClient.ERC20Decimals(testCtx, testEvmPolygonUSDTAddress) 233 | assert.Nil(t, err) 234 | spew.Dump(tokenDecimals) 235 | }) 236 | t.Run("ERC20BalanceOf", func(t *testing.T) { 237 | tokenDecimals, err := testEvmPolygonClient.ERC20Decimals(testCtx, testEvmPolygonUSDTAddress) 238 | assert.Nil(t, err) 239 | 240 | tokenBalance, err := testEvmPolygonClient.ERC20BalanceOf(testCtx, 241 | testEvmPolygonUSDTAddress, 242 | testEvmPolygonUSDTAddress) 243 | assert.Nil(t, err) 244 | spew.Dump(decimal.NewFromBigInt(tokenBalance, -int32(tokenDecimals))) 245 | }) 246 | t.Run("ERC20TotalSupply", func(t *testing.T) { 247 | tokenSymbol, err := testEvmPolygonClient.ERC20Symbol(testCtx, testEvmPolygonUSDTAddress) 248 | assert.Nil(t, err) 249 | 250 | tokenDecimals, err := testEvmPolygonClient.ERC20Decimals(testCtx, testEvmPolygonUSDTAddress) 251 | assert.Nil(t, err) 252 | 253 | tokenBalance, err := testEvmPolygonClient.ERC20TotalSupply(testCtx, testEvmPolygonUSDTAddress) 254 | assert.Nil(t, err) 255 | fmt.Printf("TotalSupply:%s %s\n", 256 | decimal.NewFromBigInt(tokenBalance, -int32(tokenDecimals)).String(), tokenSymbol) 257 | }) 258 | t.Run("ERC20Transfer", func(t *testing.T) { 259 | tokenDecimals, err := testEvmPolygonClient.ERC20Decimals(testCtx, testEvmPolygonUSDTAddress) 260 | assert.Nil(t, err) 261 | txInfo, err := testEvmPolygonClient.ERC20Transfer( 262 | testCtx, 263 | testEvmPolygonUSDTAddress, 264 | testEvmPolygonClient.GetAllSinners()[0], 265 | testEvmPolygonClient.GetAllSinners()[1], 266 | decimal.NewFromFloat(0.00001).Mul(decimal.New(1, int32(tokenDecimals))).BigInt(), 267 | ) 268 | assert.Nil(t, err) 269 | fmt.Println(txInfo.Hash().String()) 270 | }) 271 | t.Run("ERC20Approve", func(t *testing.T) { 272 | tokenDecimals, err := testEvmPolygonClient.ERC20Decimals(testCtx, testEvmPolygonUSDTAddress) 273 | assert.Nil(t, err) 274 | 275 | txInfo, err := testEvmPolygonClient.ERC20Approve( 276 | testCtx, 277 | testEvmPolygonUSDTAddress, 278 | testEvmPolygonClient.GetAllSinners()[0], testEvmPolygonClient.GetAllSinners()[1], 279 | decimal.NewFromFloat(0.00001).Mul(decimal.New(1, int32(tokenDecimals))).BigInt(), 280 | ) 281 | assert.Nil(t, err) 282 | fmt.Println("ERC20Approve", txInfo.Hash().String()) 283 | }) 284 | t.Run("ERC20IncreaseAllowance", func(t *testing.T) { 285 | tokenDecimals, err := testEvmPolygonClient.ERC20Decimals(testCtx, testEvmPolygonUSDTAddress) 286 | assert.Nil(t, err) 287 | 288 | txInfo, err := testEvmPolygonClient.ERC20IncreaseAllowance( 289 | testCtx, 290 | testEvmPolygonUSDTAddress, 291 | testEvmPolygonClient.GetAllSinners()[0], testEvmPolygonClient.GetAllSinners()[1], 292 | decimal.NewFromFloat(0.00001).Mul(decimal.New(1, int32(tokenDecimals))).BigInt(), 293 | ) 294 | assert.Nil(t, err) 295 | fmt.Println("ERC20Approve", txInfo.Hash().String()) 296 | }) 297 | t.Run("ERC20DecreaseAllowance", func(t *testing.T) { 298 | tokenDecimals, err := testEvmPolygonClient.ERC20Decimals(testCtx, testEvmPolygonUSDTAddress) 299 | assert.Nil(t, err) 300 | 301 | txInfo, err := testEvmPolygonClient.ERC20DecreaseAllowance( 302 | testCtx, 303 | testEvmPolygonUSDTAddress, 304 | testEvmPolygonClient.GetAllSinners()[0], testEvmPolygonClient.GetAllSinners()[1], 305 | decimal.NewFromFloat(0.00001).Mul(decimal.New(1, int32(tokenDecimals))).BigInt(), 306 | ) 307 | assert.Nil(t, err) 308 | fmt.Println("ERC20Approve", txInfo.Hash().String()) 309 | }) 310 | t.Run("ERC20Allowance", func(t *testing.T) { 311 | tokenSymbol, err := testEvmPolygonClient.ERC20Symbol(testCtx, testEvmPolygonUSDTAddress) 312 | assert.Nil(t, err) 313 | 314 | tokenDecimals, err := testEvmPolygonClient.ERC20Decimals(testCtx, testEvmPolygonUSDTAddress) 315 | assert.Nil(t, err) 316 | 317 | tokenAllowance, err := testEvmPolygonClient.ERC20Allowance( 318 | testCtx, 319 | testEvmPolygonUSDTAddress, 320 | testEvmPolygonClient.GetAllSinners()[0], testEvmPolygonClient.GetAllSinners()[1], 321 | ) 322 | assert.Nil(t, err) 323 | fmt.Printf("ERC20Allowance:%s %s\n", 324 | decimal.NewFromBigInt(tokenAllowance, -int32(tokenDecimals)).String(), tokenSymbol) 325 | }) 326 | } 327 | -------------------------------------------------------------------------------- /client/interface.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "math/big" 6 | 7 | "github.com/ethereum/go-ethereum" 8 | "github.com/ethereum/go-ethereum/common" 9 | "github.com/ethereum/go-ethereum/core/types" 10 | ) 11 | 12 | type EvmClientInterface interface { 13 | 14 | // Geth ChainReader 15 | 16 | BlockByHash(ctx context.Context, hash common.Hash) (*types.Block, error) 17 | BlockByNumber(ctx context.Context, number *big.Int) (*types.Block, error) 18 | HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) 19 | HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) 20 | TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) 21 | TransactionInBlock(ctx context.Context, blockHash common.Hash, index uint) (*types.Transaction, error) 22 | 23 | // Geth TransactionReader 24 | 25 | TransactionByHash(ctx context.Context, txHash common.Hash) (tx *types.Transaction, isPending bool, err error) 26 | TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) 27 | SendTransaction(ctx context.Context, tx *types.Transaction) error 28 | 29 | // Geth ChainStateReader 30 | 31 | BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) 32 | StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) 33 | CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) 34 | NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) 35 | 36 | // Geth Gas 37 | 38 | SuggestGasPrice(ctx context.Context) (*big.Int, error) 39 | SuggestGasTipCap(ctx context.Context) (*big.Int, error) 40 | FeeHistory(ctx context.Context, blockCount uint64, lastBlock *big.Int, rewardPercentiles []float64) (*ethereum.FeeHistory, error) 41 | EstimateGas(ctx context.Context, call ethereum.CallMsg) (uint64, error) 42 | 43 | // Geth PendingStateReader 44 | PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) 45 | PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) 46 | PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) 47 | PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) 48 | PendingTransactionCount(ctx context.Context) (uint, error) 49 | 50 | // Geth Other 51 | 52 | BlockNumber(ctx context.Context) (uint64, error) 53 | ChainID(ctx context.Context) (*big.Int, error) 54 | } 55 | 56 | type SolanaClientInterface interface { 57 | } 58 | -------------------------------------------------------------------------------- /client/pool.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "github.com/6boris/web3-go/model/client" 5 | "github.com/go-kratos/aegis/circuitbreaker" 6 | "github.com/go-kratos/aegis/circuitbreaker/sre" 7 | ) 8 | 9 | type Pool struct { 10 | conf *client.ConfPool 11 | // clients map[int64]map[string]*Client 12 | breakerGroup *circuitbreaker.CircuitBreaker 13 | _evmClients map[int64]map[string]*EvmClient 14 | _solanaClients map[string]*SolanaClient 15 | } 16 | 17 | // func init() { 18 | // //exporter, err := prometheus.New() 19 | // //if err != nil { 20 | // // log.Fatal(err) 21 | // //} 22 | // //provider := metricSdk.NewMeterProvider(metricSdk.WithReader(exporter)) 23 | // //meter := provider.Meter("metrics", metric.WithInstrumentationVersion(runtime.Version())) 24 | // m1, err := global.Meter("web3.go").Int64Counter("web3_abi_call", metric.WithDescription("Web3 Gateway abi call counter")) 25 | // if err != nil { 26 | // panic(err) 27 | // } 28 | // m2, err := global.Meter("web3.go").Int64Histogram("web3_abi_call", metric.WithDescription("Web3 Gateway abi call hist")) 29 | // if err != nil { 30 | // panic(err) 31 | // } 32 | // MetricsWeb3RequestCounter = m1 33 | // MetricsWeb3RequestHistogram = m2 34 | // 35 | //} 36 | 37 | func NewPool(conf *client.ConfPool) *Pool { 38 | p := &Pool{ 39 | conf: conf, 40 | _solanaClients: map[string]*SolanaClient{}, 41 | } 42 | b := sre.NewBreaker() 43 | p.breakerGroup = &b 44 | 45 | p._evmClients = make(map[int64]map[string]*EvmClient, 0) 46 | for _, chain := range conf.EvmChains { 47 | if _, ok := p._evmClients[chain.ChainID]; !ok { 48 | p._evmClients[chain.ChainID] = make(map[string]*EvmClient, 0) 49 | } 50 | for _, c := range chain.Clients { 51 | if c.TransportSchema == "https" { 52 | tmpC, err := NewEvmClient(c) 53 | tmpC._appID = conf.AppID 54 | tmpC._zone = conf.Zone 55 | tmpC._cluster = conf.Cluster 56 | tmpC._ethChainID = chain.ChainID 57 | tmpC._ethChainName = chain.ChainName 58 | tmpC._ethChainEnv = chain.ChainEnv 59 | if err != nil { 60 | panic(err) 61 | } else { 62 | p._evmClients[chain.ChainID][tmpC._clientID] = tmpC 63 | } 64 | } 65 | } 66 | } 67 | for _, c := range p.conf.SolanaChains { 68 | loopClient, loopErr := NewSolanaClient(c) 69 | if loopErr == nil { 70 | p._solanaClients[loopClient.ClientID] = loopClient 71 | } 72 | } 73 | return p 74 | } 75 | 76 | func (p *Pool) GetEvmClient(chainID int64) *EvmClient { 77 | if clientMap, ok := p._evmClients[chainID]; ok { 78 | if len(clientMap) > 0 { 79 | for _, val := range clientMap { 80 | return val 81 | } 82 | } 83 | } 84 | return nil 85 | } 86 | func (p *Pool) GetSolanaClient(chainEnv string) *SolanaClient { 87 | if len(p._solanaClients) == 0 { 88 | return nil 89 | } 90 | for _, v := range p._solanaClients { 91 | if v.ChainEnv == chainEnv { 92 | return v 93 | } 94 | } 95 | return nil 96 | } 97 | -------------------------------------------------------------------------------- /client/pool_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "crypto/ecdsa" 6 | "fmt" 7 | "log" 8 | "testing" 9 | 10 | "github.com/6boris/web3-go/consts" 11 | "github.com/6boris/web3-go/model/solana" 12 | "github.com/davecgh/go-spew/spew" 13 | "github.com/ethereum/go-ethereum/crypto" 14 | "github.com/shopspring/decimal" 15 | "github.com/stretchr/testify/assert" 16 | ) 17 | 18 | func Test_Unite_Pool(t *testing.T) { 19 | chainID := int64(1) 20 | t.Run("ChainID", func(t *testing.T) { 21 | for i := 0; i < 50; i++ { 22 | resp, err := testPool.GetEvmClient(chainID).ChainID(testCtx) 23 | assert.Nil(t, err) 24 | spew.Dump(resp) 25 | } 26 | }) 27 | t.Run("BlockNumber", func(t *testing.T) { 28 | chainCase := []int64{ 29 | 1, 5, 11155111, // Ethereum 30 | 56, 97, // Bsc 31 | 137, 80001, // Polygon 32 | 250, 4002, // Fantom 33 | 10, 420, // Optimistic 34 | } 35 | for _, c := range chainCase { 36 | resp, err := testPool.GetEvmClient(c).BlockNumber(testCtx) 37 | assert.Nil(t, err) 38 | fmt.Println(fmt.Printf("ChainID:%d BlockNumber:%d", c, resp)) 39 | } 40 | }) 41 | t.Run("Evm_SendTransaction", func(t *testing.T) { 42 | 43 | privateKey, err := crypto.HexToECDSA("fad9c8855b740a0b7ed4c221dbad0f33a83a49cad6b3fe8d5817ac83d38b6a19") 44 | if err != nil { 45 | log.Fatal(err) 46 | } 47 | publicKey := privateKey.Public() 48 | publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) 49 | if !ok { 50 | log.Fatal("error casting public key to ECDSA") 51 | } 52 | testAccount := crypto.PubkeyToAddress(*publicKeyECDSA) 53 | fmt.Println("testAccount", testAccount) 54 | testChainID := int64(137) 55 | nonce, err := testPool.GetEvmClient(testChainID).PendingNonceAt(testCtx, testAccount) 56 | assert.Nil(t, err) 57 | fmt.Println("Nonce:", nonce) 58 | //value := big.NewInt(1000000000000000000) 59 | //gasLimit := uint64(21000) 60 | gasPrice, err := testPool.GetEvmClient(testChainID).SuggestGasPrice(context.Background()) 61 | assert.Nil(t, err) 62 | fmt.Println("gasPrice:", gasPrice) 63 | 64 | //err := testPool.GetEvmClient(137).SendTransaction(testCtx, nil) 65 | //assert.Nil(t, err) 66 | }) 67 | t.Run("Evm_ChainID", func(t *testing.T) { 68 | resp, err := testPool.GetEvmClient(1).ChainID(testCtx) 69 | assert.Nil(t, err) 70 | spew.Dump(resp) 71 | }) 72 | t.Run("Evm_SuggestGasTipCap", func(t *testing.T) { 73 | gasPrice, err := testPool.GetEvmClient(1).SuggestGasTipCap(testCtx) 74 | assert.Nil(t, err) 75 | spew.Dump(decimal.NewFromBigInt(gasPrice, -18)) 76 | }) 77 | 78 | t.Run("Solana_GetBalance", func(t *testing.T) { 79 | resp, err := testPool.GetSolanaClient(consts.ChainEnvMainnet).GetBalance(testCtx, &solana.GetBalanceRequest{Account: "5EhGYUyQNrxgUbuYF4vbL2SZDT6RMfhq3yjeyevvULeC"}) 80 | assert.Nil(t, err) 81 | spew.Dump(resp) 82 | }) 83 | } 84 | -------------------------------------------------------------------------------- /client/solana.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "context" 5 | "encoding/json" 6 | "errors" 7 | "fmt" 8 | "strings" 9 | "time" 10 | 11 | clientModel "github.com/6boris/web3-go/model/client" 12 | "github.com/6boris/web3-go/model/solana" 13 | "github.com/google/uuid" 14 | "github.com/imroc/req/v3" 15 | "github.com/shopspring/decimal" 16 | "github.com/tidwall/gjson" 17 | ) 18 | 19 | type SolanaClient struct { 20 | HttpClient *req.Client 21 | ClientID string 22 | AppID string 23 | Zone string 24 | Cluster string 25 | ChainEnv string 26 | Provider string 27 | TransportURL string 28 | } 29 | 30 | func NewSolanaClient(conf *clientModel.ConfSolanaClient) (*SolanaClient, error) { 31 | client := &SolanaClient{ 32 | ClientID: strings.ReplaceAll(uuid.NewString(), "-", ""), 33 | Provider: conf.Provider, 34 | TransportURL: conf.TransportURL, 35 | ChainEnv: conf.ChainEnv, 36 | } 37 | client.HttpClient = req.C(). 38 | SetBaseURL(conf.TransportURL). 39 | WrapRoundTripFunc(func(rt req.RoundTripper) req.RoundTripFunc { 40 | return func(req *req.Request) (resp *req.Response, err error) { 41 | // before request 42 | // ... 43 | resp, err = rt.RoundTrip(req) 44 | // after response 45 | // ... 46 | return 47 | } 48 | }). 49 | SetTimeout(20 * time.Second) 50 | if conf.IsDev { 51 | client.HttpClient.DevMode() 52 | } 53 | 54 | return client, nil 55 | } 56 | 57 | func (sc *SolanaClient) GetAccountInfo(ctx context.Context, request *solana.GetAccountInfoRequest) (*solana.GetAccountInfoReply, error) { 58 | reply := &solana.GetAccountInfoReply{} 59 | response, err := sc.HttpClient. 60 | R(). 61 | SetContext(ctx). 62 | SetBody(map[string]interface{}{ 63 | "jsonrpc": "2.0", "id": 1, 64 | "method": "getAccountInfo", 65 | "params": []interface{}{ 66 | request.Account, 67 | map[string]string{ 68 | "encoding": "base58", 69 | }, 70 | }, 71 | }). 72 | Post("") 73 | if err != nil { 74 | return nil, err 75 | } 76 | if gjson.GetBytes(response.Bytes(), "error").String() != "" { 77 | return nil, errors.New(gjson.GetBytes(response.Bytes(), "error.message").String()) 78 | } 79 | err = json.Unmarshal([]byte(gjson.GetBytes(response.Bytes(), "result").String()), &reply) 80 | if err != nil { 81 | return nil, err 82 | } 83 | return reply, nil 84 | } 85 | func (sc *SolanaClient) GetVersion(ctx context.Context) (*solana.GetVersionReply, error) { 86 | reply := &solana.GetVersionReply{} 87 | response, err := sc.HttpClient. 88 | R(). 89 | SetContext(ctx). 90 | SetBody(map[string]interface{}{ 91 | "jsonrpc": "2.0", "id": 1, 92 | "method": "getVersion", 93 | }). 94 | Post("") 95 | if err != nil { 96 | return nil, err 97 | } 98 | if gjson.GetBytes(response.Bytes(), "error").String() != "" { 99 | return nil, errors.New(gjson.GetBytes(response.Bytes(), "error.message").String()) 100 | } 101 | reply.FeatureSet = gjson.GetBytes(response.Bytes(), "result.feature-set").String() 102 | reply.SolanaCore = gjson.GetBytes(response.Bytes(), "result.solana-core").String() 103 | return reply, nil 104 | } 105 | func (sc *SolanaClient) GetBalance(ctx context.Context, request *solana.GetBalanceRequest) (*solana.GetBalanceReply, error) { 106 | //callMethod := consts.SolanaMethodGetBalance 107 | reply := &solana.GetBalanceReply{} 108 | response, err := sc.HttpClient. 109 | R(). 110 | SetContext(ctx). 111 | SetBody(map[string]interface{}{ 112 | "jsonrpc": "2.0", "id": 1, 113 | "method": "getBalance", 114 | "params": []string{request.Account}, 115 | }). 116 | Post("") 117 | if err != nil { 118 | return nil, err 119 | } 120 | if gjson.GetBytes(response.Bytes(), "error").String() != "" { 121 | return nil, errors.New(gjson.GetBytes(response.Bytes(), "error.message").String()) 122 | } 123 | err = json.Unmarshal([]byte(gjson.GetBytes(response.Bytes(), "result.context").String()), &reply.Context) 124 | if err != nil { 125 | return nil, err 126 | } 127 | reply.Value, err = decimal.NewFromString(gjson.GetBytes(response.Bytes(), "result.value").String()) 128 | if err != nil { 129 | return nil, err 130 | } 131 | reply.Value = reply.Value.Div(decimal.New(1, 9)) 132 | return reply, nil 133 | } 134 | func (sc *SolanaClient) GetTokenAccountBalance(ctx context.Context, request *solana.GetTokenAccountBalanceRequest) (*solana.GetTokenAccountBalanceReply, error) { 135 | reply := &solana.GetTokenAccountBalanceReply{} 136 | response, err := sc.HttpClient. 137 | SetBaseURL(sc.TransportURL). 138 | R(). 139 | SetContext(ctx). 140 | SetBody(map[string]interface{}{ 141 | "jsonrpc": "2.0", "id": 1, 142 | "method": "getTokenAccountBalance", 143 | "params": []string{request.Account}, 144 | }). 145 | Post("") 146 | if err != nil { 147 | return nil, err 148 | } 149 | if gjson.GetBytes(response.Bytes(), "error").String() != "" { 150 | return nil, errors.New(gjson.GetBytes(response.Bytes(), "error.message").String()) 151 | } 152 | err = json.Unmarshal([]byte(gjson.GetBytes(response.Bytes(), "result.context").String()), &reply.Context) 153 | if err != nil { 154 | return nil, err 155 | } 156 | reply.Amount, err = decimal.NewFromString(gjson.GetBytes(response.Bytes(), "result.value.amount").String()) 157 | if err != nil { 158 | return nil, err 159 | } 160 | reply.Decimals, err = decimal.NewFromString(gjson.GetBytes(response.Bytes(), "result.value.decimals").String()) 161 | if err != nil { 162 | return nil, err 163 | } 164 | reply.UIAmount, err = decimal.NewFromString(gjson.GetBytes(response.Bytes(), "result.value.uiAmount").String()) 165 | if err != nil { 166 | return nil, err 167 | } 168 | reply.UIAmountString, err = decimal.NewFromString(gjson.GetBytes(response.Bytes(), "result.value.uiAmountString").String()) 169 | if err != nil { 170 | return nil, err 171 | } 172 | return reply, nil 173 | } 174 | func (sc *SolanaClient) GetBlockHeight(ctx context.Context) (int64, error) { 175 | response, err := sc.HttpClient. 176 | R(). 177 | SetContext(ctx). 178 | SetBody(map[string]interface{}{ 179 | "jsonrpc": "2.0", "id": 1, 180 | "method": "getBlockHeight", 181 | }). 182 | Post("") 183 | if err != nil { 184 | return 0, err 185 | } 186 | if gjson.GetBytes(response.Bytes(), "error").String() != "" { 187 | return 0, errors.New(gjson.GetBytes(response.Bytes(), "error.message").String()) 188 | } 189 | return gjson.GetBytes(response.Bytes(), "result").Int(), nil 190 | } 191 | func (sc *SolanaClient) GetBlockTime(ctx context.Context, blockNumber int64) (int64, error) { 192 | response, err := sc.HttpClient. 193 | R(). 194 | SetContext(ctx). 195 | SetBody(map[string]interface{}{ 196 | "jsonrpc": "2.0", "id": 1, 197 | "method": "getBlockTime", 198 | "params": []interface{}{blockNumber}, 199 | }). 200 | Post("") 201 | if err != nil { 202 | return 0, err 203 | } 204 | if gjson.GetBytes(response.Bytes(), "error").String() != "" { 205 | return 0, errors.New(gjson.GetBytes(response.Bytes(), "error.message").String()) 206 | } 207 | return gjson.GetBytes(response.Bytes(), "result").Int(), nil 208 | } 209 | func (sc *SolanaClient) GetBlock(ctx context.Context, request *solana.GetBlockRequest) (*solana.GetBlockReply, error) { 210 | //callMethod := consts.SolanaMethodGetBalance 211 | reply := &solana.GetBlockReply{} 212 | response, err := sc.HttpClient. 213 | R(). 214 | SetContext(ctx). 215 | SetBody(map[string]interface{}{ 216 | "jsonrpc": "2.0", "id": 1, 217 | "method": "getBlock", 218 | "params": []interface{}{ 219 | request.Slot, 220 | map[string]interface{}{ 221 | "encoding": request.Encoding, 222 | "transactionDetails": request.TransactionDetails, 223 | "rewards": request.Rewards, 224 | }, 225 | }, 226 | }). 227 | Post("") 228 | if err != nil { 229 | return nil, err 230 | } 231 | if gjson.GetBytes(response.Bytes(), "error").String() != "" { 232 | return nil, errors.New(gjson.GetBytes(response.Bytes(), "error.message").String()) 233 | } 234 | //err = json.Unmarshal([]byte(gjson.GetBytes(response.Bytes(), "result.context").String()), &reply.Context) 235 | //if err != nil { 236 | // return nil, err 237 | //} 238 | return reply, nil 239 | } 240 | func (sc *SolanaClient) GetClusterNodes(ctx context.Context) ([]*solana.ClusterNodesItem, error) { 241 | //callMethod := consts.SolanaMethodGetBalance 242 | reply := make([]*solana.ClusterNodesItem, 0) 243 | response, err := sc.HttpClient. 244 | R(). 245 | SetContext(ctx). 246 | SetBody(map[string]interface{}{ 247 | "jsonrpc": "2.0", "id": 1, 248 | "method": "getClusterNodes", 249 | }). 250 | Post("") 251 | if err != nil { 252 | return nil, err 253 | } 254 | if gjson.GetBytes(response.Bytes(), "error").String() != "" { 255 | return nil, errors.New(gjson.GetBytes(response.Bytes(), "error.message").String()) 256 | } 257 | gjson.GetBytes(response.Bytes(), "result").ForEach(func(key, value gjson.Result) bool { 258 | fmt.Println(key, value.String()) 259 | item := &solana.ClusterNodesItem{} 260 | if loopErr := json.Unmarshal([]byte(value.String()), item); loopErr == nil { 261 | reply = append(reply, item) 262 | } 263 | return true 264 | }) 265 | return reply, nil 266 | } 267 | -------------------------------------------------------------------------------- /client/solana_test.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/6boris/web3-go/model/solana" 7 | "github.com/davecgh/go-spew/spew" 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func TestSolana_Unite_GetAccountInfo(t *testing.T) { 12 | t.Run("Contract_USDT", func(t *testing.T) { 13 | reply, err := testSolanaClient.GetAccountInfo(testCtx, 14 | &solana.GetAccountInfoRequest{Account: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"}, 15 | ) 16 | assert.Nil(t, err) 17 | spew.Dump(reply) 18 | }) 19 | t.Run("User_Common", func(t *testing.T) { 20 | reply, err := testSolanaClient.GetAccountInfo(testCtx, 21 | &solana.GetAccountInfoRequest{Account: "Hw161dCAE9VBtbTo3EgbzwLdvVxNnEpiqqAJw7q38BYE"}, 22 | ) 23 | assert.Nil(t, err) 24 | spew.Dump(reply) 25 | }) 26 | } 27 | 28 | func TestSolana_Unite_GetBalance(t *testing.T) { 29 | t.Run("Demo", func(t *testing.T) { 30 | reply, err := testSolanaClient.GetBalance(testCtx, 31 | &solana.GetBalanceRequest{Account: "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB"}, 32 | ) 33 | assert.Nil(t, err) 34 | spew.Dump(reply) 35 | }) 36 | } 37 | 38 | func TestSolana_Unite_GetVersion(t *testing.T) { 39 | t.Run("Demo", func(t *testing.T) { 40 | reply, err := testSolanaClient.GetVersion(testCtx) 41 | assert.Nil(t, err) 42 | spew.Dump(reply) 43 | }) 44 | } 45 | 46 | func TestSolana_Unite_GetTokenAccountBalance(t *testing.T) { 47 | t.Run("SLND", func(t *testing.T) { 48 | reply, err := testSolanaClient.GetTokenAccountBalance(testCtx, 49 | &solana.GetTokenAccountBalanceRequest{Account: "3Lz6rCrXdLybFiuJGJnEjv6Z2XtCh5n4proPGP2aBkA1"}) 50 | assert.Nil(t, err) 51 | spew.Dump(reply) 52 | }) 53 | t.Run("TNSR", func(t *testing.T) { 54 | reply, err := testSolanaClient.GetTokenAccountBalance(testCtx, 55 | &solana.GetTokenAccountBalanceRequest{Account: "5FhnYa75QKfMkPjBCrM7iucf2wMBNzHE2chyyTUfJEqj"}) 56 | assert.Nil(t, err) 57 | spew.Dump(reply) 58 | }) 59 | } 60 | 61 | func TestSolana_Unite_GetBlockHeight(t *testing.T) { 62 | t.Run("Demo", func(t *testing.T) { 63 | reply, err := testSolanaClient.GetBlockHeight(testCtx) 64 | assert.Nil(t, err) 65 | spew.Dump(reply) 66 | }) 67 | } 68 | 69 | func TestSolana_Unite_GetBlockTime(t *testing.T) { 70 | t.Run("Demo", func(t *testing.T) { 71 | blockHeight, err := testSolanaClient.GetBlockHeight(testCtx) 72 | assert.Nil(t, err) 73 | reply, err := testSolanaClient.GetBlockTime(testCtx, blockHeight) 74 | assert.Nil(t, err) 75 | spew.Dump(reply) 76 | }) 77 | } 78 | 79 | func TestSolana_Unite_GetBlock(t *testing.T) { 80 | t.Run("Demo", func(t *testing.T) { 81 | reply, err := testSolanaClient.GetBlock(testCtx, &solana.GetBlockRequest{ 82 | Slot: 430, 83 | Encoding: "base58", 84 | TransactionDetails: "full", 85 | Rewards: false, 86 | }) 87 | assert.Nil(t, err) 88 | spew.Dump(reply) 89 | }) 90 | } 91 | 92 | func TestSolana_Unite_GetClusterNodes(t *testing.T) { 93 | t.Run("Demo", func(t *testing.T) { 94 | reply, err := testSolanaClient.GetClusterNodes(testCtx) 95 | assert.Nil(t, err) 96 | spew.Dump(reply) 97 | spew.Dump(len(reply)) 98 | }) 99 | } 100 | -------------------------------------------------------------------------------- /consts/abis.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | const ( 4 | AbiCallStatusSuccess = "SUCCESS" 5 | AbiCallStatusFail = "FAILED" 6 | 7 | EvmMethodBlockByHash = "EVM_BlockByHash" 8 | EvmMethodBlockByNumber = "EVM_BlockByNumber" 9 | EvmMethodHeaderByHash = "EVM_HeaderByHash" 10 | EvmMethodHeaderByNumber = "EVM_HeaderByNumber" 11 | EvmMethodTransactionCount = "EVM_TransactionCount" 12 | EvmMethodTransactionInBlock = "EVM_TransactionInBlock" 13 | EvmMethodTransactionByHash = "EVM_TransactionByHash" 14 | EvmMethodTransactionReceipt = "EVM_TransactionReceipt" 15 | EvmMethodSendTransaction = "EVM_SendTransaction" 16 | EvmMethodBalanceAt = "EVM_BalanceAt" 17 | EvmMethodStorageAt = "EVM_StorageAt" 18 | EvmMethodCodeAt = "EVM_CodeAt" 19 | EvmMethodNonceAt = "EVM_NonceAt" 20 | EvmMethodSuggestGasPrice = "EVM_SuggestGasPrice" 21 | EvmMethodSuggestGasTipCap = "EVM_SuggestGasTipCap" 22 | EvmMethodFeeHistory = "EVM_FeeHistory" 23 | EvmMethodEstimateGas = "EVM_EstimateGas" 24 | EvmMethodPendingBalanceAtp = "EVM_PendingBalanceAt" 25 | EvmMethodPendingStorageAt = "EVM_PendingStorageAt" 26 | EvmMethodPendingCodeAt = "EVM_PendingCodeAt" 27 | EvmMethodPendingNonceAt = "EVM_PendingNonceAt" 28 | EvmMethodPendingTransactionCount = "EVM_PendingTransactionCount" 29 | EvmMethodBlockNumber = "EVM_BlockNumber" 30 | EvmMethodChainID = "EVM_ChainID" 31 | EvmMethodNetworkID = "EVM_NetworkID" 32 | EvmErc20MethodBalanceOf = "EVM_ERC20_BalanceOf" 33 | EvmErc20MethodName = "EVM_ERC20_Name" 34 | EvmErc20MethodDecimals = "EVM_ERC20_Decimals" 35 | EvmErc20MethodSymbol = "EVM_ERC20_Symbol" 36 | EvmErc20MethodTotalSupply = "EVM_ERC20_TotalSupply" 37 | EvmErc20MethodTransfer = "EVM_ERC20_Transfer" 38 | EvmErc20MethodApprove = "EVM_ERC20_Approve" 39 | EvmErc20MethodIncreaseAllowance = "EVM_ERC20_IncreaseAllowance" 40 | EvmErc20MethodDecreaseAllowance = "EVM_ERC20_DecreaseAllowance" 41 | EvmErc20MethodAllowance = "EVM_ERC20_Allowance" 42 | 43 | SolanaMethodGetBalance = "SOLANA_GetBalance" 44 | SolanaMethodGetTokenAccountBalance = "SOLANA_GetTokenAccountBalance" 45 | ) 46 | -------------------------------------------------------------------------------- /consts/chain.go: -------------------------------------------------------------------------------- 1 | package consts 2 | 3 | const ( 4 | ChainEnvMainnet = "Mainnet" 5 | ChainEnvTestnet = "Testnet" 6 | ChainEnvDevnet = "Devnet" 7 | ) 8 | -------------------------------------------------------------------------------- /erc/erc20/erc20.go: -------------------------------------------------------------------------------- 1 | // Code generated - DO NOT EDIT. 2 | // This file is a generated binding and any manual changes will be lost. 3 | 4 | package erc20 5 | 6 | import ( 7 | "errors" 8 | "math/big" 9 | "strings" 10 | 11 | ethereum "github.com/ethereum/go-ethereum" 12 | "github.com/ethereum/go-ethereum/accounts/abi" 13 | "github.com/ethereum/go-ethereum/accounts/abi/bind" 14 | "github.com/ethereum/go-ethereum/common" 15 | "github.com/ethereum/go-ethereum/core/types" 16 | "github.com/ethereum/go-ethereum/event" 17 | ) 18 | 19 | // Reference imports to suppress errors if they are not otherwise used. 20 | var ( 21 | _ = errors.New 22 | _ = big.NewInt 23 | _ = strings.NewReader 24 | _ = ethereum.NotFound 25 | _ = bind.Bind 26 | _ = common.Big1 27 | _ = types.BloomLookup 28 | _ = event.NewSubscription 29 | _ = abi.ConvertType 30 | ) 31 | 32 | // ERC20MetaData contains all meta data concerning the ERC20 contract. 33 | var ERC20MetaData = &bind.MetaData{ 34 | ABI: "[{\"inputs\":[],\"stateMutability\":\"nonpayable\",\"type\":\"constructor\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"allowance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientAllowance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"balance\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"needed\",\"type\":\"uint256\"}],\"name\":\"ERC20InsufficientBalance\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"approver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidApprover\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"receiver\",\"type\":\"address\"}],\"name\":\"ERC20InvalidReceiver\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"sender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSender\",\"type\":\"error\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"ERC20InvalidSpender\",\"type\":\"error\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Approval\",\"type\":\"event\"},{\"anonymous\":false,\"inputs\":[{\"indexed\":true,\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"indexed\":true,\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"indexed\":false,\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"Transfer\",\"type\":\"event\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"owner\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"}],\"name\":\"allowance\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"approve\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"account\",\"type\":\"address\"}],\"name\":\"balanceOf\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"decimals\",\"outputs\":[{\"internalType\":\"uint8\",\"name\":\"\",\"type\":\"uint8\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"name\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"symbol\",\"outputs\":[{\"internalType\":\"string\",\"name\":\"\",\"type\":\"string\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"totalSupply\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"view\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transfer\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"from\",\"type\":\"address\"},{\"internalType\":\"address\",\"name\":\"to\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"value\",\"type\":\"uint256\"}],\"name\":\"transferFrom\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"addedValue\",\"type\":\"uint256\"}],\"name\":\"increaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"address\",\"name\":\"spender\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"subtractedValue\",\"type\":\"uint256\"}],\"name\":\"decreaseAllowance\",\"outputs\":[{\"internalType\":\"bool\",\"name\":\"\",\"type\":\"bool\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"}]", 35 | } 36 | 37 | // ERC20ABI is the input ABI used to generate the binding from. 38 | // Deprecated: Use ERC20MetaData.ABI instead. 39 | var ERC20ABI = ERC20MetaData.ABI 40 | 41 | // ERC20 is an auto generated Go binding around an Ethereum contract. 42 | type ERC20 struct { 43 | ERC20Caller // Read-only binding to the contract 44 | ERC20Transactor // Write-only binding to the contract 45 | ERC20Filterer // Log filterer for contract events 46 | } 47 | 48 | // ERC20Caller is an auto generated read-only Go binding around an Ethereum contract. 49 | type ERC20Caller struct { 50 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 51 | } 52 | 53 | // ERC20Transactor is an auto generated write-only Go binding around an Ethereum contract. 54 | type ERC20Transactor struct { 55 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 56 | } 57 | 58 | // ERC20Filterer is an auto generated log filtering Go binding around an Ethereum contract events. 59 | type ERC20Filterer struct { 60 | contract *bind.BoundContract // Generic contract wrapper for the low level calls 61 | } 62 | 63 | // ERC20Session is an auto generated Go binding around an Ethereum contract, 64 | // with pre-set call and transact options. 65 | type ERC20Session struct { 66 | Contract *ERC20 // Generic contract binding to set the session for 67 | CallOpts bind.CallOpts // Call options to use throughout this session 68 | TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session 69 | } 70 | 71 | // ERC20CallerSession is an auto generated read-only Go binding around an Ethereum contract, 72 | // with pre-set call options. 73 | type ERC20CallerSession struct { 74 | Contract *ERC20Caller // Generic contract caller binding to set the session for 75 | CallOpts bind.CallOpts // Call options to use throughout this session 76 | } 77 | 78 | // ERC20TransactorSession is an auto generated write-only Go binding around an Ethereum contract, 79 | // with pre-set transact options. 80 | type ERC20TransactorSession struct { 81 | Contract *ERC20Transactor // Generic contract transactor binding to set the session for 82 | TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session 83 | } 84 | 85 | // ERC20Raw is an auto generated low-level Go binding around an Ethereum contract. 86 | type ERC20Raw struct { 87 | Contract *ERC20 // Generic contract binding to access the raw methods on 88 | } 89 | 90 | // ERC20CallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract. 91 | type ERC20CallerRaw struct { 92 | Contract *ERC20Caller // Generic read-only contract binding to access the raw methods on 93 | } 94 | 95 | // ERC20TransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract. 96 | type ERC20TransactorRaw struct { 97 | Contract *ERC20Transactor // Generic write-only contract binding to access the raw methods on 98 | } 99 | 100 | // NewERC20 creates a new instance of ERC20, bound to a specific deployed contract. 101 | func NewERC20(address common.Address, backend bind.ContractBackend) (*ERC20, error) { 102 | contract, err := bindERC20(address, backend, backend, backend) 103 | if err != nil { 104 | return nil, err 105 | } 106 | return &ERC20{ERC20Caller: ERC20Caller{contract: contract}, ERC20Transactor: ERC20Transactor{contract: contract}, ERC20Filterer: ERC20Filterer{contract: contract}}, nil 107 | } 108 | 109 | // NewERC20Caller creates a new read-only instance of ERC20, bound to a specific deployed contract. 110 | func NewERC20Caller(address common.Address, caller bind.ContractCaller) (*ERC20Caller, error) { 111 | contract, err := bindERC20(address, caller, nil, nil) 112 | if err != nil { 113 | return nil, err 114 | } 115 | return &ERC20Caller{contract: contract}, nil 116 | } 117 | 118 | // NewERC20Transactor creates a new write-only instance of ERC20, bound to a specific deployed contract. 119 | func NewERC20Transactor(address common.Address, transactor bind.ContractTransactor) (*ERC20Transactor, error) { 120 | contract, err := bindERC20(address, nil, transactor, nil) 121 | if err != nil { 122 | return nil, err 123 | } 124 | return &ERC20Transactor{contract: contract}, nil 125 | } 126 | 127 | // NewERC20Filterer creates a new log filterer instance of ERC20, bound to a specific deployed contract. 128 | func NewERC20Filterer(address common.Address, filterer bind.ContractFilterer) (*ERC20Filterer, error) { 129 | contract, err := bindERC20(address, nil, nil, filterer) 130 | if err != nil { 131 | return nil, err 132 | } 133 | return &ERC20Filterer{contract: contract}, nil 134 | } 135 | 136 | // bindERC20 binds a generic wrapper to an already deployed contract. 137 | func bindERC20(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) { 138 | parsed, err := ERC20MetaData.GetAbi() 139 | if err != nil { 140 | return nil, err 141 | } 142 | return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil 143 | } 144 | 145 | // Call invokes the (constant) contract method with params as input values and 146 | // sets the output to result. The result type might be a single field for simple 147 | // returns, a slice of interfaces for anonymous returns and a struct for named 148 | // returns. 149 | func (_ERC20 *ERC20Raw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { 150 | return _ERC20.Contract.ERC20Caller.contract.Call(opts, result, method, params...) 151 | } 152 | 153 | // Transfer initiates a plain transaction to move funds to the contract, calling 154 | // its default method if one is available. 155 | func (_ERC20 *ERC20Raw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { 156 | return _ERC20.Contract.ERC20Transactor.contract.Transfer(opts) 157 | } 158 | 159 | // Transact invokes the (paid) contract method with params as input values. 160 | func (_ERC20 *ERC20Raw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { 161 | return _ERC20.Contract.ERC20Transactor.contract.Transact(opts, method, params...) 162 | } 163 | 164 | // Call invokes the (constant) contract method with params as input values and 165 | // sets the output to result. The result type might be a single field for simple 166 | // returns, a slice of interfaces for anonymous returns and a struct for named 167 | // returns. 168 | func (_ERC20 *ERC20CallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error { 169 | return _ERC20.Contract.contract.Call(opts, result, method, params...) 170 | } 171 | 172 | // Transfer initiates a plain transaction to move funds to the contract, calling 173 | // its default method if one is available. 174 | func (_ERC20 *ERC20TransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) { 175 | return _ERC20.Contract.contract.Transfer(opts) 176 | } 177 | 178 | // Transact invokes the (paid) contract method with params as input values. 179 | func (_ERC20 *ERC20TransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) { 180 | return _ERC20.Contract.contract.Transact(opts, method, params...) 181 | } 182 | 183 | // Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. 184 | // 185 | // Solidity: function allowance(address owner, address spender) view returns(uint256) 186 | func (_ERC20 *ERC20Caller) Allowance(opts *bind.CallOpts, owner common.Address, spender common.Address) (*big.Int, error) { 187 | var out []interface{} 188 | err := _ERC20.contract.Call(opts, &out, "allowance", owner, spender) 189 | 190 | if err != nil { 191 | return *new(*big.Int), err 192 | } 193 | 194 | out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) 195 | 196 | return out0, err 197 | 198 | } 199 | 200 | // Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. 201 | // 202 | // Solidity: function allowance(address owner, address spender) view returns(uint256) 203 | func (_ERC20 *ERC20Session) Allowance(owner common.Address, spender common.Address) (*big.Int, error) { 204 | return _ERC20.Contract.Allowance(&_ERC20.CallOpts, owner, spender) 205 | } 206 | 207 | // Allowance is a free data retrieval call binding the contract method 0xdd62ed3e. 208 | // 209 | // Solidity: function allowance(address owner, address spender) view returns(uint256) 210 | func (_ERC20 *ERC20CallerSession) Allowance(owner common.Address, spender common.Address) (*big.Int, error) { 211 | return _ERC20.Contract.Allowance(&_ERC20.CallOpts, owner, spender) 212 | } 213 | 214 | // BalanceOf is a free data retrieval call binding the contract method 0x70a08231. 215 | // 216 | // Solidity: function balanceOf(address account) view returns(uint256) 217 | func (_ERC20 *ERC20Caller) BalanceOf(opts *bind.CallOpts, account common.Address) (*big.Int, error) { 218 | var out []interface{} 219 | err := _ERC20.contract.Call(opts, &out, "balanceOf", account) 220 | 221 | if err != nil { 222 | return *new(*big.Int), err 223 | } 224 | 225 | out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) 226 | 227 | return out0, err 228 | 229 | } 230 | 231 | // BalanceOf is a free data retrieval call binding the contract method 0x70a08231. 232 | // 233 | // Solidity: function balanceOf(address account) view returns(uint256) 234 | func (_ERC20 *ERC20Session) BalanceOf(account common.Address) (*big.Int, error) { 235 | return _ERC20.Contract.BalanceOf(&_ERC20.CallOpts, account) 236 | } 237 | 238 | // BalanceOf is a free data retrieval call binding the contract method 0x70a08231. 239 | // 240 | // Solidity: function balanceOf(address account) view returns(uint256) 241 | func (_ERC20 *ERC20CallerSession) BalanceOf(account common.Address) (*big.Int, error) { 242 | return _ERC20.Contract.BalanceOf(&_ERC20.CallOpts, account) 243 | } 244 | 245 | // Decimals is a free data retrieval call binding the contract method 0x313ce567. 246 | // 247 | // Solidity: function decimals() view returns(uint8) 248 | func (_ERC20 *ERC20Caller) Decimals(opts *bind.CallOpts) (uint8, error) { 249 | var out []interface{} 250 | err := _ERC20.contract.Call(opts, &out, "decimals") 251 | 252 | if err != nil { 253 | return *new(uint8), err 254 | } 255 | 256 | out0 := *abi.ConvertType(out[0], new(uint8)).(*uint8) 257 | 258 | return out0, err 259 | 260 | } 261 | 262 | // Decimals is a free data retrieval call binding the contract method 0x313ce567. 263 | // 264 | // Solidity: function decimals() view returns(uint8) 265 | func (_ERC20 *ERC20Session) Decimals() (uint8, error) { 266 | return _ERC20.Contract.Decimals(&_ERC20.CallOpts) 267 | } 268 | 269 | // Decimals is a free data retrieval call binding the contract method 0x313ce567. 270 | // 271 | // Solidity: function decimals() view returns(uint8) 272 | func (_ERC20 *ERC20CallerSession) Decimals() (uint8, error) { 273 | return _ERC20.Contract.Decimals(&_ERC20.CallOpts) 274 | } 275 | 276 | // Name is a free data retrieval call binding the contract method 0x06fdde03. 277 | // 278 | // Solidity: function name() view returns(string) 279 | func (_ERC20 *ERC20Caller) Name(opts *bind.CallOpts) (string, error) { 280 | var out []interface{} 281 | err := _ERC20.contract.Call(opts, &out, "name") 282 | 283 | if err != nil { 284 | return *new(string), err 285 | } 286 | 287 | out0 := *abi.ConvertType(out[0], new(string)).(*string) 288 | 289 | return out0, err 290 | 291 | } 292 | 293 | // Name is a free data retrieval call binding the contract method 0x06fdde03. 294 | // 295 | // Solidity: function name() view returns(string) 296 | func (_ERC20 *ERC20Session) Name() (string, error) { 297 | return _ERC20.Contract.Name(&_ERC20.CallOpts) 298 | } 299 | 300 | // Name is a free data retrieval call binding the contract method 0x06fdde03. 301 | // 302 | // Solidity: function name() view returns(string) 303 | func (_ERC20 *ERC20CallerSession) Name() (string, error) { 304 | return _ERC20.Contract.Name(&_ERC20.CallOpts) 305 | } 306 | 307 | // Symbol is a free data retrieval call binding the contract method 0x95d89b41. 308 | // 309 | // Solidity: function symbol() view returns(string) 310 | func (_ERC20 *ERC20Caller) Symbol(opts *bind.CallOpts) (string, error) { 311 | var out []interface{} 312 | err := _ERC20.contract.Call(opts, &out, "symbol") 313 | 314 | if err != nil { 315 | return *new(string), err 316 | } 317 | 318 | out0 := *abi.ConvertType(out[0], new(string)).(*string) 319 | 320 | return out0, err 321 | 322 | } 323 | 324 | // Symbol is a free data retrieval call binding the contract method 0x95d89b41. 325 | // 326 | // Solidity: function symbol() view returns(string) 327 | func (_ERC20 *ERC20Session) Symbol() (string, error) { 328 | return _ERC20.Contract.Symbol(&_ERC20.CallOpts) 329 | } 330 | 331 | // Symbol is a free data retrieval call binding the contract method 0x95d89b41. 332 | // 333 | // Solidity: function symbol() view returns(string) 334 | func (_ERC20 *ERC20CallerSession) Symbol() (string, error) { 335 | return _ERC20.Contract.Symbol(&_ERC20.CallOpts) 336 | } 337 | 338 | // TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. 339 | // 340 | // Solidity: function totalSupply() view returns(uint256) 341 | func (_ERC20 *ERC20Caller) TotalSupply(opts *bind.CallOpts) (*big.Int, error) { 342 | var out []interface{} 343 | err := _ERC20.contract.Call(opts, &out, "totalSupply") 344 | 345 | if err != nil { 346 | return *new(*big.Int), err 347 | } 348 | 349 | out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int) 350 | 351 | return out0, err 352 | 353 | } 354 | 355 | // TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. 356 | // 357 | // Solidity: function totalSupply() view returns(uint256) 358 | func (_ERC20 *ERC20Session) TotalSupply() (*big.Int, error) { 359 | return _ERC20.Contract.TotalSupply(&_ERC20.CallOpts) 360 | } 361 | 362 | // TotalSupply is a free data retrieval call binding the contract method 0x18160ddd. 363 | // 364 | // Solidity: function totalSupply() view returns(uint256) 365 | func (_ERC20 *ERC20CallerSession) TotalSupply() (*big.Int, error) { 366 | return _ERC20.Contract.TotalSupply(&_ERC20.CallOpts) 367 | } 368 | 369 | // Approve is a paid mutator transaction binding the contract method 0x095ea7b3. 370 | // 371 | // Solidity: function approve(address spender, uint256 value) returns(bool) 372 | func (_ERC20 *ERC20Transactor) Approve(opts *bind.TransactOpts, spender common.Address, value *big.Int) (*types.Transaction, error) { 373 | return _ERC20.contract.Transact(opts, "approve", spender, value) 374 | } 375 | 376 | // Approve is a paid mutator transaction binding the contract method 0x095ea7b3. 377 | // 378 | // Solidity: function approve(address spender, uint256 value) returns(bool) 379 | func (_ERC20 *ERC20Session) Approve(spender common.Address, value *big.Int) (*types.Transaction, error) { 380 | return _ERC20.Contract.Approve(&_ERC20.TransactOpts, spender, value) 381 | } 382 | 383 | // Approve is a paid mutator transaction binding the contract method 0x095ea7b3. 384 | // 385 | // Solidity: function approve(address spender, uint256 value) returns(bool) 386 | func (_ERC20 *ERC20TransactorSession) Approve(spender common.Address, value *big.Int) (*types.Transaction, error) { 387 | return _ERC20.Contract.Approve(&_ERC20.TransactOpts, spender, value) 388 | } 389 | 390 | // DecreaseAllowance is a paid mutator transaction binding the contract method 0xa457c2d7. 391 | // 392 | // Solidity: function decreaseAllowance(address spender, uint256 subtractedValue) returns(bool) 393 | func (_ERC20 *ERC20Transactor) DecreaseAllowance(opts *bind.TransactOpts, spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) { 394 | return _ERC20.contract.Transact(opts, "decreaseAllowance", spender, subtractedValue) 395 | } 396 | 397 | // DecreaseAllowance is a paid mutator transaction binding the contract method 0xa457c2d7. 398 | // 399 | // Solidity: function decreaseAllowance(address spender, uint256 subtractedValue) returns(bool) 400 | func (_ERC20 *ERC20Session) DecreaseAllowance(spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) { 401 | return _ERC20.Contract.DecreaseAllowance(&_ERC20.TransactOpts, spender, subtractedValue) 402 | } 403 | 404 | // DecreaseAllowance is a paid mutator transaction binding the contract method 0xa457c2d7. 405 | // 406 | // Solidity: function decreaseAllowance(address spender, uint256 subtractedValue) returns(bool) 407 | func (_ERC20 *ERC20TransactorSession) DecreaseAllowance(spender common.Address, subtractedValue *big.Int) (*types.Transaction, error) { 408 | return _ERC20.Contract.DecreaseAllowance(&_ERC20.TransactOpts, spender, subtractedValue) 409 | } 410 | 411 | // IncreaseAllowance is a paid mutator transaction binding the contract method 0x39509351. 412 | // 413 | // Solidity: function increaseAllowance(address spender, uint256 addedValue) returns(bool) 414 | func (_ERC20 *ERC20Transactor) IncreaseAllowance(opts *bind.TransactOpts, spender common.Address, addedValue *big.Int) (*types.Transaction, error) { 415 | return _ERC20.contract.Transact(opts, "increaseAllowance", spender, addedValue) 416 | } 417 | 418 | // IncreaseAllowance is a paid mutator transaction binding the contract method 0x39509351. 419 | // 420 | // Solidity: function increaseAllowance(address spender, uint256 addedValue) returns(bool) 421 | func (_ERC20 *ERC20Session) IncreaseAllowance(spender common.Address, addedValue *big.Int) (*types.Transaction, error) { 422 | return _ERC20.Contract.IncreaseAllowance(&_ERC20.TransactOpts, spender, addedValue) 423 | } 424 | 425 | // IncreaseAllowance is a paid mutator transaction binding the contract method 0x39509351. 426 | // 427 | // Solidity: function increaseAllowance(address spender, uint256 addedValue) returns(bool) 428 | func (_ERC20 *ERC20TransactorSession) IncreaseAllowance(spender common.Address, addedValue *big.Int) (*types.Transaction, error) { 429 | return _ERC20.Contract.IncreaseAllowance(&_ERC20.TransactOpts, spender, addedValue) 430 | } 431 | 432 | // Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. 433 | // 434 | // Solidity: function transfer(address to, uint256 value) returns(bool) 435 | func (_ERC20 *ERC20Transactor) Transfer(opts *bind.TransactOpts, to common.Address, value *big.Int) (*types.Transaction, error) { 436 | return _ERC20.contract.Transact(opts, "transfer", to, value) 437 | } 438 | 439 | // Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. 440 | // 441 | // Solidity: function transfer(address to, uint256 value) returns(bool) 442 | func (_ERC20 *ERC20Session) Transfer(to common.Address, value *big.Int) (*types.Transaction, error) { 443 | return _ERC20.Contract.Transfer(&_ERC20.TransactOpts, to, value) 444 | } 445 | 446 | // Transfer is a paid mutator transaction binding the contract method 0xa9059cbb. 447 | // 448 | // Solidity: function transfer(address to, uint256 value) returns(bool) 449 | func (_ERC20 *ERC20TransactorSession) Transfer(to common.Address, value *big.Int) (*types.Transaction, error) { 450 | return _ERC20.Contract.Transfer(&_ERC20.TransactOpts, to, value) 451 | } 452 | 453 | // TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. 454 | // 455 | // Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) 456 | func (_ERC20 *ERC20Transactor) TransferFrom(opts *bind.TransactOpts, from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { 457 | return _ERC20.contract.Transact(opts, "transferFrom", from, to, value) 458 | } 459 | 460 | // TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. 461 | // 462 | // Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) 463 | func (_ERC20 *ERC20Session) TransferFrom(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { 464 | return _ERC20.Contract.TransferFrom(&_ERC20.TransactOpts, from, to, value) 465 | } 466 | 467 | // TransferFrom is a paid mutator transaction binding the contract method 0x23b872dd. 468 | // 469 | // Solidity: function transferFrom(address from, address to, uint256 value) returns(bool) 470 | func (_ERC20 *ERC20TransactorSession) TransferFrom(from common.Address, to common.Address, value *big.Int) (*types.Transaction, error) { 471 | return _ERC20.Contract.TransferFrom(&_ERC20.TransactOpts, from, to, value) 472 | } 473 | 474 | // ERC20ApprovalIterator is returned from FilterApproval and is used to iterate over the raw logs and unpacked data for Approval events raised by the ERC20 contract. 475 | type ERC20ApprovalIterator struct { 476 | Event *ERC20Approval // Event containing the contract specifics and raw log 477 | 478 | contract *bind.BoundContract // Generic contract to use for unpacking event data 479 | event string // Event name to use for unpacking event data 480 | 481 | logs chan types.Log // Log channel receiving the found contract events 482 | sub ethereum.Subscription // Subscription for errors, completion and termination 483 | done bool // Whether the subscription completed delivering logs 484 | fail error // Occurred error to stop iteration 485 | } 486 | 487 | // Next advances the iterator to the subsequent event, returning whether there 488 | // are any more events found. In case of a retrieval or parsing error, false is 489 | // returned and Error() can be queried for the exact failure. 490 | func (it *ERC20ApprovalIterator) Next() bool { 491 | // If the iterator failed, stop iterating 492 | if it.fail != nil { 493 | return false 494 | } 495 | // If the iterator completed, deliver directly whatever's available 496 | if it.done { 497 | select { 498 | case log := <-it.logs: 499 | it.Event = new(ERC20Approval) 500 | if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { 501 | it.fail = err 502 | return false 503 | } 504 | it.Event.Raw = log 505 | return true 506 | 507 | default: 508 | return false 509 | } 510 | } 511 | // Iterator still in progress, wait for either a data or an error event 512 | select { 513 | case log := <-it.logs: 514 | it.Event = new(ERC20Approval) 515 | if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { 516 | it.fail = err 517 | return false 518 | } 519 | it.Event.Raw = log 520 | return true 521 | 522 | case err := <-it.sub.Err(): 523 | it.done = true 524 | it.fail = err 525 | return it.Next() 526 | } 527 | } 528 | 529 | // Error returns any retrieval or parsing error occurred during filtering. 530 | func (it *ERC20ApprovalIterator) Error() error { 531 | return it.fail 532 | } 533 | 534 | // Close terminates the iteration process, releasing any pending underlying 535 | // resources. 536 | func (it *ERC20ApprovalIterator) Close() error { 537 | it.sub.Unsubscribe() 538 | return nil 539 | } 540 | 541 | // ERC20Approval represents a Approval event raised by the ERC20 contract. 542 | type ERC20Approval struct { 543 | Owner common.Address 544 | Spender common.Address 545 | Value *big.Int 546 | Raw types.Log // Blockchain specific contextual infos 547 | } 548 | 549 | // FilterApproval is a free log retrieval operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. 550 | // 551 | // Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) 552 | func (_ERC20 *ERC20Filterer) FilterApproval(opts *bind.FilterOpts, owner []common.Address, spender []common.Address) (*ERC20ApprovalIterator, error) { 553 | 554 | var ownerRule []interface{} 555 | for _, ownerItem := range owner { 556 | ownerRule = append(ownerRule, ownerItem) 557 | } 558 | var spenderRule []interface{} 559 | for _, spenderItem := range spender { 560 | spenderRule = append(spenderRule, spenderItem) 561 | } 562 | 563 | logs, sub, err := _ERC20.contract.FilterLogs(opts, "Approval", ownerRule, spenderRule) 564 | if err != nil { 565 | return nil, err 566 | } 567 | return &ERC20ApprovalIterator{contract: _ERC20.contract, event: "Approval", logs: logs, sub: sub}, nil 568 | } 569 | 570 | // WatchApproval is a free log subscription operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. 571 | // 572 | // Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) 573 | func (_ERC20 *ERC20Filterer) WatchApproval(opts *bind.WatchOpts, sink chan<- *ERC20Approval, owner []common.Address, spender []common.Address) (event.Subscription, error) { 574 | 575 | var ownerRule []interface{} 576 | for _, ownerItem := range owner { 577 | ownerRule = append(ownerRule, ownerItem) 578 | } 579 | var spenderRule []interface{} 580 | for _, spenderItem := range spender { 581 | spenderRule = append(spenderRule, spenderItem) 582 | } 583 | 584 | logs, sub, err := _ERC20.contract.WatchLogs(opts, "Approval", ownerRule, spenderRule) 585 | if err != nil { 586 | return nil, err 587 | } 588 | return event.NewSubscription(func(quit <-chan struct{}) error { 589 | defer sub.Unsubscribe() 590 | for { 591 | select { 592 | case log := <-logs: 593 | // New log arrived, parse the event and forward to the user 594 | event := new(ERC20Approval) 595 | if err := _ERC20.contract.UnpackLog(event, "Approval", log); err != nil { 596 | return err 597 | } 598 | event.Raw = log 599 | 600 | select { 601 | case sink <- event: 602 | case err := <-sub.Err(): 603 | return err 604 | case <-quit: 605 | return nil 606 | } 607 | case err := <-sub.Err(): 608 | return err 609 | case <-quit: 610 | return nil 611 | } 612 | } 613 | }), nil 614 | } 615 | 616 | // ParseApproval is a log parse operation binding the contract event 0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925. 617 | // 618 | // Solidity: event Approval(address indexed owner, address indexed spender, uint256 value) 619 | func (_ERC20 *ERC20Filterer) ParseApproval(log types.Log) (*ERC20Approval, error) { 620 | event := new(ERC20Approval) 621 | if err := _ERC20.contract.UnpackLog(event, "Approval", log); err != nil { 622 | return nil, err 623 | } 624 | event.Raw = log 625 | return event, nil 626 | } 627 | 628 | // ERC20TransferIterator is returned from FilterTransfer and is used to iterate over the raw logs and unpacked data for Transfer events raised by the ERC20 contract. 629 | type ERC20TransferIterator struct { 630 | Event *ERC20Transfer // Event containing the contract specifics and raw log 631 | 632 | contract *bind.BoundContract // Generic contract to use for unpacking event data 633 | event string // Event name to use for unpacking event data 634 | 635 | logs chan types.Log // Log channel receiving the found contract events 636 | sub ethereum.Subscription // Subscription for errors, completion and termination 637 | done bool // Whether the subscription completed delivering logs 638 | fail error // Occurred error to stop iteration 639 | } 640 | 641 | // Next advances the iterator to the subsequent event, returning whether there 642 | // are any more events found. In case of a retrieval or parsing error, false is 643 | // returned and Error() can be queried for the exact failure. 644 | func (it *ERC20TransferIterator) Next() bool { 645 | // If the iterator failed, stop iterating 646 | if it.fail != nil { 647 | return false 648 | } 649 | // If the iterator completed, deliver directly whatever's available 650 | if it.done { 651 | select { 652 | case log := <-it.logs: 653 | it.Event = new(ERC20Transfer) 654 | if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { 655 | it.fail = err 656 | return false 657 | } 658 | it.Event.Raw = log 659 | return true 660 | 661 | default: 662 | return false 663 | } 664 | } 665 | // Iterator still in progress, wait for either a data or an error event 666 | select { 667 | case log := <-it.logs: 668 | it.Event = new(ERC20Transfer) 669 | if err := it.contract.UnpackLog(it.Event, it.event, log); err != nil { 670 | it.fail = err 671 | return false 672 | } 673 | it.Event.Raw = log 674 | return true 675 | 676 | case err := <-it.sub.Err(): 677 | it.done = true 678 | it.fail = err 679 | return it.Next() 680 | } 681 | } 682 | 683 | // Error returns any retrieval or parsing error occurred during filtering. 684 | func (it *ERC20TransferIterator) Error() error { 685 | return it.fail 686 | } 687 | 688 | // Close terminates the iteration process, releasing any pending underlying 689 | // resources. 690 | func (it *ERC20TransferIterator) Close() error { 691 | it.sub.Unsubscribe() 692 | return nil 693 | } 694 | 695 | // ERC20Transfer represents a Transfer event raised by the ERC20 contract. 696 | type ERC20Transfer struct { 697 | From common.Address 698 | To common.Address 699 | Value *big.Int 700 | Raw types.Log // Blockchain specific contextual infos 701 | } 702 | 703 | // FilterTransfer is a free log retrieval operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. 704 | // 705 | // Solidity: event Transfer(address indexed from, address indexed to, uint256 value) 706 | func (_ERC20 *ERC20Filterer) FilterTransfer(opts *bind.FilterOpts, from []common.Address, to []common.Address) (*ERC20TransferIterator, error) { 707 | 708 | var fromRule []interface{} 709 | for _, fromItem := range from { 710 | fromRule = append(fromRule, fromItem) 711 | } 712 | var toRule []interface{} 713 | for _, toItem := range to { 714 | toRule = append(toRule, toItem) 715 | } 716 | 717 | logs, sub, err := _ERC20.contract.FilterLogs(opts, "Transfer", fromRule, toRule) 718 | if err != nil { 719 | return nil, err 720 | } 721 | return &ERC20TransferIterator{contract: _ERC20.contract, event: "Transfer", logs: logs, sub: sub}, nil 722 | } 723 | 724 | // WatchTransfer is a free log subscription operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. 725 | // 726 | // Solidity: event Transfer(address indexed from, address indexed to, uint256 value) 727 | func (_ERC20 *ERC20Filterer) WatchTransfer(opts *bind.WatchOpts, sink chan<- *ERC20Transfer, from []common.Address, to []common.Address) (event.Subscription, error) { 728 | 729 | var fromRule []interface{} 730 | for _, fromItem := range from { 731 | fromRule = append(fromRule, fromItem) 732 | } 733 | var toRule []interface{} 734 | for _, toItem := range to { 735 | toRule = append(toRule, toItem) 736 | } 737 | 738 | logs, sub, err := _ERC20.contract.WatchLogs(opts, "Transfer", fromRule, toRule) 739 | if err != nil { 740 | return nil, err 741 | } 742 | return event.NewSubscription(func(quit <-chan struct{}) error { 743 | defer sub.Unsubscribe() 744 | for { 745 | select { 746 | case log := <-logs: 747 | // New log arrived, parse the event and forward to the user 748 | event := new(ERC20Transfer) 749 | if err := _ERC20.contract.UnpackLog(event, "Transfer", log); err != nil { 750 | return err 751 | } 752 | event.Raw = log 753 | 754 | select { 755 | case sink <- event: 756 | case err := <-sub.Err(): 757 | return err 758 | case <-quit: 759 | return nil 760 | } 761 | case err := <-sub.Err(): 762 | return err 763 | case <-quit: 764 | return nil 765 | } 766 | } 767 | }), nil 768 | } 769 | 770 | // ParseTransfer is a log parse operation binding the contract event 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef. 771 | // 772 | // Solidity: event Transfer(address indexed from, address indexed to, uint256 value) 773 | func (_ERC20 *ERC20Filterer) ParseTransfer(log types.Log) (*ERC20Transfer, error) { 774 | event := new(ERC20Transfer) 775 | if err := _ERC20.contract.UnpackLog(event, "Transfer", log); err != nil { 776 | return nil, err 777 | } 778 | event.Raw = log 779 | return event, nil 780 | } 781 | -------------------------------------------------------------------------------- /erc/erc20/erc20.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "inputs": [], 4 | "stateMutability": "nonpayable", 5 | "type": "constructor" 6 | }, 7 | { 8 | "inputs": [ 9 | { 10 | "internalType": "address", 11 | "name": "spender", 12 | "type": "address" 13 | }, 14 | { 15 | "internalType": "uint256", 16 | "name": "allowance", 17 | "type": "uint256" 18 | }, 19 | { 20 | "internalType": "uint256", 21 | "name": "needed", 22 | "type": "uint256" 23 | } 24 | ], 25 | "name": "ERC20InsufficientAllowance", 26 | "type": "error" 27 | }, 28 | { 29 | "inputs": [ 30 | { 31 | "internalType": "address", 32 | "name": "sender", 33 | "type": "address" 34 | }, 35 | { 36 | "internalType": "uint256", 37 | "name": "balance", 38 | "type": "uint256" 39 | }, 40 | { 41 | "internalType": "uint256", 42 | "name": "needed", 43 | "type": "uint256" 44 | } 45 | ], 46 | "name": "ERC20InsufficientBalance", 47 | "type": "error" 48 | }, 49 | { 50 | "inputs": [ 51 | { 52 | "internalType": "address", 53 | "name": "approver", 54 | "type": "address" 55 | } 56 | ], 57 | "name": "ERC20InvalidApprover", 58 | "type": "error" 59 | }, 60 | { 61 | "inputs": [ 62 | { 63 | "internalType": "address", 64 | "name": "receiver", 65 | "type": "address" 66 | } 67 | ], 68 | "name": "ERC20InvalidReceiver", 69 | "type": "error" 70 | }, 71 | { 72 | "inputs": [ 73 | { 74 | "internalType": "address", 75 | "name": "sender", 76 | "type": "address" 77 | } 78 | ], 79 | "name": "ERC20InvalidSender", 80 | "type": "error" 81 | }, 82 | { 83 | "inputs": [ 84 | { 85 | "internalType": "address", 86 | "name": "spender", 87 | "type": "address" 88 | } 89 | ], 90 | "name": "ERC20InvalidSpender", 91 | "type": "error" 92 | }, 93 | { 94 | "anonymous": false, 95 | "inputs": [ 96 | { 97 | "indexed": true, 98 | "internalType": "address", 99 | "name": "owner", 100 | "type": "address" 101 | }, 102 | { 103 | "indexed": true, 104 | "internalType": "address", 105 | "name": "spender", 106 | "type": "address" 107 | }, 108 | { 109 | "indexed": false, 110 | "internalType": "uint256", 111 | "name": "value", 112 | "type": "uint256" 113 | } 114 | ], 115 | "name": "Approval", 116 | "type": "event" 117 | }, 118 | { 119 | "anonymous": false, 120 | "inputs": [ 121 | { 122 | "indexed": true, 123 | "internalType": "address", 124 | "name": "from", 125 | "type": "address" 126 | }, 127 | { 128 | "indexed": true, 129 | "internalType": "address", 130 | "name": "to", 131 | "type": "address" 132 | }, 133 | { 134 | "indexed": false, 135 | "internalType": "uint256", 136 | "name": "value", 137 | "type": "uint256" 138 | } 139 | ], 140 | "name": "Transfer", 141 | "type": "event" 142 | }, 143 | { 144 | "inputs": [ 145 | { 146 | "internalType": "address", 147 | "name": "owner", 148 | "type": "address" 149 | }, 150 | { 151 | "internalType": "address", 152 | "name": "spender", 153 | "type": "address" 154 | } 155 | ], 156 | "name": "allowance", 157 | "outputs": [ 158 | { 159 | "internalType": "uint256", 160 | "name": "", 161 | "type": "uint256" 162 | } 163 | ], 164 | "stateMutability": "view", 165 | "type": "function" 166 | }, 167 | { 168 | "inputs": [ 169 | { 170 | "internalType": "address", 171 | "name": "spender", 172 | "type": "address" 173 | }, 174 | { 175 | "internalType": "uint256", 176 | "name": "value", 177 | "type": "uint256" 178 | } 179 | ], 180 | "name": "approve", 181 | "outputs": [ 182 | { 183 | "internalType": "bool", 184 | "name": "", 185 | "type": "bool" 186 | } 187 | ], 188 | "stateMutability": "nonpayable", 189 | "type": "function" 190 | }, 191 | { 192 | "inputs": [ 193 | { 194 | "internalType": "address", 195 | "name": "account", 196 | "type": "address" 197 | } 198 | ], 199 | "name": "balanceOf", 200 | "outputs": [ 201 | { 202 | "internalType": "uint256", 203 | "name": "", 204 | "type": "uint256" 205 | } 206 | ], 207 | "stateMutability": "view", 208 | "type": "function" 209 | }, 210 | { 211 | "inputs": [], 212 | "name": "decimals", 213 | "outputs": [ 214 | { 215 | "internalType": "uint8", 216 | "name": "", 217 | "type": "uint8" 218 | } 219 | ], 220 | "stateMutability": "view", 221 | "type": "function" 222 | }, 223 | { 224 | "inputs": [], 225 | "name": "name", 226 | "outputs": [ 227 | { 228 | "internalType": "string", 229 | "name": "", 230 | "type": "string" 231 | } 232 | ], 233 | "stateMutability": "view", 234 | "type": "function" 235 | }, 236 | { 237 | "inputs": [], 238 | "name": "symbol", 239 | "outputs": [ 240 | { 241 | "internalType": "string", 242 | "name": "", 243 | "type": "string" 244 | } 245 | ], 246 | "stateMutability": "view", 247 | "type": "function" 248 | }, 249 | { 250 | "inputs": [], 251 | "name": "totalSupply", 252 | "outputs": [ 253 | { 254 | "internalType": "uint256", 255 | "name": "", 256 | "type": "uint256" 257 | } 258 | ], 259 | "stateMutability": "view", 260 | "type": "function" 261 | }, 262 | { 263 | "inputs": [ 264 | { 265 | "internalType": "address", 266 | "name": "to", 267 | "type": "address" 268 | }, 269 | { 270 | "internalType": "uint256", 271 | "name": "value", 272 | "type": "uint256" 273 | } 274 | ], 275 | "name": "transfer", 276 | "outputs": [ 277 | { 278 | "internalType": "bool", 279 | "name": "", 280 | "type": "bool" 281 | } 282 | ], 283 | "stateMutability": "nonpayable", 284 | "type": "function" 285 | }, 286 | { 287 | "inputs": [ 288 | { 289 | "internalType": "address", 290 | "name": "from", 291 | "type": "address" 292 | }, 293 | { 294 | "internalType": "address", 295 | "name": "to", 296 | "type": "address" 297 | }, 298 | { 299 | "internalType": "uint256", 300 | "name": "value", 301 | "type": "uint256" 302 | } 303 | ], 304 | "name": "transferFrom", 305 | "outputs": [ 306 | { 307 | "internalType": "bool", 308 | "name": "", 309 | "type": "bool" 310 | } 311 | ], 312 | "stateMutability": "nonpayable", 313 | "type": "function" 314 | }, 315 | { 316 | "inputs": [ 317 | { 318 | "internalType": "address", 319 | "name": "spender", 320 | "type": "address" 321 | }, 322 | { 323 | "internalType": "uint256", 324 | "name": "addedValue", 325 | "type": "uint256" 326 | } 327 | ], 328 | "name": "increaseAllowance", 329 | "outputs": [ 330 | { 331 | "internalType": "bool", 332 | "name": "", 333 | "type": "bool" 334 | } 335 | ], 336 | "stateMutability": "nonpayable", 337 | "type": "function" 338 | }, 339 | { 340 | "inputs": [ 341 | { 342 | "internalType": "address", 343 | "name": "spender", 344 | "type": "address" 345 | }, 346 | { 347 | "internalType": "uint256", 348 | "name": "subtractedValue", 349 | "type": "uint256" 350 | } 351 | ], 352 | "name": "decreaseAllowance", 353 | "outputs": [ 354 | { 355 | "internalType": "bool", 356 | "name": "", 357 | "type": "bool" 358 | } 359 | ], 360 | "stateMutability": "nonpayable", 361 | "type": "function" 362 | } 363 | ] -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/6boris/web3-go 2 | 3 | go 1.22 4 | 5 | require ( 6 | github.com/davecgh/go-spew v1.1.1 7 | github.com/ethereum/go-ethereum v1.14.0 8 | github.com/gin-gonic/gin v1.9.1 9 | github.com/go-kratos/aegis v0.2.0 10 | github.com/google/uuid v1.6.0 11 | github.com/imroc/req/v3 v3.43.3 12 | github.com/prometheus/client_golang v1.19.0 13 | github.com/shopspring/decimal v1.4.0 14 | github.com/stretchr/testify v1.9.0 15 | github.com/tidwall/gjson v1.17.1 16 | go.opentelemetry.io/otel v1.25.0 17 | go.opentelemetry.io/otel/exporters/prometheus v0.47.0 18 | go.opentelemetry.io/otel/metric v1.25.0 19 | go.opentelemetry.io/otel/sdk/metric v1.25.0 20 | ) 21 | 22 | require ( 23 | github.com/Microsoft/go-winio v0.6.2 // indirect 24 | github.com/andybalholm/brotli v1.1.0 // indirect 25 | github.com/beorn7/perks v1.0.1 // indirect 26 | github.com/bits-and-blooms/bitset v1.13.0 // indirect 27 | github.com/btcsuite/btcd/btcec/v2 v2.3.3 // indirect 28 | github.com/bytedance/sonic v1.11.6 // indirect 29 | github.com/cespare/xxhash/v2 v2.3.0 // indirect 30 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect 31 | github.com/chenzhuoyu/iasm v0.9.1 // indirect 32 | github.com/cloudflare/circl v1.3.7 // indirect 33 | github.com/consensys/bavard v0.1.13 // indirect 34 | github.com/consensys/gnark-crypto v0.12.1 // indirect 35 | github.com/crate-crypto/go-kzg-4844 v1.0.0 // indirect 36 | github.com/deckarep/golang-set/v2 v2.6.0 // indirect 37 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect 38 | github.com/ethereum/c-kzg-4844 v1.0.1 // indirect 39 | github.com/fsnotify/fsnotify v1.7.0 // indirect 40 | github.com/gabriel-vasile/mimetype v1.4.3 // indirect 41 | github.com/gin-contrib/sse v0.1.0 // indirect 42 | github.com/go-logr/logr v1.4.1 // indirect 43 | github.com/go-logr/stdr v1.2.2 // indirect 44 | github.com/go-ole/go-ole v1.3.0 // indirect 45 | github.com/go-playground/locales v0.14.1 // indirect 46 | github.com/go-playground/universal-translator v0.18.1 // indirect 47 | github.com/go-playground/validator/v10 v10.19.0 // indirect 48 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect 49 | github.com/goccy/go-json v0.10.2 // indirect 50 | github.com/google/pprof v0.0.0-20240422182052-72c8669ad3e7 // indirect 51 | github.com/gorilla/websocket v1.5.1 // indirect 52 | github.com/hashicorp/errwrap v1.1.0 // indirect 53 | github.com/hashicorp/go-multierror v1.1.1 // indirect 54 | github.com/holiman/uint256 v1.2.4 // indirect 55 | github.com/json-iterator/go v1.1.12 // indirect 56 | github.com/klauspost/compress v1.17.8 // indirect 57 | github.com/klauspost/cpuid/v2 v2.2.7 // indirect 58 | github.com/leodido/go-urn v1.4.0 // indirect 59 | github.com/mattn/go-isatty v0.0.20 // indirect 60 | github.com/mmcloughlin/addchain v0.4.0 // indirect 61 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 62 | github.com/modern-go/reflect2 v1.0.2 // indirect 63 | github.com/onsi/ginkgo/v2 v2.17.1 // indirect 64 | github.com/pelletier/go-toml/v2 v2.2.1 // indirect 65 | github.com/pmezard/go-difflib v1.0.0 // indirect 66 | github.com/prometheus/client_model v0.6.1 // indirect 67 | github.com/prometheus/common v0.53.0 // indirect 68 | github.com/prometheus/procfs v0.14.0 // indirect 69 | github.com/quic-go/qpack v0.4.0 // indirect 70 | github.com/quic-go/quic-go v0.42.0 // indirect 71 | github.com/refraction-networking/utls v1.6.4 // indirect 72 | github.com/shirou/gopsutil v3.21.11+incompatible // indirect 73 | github.com/supranational/blst v0.3.11 // indirect 74 | github.com/tidwall/match v1.1.1 // indirect 75 | github.com/tidwall/pretty v1.2.1 // indirect 76 | github.com/tklauser/go-sysconf v0.3.13 // indirect 77 | github.com/tklauser/numcpus v0.7.0 // indirect 78 | github.com/twitchyliquid64/golang-asm v0.15.1 // indirect 79 | github.com/ugorji/go/codec v1.2.12 // indirect 80 | github.com/yusufpapurcu/wmi v1.2.4 // indirect 81 | go.opentelemetry.io/otel/sdk v1.25.0 // indirect 82 | go.opentelemetry.io/otel/trace v1.25.0 // indirect 83 | go.uber.org/mock v0.4.0 // indirect 84 | golang.org/x/arch v0.7.0 // indirect 85 | golang.org/x/crypto v0.22.0 // indirect 86 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f // indirect 87 | golang.org/x/mod v0.17.0 // indirect 88 | golang.org/x/net v0.24.0 // indirect 89 | golang.org/x/sync v0.7.0 // indirect 90 | golang.org/x/sys v0.19.0 // indirect 91 | golang.org/x/text v0.14.0 // indirect 92 | golang.org/x/tools v0.20.0 // indirect 93 | google.golang.org/protobuf v1.33.0 // indirect 94 | gopkg.in/yaml.v3 v3.0.1 // indirect 95 | rsc.io/tmplfunc v0.0.3 // indirect 96 | ) 97 | 98 | replace ( 99 | github.com/bytedance/sonic => github.com/bytedance/sonic v1.11.2 100 | github.com/crate-crypto/go-kzg-4844 => github.com/crate-crypto/go-kzg-4844 v0.7.0 101 | ) 102 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/DataDog/zstd v1.4.5 h1:EndNeuB0l9syBZhut0wns3gV1hL8zX8LIu6ZiVHWLIQ= 2 | github.com/DataDog/zstd v1.4.5/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo= 3 | github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= 4 | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= 5 | github.com/VictoriaMetrics/fastcache v1.12.1 h1:i0mICQuojGDL3KblA7wUNlY5lOK6a4bwt3uRKnkZU40= 6 | github.com/VictoriaMetrics/fastcache v1.12.1/go.mod h1:tX04vaqcNoQeGLD+ra5pU5sWkuxnzWhEzLwhP9w653o= 7 | github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= 8 | github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= 9 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 10 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 11 | github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE= 12 | github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= 13 | github.com/btcsuite/btcd/btcec/v2 v2.3.3 h1:6+iXlDKE8RMtKsvK0gshlXIuPbyWM/h84Ensb7o3sC0= 14 | github.com/btcsuite/btcd/btcec/v2 v2.3.3/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04= 15 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U= 16 | github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1/go.mod h1:7SFka0XMvUgj3hfZtydOrQY2mwhPclbT2snogU7SQQc= 17 | github.com/bytedance/sonic v1.11.2 h1:ywfwo0a/3j9HR8wsYGWsIWl2mvRsI950HyoxiBERw5A= 18 | github.com/bytedance/sonic v1.11.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= 19 | github.com/cespare/cp v0.1.0 h1:SE+dxFebS7Iik5LK0tsi1k9ZCxEaFX4AjQmoyA+1dJk= 20 | github.com/cespare/cp v0.1.0/go.mod h1:SOGHArjBr4JWaSDEVpWpo/hNg6RoKrls6Oh40hiwW+s= 21 | github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= 22 | github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 23 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0= 24 | github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpVsBuRksnlj1mLy4AWzRNQYxauNi62uWcE3to6eA= 25 | github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= 26 | github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= 27 | github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= 28 | github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU= 29 | github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA= 30 | github.com/cockroachdb/errors v1.8.1 h1:A5+txlVZfOqFBDa4mGz2bUWSp0aHElvHX2bKkdbQu+Y= 31 | github.com/cockroachdb/errors v1.8.1/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac= 32 | github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= 33 | github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI= 34 | github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593 h1:aPEJyR4rPBvDmeyi+l/FS/VtA00IWvjeFvjen1m1l1A= 35 | github.com/cockroachdb/pebble v0.0.0-20230928194634-aa077af62593/go.mod h1:6hk1eMY/u5t+Cf18q5lFMUA1Rc+Sm5I6Ra1QuPyxXCo= 36 | github.com/cockroachdb/redact v1.0.8 h1:8QG/764wK+vmEYoOlfobpe12EQcS81ukx/a4hdVMxNw= 37 | github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg= 38 | github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 h1:IKgmqgMQlVJIZj19CdocBeSfSaiCbEBZGKODaixqtHM= 39 | github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ= 40 | github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06 h1:zuQyyAKVxetITBuuhv3BI9cMrmStnpT18zmgmTxunpo= 41 | github.com/cockroachdb/tokenbucket v0.0.0-20230807174530-cc333fc44b06/go.mod h1:7nc4anLGjupUW/PeY5qiNYsdNXj7zopG+eqsS7To5IQ= 42 | github.com/consensys/bavard v0.1.13 h1:oLhMLOFGTLdlda/kma4VOJazblc7IM5y5QPd2A/YjhQ= 43 | github.com/consensys/bavard v0.1.13/go.mod h1:9ItSMtA/dXMAiL7BG6bqW2m3NdSEObYWoH223nGHukI= 44 | github.com/consensys/gnark-crypto v0.12.1 h1:lHH39WuuFgVHONRl3J0LRBtuYdQTumFSDtJF7HpyG8M= 45 | github.com/consensys/gnark-crypto v0.12.1/go.mod h1:v2Gy7L/4ZRosZ7Ivs+9SfUDr0f5UlG+EM5t7MPHiLuY= 46 | github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= 47 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 48 | github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233 h1:d28BXYi+wUpz1KBmiF9bWrjEMacUEREV6MBi2ODnrfQ= 49 | github.com/crate-crypto/go-ipa v0.0.0-20231025140028-3c0104f4b233/go.mod h1:geZJZH3SzKCqnz5VT0q/DyIG/tvu/dZk+VIfXicupJs= 50 | github.com/crate-crypto/go-kzg-4844 v0.7.0 h1:C0vgZRk4q4EZ/JgPfzuSoxdCq3C3mOZMBShovmncxvA= 51 | github.com/crate-crypto/go-kzg-4844 v0.7.0/go.mod h1:1kMhvPgI0Ky3yIa+9lFySEBUBXkYxeOi8ZF1sYioxhc= 52 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 53 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 54 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 55 | github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= 56 | github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= 57 | github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= 58 | github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= 59 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= 60 | github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= 61 | github.com/ethereum/c-kzg-4844 v1.0.1 h1:pGixCbGizcVKSwoV70ge48+PrbB+iSKs2rjgfE4yJmQ= 62 | github.com/ethereum/c-kzg-4844 v1.0.1/go.mod h1:VewdlzQmpT5QSrVhbBuGoCdFJkpaJlO1aQputP83wc0= 63 | github.com/ethereum/go-ethereum v1.13.15 h1:U7sSGYGo4SPjP6iNIifNoyIAiNjrmQkz6EwQG+/EZWo= 64 | github.com/ethereum/go-ethereum v1.13.15/go.mod h1:TN8ZiHrdJwSe8Cb6x+p0hs5CxhJZPbqB7hHkaUXcmIU= 65 | github.com/ethereum/go-ethereum v1.14.0 h1:xRWC5NlB6g1x7vNy4HDBLuqVNbtLrc7v8S6+Uxim1LU= 66 | github.com/ethereum/go-ethereum v1.14.0/go.mod h1:1STrq471D0BQbCX9He0hUj4bHxX2k6mt5nOQJhDNOJ8= 67 | github.com/fjl/memsize v0.0.2 h1:27txuSD9or+NZlnOWdKUxeBzTAUkWCVh+4Gf2dWFOzA= 68 | github.com/fjl/memsize v0.0.2/go.mod h1:VvhXpOYNQvB+uIk2RvXzuaQtkQJzzIx6lSBe1xv7hi0= 69 | github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= 70 | github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= 71 | github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= 72 | github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= 73 | github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff h1:tY80oXqGNY4FhTFhk+o9oFHGINQ/+vhlm8HFzi6znCI= 74 | github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff/go.mod h1:x7DCsMOv1taUwEWCzT4cmDeAkigA5/QCwUodaVOe8Ww= 75 | github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46 h1:BAIP2GihuqhwdILrV+7GJel5lyPV3u1+PgzrWLc0TkE= 76 | github.com/gballet/go-verkle v0.1.1-0.20231031103413-a67434b50f46/go.mod h1:QNpY22eby74jVhqH4WhDLDwxc/vqsern6pW+u2kbkpc= 77 | github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= 78 | github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= 79 | github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= 80 | github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= 81 | github.com/go-kratos/aegis v0.2.0 h1:dObzCDWn3XVjUkgxyBp6ZeWtx/do0DPZ7LY3yNSJLUQ= 82 | github.com/go-kratos/aegis v0.2.0/go.mod h1:v0R2m73WgEEYB3XYu6aE2WcMwsZkJ/Rzuf5eVccm7bI= 83 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 84 | github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= 85 | github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 86 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 87 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 88 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 89 | github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= 90 | github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= 91 | github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= 92 | github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 93 | github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= 94 | github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= 95 | github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= 96 | github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= 97 | github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn0+wvQ3bZ8b/AU4= 98 | github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= 99 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= 100 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 101 | github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= 102 | github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= 103 | github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= 104 | github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= 105 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 106 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 107 | github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg= 108 | github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= 109 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 110 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 111 | github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb h1:PBC98N2aIaM3XXiurYmW7fx4GZkL8feAMVq7nEjURHk= 112 | github.com/golang/snappy v0.0.5-0.20220116011046-fa5810519dcb/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= 113 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 114 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 115 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 116 | github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 117 | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 118 | github.com/google/pprof v0.0.0-20240416155748-26353dc0451f h1:WpZiq8iqvGjJ3m3wzAVKL6+0vz7VkE79iSy9GII00II= 119 | github.com/google/pprof v0.0.0-20240416155748-26353dc0451f/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= 120 | github.com/google/pprof v0.0.0-20240422182052-72c8669ad3e7 h1:3q13T5NW3mlTJZM6B5UAsf2N5NYFbYWIyI3W8DlvBDU= 121 | github.com/google/pprof v0.0.0-20240422182052-72c8669ad3e7/go.mod h1:kf6iHlnVGwgKolg33glAes7Yg/8iWP8ukqeldJSO7jw= 122 | github.com/google/subcommands v1.2.0/go.mod h1:ZjhPrFU+Olkh9WazFPsl27BQ4UPiG37m3yTrtFlrHVk= 123 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 124 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 125 | github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY= 126 | github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY= 127 | github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 128 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 129 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 130 | github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE= 131 | github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0= 132 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 133 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 134 | github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4 h1:X4egAf/gcS1zATw6wn4Ej8vjuVGxeHdan+bRb2ebyv4= 135 | github.com/holiman/billy v0.0.0-20240216141850-2abb0c79d3c4/go.mod h1:5GuXa7vkL8u9FkFuWdVvfR5ix8hRB7DbOAaYULamFpc= 136 | github.com/holiman/bloomfilter/v2 v2.0.3 h1:73e0e/V0tCydx14a0SCYS/EWCxgwLZ18CZcZKVu0fao= 137 | github.com/holiman/bloomfilter/v2 v2.0.3/go.mod h1:zpoh+gs7qcpqrHr3dB55AMiJwo0iURXE7ZOP9L9hSkA= 138 | github.com/holiman/uint256 v1.2.4 h1:jUc4Nk8fm9jZabQuqr2JzednajVmBpC+oiTiXZJEApU= 139 | github.com/holiman/uint256 v1.2.4/go.mod h1:EOMSn4q6Nyt9P6efbI3bueV4e1b3dGlUCXeiRV4ng7E= 140 | github.com/huin/goupnp v1.3.0 h1:UvLUlWDNpoUdYzb2TCn+MuTWtcjXKSza2n6CBdQ0xXc= 141 | github.com/huin/goupnp v1.3.0/go.mod h1:gnGPsThkYa7bFi/KWmEysQRf48l2dvR5bxr2OFckNX8= 142 | github.com/imroc/req/v3 v3.43.3 h1:WdZhpUev9THtuwEZsW2LOYacl12fm7IkB7OgACv40+k= 143 | github.com/imroc/req/v3 v3.43.3/go.mod h1:SQIz5iYop16MJxbo8ib+4LnostGCok8NQf8ToyQc2xA= 144 | github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= 145 | github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= 146 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 147 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 148 | github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= 149 | github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= 150 | github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= 151 | github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= 152 | github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= 153 | github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= 154 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 155 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 156 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 157 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 158 | github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= 159 | github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= 160 | github.com/leanovate/gopter v0.2.9 h1:fQjYxZaynp97ozCzfOyOuAGOU4aU/z37zf/tOujFk7c= 161 | github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= 162 | github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= 163 | github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= 164 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 165 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 166 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= 167 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= 168 | github.com/mattn/go-runewidth v0.0.13 h1:lTGmDsbAYt5DmK6OnoV7EuIF1wEIFAcxld6ypU4OSgU= 169 | github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 170 | github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag= 171 | github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 172 | github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A= 173 | github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4= 174 | github.com/mmcloughlin/addchain v0.4.0 h1:SobOdjm2xLj1KkXN5/n0xTIWyZA2+s99UCY1iPfkHRY= 175 | github.com/mmcloughlin/addchain v0.4.0/go.mod h1:A86O+tHqZLMNO4w6ZZ4FlVQEadcoqkyU72HC5wJ4RlU= 176 | github.com/mmcloughlin/profile v0.1.1/go.mod h1:IhHD7q1ooxgwTgjxQYkACGA77oFTDdFVejUS1/tS/qU= 177 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 178 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 179 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 180 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 181 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 182 | github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= 183 | github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= 184 | github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8= 185 | github.com/onsi/ginkgo/v2 v2.17.1/go.mod h1:llBI3WDLL9Z6taip6f33H76YcWtJv+7R3HigUjbIBOs= 186 | github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= 187 | github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= 188 | github.com/pelletier/go-toml/v2 v2.2.1 h1:9TA9+T8+8CUCO2+WYnDLCgrYi9+omqKXyjDtosvtEhg= 189 | github.com/pelletier/go-toml/v2 v2.2.1/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= 190 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 191 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 192 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 193 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 194 | github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU= 195 | github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k= 196 | github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= 197 | github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= 198 | github.com/prometheus/common v0.53.0 h1:U2pL9w9nmJwJDa4qqLQ3ZaePJ6ZTwt7cMD3AG3+aLCE= 199 | github.com/prometheus/common v0.53.0/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U= 200 | github.com/prometheus/procfs v0.14.0 h1:Lw4VdGGoKEZilJsayHf0B+9YgLGREba2C6xr+Fdfq6s= 201 | github.com/prometheus/procfs v0.14.0/go.mod h1:XL+Iwz8k8ZabyZfMFHPiilCniixqQarAy5Mu67pHlNQ= 202 | github.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo= 203 | github.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A= 204 | github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM= 205 | github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M= 206 | github.com/refraction-networking/utls v1.6.4 h1:aeynTroaYn7y+mFtqv8D0bQ4bw0y9nJHneGxJ7lvRDM= 207 | github.com/refraction-networking/utls v1.6.4/go.mod h1:2VL2xfiqgFAZtJKeUTlf+PSYFs3Eu7km0gCtXJ3m8zs= 208 | github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= 209 | github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= 210 | github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= 211 | github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= 212 | github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik= 213 | github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= 214 | github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= 215 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 216 | github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI= 217 | github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA= 218 | github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= 219 | github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= 220 | github.com/status-im/keycard-go v0.2.0 h1:QDLFswOQu1r5jsycloeQh3bVU8n/NatHHaZobtDnDzA= 221 | github.com/status-im/keycard-go v0.2.0/go.mod h1:wlp8ZLbsmrF6g6WjugPAx+IzoLrkdf9+mHxBEeo3Hbg= 222 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 223 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 224 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 225 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 226 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 227 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 228 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 229 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 230 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 231 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 232 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 233 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 234 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 235 | github.com/supranational/blst v0.3.11 h1:LyU6FolezeWAhvQk0k6O/d49jqgO52MSDDfYgbeoEm4= 236 | github.com/supranational/blst v0.3.11/go.mod h1:jZJtfjgudtNl4en1tzwPIV3KjUnQUvG3/j+w+fVonLw= 237 | github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 h1:epCh84lMvA70Z7CTTCmYQn2CKbY8j86K7/FAIr141uY= 238 | github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc= 239 | github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U= 240 | github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= 241 | github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= 242 | github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= 243 | github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 244 | github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= 245 | github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= 246 | github.com/tklauser/go-sysconf v0.3.13 h1:GBUpcahXSpR2xN01jhkNAbTLRk2Yzgggk8IM08lq3r4= 247 | github.com/tklauser/go-sysconf v0.3.13/go.mod h1:zwleP4Q4OehZHGn4CYZDipCgg9usW5IJePewFCGVEa0= 248 | github.com/tklauser/numcpus v0.7.0 h1:yjuerZP127QG9m5Zh/mSO4wqurYil27tHrqwRoRjpr4= 249 | github.com/tklauser/numcpus v0.7.0/go.mod h1:bb6dMVcj8A42tSE7i32fsIUCbQNllK5iDguyOZRUzAY= 250 | github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= 251 | github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= 252 | github.com/tyler-smith/go-bip39 v1.1.0 h1:5eUemwrMargf3BSLRRCalXT93Ns6pQJIjYQN2nyfOP8= 253 | github.com/tyler-smith/go-bip39 v1.1.0/go.mod h1:gUYDtqQw1JS3ZJ8UWVcGTGqqr6YIN3CWg+kkNaLt55U= 254 | github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= 255 | github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= 256 | github.com/urfave/cli/v2 v2.25.7 h1:VAzn5oq403l5pHjc4OhD54+XGO9cdKVL/7lDjF+iKUs= 257 | github.com/urfave/cli/v2 v2.25.7/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= 258 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= 259 | github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= 260 | github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= 261 | github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 262 | go.opentelemetry.io/otel v1.25.0 h1:gldB5FfhRl7OJQbUHt/8s0a7cE8fbsPAtdpRaApKy4k= 263 | go.opentelemetry.io/otel v1.25.0/go.mod h1:Wa2ds5NOXEMkCmUou1WA7ZBfLTHWIsp034OVD7AO+Vg= 264 | go.opentelemetry.io/otel/exporters/prometheus v0.47.0 h1:OL6yk1Z/pEGdDnrBbxSsH+t4FY1zXfBRGd7bjwhlMLU= 265 | go.opentelemetry.io/otel/exporters/prometheus v0.47.0/go.mod h1:xF3N4OSICZDVbbYZydz9MHFro1RjmkPUKEvar2utG+Q= 266 | go.opentelemetry.io/otel/metric v1.25.0 h1:LUKbS7ArpFL/I2jJHdJcqMGxkRdxpPHE0VU/D4NuEwA= 267 | go.opentelemetry.io/otel/metric v1.25.0/go.mod h1:rkDLUSd2lC5lq2dFNrX9LGAbINP5B7WBkC78RXCpH5s= 268 | go.opentelemetry.io/otel/sdk v1.25.0 h1:PDryEJPC8YJZQSyLY5eqLeafHtG+X7FWnf3aXMtxbqo= 269 | go.opentelemetry.io/otel/sdk v1.25.0/go.mod h1:oFgzCM2zdsxKzz6zwpTZYLLQsFwc+K0daArPdIhuxkw= 270 | go.opentelemetry.io/otel/sdk/metric v1.25.0 h1:7CiHOy08LbrxMAp4vWpbiPcklunUshVpAvGBrdDRlGw= 271 | go.opentelemetry.io/otel/sdk/metric v1.25.0/go.mod h1:LzwoKptdbBBdYfvtGCzGwk6GWMA3aUzBOwtQpR6Nz7o= 272 | go.opentelemetry.io/otel/trace v1.25.0 h1:tqukZGLwQYRIFtSQM2u2+yfMVTgGVeqRLPUYx1Dq6RM= 273 | go.opentelemetry.io/otel/trace v1.25.0/go.mod h1:hCCs70XM/ljO+BeQkyFnbK28SBIJ/Emuha+ccrCRT7I= 274 | go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU= 275 | go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc= 276 | golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= 277 | golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= 278 | golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= 279 | golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30= 280 | golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= 281 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= 282 | golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= 283 | golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= 284 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 285 | golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= 286 | golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= 287 | golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= 288 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 289 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 290 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 291 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 292 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 293 | golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= 294 | golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 295 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 296 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 297 | golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= 298 | golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 299 | golang.org/x/tools v0.20.0 h1:hz/CVckiOxybQvFw6h7b/q80NTr9IUQb4s1IIzW7KNY= 300 | golang.org/x/tools v0.20.0/go.mod h1:WvitBU7JJf6A4jOdg4S1tviW9bhUxkgeCui/0JHctQg= 301 | google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= 302 | google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 303 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 304 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 305 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 306 | gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= 307 | gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= 308 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 309 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 310 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 311 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 312 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 313 | nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= 314 | rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= 315 | rsc.io/tmplfunc v0.0.3 h1:53XFQh69AfOa8Tw0Jm7t+GV7KZhOi6jzsCzTtKbMvzU= 316 | rsc.io/tmplfunc v0.0.3/go.mod h1:AG3sTPzElb1Io3Yg4voV9AGZJuleGAwaVRxL9M49PhA= 317 | -------------------------------------------------------------------------------- /model/client/conf.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "time" 6 | 7 | "github.com/shopspring/decimal" 8 | 9 | "github.com/ethereum/go-ethereum/common" 10 | ) 11 | 12 | type ConfPool struct { 13 | AppID string `yaml:"app_id" json:"app_id"` 14 | Zone string `yaml:"zone" json:"zone"` 15 | Cluster string `yaml:"cluster" json:"cluster"` 16 | EvmChains map[int64]*ConfEvmChainInfo `yaml:"evm_chains" json:"evm_chains"` 17 | SolanaChains []*ConfSolanaClient `yaml:"solana_chains" json:"solana_chains"` 18 | } 19 | type ConfEvmChainInfo struct { 20 | ChainID int64 `yaml:"chain_id" json:"chain_id"` 21 | ChainName string `yaml:"chain_name" json:"chain_name"` 22 | ChainEnv string `yaml:"chain_env" json:"chain_env"` 23 | OfficialWebsite string `yaml:"official_website_url" json:"official_website"` 24 | ExplorerURL string `yaml:"explorer_url" json:"explorer_url"` 25 | Faucets []string `yaml:"faucets" json:"faucets"` 26 | Clients []*ConfEvmChainClient `yaml:"clients" json:"clients"` 27 | } 28 | type ConfEvmChainClient struct { 29 | ClientID string `yaml:"client_id" json:"client_id"` 30 | Provider string `yaml:"provider" json:"provider"` 31 | ProviderWebsite string `yaml:"provider_website" json:"provider_website"` 32 | TransportSchema string `yaml:"transport_schema" json:"transport_schema"` 33 | TransportURL string `yaml:"transport_url" json:"transport_url"` 34 | GasFeeRate decimal.Decimal `yaml:"gas_fee_rate" json:"gas_fee_rate"` 35 | GasLimitRate decimal.Decimal `yaml:"gas_limit_rate" json:"gas_limit_rate"` 36 | GasLimitMax decimal.Decimal `yaml:"gas_limit_max" json:"gas_limit_max"` 37 | Signers []*ConfEvmChainSigner `yaml:"signers" json:"signers"` 38 | } 39 | 40 | type ConfEvmChainSigner struct { 41 | PublicAddress common.Address `json:"public_address"` 42 | PrivateKey *ecdsa.PrivateKey `json:"-"` 43 | } 44 | type ConfEvmChain struct { 45 | ChainID int64 `yaml:"chain_id" json:"chain_id"` 46 | ChainName string `yaml:"chain_name" json:"chain_name"` 47 | ChainEnv string `yaml:"chain_env" json:"chain_env"` 48 | OfficialWebsite string `yaml:"official_website_url" json:"official_website"` 49 | ExplorerURL string `yaml:"explorer_url" json:"explorer_url"` 50 | Faucets []string `yaml:"faucets" json:"faucets"` 51 | Clients []*ConfClient `yaml:"clients" json:"clients"` 52 | } 53 | 54 | type ConfClient struct { 55 | ClientID string `yaml:"client_id" json:"client_id"` 56 | Provider string `yaml:"provider" json:"provider"` 57 | ProviderWebsite string `yaml:"provider_website" json:"provider_website"` 58 | TransportSchema string `yaml:"transport_schema" json:"transport_schema"` 59 | TransportURL string `yaml:"transport_url" json:"transport_url"` 60 | } 61 | type ConfEvmClient struct { 62 | ClientID string `yaml:"client_id" json:"client_id"` 63 | Provider string `yaml:"provider" json:"provider"` 64 | ProviderWebsite string `yaml:"provider_website" json:"provider_website"` 65 | TransportSchema string `yaml:"transport_schema" json:"transport_schema"` 66 | TransportURL string `yaml:"transport_url" json:"transport_url"` 67 | } 68 | type ConfSolanaClient struct { 69 | ClientID string `yaml:"client_id" json:"client_id"` 70 | Provider string `yaml:"provider" json:"provider"` 71 | TransportSchema string `yaml:"transport_schema" json:"transport_schema"` 72 | ChainEnv string `yaml:"chain_env" json:"chain_env"` 73 | TransportURL string `yaml:"transport_url" json:"transport_url"` 74 | IsDev bool `yaml:"is_dev" json:"is_dev"` 75 | } 76 | 77 | type Metadata struct { 78 | CallMethod string `yaml:"call_method" json:"call_method"` 79 | StartAt time.Time `yaml:"start_at" json:"start_at"` 80 | Status string `yaml:"status" json:"status"` 81 | } 82 | 83 | type EvmCallProxyRequest struct { 84 | ChainID int64 `json:"chain_id"` 85 | ID int64 `json:"id"` 86 | JsonRpc string `json:"jsonrpc"` 87 | Method string `json:"method"` 88 | Params []interface{} `json:"params"` 89 | } 90 | type SolanaCallProxyRequest struct { 91 | ChainEnv string `json:"chain_env"` 92 | ID int64 `json:"id"` 93 | JsonRpc string `json:"jsonrpc"` 94 | Method string `json:"method"` 95 | Params []interface{} `json:"params"` 96 | } 97 | 98 | type EvmCallProxyReply struct { 99 | ID int64 `json:"id"` 100 | JsonRpc string `json:"json_rpc"` 101 | Result interface{} `json:"result"` 102 | } 103 | type ErrReply struct { 104 | Code int64 `json:"code"` 105 | Reason string `json:"reason"` 106 | Message string `json:"message"` 107 | Metadata map[string]string `json:"metadata,omitempty"` 108 | } 109 | -------------------------------------------------------------------------------- /model/client/default.go: -------------------------------------------------------------------------------- 1 | package client 2 | 3 | import "github.com/6boris/web3-go/consts" 4 | 5 | func GetDefaultConfPool() *ConfPool { 6 | conf := &ConfPool{ 7 | AppID: "web3.app_id.default", 8 | Zone: "web3.zone.default", 9 | Cluster: "web3.cluster.default", 10 | SolanaChains: []*ConfSolanaClient{ 11 | { 12 | ClientID: "", 13 | Provider: "", 14 | ChainEnv: consts.ChainEnvTestnet, 15 | TransportSchema: "http", 16 | TransportURL: "https://api.testnet.solana.com", 17 | }, 18 | { 19 | ClientID: "", 20 | Provider: "", 21 | ChainEnv: consts.ChainEnvDevnet, 22 | TransportSchema: "http", 23 | TransportURL: "https://api.devnet.solana.com", 24 | }, 25 | { 26 | ClientID: "", 27 | Provider: "", 28 | ChainEnv: consts.ChainEnvMainnet, 29 | TransportSchema: "http", 30 | TransportURL: "https://api.mainnet-beta.solana.com", 31 | }, 32 | }, 33 | EvmChains: map[int64]*ConfEvmChainInfo{ 34 | 1: { 35 | ChainID: 1, 36 | ChainName: "Ethereum Mainnet", 37 | ChainEnv: consts.ChainEnvMainnet, 38 | OfficialWebsite: "https://ethereum.org", 39 | ExplorerURL: "https://etherscan.io", 40 | Faucets: []string{}, 41 | Clients: []*ConfEvmChainClient{ 42 | {Provider: "LlamaNodes", ProviderWebsite: "https://llamanodes.com", TransportSchema: "https", TransportURL: "https://eth.llamarpc.com"}, 43 | {Provider: "OMNIA", ProviderWebsite: "https://omniatech.io", TransportSchema: "https", TransportURL: "https://endpoints.omniatech.io/v1/eth/mainnet/public"}, 44 | {Provider: "Ankr", ProviderWebsite: "https://www.ankr.com", TransportSchema: "https", TransportURL: "https://rpc.ankr.com/eth"}, 45 | {Provider: "PublicNode", ProviderWebsite: "https://ethereum.publicnode.com", TransportSchema: "https", TransportURL: "https://ethereum.publicnode.com"}, 46 | {Provider: "1RPC", ProviderWebsite: "https://www.1rpc.io", TransportSchema: "https", TransportURL: "https://1rpc.io/eth"}, 47 | {Provider: "MEV Blocker", ProviderWebsite: "https://mevblocker.io", TransportSchema: "https", TransportURL: "https://rpc.mevblocker.io"}, 48 | {Provider: "FlashBots", ProviderWebsite: "https://www.flashbots.net", TransportSchema: "https", TransportURL: "https://rpc.flashbots.net"}, 49 | {Provider: "CloudFlare", ProviderWebsite: "https://www.cloudflare.com/web3", TransportSchema: "https", TransportURL: "https://cloudflare-eth.com"}, 50 | {Provider: "SecureRpc", ProviderWebsite: "https://securerpc.com", TransportSchema: "https", TransportURL: "https://api.securerpc.com/v1"}, 51 | {Provider: "BlockPI", ProviderWebsite: "https://public.blockpi.io", TransportSchema: "https", TransportURL: "https://ethereum.blockpi.network/v1/rpc/public"}, 52 | {Provider: "Payload.De", ProviderWebsite: "https://payload.de", TransportSchema: "https", TransportURL: "https://rpc.payload.de"}, 53 | {Provider: "Alchemy", ProviderWebsite: "https://www.alchemy.com", TransportSchema: "https", TransportURL: "https://eth-mainnet.g.alchemy.com/v2/demo"}, 54 | {Provider: "GasHawk", ProviderWebsite: "https://gashawk.io", TransportSchema: "https", TransportURL: "https://core.gashawk.io/rpc"}, 55 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://rpc.mevblocker.io"}, 56 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://core.gashawk.io/rpc"}, 57 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://eth.api.onfinality.io/public"}, 58 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://eth-rpc.gateway.pokt.network"}, 59 | }, 60 | }, 61 | 11155111: { 62 | ChainID: 11155111, 63 | ChainName: "Ethereum Sepolia", 64 | ChainEnv: consts.ChainEnvTestnet, 65 | OfficialWebsite: "https://ethereum.org", 66 | ExplorerURL: "https://sepolia.etherscan.io", 67 | Faucets: []string{}, 68 | Clients: []*ConfEvmChainClient{ 69 | {Provider: "BlockPI", ProviderWebsite: "https://public.blockpi.io/", TransportSchema: "https", TransportURL: "https://ethereum-sepolia.blockpi.network/v1/rpc/public"}, 70 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://eth-sepolia.public.blastapi.io"}, 71 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://rpc2.sepolia.org"}, 72 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://rpc.sepolia.org"}, 73 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://eth-sepolia-public.unifra.io"}, 74 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://endpoints.omniatech.io/v1/eth/sepolia/public"}, 75 | }, 76 | }, 77 | 5: { 78 | ChainID: 5, 79 | ChainName: "Ethereum Goerli", 80 | ChainEnv: consts.ChainEnvTestnet, 81 | OfficialWebsite: "https://ethereum.org", 82 | ExplorerURL: "https://goerli.etherscan.io", 83 | Faucets: []string{}, 84 | Clients: []*ConfEvmChainClient{ 85 | {Provider: "BlockPI", ProviderWebsite: "https://public.blockpi.io/", TransportSchema: "https", TransportURL: "https://goerli.blockpi.network/v1/rpc/public"}, 86 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://rpc.ankr.com/eth_goerli"}, 87 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://eth-goerli.public.blastapi.io"}, 88 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://eth-goerli.g.alchemy.com/v2/demo"}, 89 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://goerli.blockpi.network/v1/rpc/public"}, 90 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://eth-goerli.api.onfinality.io/public"}, 91 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://rpc.goerli.eth.gateway.fm"}, 92 | }, 93 | }, 94 | 137: { 95 | ChainID: 137, 96 | ChainName: "Polygon PoS Chain", 97 | ChainEnv: consts.ChainEnvMainnet, 98 | OfficialWebsite: "https://polygon.technology", 99 | ExplorerURL: "https://polygonscan.com", 100 | Faucets: []string{}, 101 | Clients: []*ConfEvmChainClient{ 102 | {Provider: "LlamaNodes", ProviderWebsite: "https://llamanodes.com", TransportSchema: "https", TransportURL: "https://polygon.llamarpc.com"}, 103 | {Provider: "Ankr", ProviderWebsite: "https://polygon-rpc.com", TransportSchema: "https", TransportURL: "https://polygon-rpc.com"}, 104 | {Provider: "QuickNode", ProviderWebsite: "https://www.quicknode.com", TransportSchema: "https", TransportURL: "https://rpc-mainnet.matic.quiknode.pro"}, 105 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://rpc-mainnet.matic.network"}, 106 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://rpc.ankr.com/polygon"}, 107 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://1rpc.io/matic"}, 108 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://poly-rpc.gateway.pokt.network"}, 109 | }, 110 | }, 111 | 80001: { 112 | ChainID: 80001, 113 | ChainName: "Polygon PoS Chain Testnet", 114 | ChainEnv: consts.ChainEnvTestnet, 115 | OfficialWebsite: "https://polygon.technology", 116 | ExplorerURL: "https://mumbai.polygonscan.com", 117 | Faucets: []string{}, 118 | Clients: []*ConfEvmChainClient{ 119 | {Provider: "BlockPI", ProviderWebsite: "https://public.blockpi.io/", TransportSchema: "https", TransportURL: "https://polygon-mumbai.blockpi.network/v1/rpc/public"}, 120 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://rpc-mumbai.maticvigil.com"}, 121 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://polygon-testnet.public.blastapi.io"}, 122 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://matic-mumbai.chainstacklabs.com"}, 123 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://rpc.ankr.com/polygon_mumbai"}, 124 | }, 125 | }, 126 | 10: { 127 | ChainID: 10, 128 | ChainName: "Optimism", 129 | ChainEnv: consts.ChainEnvMainnet, 130 | OfficialWebsite: "https://www.optimism.io", 131 | ExplorerURL: "https://optimistic.etherscan.io", 132 | Faucets: []string{}, 133 | Clients: []*ConfEvmChainClient{ 134 | {Provider: "BlockPI", ProviderWebsite: "https://public.blockpi.io/", TransportSchema: "https", TransportURL: "https://optimism.blockpi.network/v1/rpc/public"}, 135 | {Provider: "Alchemy", ProviderWebsite: "https://www.alchemy.com/", TransportSchema: "https", TransportURL: "https://mainnet.optimism.io"}, 136 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://optimism-mainnet.public.blastapi.io"}, 137 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://rpc.ankr.com/optimism"}, 138 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://1rpc.io/op"}, 139 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://optimism.blockpi.network/v1/rpc/public"}, 140 | }, 141 | }, 142 | 420: { 143 | ChainID: 420, 144 | ChainName: "Optimism Goerli", 145 | ChainEnv: consts.ChainEnvTestnet, 146 | OfficialWebsite: "https://www.optimism.io", 147 | ExplorerURL: "https://goerli-explorer.optimism.io", 148 | Faucets: []string{}, 149 | Clients: []*ConfEvmChainClient{ 150 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://optimism-goerli.public.blastapi.io"}, 151 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://goerli.optimism.io"}, 152 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://opt-goerli.g.alchemy.com/v2/demo"}, 153 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://endpoints.omniatech.io/v1/op/goerli/public"}, 154 | }, 155 | }, 156 | 42161: { 157 | ChainID: 42161, 158 | ChainName: "Arbitrum One", 159 | ChainEnv: consts.ChainEnvMainnet, 160 | OfficialWebsite: "https://arbitrum.io", 161 | ExplorerURL: "https://arbiscan.io", 162 | Faucets: []string{}, 163 | Clients: []*ConfEvmChainClient{ 164 | {Provider: "BlockPI", ProviderWebsite: "https://public.blockpi.io/", TransportSchema: "https", TransportURL: "https://arbitrum.blockpi.network/v1/rpc/public"}, 165 | {Provider: "Ankr", ProviderWebsite: "https://www.ankr.com", TransportSchema: "https", TransportURL: "https://rpc.ankr.com/arbitrum"}, 166 | {Provider: "Arbitrum", ProviderWebsite: "https://arbitrum.io", TransportSchema: "https", TransportURL: "https://arb1.arbitrum.io/rpc"}, 167 | }, 168 | }, 169 | 42170: { 170 | ChainID: 42170, 171 | ChainName: "Arbitrum Nova", 172 | ChainEnv: consts.ChainEnvTestnet, 173 | OfficialWebsite: "https://arbitrum.io", 174 | ExplorerURL: "https://nova.arbiscan.io", 175 | Faucets: []string{}, 176 | Clients: []*ConfEvmChainClient{ 177 | {Provider: "Arbitrum", ProviderWebsite: "https://developer.arbitrum.io/public-chains", TransportSchema: "https", TransportURL: "https://nova.arbitrum.io/rpc"}, 178 | }, 179 | }, 180 | 421613: { 181 | ChainID: 421613, 182 | ChainName: "Arbitrum Goerli", 183 | ChainEnv: consts.ChainEnvTestnet, 184 | OfficialWebsite: "https://arbitrum.io", 185 | ExplorerURL: "https://goerli.arbiscan.io", 186 | Faucets: []string{}, 187 | Clients: []*ConfEvmChainClient{ 188 | {Provider: "Arbitrum", ProviderWebsite: "https://developer.arbitrum.io/public-chains", TransportSchema: "https", TransportURL: "https://goerli-rollup.arbitrum.io/rpc"}, 189 | }, 190 | }, 191 | 43113: { 192 | ChainID: 43113, 193 | ChainName: "Avalanche Fuji", 194 | ChainEnv: consts.ChainEnvMainnet, 195 | OfficialWebsite: "https://www.avax.network", 196 | ExplorerURL: "https://testnet.snowtrace.io", 197 | Faucets: []string{}, 198 | Clients: []*ConfEvmChainClient{ 199 | {Provider: "Ankr", ProviderWebsite: "https://developer.arbitrum.io/public-chains", TransportSchema: "https", TransportURL: "https://rpc.ankr.com/avalanche_fuji"}, 200 | }, 201 | }, 202 | 43114: { 203 | ChainID: 43114, 204 | ChainName: "Avalanche Fuji", 205 | ChainEnv: consts.ChainEnvTestnet, 206 | OfficialWebsite: "https://www.avax.network", 207 | ExplorerURL: "https://testnet.snowtrace.io", 208 | Faucets: []string{}, 209 | Clients: []*ConfEvmChainClient{ 210 | {Provider: "Arbitrum", ProviderWebsite: "https://developer.arbitrum.io/public-chains", TransportSchema: "https", TransportURL: "https://rpc.ankr.com/avalanche"}, 211 | }, 212 | }, 213 | 214 | 100: { 215 | ChainID: 100, 216 | ChainName: "Gnosis", 217 | ChainEnv: consts.ChainEnvMainnet, 218 | OfficialWebsite: "https://www.gnosis.io", 219 | ExplorerURL: "https://testnet.snowtrace.io", 220 | Faucets: []string{}, 221 | Clients: []*ConfEvmChainClient{ 222 | {Provider: "BlockPI", ProviderWebsite: "https://blockscout.com/xdai/mainnet", TransportSchema: "https", TransportURL: "https://gnosis.blockpi.network/v1/rpc/public"}, 223 | }, 224 | }, 225 | 56: { 226 | ChainID: 56, 227 | ChainName: "BNB Smart Chain", 228 | ChainEnv: consts.ChainEnvMainnet, 229 | OfficialWebsite: "https://bscscan.com", 230 | ExplorerURL: "https://bscscan.com", 231 | Faucets: []string{}, 232 | Clients: []*ConfEvmChainClient{ 233 | {Provider: "BlockPI", ProviderWebsite: "https://public.blockpi.io/", TransportSchema: "https", TransportURL: "https://bsc.blockpi.network/v1/rpc/public"}, 234 | }, 235 | }, 236 | 97: { 237 | ChainID: 97, 238 | ChainName: "BSC Testnet", 239 | ChainEnv: consts.ChainEnvTestnet, 240 | OfficialWebsite: "https://bscscan.com", 241 | ExplorerURL: "https://testnet.bscscan.com", 242 | Faucets: []string{}, 243 | Clients: []*ConfEvmChainClient{ 244 | {Provider: "Blast", ProviderWebsite: "https://blastapi.io", TransportSchema: "https", TransportURL: "https://bsc-testnet.public.blastapi.io"}, 245 | }, 246 | }, 247 | 250: { 248 | ChainID: 250, 249 | ChainName: "Fantom Mainnet", 250 | ChainEnv: consts.ChainEnvMainnet, 251 | OfficialWebsite: "", 252 | ExplorerURL: "", 253 | Faucets: []string{}, 254 | Clients: []*ConfEvmChainClient{ 255 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://rpc.fantom.network"}, 256 | }, 257 | }, 258 | 4002: { 259 | ChainID: 4002, 260 | ChainName: "Fantom Testnet", 261 | ChainEnv: consts.ChainEnvTestnet, 262 | OfficialWebsite: "", 263 | ExplorerURL: "", 264 | Faucets: []string{}, 265 | Clients: []*ConfEvmChainClient{ 266 | {Provider: "Other", ProviderWebsite: "", TransportSchema: "https", TransportURL: "https://rpc.ankr.com/fantom_testnet"}, 267 | }, 268 | }, 269 | }, 270 | } 271 | return conf 272 | } 273 | -------------------------------------------------------------------------------- /model/solana/rpc.go: -------------------------------------------------------------------------------- 1 | package solana 2 | 3 | import "github.com/shopspring/decimal" 4 | 5 | type ContextItem struct { 6 | ApiVersion string `json:"apiVersion"` 7 | Slot int64 `json:"slot"` 8 | } 9 | type AccountInfoItem struct { 10 | Lamports decimal.Decimal `json:"lamports"` 11 | Owner string `json:"owner"` 12 | Executable bool `json:"executable"` 13 | RentEpoch decimal.Decimal `json:"rentEpoch"` 14 | Space decimal.Decimal `json:"space"` 15 | Data []string `json:"data"` 16 | } 17 | 18 | type GetAccountInfoRequest struct { 19 | Account string `json:"account"` 20 | } 21 | type GetAccountInfoReply struct { 22 | Context *ContextItem `json:"context"` 23 | Value *AccountInfoItem `json:"value"` 24 | } 25 | 26 | type GetVersionRequest struct{} 27 | type GetVersionReply struct { 28 | FeatureSet string `json:"feature_set"` 29 | SolanaCore string `json:"solana_core"` 30 | } 31 | 32 | type GetBalanceRequest struct { 33 | Account string `json:"account"` 34 | } 35 | type GetBalanceReply struct { 36 | Context *ContextItem `json:"context"` 37 | Value decimal.Decimal `json:"value"` 38 | } 39 | 40 | type GetTokenAccountBalanceRequest struct { 41 | Account string `json:"account"` 42 | } 43 | type GetTokenAccountBalanceReply struct { 44 | Context *ContextItem `json:"context"` 45 | Amount decimal.Decimal `json:"amount"` 46 | Decimals decimal.Decimal `json:"decimals"` 47 | UIAmount decimal.Decimal `json:"ui_amount"` 48 | UIAmountString decimal.Decimal `json:"ui_amount_string"` 49 | } 50 | 51 | type GetBlockHeightRequest struct { 52 | } 53 | type GetBlockHeightReply struct { 54 | BlockHeight int64 `json:"block_height"` 55 | } 56 | 57 | type GetBlockRequest struct { 58 | Slot int64 `json:"slot"` 59 | Encoding string `json:"encoding"` 60 | TransactionDetails string `json:"transaction_details"` 61 | Rewards bool `json:"rewards"` 62 | } 63 | type GetBlockReply struct { 64 | Context *ContextItem `json:"context"` 65 | Value decimal.Decimal `json:"value"` 66 | } 67 | 68 | type ClusterNodesItem struct { 69 | Gossip string `json:"gossip"` 70 | PubKey string `json:"pubkey"` 71 | RPC string `json:"rpc"` 72 | TPU string `json:"tpu"` 73 | Version string `json:"version"` 74 | } 75 | -------------------------------------------------------------------------------- /pkg/otel/metrics.go: -------------------------------------------------------------------------------- 1 | package otel 2 | 3 | import ( 4 | otelProm "go.opentelemetry.io/otel/exporters/prometheus" 5 | otelMetrics "go.opentelemetry.io/otel/metric" 6 | metricSdk "go.opentelemetry.io/otel/sdk/metric" 7 | ) 8 | 9 | var MetricsWeb3RequestCounter otelMetrics.Int64Counter 10 | var MetricsWeb3RequestHistogram otelMetrics.Int64Histogram 11 | 12 | func init() { 13 | opts := []otelProm.Option{ 14 | otelProm.WithoutTargetInfo(), 15 | } 16 | exporter, err := otelProm.New(opts...) 17 | if err != nil { 18 | panic(err) 19 | } 20 | provider := metricSdk.NewMeterProvider(metricSdk.WithReader(exporter)) 21 | meter := provider.Meter("Web3 Go") 22 | m1, err := meter.Int64Counter("web3_abi_call", otelMetrics.WithDescription("Web3 Gateway abi call counter")) 23 | if err != nil { 24 | panic(err) 25 | } 26 | 27 | m2, err := meter.Int64Histogram("web3_abi_call", otelMetrics.WithDescription("Web3 Gateway abi call hist")) 28 | if err != nil { 29 | panic(err) 30 | } 31 | 32 | MetricsWeb3RequestCounter = m1 33 | MetricsWeb3RequestHistogram = m2 34 | } 35 | -------------------------------------------------------------------------------- /pkg/pk/signer.go: -------------------------------------------------------------------------------- 1 | package pk 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "errors" 6 | 7 | clientModel "github.com/6boris/web3-go/model/client" 8 | "github.com/ethereum/go-ethereum/crypto" 9 | ) 10 | 11 | func TransformPkToEvmSigner(privateKey string) (*clientModel.ConfEvmChainSigner, error) { 12 | signer := &clientModel.ConfEvmChainSigner{} 13 | pk, err := crypto.HexToECDSA(privateKey) 14 | if err != nil { 15 | return nil, err 16 | } 17 | publicKey := pk.Public() 18 | publicKeyECDSA, ok := publicKey.(*ecdsa.PublicKey) 19 | if !ok { 20 | return nil, errors.New("TransformPkToEvmSigner Failed") 21 | } 22 | signer.PrivateKey = pk 23 | signer.PublicAddress = crypto.PubkeyToAddress(*publicKeyECDSA) 24 | return signer, err 25 | } 26 | -------------------------------------------------------------------------------- /pkg/wjson/json.go: -------------------------------------------------------------------------------- 1 | package wjson 2 | 3 | import "encoding/json" 4 | 5 | func StructToJsonString(data interface{}) string { 6 | dataB, err := json.Marshal(data) 7 | if err != nil { 8 | return "" 9 | } 10 | return string(dataB) 11 | } 12 | 13 | func StructToJsonStringWithIndent(data interface{}, prefix, indent string) string { 14 | dataB, err := json.MarshalIndent(data, prefix, indent) 15 | if err != nil { 16 | return "" 17 | } 18 | return string(dataB) 19 | } 20 | --------------------------------------------------------------------------------