From caf06a33dbcb0723ee242d9fb880b1040c1ac6f6 Mon Sep 17 00:00:00 2001 From: avislash Date: Mon, 24 Oct 2022 11:13:06 -0400 Subject: [PATCH 01/12] Implemented Free Gas Tracker Endpoints --- gas_tracker.go | 21 ++++++++++++++++ gas_tracker_e2e_test.go | 53 +++++++++++++++++++++++++++++++++++++++++ response.go | 10 ++++++++ 3 files changed, 84 insertions(+) create mode 100644 gas_tracker.go create mode 100644 gas_tracker_e2e_test.go diff --git a/gas_tracker.go b/gas_tracker.go new file mode 100644 index 0000000..6076b2a --- /dev/null +++ b/gas_tracker.go @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2022 Avi Misra + * + * Use of this work is governed by a MIT License. + * You may find a license copy in project root. + */ + +package etherscan + +// GasEstiamte gets estiamted confirmation time (in seconds) at the given gas price +func (c *Client) GasEstimate(gasPrice int) (confirmationTimeInSec string, err error) { + params := M{"gasPrice": gasPrice} + err = c.call("gastracker", "gasestimate", params, &confirmationTimeInSec) + return +} + +// GasOracle gets suggested gas prices (in Gwei) +func (c *Client) GasOracle() (gasPrices GasPrices, err error) { + err = c.call("gastracker", "gasoracle", M{}, &gasPrices) + return +} diff --git a/gas_tracker_e2e_test.go b/gas_tracker_e2e_test.go new file mode 100644 index 0000000..5dc6c4b --- /dev/null +++ b/gas_tracker_e2e_test.go @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2018 LI Zhennan + * + * Use of this work is governed by a MIT License. + * You may find a license copy in project root. + */ + +package etherscan + +import ( + "testing" +) + +//GasEstiamte generates dynamic data. Best we can do is ensure all fields are populated +func TestClient_GasEstimate(t *testing.T) { + confirmationTime, err := api.GasEstimate(20000000) + noError(t, err, "api.GasEstimate") + + if 0 == len(confirmationTime) { + t.Errorf("confirmationTime empty string") + } +} + +//GasOracle generates dynamic data. Best we can do is ensure all fields are populated +func TestClient_GasOracle(t *testing.T) { + gasPrice, err := api.GasOracle() + noError(t, err, "api.GasOrcale") + + if 0 == len(gasPrice.LastBlock) { + t.Errorf("gasPrice.LastBlock empty string") + } + + if 0 == len(gasPrice.SafeGasPrice) { + t.Errorf("gasPrice.SafeGasPrice empty string") + } + + if 0 == len(gasPrice.ProposeGasPrice) { + t.Errorf("gasPrice.ProposeGasPrice empty string") + } + + if 0 == len(gasPrice.FastGasPrice) { + t.Errorf("gasPrice.FastGasPrice empty string") + } + + if 0 == len(gasPrice.SuggestBaseFeeInGwei) { + t.Errorf("gasPrice.SuggestBaseFeeInGwei empty string") + } + + if 0 == len(gasPrice.GasUsedRatio) { + t.Errorf("gasPrice.GasUsedRatio empty string") + } + +} diff --git a/response.go b/response.go index e28007e..9cd2570 100644 --- a/response.go +++ b/response.go @@ -175,3 +175,13 @@ type Log struct { Removed bool `json:"removed"` } +//GasPrices holds info for Gas Oracle queries +//Gas Prices are returned in Gwei +type GasPrices struct { + LastBlock string + SafeGasPrice string + ProposeGasPrice string + FastGasPrice string + SuggestBaseFeeInGwei string `json:"suggestBaseFee"` + GasUsedRatio string `json:"gasUsedRatio"` +} From 384329f2261b7e66a1ab6dc175af1010a537a8e1 Mon Sep 17 00:00:00 2001 From: avislash Date: Tue, 25 Oct 2022 10:36:52 -0400 Subject: [PATCH 02/12] Use numeric data types for gas prices, confirmation times, and block numbers --- gas_tracker.go | 12 +++++-- gas_tracker_e2e_test.go | 28 ++-------------- response.go | 71 +++++++++++++++++++++++++++++++++++++---- 3 files changed, 75 insertions(+), 36 deletions(-) diff --git a/gas_tracker.go b/gas_tracker.go index 6076b2a..02fe5ca 100644 --- a/gas_tracker.go +++ b/gas_tracker.go @@ -7,11 +7,17 @@ package etherscan +import "time" + // GasEstiamte gets estiamted confirmation time (in seconds) at the given gas price -func (c *Client) GasEstimate(gasPrice int) (confirmationTimeInSec string, err error) { +func (c *Client) GasEstimate(gasPrice int) (confirmationTimeInSec time.Duration, err error) { params := M{"gasPrice": gasPrice} - err = c.call("gastracker", "gasestimate", params, &confirmationTimeInSec) - return + var confTime string + err = c.call("gastracker", "gasestimate", params, &confTime) + if err != nil { + return + } + return time.ParseDuration(confTime + "s") } // GasOracle gets suggested gas prices (in Gwei) diff --git a/gas_tracker_e2e_test.go b/gas_tracker_e2e_test.go index 5dc6c4b..3905bc3 100644 --- a/gas_tracker_e2e_test.go +++ b/gas_tracker_e2e_test.go @@ -13,12 +13,8 @@ import ( //GasEstiamte generates dynamic data. Best we can do is ensure all fields are populated func TestClient_GasEstimate(t *testing.T) { - confirmationTime, err := api.GasEstimate(20000000) + _, err := api.GasEstimate(20000000) noError(t, err, "api.GasEstimate") - - if 0 == len(confirmationTime) { - t.Errorf("confirmationTime empty string") - } } //GasOracle generates dynamic data. Best we can do is ensure all fields are populated @@ -26,28 +22,8 @@ func TestClient_GasOracle(t *testing.T) { gasPrice, err := api.GasOracle() noError(t, err, "api.GasOrcale") - if 0 == len(gasPrice.LastBlock) { - t.Errorf("gasPrice.LastBlock empty string") - } - - if 0 == len(gasPrice.SafeGasPrice) { - t.Errorf("gasPrice.SafeGasPrice empty string") - } - - if 0 == len(gasPrice.ProposeGasPrice) { - t.Errorf("gasPrice.ProposeGasPrice empty string") - } - - if 0 == len(gasPrice.FastGasPrice) { - t.Errorf("gasPrice.FastGasPrice empty string") - } - - if 0 == len(gasPrice.SuggestBaseFeeInGwei) { - t.Errorf("gasPrice.SuggestBaseFeeInGwei empty string") - } - if 0 == len(gasPrice.GasUsedRatio) { - t.Errorf("gasPrice.GasUsedRatio empty string") + t.Errorf("gasPrice.GasUsedRatio empty") } } diff --git a/response.go b/response.go index 9cd2570..b0517cd 100644 --- a/response.go +++ b/response.go @@ -7,7 +7,12 @@ package etherscan -import "encoding/json" +import ( + "encoding/json" + "fmt" + "strconv" + "strings" +) // Envelope is the carrier of nearly every response type Envelope struct { @@ -178,10 +183,62 @@ type Log struct { //GasPrices holds info for Gas Oracle queries //Gas Prices are returned in Gwei type GasPrices struct { - LastBlock string - SafeGasPrice string - ProposeGasPrice string - FastGasPrice string - SuggestBaseFeeInGwei string `json:"suggestBaseFee"` - GasUsedRatio string `json:"gasUsedRatio"` + LastBlock int + SafeGasPrice float64 + ProposeGasPrice float64 + FastGasPrice float64 + SuggestBaseFeeInGwei float64 `json:"suggestBaseFee"` + GasUsedRatio []float64 `json:"gasUsedRatio"` +} + +func (gp *GasPrices) UnmarshalJSON(data []byte) error { + _gp := struct { + LastBlock string + SafeGasPrice string + ProposeGasPrice string + FastGasPrice string + SuggestBaseFeeInGwei string `json:"suggestBaseFee"` + GasUsedRatio string `json:"gasUsedRatio"` + }{} + + err := json.Unmarshal(data, &_gp) + if err != nil { + return err + } + + gp.LastBlock, err = strconv.Atoi(_gp.LastBlock) + if err != nil { + return fmt.Errorf("Unable to convert LastBlock %s to int: %w", _gp.LastBlock, err) + } + + gp.SafeGasPrice, err = strconv.ParseFloat(_gp.SafeGasPrice, 64) + if err != nil { + return fmt.Errorf("Unable to convert SafeGasPrice %s to float64: %w", _gp.SafeGasPrice, err) + } + + gp.ProposeGasPrice, err = strconv.ParseFloat(_gp.ProposeGasPrice, 64) + if err != nil { + return fmt.Errorf("Unable to convert ProposeGasPrice %s to float64: %w", _gp.ProposeGasPrice, err) + } + + gp.FastGasPrice, err = strconv.ParseFloat(_gp.FastGasPrice, 64) + if err != nil { + return fmt.Errorf("Unable to convert FastGasPrice %s to float64: %w", _gp.FastGasPrice, err) + } + + gp.SuggestBaseFeeInGwei, err = strconv.ParseFloat(_gp.SuggestBaseFeeInGwei, 64) + if err != nil { + return fmt.Errorf("Unable to convert SuggestBaseFeeInGwei %s to float64: %w", _gp.SuggestBaseFeeInGwei, err) + } + + gasRatios := strings.Split(_gp.GasUsedRatio, ",") + gp.GasUsedRatio = make([]float64, len(gasRatios)) + for i, gasRatio := range gasRatios { + gp.GasUsedRatio[i], err = strconv.ParseFloat(gasRatio, 64) + if err != nil { + return fmt.Errorf("Unable to convert gasRatio %s to float64: %w", gasRatio, err) + } + } + + return nil } From cf99fda6d3e7b8c40e2f2e93ed29499c0fed52af Mon Sep 17 00:00:00 2001 From: Dodonov Pavel Date: Mon, 12 Dec 2022 17:09:45 +0400 Subject: [PATCH 03/12] erc1155 tx struct and ERC1155Transfers added --- account.go | 24 ++++++++++++++++++++++++ account_e2e_test.go | 18 ++++++++++++++++++ response.go | 28 ++++++++++++++++++++++++++-- 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/account.go b/account.go index 5c20824..e6a9ac1 100644 --- a/account.go +++ b/account.go @@ -131,6 +131,30 @@ func (c *Client) ERC721Transfers(contractAddress, address *string, startBlock *i return } +// ERC1155Transfers get a list of "erc1155 - token transfer events" by +// contract address and/or from/to address. +// +// leave undesired condition to nil. +func (c *Client) ERC1155Transfers(contractAddress, address *string, startBlock *int, endBlock *int, page int, offset int, desc bool) (txs []ERC1155Transfer, err error) { + param := M{ + "page": page, + "offset": offset, + } + compose(param, "contractaddress", contractAddress) + compose(param, "address", address) + compose(param, "startblock", startBlock) + compose(param, "endblock", endBlock) + + if desc { + param["sort"] = "desc" + } else { + param["sort"] = "asc" + } + + err = c.call("account", "token1155tx", param, &txs) + return +} + // BlocksMinedByAddress gets list of blocks mined by address func (c *Client) BlocksMinedByAddress(address string, page int, offset int) (mined []MinedBlock, err error) { param := M{ diff --git a/account_e2e_test.go b/account_e2e_test.go index ac9094b..f7688e3 100644 --- a/account_e2e_test.go +++ b/account_e2e_test.go @@ -169,3 +169,21 @@ func TestClient_ERC721Transfers(t *testing.T) { t.Errorf("got txs length %v, want %v", len(txs), wantLen) } } + +func TestClient_ERC1155Transfers(t *testing.T) { + const ( + wantLen = 1 + ) + + var a, b = 128135633, 1802672 + var contract, address = "0x3edf71a31b80Ff6a45Fdb0858eC54DE98dF047AA", "0x4b986EF20Bb83532911521FB4F6F5605122a0721" + txs, err := api.ERC1155Transfers(&contract, &address, &b, &a, 0, 0, true) + noError(t, err, "api.ERC721Transfers") + + j, _ := json.MarshalIndent(txs, "", " ") + fmt.Printf("%s\n", j) + + if len(txs) != wantLen { + t.Errorf("got txs length %v, want %v", len(txs), wantLen) + } +} diff --git a/response.go b/response.go index b0517cd..d096aef 100644 --- a/response.go +++ b/response.go @@ -116,6 +116,30 @@ type ERC721Transfer struct { Confirmations int `json:"confirmations,string"` } +// ERC1155Transfer holds info from ERC1155 token transfer event query +type ERC1155Transfer struct { + BlockNumber int `json:"blockNumber,string"` + TimeStamp Time `json:"timeStamp"` + Hash string `json:"hash"` + Nonce int `json:"nonce,string"` + BlockHash string `json:"blockHash"` + From string `json:"from"` + ContractAddress string `json:"contractAddress"` + To string `json:"to"` + TokenID *BigInt `json:"tokenID"` + TokenName string `json:"tokenName"` + TokenSymbol string `json:"tokenSymbol"` + TokenDecimal uint8 `json:"tokenDecimal,string"` + TokenValue uint8 `json:"tokenValue,string"` + TransactionIndex int `json:"transactionIndex,string"` + Gas int `json:"gas,string"` + GasPrice *BigInt `json:"gasPrice"` + GasUsed int `json:"gasUsed,string"` + CumulativeGasUsed int `json:"cumulativeGasUsed,string"` + Input string `json:"input"` + Confirmations int `json:"confirmations,string"` +} + // MinedBlock holds info from query for mined block by address type MinedBlock struct { BlockNumber int `json:"blockNumber,string"` @@ -180,8 +204,8 @@ type Log struct { Removed bool `json:"removed"` } -//GasPrices holds info for Gas Oracle queries -//Gas Prices are returned in Gwei +// GasPrices holds info for Gas Oracle queries +// Gas Prices are returned in Gwei type GasPrices struct { LastBlock int SafeGasPrice float64 From 5b06ae309b15fd8f7c57c86826e71e939ed0be7c Mon Sep 17 00:00:00 2001 From: Dodonov Pavel Date: Mon, 12 Dec 2022 18:22:27 +0400 Subject: [PATCH 04/12] go mod replace --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 3e16f01..4083741 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/nanmu42/etherscan-api +module github.com/Pashteto/etherscan-api go 1.13 From f615701789b83f9e2e33270c8f99d519bb0514cc Mon Sep 17 00:00:00 2001 From: Dodonov Pavel Date: Mon, 12 Dec 2022 18:22:27 +0400 Subject: [PATCH 05/12] go mod replace --- go.mod | 4 +++- go.sum | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 3e16f01..b6871ed 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,6 @@ module github.com/nanmu42/etherscan-api go 1.13 -require github.com/google/go-cmp v0.5.7 // indirect +require github.com/google/go-cmp v0.5.7 + +replace github.com/nanmu42/etherscan-api => github.com/Pashteto/etherscan-api v0.0.0-20221212135811-6be05412311b diff --git a/go.sum b/go.sum index a365b08..a6ca3a4 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,4 @@ github.com/google/go-cmp v0.5.7 h1:81/ik6ipDQS2aGcBfIN5dHDB36BwrStyeAQquSYCV4o= github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= From d7387da310f0d76973d3179acc66c194c3b3266f Mon Sep 17 00:00:00 2001 From: Dodonov Pavel Date: Mon, 12 Dec 2022 18:40:01 +0400 Subject: [PATCH 06/12] go mod replace --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index b6871ed..8978d26 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/nanmu42/etherscan-api +module github.com/Pashteto/etherscan-api go 1.13 From 0e26ffd32b36d75c914e7a6857ea9da64f4bc02f Mon Sep 17 00:00:00 2001 From: Dodonov Pavel Date: Mon, 12 Dec 2022 18:44:47 +0400 Subject: [PATCH 07/12] go mod replace --- go.mod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index 8978d26..9ecb506 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ -module github.com/Pashteto/etherscan-api +module github.com/nanmu42/etherscan-api -go 1.13 +go 1.19 require github.com/google/go-cmp v0.5.7 From 6a8ea3a978b5db6796f814d1cbf2d5e7cac0b568 Mon Sep 17 00:00:00 2001 From: Dodonov Pavel Date: Mon, 12 Dec 2022 18:56:57 +0400 Subject: [PATCH 08/12] go mod replace --- go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 9ecb506..dedf8f1 100644 --- a/go.mod +++ b/go.mod @@ -4,4 +4,4 @@ go 1.19 require github.com/google/go-cmp v0.5.7 -replace github.com/nanmu42/etherscan-api => github.com/Pashteto/etherscan-api v0.0.0-20221212135811-6be05412311b +replace github.com/nanmu42/etherscan-api v1.9.0 => github.com/Pashteto/etherscan-api v1.0.0 From 890f9df7fab134dc25e3a08d1f83007df6fcd6b3 Mon Sep 17 00:00:00 2001 From: Dodonov Pavel Date: Tue, 13 Dec 2022 13:32:36 +0400 Subject: [PATCH 09/12] go mod replace --- go.mod | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/go.mod b/go.mod index dedf8f1..be10176 100644 --- a/go.mod +++ b/go.mod @@ -1,7 +1,5 @@ -module github.com/nanmu42/etherscan-api +module github.com/Pashteto/etherscan-api go 1.19 require github.com/google/go-cmp v0.5.7 - -replace github.com/nanmu42/etherscan-api v1.9.0 => github.com/Pashteto/etherscan-api v1.0.0 From b8dd9f49cd38d336b0b7565c8cbc22c010585f8f Mon Sep 17 00:00:00 2001 From: Dodonov Pavel Date: Tue, 13 Dec 2022 14:07:11 +0400 Subject: [PATCH 10/12] go mod replace --- go.mod | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/go.mod b/go.mod index be10176..5837de6 100644 --- a/go.mod +++ b/go.mod @@ -1,5 +1,5 @@ -module github.com/Pashteto/etherscan-api +module github.com/nanmu42/etherscan-api -go 1.19 +go 1.13 require github.com/google/go-cmp v0.5.7 From 07a1d97ed94a5c900a51eed10dac08f70b52096d Mon Sep 17 00:00:00 2001 From: LI Zhennan Date: Tue, 14 Feb 2023 11:05:29 +0800 Subject: [PATCH 11/12] Create FUNDING.yml --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..71b970a --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: nanmu42 From 67da1ed8aa6e01a995b0f1b21eb1cdb0c74099d5 Mon Sep 17 00:00:00 2001 From: coolestowl <97215481+coolestowl@users.noreply.github.com> Date: Mon, 11 Mar 2024 11:44:46 +0800 Subject: [PATCH 12/12] Update response.go NormalTx add fields --- response.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/response.go b/response.go index d096aef..5c418cf 100644 --- a/response.go +++ b/response.go @@ -50,6 +50,8 @@ type NormalTx struct { CumulativeGasUsed int `json:"cumulativeGasUsed,string"` GasUsed int `json:"gasUsed,string"` Confirmations int `json:"confirmations,string"` + FunctionName string `json:"functionName"` + MethodId string `json:"methodId"` } // InternalTx holds info from internal tx query