From f60d7959a7afadaa77e4059c1304f9465fbff6d7 Mon Sep 17 00:00:00 2001 From: nanmu42 Date: Fri, 15 Oct 2021 14:30:28 +0800 Subject: [PATCH 1/6] ci: use Github Actions --- .github/workflows/ci.yaml | 27 +++++++++++++++++++++++++++ .travis.yml | 10 ---------- 2 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 .github/workflows/ci.yaml delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..5a3db42 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,27 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Set up Go + uses: actions/setup-go@v2 + with: + go-version: "1.x" + + - name: Test + run: go test -v -coverprofile=coverage.txt -covermode=count ./... + + - name: Codecov + uses: codecov/codecov-action@v2.1.0 + + - name: golangci-lint + uses: golangci/golangci-lint-action@v2 \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 297a5cc..0000000 --- a/.travis.yml +++ /dev/null @@ -1,10 +0,0 @@ -language: go - -env: - - GO111MODULE=on - -script: - - go test -v -coverprofile=coverage.txt -covermode=count ./... - -after_success: - - bash <(curl -s https://codecov.io/bash) \ No newline at end of file From d7619c6ac7267397a501a8b243469c33842e26e8 Mon Sep 17 00:00:00 2001 From: nanmu42 Date: Fri, 15 Oct 2021 14:31:09 +0800 Subject: [PATCH 2/6] doc: update docs --- README.md | 19 ++++++++++++------- README_ZH.md | 17 +++++++++++------ 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 38b149a..d300d3e 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,21 @@ +**English** | [中文](https://github.com/nanmu42/etherscan-api/blob/master/README_ZH.md) + # etherscan-api -[![Build Status](https://travis-ci.org/nanmu42/etherscan-api.svg?branch=master)](https://travis-ci.org/nanmu42/etherscan-api) -[![Go Report Card](https://goreportcard.com/badge/github.com/nanmu42/etherscan-api)](https://goreportcard.com/report/github.com/nanmu42/etherscan-api) -[![codecov](https://codecov.io/gh/nanmu42/etherscan-api/branch/master/graph/badge.svg)](https://codecov.io/gh/nanmu42/etherscan-api) [![GoDoc](https://godoc.org/github.com/nanmu42/etherscan-api?status.svg)](https://godoc.org/github.com/nanmu42/etherscan-api) -[中文文档](https://github.com/nanmu42/etherscan-api/blob/master/README_ZH.md) +[![CI status](https://github.com/nanmu42/etherscan-api/workflows/build/ci.svg)](https://github.com/nanmu42/etherscan-api/actions) +[![codecov](https://codecov.io/gh/nanmu42/etherscan-api/branch/master/graph/badge.svg)](https://codecov.io/gh/nanmu42/etherscan-api) +[![Go Report Card](https://goreportcard.com/badge/github.com/nanmu42/etherscan-api)](https://goreportcard.com/report/github.com/nanmu42/etherscan-api) -Go bindings to the Etherscan.io API(and its families like BscScan), with nearly Full implementation(accounts, transactions, tokens, contracts, blocks, stats), full network support(Mainnet, Ropsten, Kovan, Rinkby, Goerli, Tobalaba), and only depending on standard library. :wink: +Golang client for the Etherscan.io API(and its families like BscScan), with nearly full implementation(accounts, transactions, tokens, contracts, blocks, stats), full network support(Mainnet, Ropsten, Kovan, Rinkby, Goerli, Tobalaba), and only depending on standard library. :wink: # Usage -Create a API instance and off you go. :rocket: +```bash +go get github.com/nanmu42/etherscan-api +``` + +Create an API instance and off you go. :rocket: ```go import ( @@ -70,6 +75,6 @@ I am not from Etherscan and I just find their service really useful, so I implem # License -Use of this work is governed by a MIT License. +Use of this work is governed by an MIT License. You may find a license copy in project root. diff --git a/README_ZH.md b/README_ZH.md index 72ace44..dc48f68 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -1,17 +1,22 @@ +[English](https://github.com/nanmu42/etherscan-api/blob/master/README.md) | **中文** + # etherscan-api -[![Build Status](https://travis-ci.org/nanmu42/etherscan-api.svg?branch=master)](https://travis-ci.org/nanmu42/etherscan-api) -[![Go Report Card](https://goreportcard.com/badge/github.com/nanmu42/etherscan-api)](https://goreportcard.com/report/github.com/nanmu42/etherscan-api) -[![codecov](https://codecov.io/gh/nanmu42/etherscan-api/branch/master/graph/badge.svg)](https://codecov.io/gh/nanmu42/etherscan-api) [![GoDoc](https://godoc.org/github.com/nanmu42/etherscan-api?status.svg)](https://godoc.org/github.com/nanmu42/etherscan-api) -[English Readme](https://github.com/nanmu42/etherscan-api/blob/master/README.md) +[![CI status](https://github.com/nanmu42/etherscan-api/workflows/build/ci.svg)](https://github.com/nanmu42/etherscan-api/actions) +[![codecov](https://codecov.io/gh/nanmu42/etherscan-api/branch/master/graph/badge.svg)](https://codecov.io/gh/nanmu42/etherscan-api) +[![Go Report Card](https://goreportcard.com/badge/github.com/nanmu42/etherscan-api)](https://goreportcard.com/report/github.com/nanmu42/etherscan-api) -Etherscan.io的Golang实现, +Etherscan API的Golang客户端, 支持几乎所有功能(accounts, transactions, tokens, contracts, blocks, stats), 所有公共网络(Mainnet, Ropsten, Kovan, Rinkby, Goerli, Tobalaba)。 本项目只依赖于官方库。 :wink: -# Usage +# 使用方法 + +```bash +go get github.com/nanmu42/etherscan-api +``` 填入网络选项和API Key即可开始使用。 :rocket: From b2589b72cbff8b24c38fe4a4a4d92578b56e659b Mon Sep 17 00:00:00 2001 From: nanmu42 Date: Fri, 15 Oct 2021 14:31:30 +0800 Subject: [PATCH 3/6] refactor: cleanup code --- block.go | 24 +++++++++++++++++------- block_e2e_test.go | 14 +++++++------- 2 files changed, 24 insertions(+), 14 deletions(-) diff --git a/block.go b/block.go index 9da0efa..aea4ea1 100644 --- a/block.go +++ b/block.go @@ -7,7 +7,10 @@ package etherscan -import "strconv" +import ( + "fmt" + "strconv" +) // BlockReward gets block and uncle rewards by block number func (c *Client) BlockReward(blockNum int) (rewards BlockRewards, err error) { @@ -19,21 +22,28 @@ func (c *Client) BlockReward(blockNum int) (rewards BlockRewards, err error) { return } -// BlockNumber gets closest block number by UNIX timestamp +// BlockNumber gets the closest block number by UNIX timestamp +// +// valid closest option: before, after func (c *Client) BlockNumber(timestamp int64, closest string) (blockNumber int, err error) { - var result string + var blockNumberStr string + param := M{ - "timestamp": strconv.Itoa(int(timestamp)), + "timestamp": strconv.FormatInt(timestamp, 10), "closest": closest, } - err = c.call("block", "getblocknobytime", param, &result) + err = c.call("block", "getblocknobytime", param, &blockNumberStr) + + if err != nil { + return + } + blockNumber, err = strconv.Atoi(blockNumberStr) if err != nil { + err = fmt.Errorf("parsing block number %q: %w", blockNumberStr, err) return } - blockNum, err := strconv.ParseInt(result, 10, 64) - blockNumber = int(blockNum) return } diff --git a/block_e2e_test.go b/block_e2e_test.go index e05ff05..c49a8ac 100644 --- a/block_e2e_test.go +++ b/block_e2e_test.go @@ -26,21 +26,21 @@ func TestClient_BlockReward(t *testing.T) { } func TestClient_BlockNumber(t *testing.T) { - //Note: All values taken from docs.etherscan.io/api-endpoints/blocks - const ans_before = 9251482 - const ans_after = 9251483 + // Note: All values taken from docs.etherscan.io/api-endpoints/blocks + const ansBefore = 9251482 + const ansAfter = 9251483 blockNumber, err := api.BlockNumber(1578638524, "before") noError(t, err, "api.BlockNumber") - if blockNumber != ans_before { - t.Errorf(`api.BlockNumber(1578638524, "before") not working, got %d, want %d`, blockNumber, ans_before) + if blockNumber != ansBefore { + t.Errorf(`api.BlockNumber(1578638524, "before") not working, got %d, want %d`, blockNumber, ansBefore) } blockNumber, err = api.BlockNumber(1578638524, "after") noError(t, err, "api.BlockNumber") - if blockNumber != ans_after { - t.Errorf(`api.BlockNumber(1578638524,"after") not working, got %d, want %d`, blockNumber, ans_after) + if blockNumber != ansAfter { + t.Errorf(`api.BlockNumber(1578638524,"after") not working, got %d, want %d`, blockNumber, ansAfter) } } From 45bbbc62ef06ec25b61d88dfc2598dff694727e5 Mon Sep 17 00:00:00 2001 From: nanmu42 Date: Fri, 15 Oct 2021 14:45:28 +0800 Subject: [PATCH 4/6] ci: fix env access, add cron --- .github/workflows/ci.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 5a3db42..269770b 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -5,6 +5,8 @@ on: branches: [ master ] pull_request: branches: [ master ] + schedule: + - cron: "42 6 * * 0" jobs: build: @@ -18,6 +20,8 @@ jobs: go-version: "1.x" - name: Test + env: + ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }} run: go test -v -coverprofile=coverage.txt -covermode=count ./... - name: Codecov From 52fa07a81c30c2fdfd87b8617ed3cd025468baba Mon Sep 17 00:00:00 2001 From: nanmu42 Date: Fri, 15 Oct 2021 14:53:50 +0800 Subject: [PATCH 5/6] doc: fix ci badge link --- README.md | 2 +- README_ZH.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d300d3e..56acd1f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # etherscan-api [![GoDoc](https://godoc.org/github.com/nanmu42/etherscan-api?status.svg)](https://godoc.org/github.com/nanmu42/etherscan-api) -[![CI status](https://github.com/nanmu42/etherscan-api/workflows/build/ci.svg)](https://github.com/nanmu42/etherscan-api/actions) +[![CI status](https://github.com/nanmu42/etherscan-api/actions/workflows/ci.yaml/badge.svg)](https://github.com/nanmu42/etherscan-api/actions) [![codecov](https://codecov.io/gh/nanmu42/etherscan-api/branch/master/graph/badge.svg)](https://codecov.io/gh/nanmu42/etherscan-api) [![Go Report Card](https://goreportcard.com/badge/github.com/nanmu42/etherscan-api)](https://goreportcard.com/report/github.com/nanmu42/etherscan-api) diff --git a/README_ZH.md b/README_ZH.md index dc48f68..905a770 100644 --- a/README_ZH.md +++ b/README_ZH.md @@ -3,7 +3,7 @@ # etherscan-api [![GoDoc](https://godoc.org/github.com/nanmu42/etherscan-api?status.svg)](https://godoc.org/github.com/nanmu42/etherscan-api) -[![CI status](https://github.com/nanmu42/etherscan-api/workflows/build/ci.svg)](https://github.com/nanmu42/etherscan-api/actions) +[![CI status](https://github.com/nanmu42/etherscan-api/actions/workflows/ci.yaml/badge.svg)](https://github.com/nanmu42/etherscan-api/actions) [![codecov](https://codecov.io/gh/nanmu42/etherscan-api/branch/master/graph/badge.svg)](https://codecov.io/gh/nanmu42/etherscan-api) [![Go Report Card](https://goreportcard.com/badge/github.com/nanmu42/etherscan-api)](https://goreportcard.com/report/github.com/nanmu42/etherscan-api) From 2ab97d129a21340a270c7c0ccbebdd66839565dc Mon Sep 17 00:00:00 2001 From: avislash Date: Thu, 14 Oct 2021 18:33:54 -0400 Subject: [PATCH 6/6] Query Transaction Receipt using Transaction Hash Also: *Added Test for GetTxReceipt *Updated CI to download go mod dependencies --- .github/workflows/ci.yaml | 7 ++++++- client.go | 10 ++++++++-- go.mod | 5 +++++ proxy.go | 18 ++++++++++++++++++ proxy_e2e_test.go | 31 +++++++++++++++++++++++++++++++ response.go | 31 ++++++++++++++++++++++++++++++- 6 files changed, 98 insertions(+), 4 deletions(-) create mode 100644 proxy.go create mode 100644 proxy_e2e_test.go diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 269770b..df6083f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -19,6 +19,11 @@ jobs: with: go-version: "1.x" + - name: Install Dependencies + run: | + go mod download github.com/davecgh/go-spew + go mod download github.com/google/go-cmp + - name: Test env: ETHERSCAN_API_KEY: ${{ secrets.ETHERSCAN_API_KEY }} @@ -28,4 +33,4 @@ jobs: uses: codecov/codecov-action@v2.1.0 - name: golangci-lint - uses: golangci/golangci-lint-action@v2 \ No newline at end of file + uses: golangci/golangci-lint-action@v2 diff --git a/client.go b/client.go index e69679a..7e11a0c 100644 --- a/client.go +++ b/client.go @@ -172,8 +172,14 @@ func (c *Client) call(module, action string, param map[string]interface{}, outco err = wrapErr(err, "json unmarshal envelope") return } - if envelope.Status != 1 { - err = fmt.Errorf("etherscan server: %s", envelope.Message) + + if module != "proxy" { //proxy calls use jsonRPC + if envelope.Status != 1 { + err = fmt.Errorf("etherscan server: %s", envelope.Message) + return + } + } else if envelope.Error != nil { + err = fmt.Errorf("etherscan server: %s", envelope.Error) return } diff --git a/go.mod b/go.mod index 0a30134..71da4ac 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,8 @@ module github.com/nanmu42/etherscan-api go 1.13 + +require ( + github.com/davecgh/go-spew v1.1.1 + github.com/google/go-cmp v0.5.6 +) diff --git a/proxy.go b/proxy.go new file mode 100644 index 0000000..ab87dea --- /dev/null +++ b/proxy.go @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2021 LI Zhennan + * + * Use of this work is governed by a MIT License. + * You may find a license copy in project root. + */ + +package etherscan + +// GetTransactionReceipt gets ETH Transaction Receipt for a given transaction hash +func (c *Client) GetTransactionReceipt(txHash string) (receipt TxReceipt, err error) { + param := M{ + "txhash": txHash, + } + + err = c.call("proxy", "eth_getTransactionReceipt", param, &receipt) + return +} diff --git a/proxy_e2e_test.go b/proxy_e2e_test.go new file mode 100644 index 0000000..96eb36d --- /dev/null +++ b/proxy_e2e_test.go @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021 LI Zhennan + * + * Use of this work is governed by a MIT License. + * You may find a license copy in project root. + */ + +package etherscan + +import ( + "encoding/json" + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/google/go-cmp/cmp" +) + +func TestClient_GetTransactionReceipt(t *testing.T) { + var expectedReceipt TxReceipt + //Response grabbed from random transaction on Etherscan.io. This is a good test since it has a couple of embedded Logs + cannedResp := []byte(`{"blockHash":"0x9d4d4c6bab3dcd3b0b3a65f3b1880a9414990090da9315183b81a1b9a309b79e","blockNumber":"0xccc210","contractAddress":null,"cumulativeGasUsed":"0x35365c","effectiveGasPrice":"0x26d7dd3724","from":"0xf878c632317b5c58c88e8b7e8de6aa17bb3dfe83","gasUsed":"0x200ea","logs":[{"address":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","topics":["0xe1fffcc4923d04b559f4d29a8bfc6cda04eb5b0d3c460751c2402c5c5cc9109c","0x00000000000000000000000003f7724180aa6b939894b5ca4314783b0b36b329"],"data":"0x000000000000000000000000000000000000000000000000008e1bc9bf040000","blockNumber":"0xccc210","transactionHash":"0x162681c37d4e5db904b85a9953cbbe213e9631d5c928ad6b772f696aac9f67f5","transactionIndex":"0x12","blockHash":"0x9d4d4c6bab3dcd3b0b3a65f3b1880a9414990090da9315183b81a1b9a309b79e","logIndex":"0x37","removed":false},{"address":"0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000003f7724180aa6b939894b5ca4314783b0b36b329","0x000000000000000000000000d0dcb7a4f8cfcdb29364d621ca5d997b7eddbc46"],"data":"0x000000000000000000000000000000000000000000000000008e1bc9bf040000","blockNumber":"0xccc210","transactionHash":"0x162681c37d4e5db904b85a9953cbbe213e9631d5c928ad6b772f696aac9f67f5","transactionIndex":"0x12","blockHash":"0x9d4d4c6bab3dcd3b0b3a65f3b1880a9414990090da9315183b81a1b9a309b79e","logIndex":"0x38","removed":false},{"address":"0x8b3192f5eebd8579568a2ed41e6feb402f93f73f","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000d0dcb7a4f8cfcdb29364d621ca5d997b7eddbc46","0x000000000000000000000000f878c632317b5c58c88e8b7e8de6aa17bb3dfe83"],"data":"0x000000000000000000000000000000000000000000000000932a9ca104b8bd31","blockNumber":"0xccc210","transactionHash":"0x162681c37d4e5db904b85a9953cbbe213e9631d5c928ad6b772f696aac9f67f5","transactionIndex":"0x12","blockHash":"0x9d4d4c6bab3dcd3b0b3a65f3b1880a9414990090da9315183b81a1b9a309b79e","logIndex":"0x39","removed":false},{"address":"0xd0dcb7a4f8cfcdb29364d621ca5d997b7eddbc46","topics":["0x1c411e9a96e071241c2f21f7726b17ae89e3cab4c78be50e062b03a9fffbbad1"],"data":"0x0000000000000000000000000000000000000000000000f2ce932331c81806b2000000000000000000000000000000000000000000000000e5a3780b0117856d","blockNumber":"0xccc210","transactionHash":"0x162681c37d4e5db904b85a9953cbbe213e9631d5c928ad6b772f696aac9f67f5","transactionIndex":"0x12","blockHash":"0x9d4d4c6bab3dcd3b0b3a65f3b1880a9414990090da9315183b81a1b9a309b79e","logIndex":"0x3a","removed":false},{"address":"0xd0dcb7a4f8cfcdb29364d621ca5d997b7eddbc46","topics":["0xd78ad95fa46c994b6551d0da85fc275fe613ce37657fb8d5e3d130840159d822","0x00000000000000000000000003f7724180aa6b939894b5ca4314783b0b36b329","0x000000000000000000000000f878c632317b5c58c88e8b7e8de6aa17bb3dfe83"],"data":"0x000000000000000000000000000000000000000000000000000008d0e801213b000000000000000000000000000000000000000000000000008e1bc9bf040000000000000000000000000000000000000000000000000000962b7b410a0ae0650000000000000000000000000000000000000000000000000000000000000000","blockNumber":"0xccc210","transactionHash":"0x162681c37d4e5db904b85a9953cbbe213e9631d5c928ad6b772f696aac9f67f5","transactionIndex":"0x12","blockHash":"0x9d4d4c6bab3dcd3b0b3a65f3b1880a9414990090da9315183b81a1b9a309b79e","logIndex":"0x3b","removed":false}],"logsBloom":"0x00200000000000000000000080000004000000000800000000000000000040000000000000000000000000000000000003000000080001000000000000000000000000000000000000000008000000200000000000000000000020008000000000000000000000000000000000000000000210000000000000000010000000000000000000000000100000000000000400000001000000080000004000000000000000000000000000000000000200000000001000000000000000000000000100000002001000000000000000000000000000000000001000000000000000000000200000000080000000000000000000000000000000400000000000000000","status":"0x1","to":"0x03f7724180aa6b939894b5ca4314783b0b36b329","transactionHash":"0x162681c37d4e5db904b85a9953cbbe213e9631d5c928ad6b772f696aac9f67f5","transactionIndex":"0x12","type":"0x2"}`) + + json.Unmarshal(cannedResp, &expectedReceipt) + + actualReceipt, err := api.GetTransactionReceipt("0x162681c37d4e5db904b85a9953cbbe213e9631d5c928ad6b772f696aac9f67f5") + noError(t, err, "api.GetTransactionReceipt") + + if !cmp.Equal(expectedReceipt, actualReceipt) { + t.Errorf(`api.GetTransactionReceipt("0x162681c37d4e5db904b85a9953cbbe213e9631d5c928ad6b772f696aac9f67f5" not working, got %s, want %s`, spew.Sdump(actualReceipt), spew.Sdump(expectedReceipt)) + } +} diff --git a/response.go b/response.go index b1338d3..61fa08b 100644 --- a/response.go +++ b/response.go @@ -14,7 +14,8 @@ type Envelope struct { // 1 for good, 0 for error Status int `json:"status,string"` // OK for good, other words when Status equals 0 - Message string `json:"message"` + Message string `json:"message"` + Error json.RawMessage `json:"error"` //Used for JSON RPC calls (i.e. when module==proxy) // where response lies Result json.RawMessage `json:"result"` } @@ -136,3 +137,31 @@ type LatestPrice struct { ETHUSD float64 `json:"ethusd,string"` ETHUSDTimestamp Time `json:"ethusd_timestamp"` } + +type Log struct { + Address string `json:"address"` + Topics []string `json:"topics"` + Data string `json:"data"` + BlockNumber string `json:"blockNumber"` + TransactionHash string `json:"transactionHash"` + BlockHash string `json:"blockHash"` + LogIndex string `json:"logIndex"` + Removed bool `json:"removed"` +} + +type TxReceipt struct { + BlockHash string `json:"blockHash"` + BlockNumber string `json:"blockNumber"` + ContractAddress string `json:"contractAddress"` + CumulativeGasUsed string `json:"cumulativeGasUsed"` + EffectiveGasPrice string `json:"effectiveGasPrice"` + From string `json:"from"` + GasUsed string `json:"gasUsed"` + Logs []Log `json:"logs"` + LogsBloom string `json:"logsBloom"` + Status string `json:"status"` + To string `json:"to"` + TransactionHash string `json:"transactionHash"` + TransactionIndex string `json:"transactionIndex"` + Type string `json:"type"` +}