-
Notifications
You must be signed in to change notification settings - Fork 2
/
etherego.go
153 lines (126 loc) · 3.52 KB
/
etherego.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
package etherego
import (
"context"
"crypto/ecdsa"
"math/big"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
)
var (
WeiEth = big.NewInt(0).Exp(big.NewInt(10), big.NewInt(18), nil)
GweiEth = big.NewInt(0).Exp(big.NewInt(10), big.NewInt(9), nil)
)
// Default gas limit in ethereum network
const EtherDefaultGasLimit uint64 = 21_000
// Accounts it's a mapping of account address to account private key
type Accounts map[string]string
// ETHClient wraps *ethclient.Client
type ETHClient struct {
// *ethclient.Client composition
*ethclient.Client
Accounts *Accounts
}
// New ETHClient
func New(addr string, accs *Accounts) (*ETHClient, error) {
client, err := ethclient.Dial(addr)
if err != nil {
return nil, err
}
return ÐClient{client, accs}, nil
}
// TransactionFrom returns transaction sender address
func (e *ETHClient) TransactionFrom(ctx context.Context, tx *types.Transaction) (string, error) {
chainID, err := e.ChainID(ctx)
if err != nil {
return "", nil
}
msg, err := tx.AsMessage(types.NewEIP155Signer(chainID), tx.GasFeeCap())
if err != nil {
return "", nil
}
return msg.From().Hex(), nil
}
// BlocksRange returns blocks by range
func (e *ETHClient) BlocksRange(ctx context.Context, beg, end int) ([]*types.Block, error) {
blocks := make([]*types.Block, 0)
for i := beg; i < end; i++ {
b, err := e.BlockByNumber(ctx, big.NewInt(int64(i)))
if err != nil {
// maybe return blocks, err if exception occured on non zero block?
return nil, err
}
blocks = append(blocks, b)
}
return blocks, nil
}
// TransferTokens transfer tokens from one account to another, and returns transaction hash
func (e *ETHClient) TransferTokens(ctx context.Context,
fromAddr, toAddr string, gasLimit, ethVal *big.Int) (txHash common.Hash, err error) {
pk := (*e.Accounts)[fromAddr][2:]
privateKey, err := crypto.HexToECDSA(pk)
if err != nil {
return
}
pubKey := privateKey.Public()
publicKeyECDSA, ok := pubKey.(*ecdsa.PublicKey)
if !ok {
err = ErrKeyNotECDSA
return
}
fromAddress := crypto.PubkeyToAddress(*publicKeyECDSA)
dstAddr := common.HexToAddress(toAddr)
nonce, err := e.PendingNonceAt(ctx, fromAddress)
if err != nil {
return
}
gasPrice, err := e.SuggestGasPrice(ctx)
if err != nil {
return
}
if gasLimit == nil {
gasLimit = big.NewInt(0).SetUint64(EtherDefaultGasLimit)
}
tx := types.NewTransaction(nonce, dstAddr, ethVal, gasLimit.Uint64(), gasPrice, nil) // add message
chainID, err := e.ChainID(ctx)
if err != nil {
return
}
signedTx, err := types.SignTx(tx, types.NewEIP155Signer(chainID), privateKey)
if err != nil {
return
}
err = e.SendTransaction(ctx, signedTx)
if err != nil {
return
}
return signedTx.Hash(), nil
}
// SubscribeNewBlocks subscribes to new block creation events.
// Works only with (ws/wss):// connection scheme
func (e *ETHClient) SubscribeNewBlocks(ctx context.Context) (
<-chan *types.Header, <-chan error, error) {
var (
headsChan = make(chan *types.Header)
outChan = make(chan *types.Header)
errChan = make(chan error)
sub, err = e.SubscribeNewHead(ctx, headsChan)
)
if err != nil {
return nil, nil, err
}
// event listener goroutine
go func(sub ethereum.Subscription, ch chan *types.Header) {
for {
select {
case err = <-sub.Err():
errChan <- err
case header := <-headsChan:
outChan <- header
}
}
}(sub, headsChan)
return outChan, errChan, nil
}