-
Notifications
You must be signed in to change notification settings - Fork 375
/
client_txs.go
127 lines (109 loc) · 3.41 KB
/
client_txs.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
package gnoclient
import (
"github.com/gnolang/gno/gno.land/pkg/sdk/vm"
"github.com/gnolang/gno/tm2/pkg/amino"
ctypes "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types"
"github.com/gnolang/gno/tm2/pkg/errors"
"github.com/gnolang/gno/tm2/pkg/std"
)
// CallCfg contains configuration options for executing a contract call.
type CallCfg struct {
PkgPath string // Package path
FuncName string // Function name
Args []string // Function arguments
GasFee string // Gas fee
GasWanted int64 // Gas wanted
Send string // Send amount
AccountNumber uint64 // Account number
SequenceNumber uint64 // Sequence number
Memo string // Memo
}
// Call executes a contract call on the blockchain.
func (c *Client) Call(cfg CallCfg) (*ctypes.ResultBroadcastTxCommit, error) {
// Validate required client fields.
if err := c.validateSigner(); err != nil {
return nil, errors.Wrap(err, "validate signer")
}
if err := c.validateRPCClient(); err != nil {
return nil, errors.Wrap(err, "validate RPC client")
}
pkgPath := cfg.PkgPath
funcName := cfg.FuncName
args := cfg.Args
gasWanted := cfg.GasWanted
gasFee := cfg.GasFee
send := cfg.Send
sequenceNumber := cfg.SequenceNumber
accountNumber := cfg.AccountNumber
memo := cfg.Memo
// Validate config.
if pkgPath == "" {
return nil, errors.New("missing PkgPath")
}
if funcName == "" {
return nil, errors.New("missing FuncName")
}
// Parse send amount.
sendCoins, err := std.ParseCoins(send)
if err != nil {
return nil, errors.Wrap(err, "parsing send coins")
}
// Parse gas wanted & fee.
gasFeeCoins, err := std.ParseCoin(gasFee)
if err != nil {
return nil, errors.Wrap(err, "parsing gas fee coin")
}
caller := c.Signer.Info().GetAddress()
// Construct message & transaction and marshal.
msg := vm.MsgCall{
Caller: caller,
Send: sendCoins,
PkgPath: pkgPath,
Func: funcName,
Args: args,
}
tx := std.Tx{
Msgs: []std.Msg{msg},
Fee: std.NewFee(gasWanted, gasFeeCoins),
Signatures: nil,
Memo: memo,
}
return c.signAndBroadcastTxCommit(tx, accountNumber, sequenceNumber)
}
// signAndBroadcastTxCommit signs a transaction and broadcasts it, returning the result.
func (c Client) signAndBroadcastTxCommit(tx std.Tx, accountNumber, sequenceNumber uint64) (*ctypes.ResultBroadcastTxCommit, error) {
caller := c.Signer.Info().GetAddress()
if sequenceNumber == 0 || accountNumber == 0 {
account, _, err := c.QueryAccount(caller)
if err != nil {
return nil, errors.Wrap(err, "query account")
}
accountNumber = account.AccountNumber
sequenceNumber = account.Sequence
}
signCfg := SignCfg{
UnsignedTX: tx,
SequenceNumber: sequenceNumber,
AccountNumber: accountNumber,
}
signedTx, err := c.Signer.Sign(signCfg)
if err != nil {
return nil, errors.Wrap(err, "sign")
}
bz, err := amino.Marshal(signedTx)
if err != nil {
return nil, errors.Wrap(err, "marshaling tx binary bytes")
}
bres, err := c.RPCClient.BroadcastTxCommit(bz)
if err != nil {
return nil, errors.Wrap(err, "broadcasting bytes")
}
if bres.CheckTx.IsErr() {
return bres, errors.Wrap(bres.CheckTx.Error, "check transaction failed: log:%s", bres.CheckTx.Log)
}
if bres.DeliverTx.IsErr() {
return bres, errors.Wrap(bres.DeliverTx.Error, "deliver transaction failed: log:%s", bres.DeliverTx.Log)
}
return bres, nil
}
// TODO: Add more functionality, examples, and unit tests.