/
client.go
167 lines (144 loc) · 4.25 KB
/
client.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
154
155
156
157
158
159
160
161
162
163
164
165
166
167
package blockchain
import (
"context"
"math/big"
"net/url"
"time"
ablbind "github.com/airbloc/airbloc-go/bind"
"github.com/airbloc/airbloc-go/pkg/kas"
"github.com/airbloc/logger"
"github.com/klaytn/klaytn/accounts/abi/bind"
"github.com/klaytn/klaytn/blockchain/types"
klayClient "github.com/klaytn/klaytn/client"
"github.com/klaytn/klaytn/common"
"github.com/pkg/errors"
)
var log = logger.New("klaytn")
// getChainName returns chain name by chain ID (network ID), according to EIP-155.
func getChainName(cid *big.Int) string {
switch cid.String() {
case "1":
return "Ethereum Main"
case "3":
return "Ethereum Ropsten Test"
case "4":
return "Ethereum Rinkeby Test"
case "1000":
return "Klaytn Aspen Test"
case "1001":
return "Klaytn Baobab Test"
case "8217":
return "Klaytn Main"
}
return "EVM Private"
}
type clientData struct{ *klayClient.Client }
type Client struct {
clientData
}
func NewClient(ctx context.Context, endpoint string) (*Client, error) {
if _, err := url.Parse(endpoint); err != nil {
return nil, errors.Wrapf(err, "invalid URL: %s", endpoint)
}
client, err := klayClient.Dial(endpoint)
if err != nil {
return nil, errors.Wrap(err, "dial klaytn api")
}
cid, err := client.NetworkID(ctx)
if err != nil {
return nil, errors.Wrap(err, "fetch network id")
}
log.Info("Using {} network", getChainName(cid))
return &Client{
clientData: clientData{client},
}, nil
}
func NewClientWithKAS(ctx context.Context, cfg kas.Config) (*Client, error) {
client, err := kas.Dial(cfg)
if err != nil {
return nil, errors.Wrap(err, "dial klaytn api")
}
cid, err := client.NetworkID(ctx)
if err != nil {
return nil, errors.Wrap(err, "fetch network id")
}
log.Info("Using {} network", getChainName(cid))
return &Client{
clientData: clientData{client},
}, nil
}
func (c *Client) Client() *klayClient.Client {
return c.clientData.Client
}
func (c *Client) Deployment(string) (ablbind.Deployment, bool) {
return ablbind.Deployment{}, false // use default information (mainnet)
}
func (c *Client) Transactor(ctx context.Context, opts ...*ablbind.TransactOpts) *ablbind.TransactOpts {
return ablbind.MergeTxOpts(ctx, nil, opts...)
}
func (c *Client) waitMined(ctx context.Context, hash common.Hash) (*types.Receipt, error) {
queryTicker := time.NewTicker(time.Second)
defer queryTicker.Stop()
for {
receipt, err := c.TransactionReceipt(ctx, hash)
if receipt != nil {
return receipt, err
}
if err != nil {
log.Debug("Receipt retrieval failed", "err", err)
} else {
log.Debug("Transaction not yet mined")
}
// Wait for the next round.
select {
case <-ctx.Done():
return nil, ctx.Err()
case <-queryTicker.C:
}
}
}
func (c *Client) WaitMinedWithHash(ctx context.Context, hash common.Hash) (*types.Receipt, error) {
receipt, err := c.waitMined(ctx, hash)
if err != nil {
return nil, err
}
if receipt.Status == types.ReceiptStatusFailed {
return nil, errors.New("tx failed")
}
return receipt, err
}
// WaitMined waits until transcaction created.
func (c *Client) WaitMined(ctx context.Context, tx *types.Transaction) (*types.Receipt, error) {
return c.WaitMinedWithHash(ctx, tx.Hash())
}
// WaitDeployed waits until contract created.
func (c *Client) WaitDeployed(ctx context.Context, tx *types.Transaction) (*types.Receipt, error) {
txType := tx.Type()
if txType != types.TxTypeSmartContractDeploy &&
txType != types.TxTypeFeeDelegatedSmartContractDeploy &&
txType != types.TxTypeFeeDelegatedSmartContractDeployWithRatio {
return nil, errors.New("tx is not contract creation")
}
receipt, err := c.WaitMined(ctx, tx)
if err != nil {
return nil, err
}
if receipt.ContractAddress == (common.Address{}) {
return nil, errors.New("zero address")
}
code, err := c.CodeAt(ctx, receipt.ContractAddress, nil)
if err == nil && len(code) == 0 {
err = bind.ErrNoCodeAfterDeploy
}
return receipt, err
}
func (c *Client) SendTransaction(ctx context.Context, tx *types.Transaction) (*types.Receipt, error) {
hash, err := c.SendRawTransaction(ctx, tx)
if err != nil {
return nil, err
}
return c.WaitMinedWithHash(ctx, hash)
}
func (c *Client) MakeTransaction(opts *ablbind.TransactOpts, contract *common.Address, input []byte) (*types.Transaction, error) {
return opts.MakeTransaction(c, contract, input)
}