-
Notifications
You must be signed in to change notification settings - Fork 7
/
wallet.go
207 lines (176 loc) · 5.43 KB
/
wallet.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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
package wallet
import (
"fmt"
sdkclient "github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/client/tx"
"github.com/cosmos/cosmos-sdk/crypto/hd"
"github.com/cosmos/cosmos-sdk/crypto/keys/secp256k1"
cryptotypes "github.com/cosmos/cosmos-sdk/crypto/types"
sdk "github.com/cosmos/cosmos-sdk/types"
"github.com/cosmos/cosmos-sdk/types/bech32"
"github.com/cosmos/cosmos-sdk/types/tx/signing"
authsigning "github.com/cosmos/cosmos-sdk/x/auth/signing"
authtypes "github.com/cosmos/cosmos-sdk/x/auth/types"
"github.com/desmos-labs/cosmos-go-wallet/client"
"github.com/desmos-labs/cosmos-go-wallet/types"
)
// Wallet represents a Cosmos wallet that should be used to create and send transactions to the chain
type Wallet struct {
privKey cryptotypes.PrivKey
TxConfig sdkclient.TxConfig
Client *client.Client
}
// NewWallet allows to build a new Wallet instance
func NewWallet(accountCfg *types.AccountConfig, client *client.Client, txConfig sdkclient.TxConfig) (*Wallet, error) {
// Get the private types
algo := hd.Secp256k1
derivedPriv, err := algo.Derive()(accountCfg.Mnemonic, "", accountCfg.HDPath)
if err != nil {
return nil, err
}
return &Wallet{
privKey: algo.Generate()(derivedPriv),
TxConfig: txConfig,
Client: client,
}, nil
}
// AccAddress returns the address of the account that is going to be used to sign the transactions
func (w *Wallet) AccAddress() string {
bech32Addr, err := bech32.ConvertAndEncode(w.Client.GetAccountPrefix(), w.privKey.PubKey().Address())
if err != nil {
panic(err)
}
return bech32Addr
}
// BroadcastTxAsync creates and signs a transaction with the provided messages and fees,
// then broadcasts it using the async method
func (w *Wallet) BroadcastTxAsync(data *types.TransactionData) (*sdk.TxResponse, error) {
builder, err := w.BuildTx(data)
if err != nil {
return nil, err
}
return w.Client.BroadcastTxAsync(builder.GetTx())
}
// BroadcastTxSync creates and signs a transaction with the provided messages and fees,
// then broadcasts it using the sync method
func (w *Wallet) BroadcastTxSync(data *types.TransactionData) (*sdk.TxResponse, error) {
builder, err := w.BuildTx(data)
if err != nil {
return nil, err
}
return w.Client.BroadcastTxSync(builder.GetTx())
}
// BroadcastTxCommit creates and signs a transaction with the provided messages and fees,
// then broadcasts it using the commit method
func (w *Wallet) BroadcastTxCommit(data *types.TransactionData) (*sdk.TxResponse, error) {
builder, err := w.BuildTx(data)
if err != nil {
return nil, err
}
return w.Client.BroadcastTxCommit(builder.GetTx())
}
func (w *Wallet) BuildTx(data *types.TransactionData) (sdkclient.TxBuilder, error) {
// Get the account
account, err := w.Client.GetAccount(w.AccAddress())
if err != nil {
return nil, fmt.Errorf("error while getting the account from the chain: %s", err)
}
// Set account sequence
if data.Sequence != nil {
account.SetSequence(*data.Sequence)
}
// Build the transaction
builder := w.TxConfig.NewTxBuilder()
if data.Memo != "" {
builder.SetMemo(data.Memo)
}
if data.FeeGranter != nil {
builder.SetFeeGranter(data.FeeGranter)
}
if len(data.Messages) == 0 {
return nil, fmt.Errorf("error while building a transaction with no messages")
}
err = builder.SetMsgs(data.Messages...)
if err != nil {
return nil, err
}
gasLimit := data.GasLimit
if data.GasAuto {
adjusted, err := w.simulateTx(account, builder)
if err != nil {
return nil, err
}
gasLimit = adjusted
}
feeAmount := data.FeeAmount
if data.FeeAuto {
// Compute the fee amount based on the gas limit and the gas price
feeAmount = w.Client.GetFees(int64(gasLimit))
}
// Set the new gas and fee
builder.SetGasLimit(gasLimit)
builder.SetFeeAmount(feeAmount)
// Set an empty signature first
sigData := signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_DIRECT,
}
sig := signing.SignatureV2{
PubKey: w.privKey.PubKey(),
Data: &sigData,
Sequence: account.GetSequence(),
}
err = builder.SetSignatures(sig)
if err != nil {
return nil, err
}
chainID, err := w.Client.GetChainID()
if err != nil {
return nil, err
}
// Sign the transaction with the private key
sig, err = tx.SignWithPrivKey(
signing.SignMode_SIGN_MODE_DIRECT,
authsigning.SignerData{
ChainID: chainID,
AccountNumber: account.GetAccountNumber(),
Sequence: account.GetSequence(),
},
builder,
w.privKey,
w.TxConfig,
account.GetSequence(),
)
if err != nil {
return nil, err
}
err = builder.SetSignatures(sig)
if err != nil {
return nil, err
}
return builder, nil
}
// simulateTx simulates the given transaction and returns the amount of adjusted gas that should be used
func (w *Wallet) simulateTx(account authtypes.AccountI, builder sdkclient.TxBuilder) (uint64, error) {
// Create an empty signature literal as the ante handler will populate with a
// sentinel pubkey.
sig := signing.SignatureV2{
PubKey: &secp256k1.PubKey{},
Data: &signing.SingleSignatureData{
SignMode: signing.SignMode_SIGN_MODE_DIRECT,
},
Sequence: account.GetSequence(),
}
err := builder.SetSignatures(sig)
if err != nil {
return 0, err
}
// Set a fake amount of gas and fees
builder.SetGasLimit(200_000)
builder.SetFeeAmount(w.Client.GetFees(int64(200_000)))
// Simulate the execution of the transaction
adjusted, err := w.Client.SimulateTx(builder.GetTx())
if err != nil {
return 0, fmt.Errorf("error while simulating tx: %s", err)
}
return adjusted, nil
}