diff --git a/account.go b/account.go index 85d48fa..0effe2c 100644 --- a/account.go +++ b/account.go @@ -79,6 +79,14 @@ func (c *Client) InternalTxByAddress(address string, startBlock *int, endBlock * // contract address and/or from/to address. // // leave undesired condition to nil. +// +// Note on a Etherscan bug: +// Some ERC20 contract does not have valid decimals information in Etherscan. +// When that happens, TokenName, TokenSymbol are empty strings, +// and TokenDecimal is 0. +// +// More information can be found at: +// https://github.com/nanmu42/etherscan-api/issues/8 func (c *Client) ERC20Transfers(contractAddress, address *string, startBlock *int, endBlock *int, page int, offset int) (txs []ERC20Transfer, err error) { param := M{ "page": page, diff --git a/account_e2e_test.go b/account_e2e_test.go index c489697..fffd9fa 100644 --- a/account_e2e_test.go +++ b/account_e2e_test.go @@ -73,6 +73,7 @@ func TestClient_ERC20Transfers(t *testing.T) { const ( wantLen1 = 3 wantLen2 = 458 + wantLen3 = 2 ) var a, b = 3273004, 3328071 @@ -92,6 +93,17 @@ func TestClient_ERC20Transfers(t *testing.T) { if len(txs) != wantLen2 { t.Errorf("got txs length %v, want %v", len(txs), wantLen2) } + + // some ERC20 contract does not have valid decimals information in Etherscan, + // which brings errors like `json: invalid use of ,string struct tag, trying to unmarshal "" into uint8` + var specialContract = "0x5eac95ad5b287cf44e058dcf694419333b796123" + var specialStartHeight = 6024142 + var specialEndHeight = 6485274 + txs, err = api.ERC20Transfers(&specialContract, nil, &specialStartHeight, &specialEndHeight, 1, 500) + noError(t, err, "api.ERC20Transfers 2") + if len(txs) != wantLen3 { + t.Errorf("got txs length %v, want %v", len(txs), wantLen3) + } } func TestClient_BlocksMinedByAddress(t *testing.T) { diff --git a/client.go b/client.go index e7422cd..b981d59 100644 --- a/client.go +++ b/client.go @@ -15,6 +15,7 @@ import ( "net/http" "net/http/httputil" "net/url" + "time" ) // Client etherscan API client @@ -41,7 +42,9 @@ type Client struct { // please use pre-defined network value func New(network Network, APIKey string) *Client { return &Client{ - coon: &http.Client{}, + coon: &http.Client{ + Timeout: 30 * time.Second, + }, network: network, key: APIKey, baseURL: fmt.Sprintf(`https://%s.etherscan.io/api?`, network.SubDomain()), @@ -134,7 +137,12 @@ func (c *Client) call(module, action string, param map[string]interface{}, outco return } - err = json.Unmarshal(envelope.Result, outcome) + // workaround for missing tokenDecimal for some tokentx calls + if action == "tokentx" { + err = json.Unmarshal(bytes.Replace(envelope.Result, []byte(`"tokenDecimal":""`), []byte(`"tokenDecimal":"0"`), -1), outcome) + } else { + err = json.Unmarshal(envelope.Result, outcome) + } if err != nil { err = wrapErr(err, "json unmarshal outcome") return