From c0e044961da866ab153cd2b1c431dbbba66aaa94 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Mon, 1 Jun 2020 21:46:01 +0300 Subject: [PATCH] cli/wallet: add ability to save NEP5 transfer transaction It allows to use multisig cli operations for multisig transaction creation and send. --- cli/wallet/nep5.go | 27 +++++++++++++++++++++++++-- pkg/rpc/client/nep5.go | 32 +++++++++++++++++++++++--------- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/cli/wallet/nep5.go b/cli/wallet/nep5.go index 306fc5f4b..cf61345ff 100644 --- a/cli/wallet/nep5.go +++ b/cli/wallet/nep5.go @@ -1,12 +1,15 @@ package wallet import ( + "encoding/json" "errors" "fmt" + "io/ioutil" "github.com/nspcc-dev/neo-go/cli/flags" "github.com/nspcc-dev/neo-go/pkg/encoding/address" "github.com/nspcc-dev/neo-go/pkg/rpc/client" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/context" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/urfave/cli" @@ -82,6 +85,7 @@ func newNEP5Commands() []cli.Command { Flags: []cli.Flag{ walletPathFlag, rpcFlag, + outFlag, timeoutFlag, fromAddrFlag, toAddrFlag, @@ -341,11 +345,30 @@ func transferNEP5(ctx *cli.Context) error { return cli.NewExitError(err, 1) } - hash, err := c.TransferNEP5(acc, to, token.Hash, amount, gas) + tx, err := c.CreateNEP5TransferTx(acc, to, token.Hash, amount, gas) if err != nil { return cli.NewExitError(err, 1) } - fmt.Println(hash.StringLE()) + if outFile := ctx.String("out"); outFile != "" { + priv := acc.PrivateKey() + pub := priv.PublicKey() + sign := priv.Sign(tx.GetSignedPart()) + scCtx := context.NewParameterContext("Neo.Core.ContractTransaction", tx) + if err := scCtx.AddSignature(acc.Contract, pub, sign); err != nil { + return cli.NewExitError(fmt.Errorf("can't add signature: %v", err), 1) + } else if data, err := json.Marshal(scCtx); err != nil { + return cli.NewExitError(fmt.Errorf("can't marshal tx to JSON: %v", err), 1) + } else if err := ioutil.WriteFile(outFile, data, 0644); err != nil { + return cli.NewExitError(fmt.Errorf("can't write tx to file: %v", err), 1) + } + } else { + _ = acc.SignTx(tx) + if err := c.SendRawTransaction(tx); err != nil { + return cli.NewExitError(err, 1) + } + } + + fmt.Println(tx.Hash().StringLE()) return nil } diff --git a/pkg/rpc/client/nep5.go b/pkg/rpc/client/nep5.go index 1bdccdcb3..d04a085be 100644 --- a/pkg/rpc/client/nep5.go +++ b/pkg/rpc/client/nep5.go @@ -92,13 +92,14 @@ func (c *Client) NEP5TokenInfo(tokenHash util.Uint160) (*wallet.Token, error) { return wallet.NewToken(tokenHash, name, symbol, decimals), nil } -// TransferNEP5 creates an invocation transaction that invokes 'transfer' method -// on a given token to move specified amount of NEP5 assets (in FixedN format -// using contract's number of decimals) to given account. -func (c *Client) TransferNEP5(acc *wallet.Account, to util.Uint160, token util.Uint160, amount int64, gas util.Fixed8) (util.Uint256, error) { +// CreateNEP5TransferTx creates an invocation transaction for the 'transfer' +// method of a given contract (token) to move specified amount of NEP5 assets +// (in FixedN format using contract's number of decimals) to given account and +// returns it. The returned transaction is not signed. +func (c *Client) CreateNEP5TransferTx(acc *wallet.Account, to util.Uint160, token util.Uint160, amount int64, gas util.Fixed8) (*transaction.Transaction, error) { from, err := address.StringToUint160(acc.Address) if err != nil { - return util.Uint256{}, fmt.Errorf("bad account address: %v", err) + return nil, fmt.Errorf("bad account address: %v", err) } // Note: we don't use invoke function here because it requires // 2 round trips instead of one. @@ -120,11 +121,11 @@ func (c *Client) TransferNEP5(acc *wallet.Account, to util.Uint160, token util.U result, err := c.InvokeScript(hex.EncodeToString(script)) if err != nil { - return util.Uint256{}, fmt.Errorf("can't add system fee to transaction: %v", err) + return nil, fmt.Errorf("can't add system fee to transaction: %v", err) } gasConsumed, err := util.Fixed8FromString(result.GasConsumed) if err != nil { - return util.Uint256{}, fmt.Errorf("can't add system fee to transaction: %v", err) + return nil, fmt.Errorf("can't add system fee to transaction: %v", err) } if gasConsumed > 0 { tx.SystemFee = gasConsumed @@ -132,12 +133,25 @@ func (c *Client) TransferNEP5(acc *wallet.Account, to util.Uint160, token util.U tx.ValidUntilBlock, err = c.CalculateValidUntilBlock() if err != nil { - return util.Uint256{}, fmt.Errorf("can't calculate validUntilBlock: %v", err) + return nil, fmt.Errorf("can't calculate validUntilBlock: %v", err) } err = c.AddNetworkFee(tx, acc) if err != nil { - return util.Uint256{}, fmt.Errorf("can't add network fee to transaction: %v", err) + return nil, fmt.Errorf("can't add network fee to transaction: %v", err) + } + + return tx, nil +} + +// TransferNEP5 creates an invocation transaction that invokes 'transfer' method +// on a given token to move specified amount of NEP5 assets (in FixedN format +// using contract's number of decimals) to given account and sends it to the +// network returning just a hash of it. +func (c *Client) TransferNEP5(acc *wallet.Account, to util.Uint160, token util.Uint160, amount int64, gas util.Fixed8) (util.Uint256, error) { + tx, err := c.CreateNEP5TransferTx(acc, to, token, amount, gas) + if err != nil { + return util.Uint256{}, err } if err := acc.SignTx(tx); err != nil {