diff --git a/cli/wallet/nep5.go b/cli/wallet/nep5.go index 746df9796..5c475eacf 100644 --- a/cli/wallet/nep5.go +++ b/cli/wallet/nep5.go @@ -5,15 +5,9 @@ import ( "fmt" "github.com/nspcc-dev/neo-go/cli/flags" - "github.com/nspcc-dev/neo-go/pkg/core" - "github.com/nspcc-dev/neo-go/pkg/core/transaction" "github.com/nspcc-dev/neo-go/pkg/encoding/address" - "github.com/nspcc-dev/neo-go/pkg/io" "github.com/nspcc-dev/neo-go/pkg/rpc/client" - "github.com/nspcc-dev/neo-go/pkg/rpc/request" "github.com/nspcc-dev/neo-go/pkg/util" - "github.com/nspcc-dev/neo-go/pkg/vm/emit" - "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/wallet" "github.com/urfave/cli" ) @@ -339,38 +333,19 @@ func transferNEP5(ctx *cli.Context) error { return cli.NewExitError(fmt.Errorf("invalid amount: %v", err), 1) } - // Note: we don't use invoke function here because it requires - // 2 round trips instead of one. - w := io.NewBufBinWriter() - emit.Int(w.BinWriter, amount) - emit.Bytes(w.BinWriter, to.BytesBE()) - emit.Bytes(w.BinWriter, from.BytesBE()) - emit.Int(w.BinWriter, 3) - emit.Opcode(w.BinWriter, opcode.PACK) - emit.String(w.BinWriter, "transfer") - emit.AppCall(w.BinWriter, token.Hash, false) - emit.Opcode(w.BinWriter, opcode.THROWIFNOT) - gas := flags.Fixed8FromContext(ctx, "gas") - tx := transaction.NewInvocationTX(w.Bytes(), gas) - tx.AddVerificationHash(from) - - if err := request.AddInputsAndUnspentsToTx(tx, fromFlag.String(), core.UtilityTokenID(), gas, c); err != nil { - return cli.NewExitError(fmt.Errorf("can't add GAS to a tx: %v", err), 1) - } if pass, err := readPassword("Password > "); err != nil { return cli.NewExitError(err, 1) } else if err := acc.Decrypt(pass); err != nil { return cli.NewExitError(err, 1) - } else if err := acc.SignTx(tx); err != nil { - return cli.NewExitError(fmt.Errorf("can't sign tx: %v", err), 1) } - if err := c.SendRawTransaction(tx); err != nil { + hash, err := c.TransferNEP5(acc, to, token, amount, gas) + if err != nil { return cli.NewExitError(err, 1) } - fmt.Println(tx.Hash()) + fmt.Println(hash.StringLE()) return nil } diff --git a/pkg/rpc/client/nep5.go b/pkg/rpc/client/nep5.go index 9f6acbd2e..5505a9aad 100644 --- a/pkg/rpc/client/nep5.go +++ b/pkg/rpc/client/nep5.go @@ -4,9 +4,15 @@ import ( "errors" "fmt" + "github.com/nspcc-dev/neo-go/pkg/core" + "github.com/nspcc-dev/neo-go/pkg/core/transaction" + "github.com/nspcc-dev/neo-go/pkg/encoding/address" + "github.com/nspcc-dev/neo-go/pkg/io" + "github.com/nspcc-dev/neo-go/pkg/rpc/request" "github.com/nspcc-dev/neo-go/pkg/smartcontract" "github.com/nspcc-dev/neo-go/pkg/util" "github.com/nspcc-dev/neo-go/pkg/vm/emit" + "github.com/nspcc-dev/neo-go/pkg/vm/opcode" "github.com/nspcc-dev/neo-go/pkg/wallet" ) @@ -87,6 +93,47 @@ 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 *wallet.Token, amount int64, gas util.Fixed8) (util.Uint256, error) { + from, err := address.StringToUint160(acc.Address) + if err != nil { + return util.Uint256{}, fmt.Errorf("bad account address: %v", err) + } + // Note: we don't use invoke function here because it requires + // 2 round trips instead of one. + w := io.NewBufBinWriter() + emit.Int(w.BinWriter, amount) + emit.Bytes(w.BinWriter, to.BytesBE()) + emit.Bytes(w.BinWriter, from.BytesBE()) + emit.Int(w.BinWriter, 3) + emit.Opcode(w.BinWriter, opcode.PACK) + emit.String(w.BinWriter, "transfer") + emit.AppCall(w.BinWriter, token.Hash, false) + emit.Opcode(w.BinWriter, opcode.THROWIFNOT) + + tx := transaction.NewInvocationTX(w.Bytes(), gas) + tx.Attributes = append(tx.Attributes, transaction.Attribute{ + Usage: transaction.Script, + Data: from.BytesBE(), + }) + + if err := request.AddInputsAndUnspentsToTx(tx, acc.Address, core.UtilityTokenID(), gas, c); err != nil { + return util.Uint256{}, fmt.Errorf("can't add GAS to transaction: %v", err) + } + + if err := acc.SignTx(tx); err != nil { + return util.Uint256{}, fmt.Errorf("can't sign tx: %v", err) + } + + if err := c.SendRawTransaction(tx); err != nil { + return util.Uint256{}, err + } + + return tx.Hash(), nil +} + func topIntFromStack(st []smartcontract.Parameter) (int64, error) { index := len(st) - 1 // top stack element is last in the array var decimals int64