代码拉取完成,页面将自动刷新
同步操作将从 楠木/etherscan-api 强制同步,此操作会覆盖自 Fork 仓库以来所做的任何修改,且无法恢复!!!
确定后同步将在后台操作,完成时将刷新页面,请耐心等待。
/*
* 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 (
"bytes"
"encoding/json"
"fmt"
"io"
"net/http"
"net/http/httputil"
"net/url"
"time"
)
// Client etherscan API client
// Clients are safe for concurrent use by multiple goroutines.
type Client struct {
coon *http.Client
key string
baseURL string
// Verbose when true, talks a lot
Verbose bool
// BeforeRequest runs before every client request, in the same goroutine.
// May be used in rate limit.
// Request will be aborted, if BeforeRequest returns non-nil err.
BeforeRequest func(module, action string, param map[string]interface{}) error
// AfterRequest runs after every client request, even when there is an error.
AfterRequest func(module, action string, param map[string]interface{}, outcome interface{}, requestErr error)
}
// New initialize a new etherscan API client
// please use pre-defined network value
func New(network Network, APIKey string) *Client {
return NewCustomized(Customization{
Timeout: 30 * time.Second,
Key: APIKey,
BaseURL: fmt.Sprintf(`https://%s.etherscan.io/api?`, network.SubDomain()),
})
}
// Customization is used in NewCustomized()
type Customization struct {
// Timeout for API call
Timeout time.Duration
// API key applied from Etherscan
Key string
// Base URL like `https://api.etherscan.io/api?`
BaseURL string
// When true, talks a lot
Verbose bool
// HTTP Client to be used. Specifying this value will ignore the Timeout value set
// Set your own timeout.
Client *http.Client
// BeforeRequest runs before every client request, in the same goroutine.
// May be used in rate limit.
// Request will be aborted, if BeforeRequest returns non-nil err.
BeforeRequest func(module, action string, param map[string]interface{}) error
// AfterRequest runs after every client request, even when there is an error.
AfterRequest func(module, action string, param map[string]interface{}, outcome interface{}, requestErr error)
}
// NewCustomized initialize a customized API client,
// useful when calling against etherscan-family API like BscScan.
func NewCustomized(config Customization) *Client {
var httpClient *http.Client
if config.Client != nil {
httpClient = config.Client
} else {
httpClient = &http.Client{
Timeout: config.Timeout,
}
}
return &Client{
coon: httpClient,
key: config.Key,
baseURL: config.BaseURL,
Verbose: config.Verbose,
BeforeRequest: config.BeforeRequest,
AfterRequest: config.AfterRequest,
}
}
// call does almost all the dirty work.
func (c *Client) call(module, action string, param map[string]interface{}, outcome interface{}) (err error) {
// fire hooks if in need
if c.BeforeRequest != nil {
err = c.BeforeRequest(module, action, param)
if err != nil {
err = wrapErr(err, "beforeRequest")
return
}
}
if c.AfterRequest != nil {
defer c.AfterRequest(module, action, param, outcome, err)
}
// recover if there shall be an panic
defer func() {
if r := recover(); r != nil {
err = fmt.Errorf("[ouch! panic recovered] please report this with what you did and what you expected, panic detail: %v", r)
}
}()
req, err := http.NewRequest(http.MethodGet, c.craftURL(module, action, param), http.NoBody)
if err != nil {
err = wrapErr(err, "http.NewRequest")
return
}
req.Header.Set("User-Agent", "etherscan-api(Go)")
req.Header.Set("Content-Type", "application/json; charset=utf-8")
if c.Verbose {
var reqDump []byte
reqDump, err = httputil.DumpRequestOut(req, false)
if err != nil {
err = wrapErr(err, "verbose mode req dump failed")
return
}
fmt.Printf("\n%s\n", reqDump)
defer func() {
if err != nil {
fmt.Printf("[Error] %v\n", err)
}
}()
}
res, err := c.coon.Do(req)
if err != nil {
err = wrapErr(err, "sending request")
return
}
defer res.Body.Close()
if c.Verbose {
var resDump []byte
resDump, err = httputil.DumpResponse(res, true)
if err != nil {
err = wrapErr(err, "verbose mode res dump failed")
return
}
fmt.Printf("%s\n", resDump)
}
var content bytes.Buffer
if _, err = io.Copy(&content, res.Body); err != nil {
err = wrapErr(err, "reading response")
return
}
if res.StatusCode != http.StatusOK {
err = fmt.Errorf("response status %v %s, response body: %s", res.StatusCode, res.Status, content.String())
return
}
var envelope Envelope
err = json.Unmarshal(content.Bytes(), &envelope)
if err != nil {
err = wrapErr(err, "json unmarshal envelope")
return
}
if envelope.Status != 1 {
err = fmt.Errorf("etherscan server: %s", envelope.Message)
return
}
// 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
}
return
}
// craftURL returns desired URL via param provided
func (c *Client) craftURL(module, action string, param map[string]interface{}) (URL string) {
q := url.Values{
"module": []string{module},
"action": []string{action},
"apikey": []string{c.key},
}
for k, v := range param {
q[k] = extractValue(v)
}
URL = c.baseURL + q.Encode()
return
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。