cli/wallet: add ability to save NEP5 transfer transaction

It allows to use multisig cli operations for multisig transaction creation and
send.
This commit is contained in:
Roman Khimov 2020-06-01 21:46:01 +03:00
parent 9a4e53b58e
commit c0e044961d
2 changed files with 48 additions and 11 deletions

View file

@ -1,12 +1,15 @@
package wallet package wallet
import ( import (
"encoding/json"
"errors" "errors"
"fmt" "fmt"
"io/ioutil"
"github.com/nspcc-dev/neo-go/cli/flags" "github.com/nspcc-dev/neo-go/cli/flags"
"github.com/nspcc-dev/neo-go/pkg/encoding/address" "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/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/util"
"github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/nspcc-dev/neo-go/pkg/wallet"
"github.com/urfave/cli" "github.com/urfave/cli"
@ -82,6 +85,7 @@ func newNEP5Commands() []cli.Command {
Flags: []cli.Flag{ Flags: []cli.Flag{
walletPathFlag, walletPathFlag,
rpcFlag, rpcFlag,
outFlag,
timeoutFlag, timeoutFlag,
fromAddrFlag, fromAddrFlag,
toAddrFlag, toAddrFlag,
@ -341,11 +345,30 @@ func transferNEP5(ctx *cli.Context) error {
return cli.NewExitError(err, 1) 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 { if err != nil {
return cli.NewExitError(err, 1) 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 return nil
} }

View file

@ -92,13 +92,14 @@ func (c *Client) NEP5TokenInfo(tokenHash util.Uint160) (*wallet.Token, error) {
return wallet.NewToken(tokenHash, name, symbol, decimals), nil return wallet.NewToken(tokenHash, name, symbol, decimals), nil
} }
// TransferNEP5 creates an invocation transaction that invokes 'transfer' method // CreateNEP5TransferTx creates an invocation transaction for the 'transfer'
// on a given token to move specified amount of NEP5 assets (in FixedN format // method of a given contract (token) to move specified amount of NEP5 assets
// using contract's number of decimals) to given account. // (in FixedN format using contract's number of decimals) to given account and
func (c *Client) TransferNEP5(acc *wallet.Account, to util.Uint160, token util.Uint160, amount int64, gas util.Fixed8) (util.Uint256, error) { // 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) from, err := address.StringToUint160(acc.Address)
if err != nil { 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 // Note: we don't use invoke function here because it requires
// 2 round trips instead of one. // 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)) result, err := c.InvokeScript(hex.EncodeToString(script))
if err != nil { 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) gasConsumed, err := util.Fixed8FromString(result.GasConsumed)
if err != nil { 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 { if gasConsumed > 0 {
tx.SystemFee = gasConsumed 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() tx.ValidUntilBlock, err = c.CalculateValidUntilBlock()
if err != nil { 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) err = c.AddNetworkFee(tx, acc)
if err != nil { 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 { if err := acc.SignTx(tx); err != nil {