github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/tm2/pkg/crypto/keys/client/maketx.go (about) 1 package client 2 3 import ( 4 "flag" 5 "fmt" 6 7 "github.com/gnolang/gno/tm2/pkg/amino" 8 types "github.com/gnolang/gno/tm2/pkg/bft/rpc/core/types" 9 "github.com/gnolang/gno/tm2/pkg/commands" 10 "github.com/gnolang/gno/tm2/pkg/crypto/keys" 11 "github.com/gnolang/gno/tm2/pkg/errors" 12 "github.com/gnolang/gno/tm2/pkg/std" 13 ) 14 15 type MakeTxCfg struct { 16 RootCfg *BaseCfg 17 18 GasWanted int64 19 GasFee string 20 Memo string 21 22 Broadcast bool 23 // Valid options are SimulateTest, SimulateSkip or SimulateOnly. 24 Simulate string 25 ChainID string 26 } 27 28 // These are the valid options for MakeTxConfig.Simulate. 29 const ( 30 SimulateTest = "test" 31 SimulateSkip = "skip" 32 SimulateOnly = "only" 33 ) 34 35 func (c *MakeTxCfg) Validate() error { 36 switch c.Simulate { 37 case SimulateTest, SimulateSkip, SimulateOnly: 38 default: 39 return fmt.Errorf("invalid simulate option: %q", c.Simulate) 40 } 41 return nil 42 } 43 44 func NewMakeTxCmd(rootCfg *BaseCfg, io commands.IO) *commands.Command { 45 cfg := &MakeTxCfg{ 46 RootCfg: rootCfg, 47 } 48 49 cmd := commands.NewCommand( 50 commands.Metadata{ 51 Name: "maketx", 52 ShortUsage: "<subcommand> [flags] [<arg>...]", 53 ShortHelp: "composes a tx document to sign", 54 }, 55 cfg, 56 commands.HelpExec, 57 ) 58 59 cmd.AddSubCommands( 60 NewMakeSendCmd(cfg, io), 61 ) 62 63 return cmd 64 } 65 66 func (c *MakeTxCfg) RegisterFlags(fs *flag.FlagSet) { 67 fs.Int64Var( 68 &c.GasWanted, 69 "gas-wanted", 70 0, 71 "gas requested for tx", 72 ) 73 74 fs.StringVar( 75 &c.GasFee, 76 "gas-fee", 77 "", 78 "gas payment fee", 79 ) 80 81 fs.StringVar( 82 &c.Memo, 83 "memo", 84 "", 85 "any descriptive text", 86 ) 87 88 fs.BoolVar( 89 &c.Broadcast, 90 "broadcast", 91 false, 92 "sign, simulate and broadcast", 93 ) 94 95 fs.StringVar( 96 &c.Simulate, 97 "simulate", 98 "test", 99 `select how to simulate the transaction (only useful with --broadcast); valid options are 100 - test: attempts simulating the transaction, and if successful performs broadcasting (default) 101 - skip: avoids performing transaction simulation 102 - only: avoids broadcasting transaction (ie. dry run)`, 103 ) 104 105 fs.StringVar( 106 &c.ChainID, 107 "chainid", 108 "dev", 109 "chainid to sign for (only useful with --broadcast)", 110 ) 111 } 112 113 func SignAndBroadcastHandler( 114 cfg *MakeTxCfg, 115 nameOrBech32 string, 116 tx std.Tx, 117 pass string, 118 ) (*types.ResultBroadcastTxCommit, error) { 119 baseopts := cfg.RootCfg 120 txopts := cfg 121 122 kb, err := keys.NewKeyBaseFromDir(cfg.RootCfg.Home) 123 if err != nil { 124 return nil, err 125 } 126 127 info, err := kb.GetByNameOrAddress(nameOrBech32) 128 if err != nil { 129 return nil, err 130 } 131 accountAddr := info.GetAddress() 132 133 qopts := &QueryCfg{ 134 RootCfg: baseopts, 135 Path: fmt.Sprintf("auth/accounts/%s", accountAddr), 136 } 137 qres, err := QueryHandler(qopts) 138 if err != nil { 139 return nil, errors.Wrap(err, "query account") 140 } 141 var qret struct{ BaseAccount std.BaseAccount } 142 err = amino.UnmarshalJSON(qres.Response.Data, &qret) 143 if err != nil { 144 return nil, err 145 } 146 147 // sign tx 148 accountNumber := qret.BaseAccount.AccountNumber 149 sequence := qret.BaseAccount.Sequence 150 151 sOpts := signOpts{ 152 chainID: txopts.ChainID, 153 accountSequence: sequence, 154 accountNumber: accountNumber, 155 } 156 157 kOpts := keyOpts{ 158 keyName: nameOrBech32, 159 decryptPass: pass, 160 } 161 162 if err := signTx(&tx, kb, sOpts, kOpts); err != nil { 163 return nil, fmt.Errorf("unable to sign transaction, %w", err) 164 } 165 166 // broadcast signed tx 167 bopts := &BroadcastCfg{ 168 RootCfg: baseopts, 169 tx: &tx, 170 171 DryRun: cfg.Simulate == SimulateOnly, 172 testSimulate: cfg.Simulate == SimulateTest, 173 } 174 175 return BroadcastHandler(bopts) 176 } 177 178 func ExecSignAndBroadcast( 179 cfg *MakeTxCfg, 180 args []string, 181 tx std.Tx, 182 io commands.IO, 183 ) error { 184 if err := cfg.Validate(); err != nil { 185 return err 186 } 187 188 baseopts := cfg.RootCfg 189 190 // query account 191 nameOrBech32 := args[0] 192 193 var err error 194 var pass string 195 if baseopts.Quiet { 196 pass, err = io.GetPassword("", baseopts.InsecurePasswordStdin) 197 } else { 198 pass, err = io.GetPassword("Enter password.", baseopts.InsecurePasswordStdin) 199 } 200 201 if err != nil { 202 return err 203 } 204 205 bres, err := SignAndBroadcastHandler(cfg, nameOrBech32, tx, pass) 206 if err != nil { 207 return errors.Wrap(err, "broadcast tx") 208 } 209 if bres.CheckTx.IsErr() { 210 return errors.Wrap(bres.CheckTx.Error, "check transaction failed: log:%s", bres.CheckTx.Log) 211 } 212 if bres.DeliverTx.IsErr() { 213 return errors.Wrap(bres.DeliverTx.Error, "deliver transaction failed: log:%s", bres.DeliverTx.Log) 214 } 215 216 io.Println(string(bres.DeliverTx.Data)) 217 io.Println("OK!") 218 io.Println("GAS WANTED:", bres.DeliverTx.GasWanted) 219 io.Println("GAS USED: ", bres.DeliverTx.GasUsed) 220 io.Println("HEIGHT: ", bres.Height) 221 io.Println("EVENTS: ", string(bres.DeliverTx.EncodeEvents())) 222 223 return nil 224 }