@@ -35,11 +35,13 @@ import (
35
35
"github.com/ethereum/go-ethereum/common"
36
36
"github.com/ethereum/go-ethereum/common/hexutil"
37
37
"github.com/ethereum/go-ethereum/core/types"
38
+ "golang.org/x/time/rate"
38
39
)
39
40
40
- // CallInterval is the duration between etherscan requests.
41
+ // CallsPerSec is thenumber of etherscanr equests allowed
42
+ // per second.
41
43
// Etherscan rate limits to one request per 0.2 seconds.
42
- var CallInterval = 260 * time . Millisecond
44
+ var CallsPerSec = 3.8
43
45
44
46
const apiKey = "X3AFAGQT2QCAFTFPIH9VJY88H9PIQ2UWP7"
45
47
@@ -50,17 +52,22 @@ const ERC20GasErr = "insufficient funds for gas * price + value"
50
52
type EtherScan struct {
51
53
url string
52
54
httpClient * http.Client
55
+ limiter * rate.Limiter
53
56
}
54
57
55
58
// NewEtherScan creates a new instance of EtherScan.
56
59
func NewEtherScan (url string , httpClient * http.Client ) * EtherScan {
57
60
return & EtherScan {
58
61
url : url ,
59
62
httpClient : httpClient ,
63
+ limiter : rate .NewLimiter (rate .Limit (CallsPerSec ), 1 ),
60
64
}
61
65
}
62
66
63
- func (etherScan * EtherScan ) call (params url.Values , result interface {}) error {
67
+ func (etherScan * EtherScan ) call (ctx context.Context , params url.Values , result interface {}) error {
68
+ if err := etherScan .limiter .Wait (ctx ); err != nil {
69
+ return errp .WithStack (err )
70
+ }
64
71
params .Set ("apikey" , apiKey )
65
72
response , err := etherScan .httpClient .Get (etherScan .url + "?" + params .Encode ())
66
73
if err != nil {
@@ -323,7 +330,7 @@ func (etherScan *EtherScan) Transactions(
323
330
result := struct {
324
331
Result []* Transaction
325
332
}{}
326
- if err := etherScan .call (params , & result ); err != nil {
333
+ if err := etherScan .call (context . TODO (), params , & result ); err != nil {
327
334
return nil , err
328
335
}
329
336
isERC20 := erc20Token != nil
@@ -338,7 +345,7 @@ func (etherScan *EtherScan) Transactions(
338
345
resultInternal := struct {
339
346
Result []* Transaction
340
347
}{}
341
- if err := etherScan .call (params , & resultInternal ); err != nil {
348
+ if err := etherScan .call (context . TODO (), params , & resultInternal ); err != nil {
342
349
return nil , err
343
350
}
344
351
var err error
@@ -353,7 +360,7 @@ func (etherScan *EtherScan) Transactions(
353
360
354
361
// ----- RPC node proxy methods follow
355
362
356
- func (etherScan * EtherScan ) rpcCall (params url.Values , result interface {}) error {
363
+ func (etherScan * EtherScan ) rpcCall (ctx context. Context , params url.Values , result interface {}) error {
357
364
params .Set ("module" , "proxy" )
358
365
359
366
var wrapped struct {
@@ -364,7 +371,7 @@ func (etherScan *EtherScan) rpcCall(params url.Values, result interface{}) error
364
371
} `json:"error"`
365
372
Result * json.RawMessage `json:"result"`
366
373
}
367
- if err := etherScan .call (params , & wrapped ); err != nil {
374
+ if err := etherScan .call (ctx , params , & wrapped ); err != nil {
368
375
return err
369
376
}
370
377
if wrapped .Error != nil {
@@ -389,7 +396,7 @@ func (etherScan *EtherScan) TransactionReceiptWithBlockNumber(
389
396
params .Set ("action" , "eth_getTransactionReceipt" )
390
397
params .Set ("txhash" , hash .Hex ())
391
398
var result * rpcclient.RPCTransactionReceipt
392
- if err := etherScan .rpcCall (params , & result ); err != nil {
399
+ if err := etherScan .rpcCall (ctx , params , & result ); err != nil {
393
400
return nil , err
394
401
}
395
402
return result , nil
@@ -402,7 +409,7 @@ func (etherScan *EtherScan) TransactionByHash(
402
409
params .Set ("action" , "eth_getTransactionByHash" )
403
410
params .Set ("txhash" , hash .Hex ())
404
411
var result rpcclient.RPCTransaction
405
- if err := etherScan .rpcCall (params , & result ); err != nil {
412
+ if err := etherScan .rpcCall (ctx , params , & result ); err != nil {
406
413
return nil , false , err
407
414
}
408
415
return & result .Transaction , result .BlockNumber == nil , nil
@@ -415,7 +422,7 @@ func (etherScan *EtherScan) BlockNumber(ctx context.Context) (*big.Int, error) {
415
422
params .Set ("tag" , "latest" )
416
423
params .Set ("boolean" , "false" )
417
424
var header * types.Header
418
- if err := etherScan .rpcCall (params , & header ); err != nil {
425
+ if err := etherScan .rpcCall (ctx , params , & header ); err != nil {
419
426
return nil , err
420
427
}
421
428
return header .Number , nil
@@ -434,7 +441,7 @@ func (etherScan *EtherScan) Balance(ctx context.Context, account common.Address)
434
441
params .Set ("action" , "balance" )
435
442
params .Set ("address" , account .Hex ())
436
443
params .Set ("tag" , "latest" )
437
- if err := etherScan .call (params , & result ); err != nil {
444
+ if err := etherScan .call (ctx , params , & result ); err != nil {
438
445
return nil , err
439
446
}
440
447
if result .Status != "1" {
@@ -461,7 +468,7 @@ func (etherScan *EtherScan) ERC20Balance(account common.Address, erc20Token *erc
461
468
params .Set ("address" , account .Hex ())
462
469
params .Set ("contractaddress" , erc20Token .ContractAddress ().Hex ())
463
470
params .Set ("tag" , "latest" )
464
- if err := etherScan .call (params , & result ); err != nil {
471
+ if err := etherScan .call (context . TODO (), params , & result ); err != nil {
465
472
return nil , err
466
473
}
467
474
if result .Status != "1" {
@@ -485,7 +492,7 @@ func (etherScan *EtherScan) CallContract(ctx context.Context, msg ethereum.CallM
485
492
panic ("not implemented" )
486
493
}
487
494
var result hexutil.Bytes
488
- if err := etherScan .rpcCall (params , & result ); err != nil {
495
+ if err := etherScan .rpcCall (ctx , params , & result ); err != nil {
489
496
return nil , err
490
497
}
491
498
return result , nil
@@ -515,7 +522,7 @@ func (etherScan *EtherScan) EstimateGas(ctx context.Context, msg ethereum.CallMs
515
522
callMsgParams (& params , msg )
516
523
517
524
var result hexutil.Uint64
518
- if err := etherScan .rpcCall (params , & result ); err != nil {
525
+ if err := etherScan .rpcCall (ctx , params , & result ); err != nil {
519
526
return 0 , err
520
527
}
521
528
return uint64 (result ), nil
@@ -528,7 +535,7 @@ func (etherScan *EtherScan) PendingNonceAt(ctx context.Context, account common.A
528
535
params .Set ("address" , account .Hex ())
529
536
params .Set ("tag" , "pending" )
530
537
var result hexutil.Uint64
531
- if err := etherScan .rpcCall (params , & result ); err != nil {
538
+ if err := etherScan .rpcCall (ctx , params , & result ); err != nil {
532
539
return 0 , err
533
540
}
534
541
return uint64 (result ), nil
@@ -544,15 +551,15 @@ func (etherScan *EtherScan) SendTransaction(ctx context.Context, tx *types.Trans
544
551
params := url.Values {}
545
552
params .Set ("action" , "eth_sendRawTransaction" )
546
553
params .Set ("hex" , hexutil .Encode (encodedTx ))
547
- return etherScan .rpcCall (params , nil )
554
+ return etherScan .rpcCall (ctx , params , nil )
548
555
}
549
556
550
557
// SuggestGasPrice implements rpc.Interface.
551
558
func (etherScan * EtherScan ) SuggestGasPrice (ctx context.Context ) (* big.Int , error ) {
552
559
params := url.Values {}
553
560
params .Set ("action" , "eth_gasPrice" )
554
561
var result hexutil.Big
555
- if err := etherScan .rpcCall (params , & result ); err != nil {
562
+ if err := etherScan .rpcCall (ctx , params , & result ); err != nil {
556
563
return nil , err
557
564
}
558
565
return (* big .Int )(& result ), nil
@@ -581,7 +588,7 @@ func (etherScan *EtherScan) FeeTargets(ctx context.Context) ([]*ethtypes.FeeTarg
581
588
params := url.Values {}
582
589
params .Set ("module" , "gastracker" )
583
590
params .Set ("action" , "gasoracle" )
584
- if err := etherScan .call (params , & result ); err != nil {
591
+ if err := etherScan .call (ctx , params , & result ); err != nil {
585
592
return nil , err
586
593
}
587
594
// Convert string fields to int64
0 commit comments