Merge pull request #1950 from nspcc-dev/nep11/data

*: add `data` to NEP11 Transfer
This commit is contained in:
Roman Khimov 2021-05-06 10:39:56 +03:00 committed by GitHub
commit ba5273999f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 163 additions and 100 deletions

View file

@ -4,11 +4,13 @@ import (
"fmt"
"io"
"io/ioutil"
"math/big"
"os"
"path"
"testing"
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/encoding/address"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
@ -267,16 +269,51 @@ func TestNEP11_OwnerOf_BalanceOf_Transfer(t *testing.T) {
// transfer: no id specified
e.In.WriteString(nftOwnerPass + "\r")
e.RunWithError(t, cmdTransfer...)
cmdTransfer = append(cmdTransfer, "--id", string(tokenID))
// transfer: good
e.In.WriteString(nftOwnerPass + "\r")
e.Run(t, cmdTransfer...)
e.Run(t, append(cmdTransfer, "--id", string(tokenID))...)
e.checkTxPersisted(t)
// check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr, "1") // tokenID1
// transfer: good, to NEP11-Payable contract, with data
verifyH := deployVerifyContract(t, e)
cmdTransfer = []string{
"neo-go", "wallet", "nep11", "transfer",
"--rpc-endpoint", "http://" + e.RPC.Addr,
"--wallet", wall,
"--to", verifyH.StringLE(),
"--from", nftOwnerAddr,
"--token", h.StringLE(),
"--id", string(tokenID1),
"string:some_data",
}
e.In.WriteString(nftOwnerPass + "\r")
e.Run(t, cmdTransfer...)
tx, _ := e.checkTxPersisted(t)
// check OnNEP11Payment event
aer, err := e.Chain.GetAppExecResults(tx.Hash(), trigger.Application)
require.NoError(t, err)
require.Equal(t, 2, len(aer[0].Events))
nftOwnerHash, err := address.StringToUint160(nftOwnerAddr)
require.NoError(t, err)
require.Equal(t, state.NotificationEvent{
ScriptHash: verifyH,
Name: "OnNEP11Payment",
Item: stackitem.NewArray([]stackitem.Item{
stackitem.NewByteArray(nftOwnerHash.BytesBE()),
stackitem.NewBigInteger(big.NewInt(1)),
stackitem.NewByteArray(tokenID1),
stackitem.NewByteArray([]byte("some_data")),
}),
}, aer[0].Events[1])
// check balance after transfer
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
checkBalanceResult(t, nftOwnerAddr, "0")
}
func deployNFTContract(t *testing.T, e *executor) util.Uint160 {

View file

@ -86,7 +86,7 @@ func TestNEP17Balance(t *testing.T) {
}
e.checkNextLine(t, "^\\s*$")
addr4, err := address.StringToUint160("NTe3yHH5zsaEGvEHTsFRpCjTef6Aod4yb6") // deployed verify.go contract
addr4, err := address.StringToUint160("NaZjSxmRZ4ErG2QEXCQMjjJfvAxMPiutmi") // deployed verify.go contract
require.NoError(t, err)
e.checkNextLine(t, "^Account "+address.Uint160ToString(addr4))
e.checkEOF(t)

View file

@ -1,6 +1,9 @@
package testdata
import "github.com/nspcc-dev/neo-go/pkg/interop"
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
)
func Verify() bool {
return true
@ -8,3 +11,10 @@ func Verify() bool {
func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) {
}
// OnNEP11Payment notifies about NEP11 payment. You don't call this method directly,
// instead it's called by NEP11 contract when you transfer funds from your address
// to the address of this NFT contract.
func OnNEP11Payment(from interop.Hash160, amount int, token []byte, data interface{}) {
runtime.Notify("OnNEP11Payment", from, amount, token, data)
}

View file

@ -1 +1,12 @@
name: Test verify
Events:
- name: OnNEP11Payment
parameters:
- name: from
type: Hash160
- name: amount
type: Integer
- name: tokenId
type: ByteArray
- name: data
type: Any

View file

@ -1,84 +1,84 @@
{
"version": "3.0",
"accounts": [
"scrypt" : {
"r" : 8,
"p" : 8,
"n" : 16384
},
"version" : "3.0",
"accounts" : [
{
"address": "NTh9TnZTstvAePEYWDGLLxidBikJE24uTo",
"key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL",
"label": "",
"contract": {
"script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBdHR2qg==",
"parameters": [
"address" : "NTh9TnZTstvAePEYWDGLLxidBikJE24uTo",
"label" : "",
"lock" : false,
"contract" : {
"parameters" : [
{
"name": "parameter0",
"type": "Signature"
"type" : "Signature",
"name" : "parameter0"
}
],
"deployed": false
"script" : "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBdHR2qg==",
"deployed" : false
},
"lock": false,
"isdefault": true
"isdefault" : true,
"key" : "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL"
},
{
"address": "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6",
"key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL",
"label": "",
"contract": {
"script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl",
"parameters": [
"label" : "",
"address" : "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6",
"isdefault" : false,
"key" : "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL",
"contract" : {
"parameters" : [
{
"name": "parameter0",
"type": "Signature"
"name" : "parameter0",
"type" : "Signature"
},
{
"name": "parameter1",
"type": "Signature"
"type" : "Signature",
"name" : "parameter1"
},
{
"name": "parameter2",
"type": "Signature"
"type" : "Signature",
"name" : "parameter2"
}
],
"deployed": false
"deployed" : false,
"script" : "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl"
},
"lock": false,
"isdefault": false
"lock" : false
},
{
"address": "NNudMSGzEoktFzdYGYoNb3bzHzbmM1genF",
"key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL",
"label": "",
"contract": {
"script": "EQwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CEUF7zmyl",
"parameters": [
{
"name": "parameter0",
"type": "Signature"
}
],
"deployed": false
},
"lock": false,
"isdefault": false
},
{
"address" : "NTe3yHH5zsaEGvEHTsFRpCjTef6Aod4yb6",
"key" : "6PYSgdjUPVjo3ZJLg2CsheXnEZzyvUuSm4jCtXP6X7FT82sAQHWt2wpu5A",
"address" : "NNudMSGzEoktFzdYGYoNb3bzHzbmM1genF",
"label" : "",
"contract" : {
"script" : "VwEAEdsgQFcAA0A=",
"deployed" : true,
"parameters" : []
"parameters" : [
{
"name" : "parameter0",
"type" : "Signature"
}
],
"deployed" : false,
"script" : "EQwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CEUF7zmyl"
},
"lock" : false,
"isdefault" : false
"isdefault" : false,
"key" : "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL"
},
{
"contract" : {
"parameters" : [],
"script" : "VwEAEdsgQFcAA0BXAQR4eXp7VBTAcAwOT25ORVAxMVBheW1lbnRoUEGVAW9hIUA=",
"deployed" : true
},
"lock" : false,
"isdefault" : false,
"key" : "6PYVxVpeNjxGDFTTG61ARGy6mCz6fQsGm9qW2tsFW3ox1zM6KpCoWSE4PB",
"address" : "NaZjSxmRZ4ErG2QEXCQMjjJfvAxMPiutmi",
"label" : "acc"
}
],
"scrypt": {
"n": 16384,
"r": 8,
"p": 8
},
"extra": {
"Tokens": null
"extra" : {
"Tokens" : null
}
}

View file

@ -80,7 +80,7 @@ func newNEP11Commands() []cli.Command {
{
Name: "transfer",
Usage: "transfer NEP11 tokens",
UsageText: "transfer --wallet <path> --rpc-endpoint <node> --timeout <time> --from <addr> --to <addr> --token <hash-or-name> --id <token-id> [--amount string] [-- <cosigner1:Scope> [<cosigner2> [...]]]",
UsageText: "transfer --wallet <path> --rpc-endpoint <node> --timeout <time> --from <addr> --to <addr> --token <hash-or-name> --id <token-id> [--amount string] [data] [-- <cosigner1:Scope> [<cosigner2> [...]]]",
Action: transferNEP11,
Flags: transferFlags,
Description: `Transfers specified NEP11 token with optional cosigners list attached to
@ -235,7 +235,7 @@ func transferNEP11(ctx *cli.Context) error {
return transferNEP(ctx, manifest.NEP11StandardName)
}
func signAndSendNEP11Transfer(ctx *cli.Context, c *client.Client, acc *wallet.Account, token, to util.Uint160, tokenID string, amount *big.Int, cosigners []client.SignerAccount) error {
func signAndSendNEP11Transfer(ctx *cli.Context, c *client.Client, acc *wallet.Account, token, to util.Uint160, tokenID string, amount *big.Int, data interface{}, cosigners []client.SignerAccount) error {
gas := flags.Fixed8FromContext(ctx, "gas")
var (
@ -247,9 +247,9 @@ func signAndSendNEP11Transfer(ctx *cli.Context, c *client.Client, acc *wallet.Ac
if err != nil {
return cli.NewExitError(fmt.Errorf("bad account address: %w", err), 1)
}
tx, err = c.CreateNEP11TransferTx(acc, token, int64(gas), cosigners, from, to, amount, tokenID)
tx, err = c.CreateNEP11TransferTx(acc, token, int64(gas), cosigners, from, to, amount, tokenID, data)
} else {
tx, err = c.CreateNEP11TransferTx(acc, token, int64(gas), cosigners, to, tokenID)
tx, err = c.CreateNEP11TransferTx(acc, token, int64(gas), cosigners, to, tokenID, data)
}
if err != nil {
return cli.NewExitError(err, 1)

View file

@ -549,13 +549,13 @@ func transferNEP(ctx *cli.Context, standard string) error {
return cli.NewExitError(errors.New("token ID should be specified"), 1)
}
if amountArg == "" {
return signAndSendNEP11Transfer(ctx, c, acc, token.Hash, to, tokenID, nil, cosignersAccounts)
return signAndSendNEP11Transfer(ctx, c, acc, token.Hash, to, tokenID, nil, data, cosignersAccounts)
}
amount, err := fixedn.FromString(amountArg, int(token.Decimals))
if err != nil {
return cli.NewExitError(fmt.Errorf("invalid amount: %w", err), 1)
}
return signAndSendNEP11Transfer(ctx, c, acc, token.Hash, to, tokenID, amount, cosignersAccounts)
return signAndSendNEP11Transfer(ctx, c, acc, token.Hash, to, tokenID, amount, data, cosignersAccounts)
default:
return cli.NewExitError(fmt.Errorf("unsupported token standard %s", standard), 1)
}

View file

@ -181,10 +181,9 @@ func OwnerOf(token []byte) interop.Hash160 {
return getOwnerOf(ctx, token)
}
// Transfer token from its owner to another user, notice that it only has two
// parameters because token owner can be deduced from token ID itself and RC1
// implementation doesn't yet have 'data' parameter as in NEP-17 Transfer.
func Transfer(to interop.Hash160, token []byte) bool {
// Transfer token from its owner to another user, notice that it only has three
// parameters because token owner can be deduced from token ID itself.
func Transfer(to interop.Hash160, token []byte, data interface{}) bool {
if len(to) != 20 {
panic("invalid 'to' address")
}
@ -212,15 +211,15 @@ func Transfer(to interop.Hash160, token []byte) bool {
setTokensOf(ctx, to, toksTo)
setOwnerOf(ctx, token, to)
}
postTransfer(owner, to, token)
postTransfer(owner, to, token, data)
return true
}
// postTransfer emits Transfer event and calls onNEP11Payment if needed.
func postTransfer(from interop.Hash160, to interop.Hash160, token []byte) {
func postTransfer(from interop.Hash160, to interop.Hash160, token []byte, data interface{}) {
runtime.Notify("Transfer", from, to, 1, token)
if management.GetContract(to) != nil {
contract.Call(to, "onNEP11Payment", contract.All, from, 1, token)
contract.Call(to, "onNEP11Payment", contract.All, from, 1, token, data)
}
}
@ -259,7 +258,7 @@ func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) {
total++
storage.Put(ctx, []byte(totalSupplyPrefix), total)
postTransfer(nil, from, []byte(token))
postTransfer(nil, from, []byte(token), nil) // no `data` during minting
}
// Verify allows owner to manage contract's address, including earned GAS

View file

@ -108,13 +108,13 @@ func TestOnPayableChecks(t *testing.T) {
t.Run("NEP-11, good", func(t *testing.T) {
src := `package payable
import "github.com/nspcc-dev/neo-go/pkg/interop"
func OnNEP11Payment(from interop.Hash160, amount int, tokenID []byte) {}`
func OnNEP11Payment(from interop.Hash160, amount int, tokenID []byte, data interface{}) {}`
require.NoError(t, compileAndCheck(t, src))
})
t.Run("NEP-11, bad", func(t *testing.T) {
src := `package payable
import "github.com/nspcc-dev/neo-go/pkg/interop"
func OnNEP11Payment(from interop.Hash160, amount int, oldParam string, tokenID []byte) {}`
func OnNEP11Payment(from interop.Hash160, amount int, oldParam string, tokenID []byte, data interface{}) {}`
require.Error(t, compileAndCheck(t, src))
})
t.Run("NEP-17, good", func(t *testing.T) {

View file

@ -169,7 +169,7 @@ func TestNativeHelpersCompile(t *testing.T) {
{"properties", []string{`"neo.com"`}},
{"tokens", nil},
{"tokensOf", []string{u160}},
{"transfer", []string{u160, `"neo.com"`}},
{"transfer", []string{u160, `"neo.com"`, "nil"}},
// name service
{"addRoot", []string{`"com"`}},

View file

@ -319,7 +319,7 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
emit.Opcodes(w.BinWriter, opcode.RET)
onNEP11PaymentOff := w.Len()
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetCallingScriptHash)
emit.Int(w.BinWriter, 4)
emit.Int(w.BinWriter, 5)
emit.Opcodes(w.BinWriter, opcode.PACK)
emit.String(w.BinWriter, "LostPayment")
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeNotify)
@ -454,6 +454,7 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
manifest.NewParameter("from", smartcontract.Hash160Type),
manifest.NewParameter("amount", smartcontract.IntegerType),
manifest.NewParameter("tokenid", smartcontract.ByteArrayType),
manifest.NewParameter("data", smartcontract.AnyType),
},
ReturnType: smartcontract.VoidType,
},

View file

@ -107,7 +107,8 @@ func newNonFungible(name string, id int32, symbol string, decimals byte) *nonfun
desc = newDescriptor("transfer", smartcontract.BoolType,
manifest.NewParameter("to", smartcontract.Hash160Type),
manifest.NewParameter("tokenId", smartcontract.ByteArrayType))
manifest.NewParameter("tokenId", smartcontract.ByteArrayType),
manifest.NewParameter("data", smartcontract.AnyType))
md = newMethodAndPrice(n.transfer, 1<<17, callflag.States|callflag.AllowNotify)
md.StorageFee = 50
n.AddMethod(md, desc)
@ -267,10 +268,10 @@ func (n *nonfungible) mint(ic *interop.Context, s nftTokenState) {
ts := n.TotalSupply(ic.DAO)
ts.Add(ts, intOne)
n.setTotalSupply(ic.DAO, ts)
n.postTransfer(ic, nil, &owner, s.ID())
n.postTransfer(ic, nil, &owner, s.ID(), stackitem.Null{})
}
func (n *nonfungible) postTransfer(ic *interop.Context, from, to *util.Uint160, tokenID []byte) {
func (n *nonfungible) postTransfer(ic *interop.Context, from, to *util.Uint160, tokenID []byte, data stackitem.Item) {
ne := state.NotificationEvent{
ScriptHash: n.Hash,
Name: "Transfer",
@ -298,6 +299,7 @@ func (n *nonfungible) postTransfer(ic *interop.Context, from, to *util.Uint160,
fromArg,
stackitem.NewBigInteger(intOne),
stackitem.NewByteArray(tokenID),
data,
}
if err := contract.CallFromNative(ic, n.Hash, cs, manifest.MethodOnNEP11Payment, args, false); err != nil {
panic(err)
@ -332,7 +334,7 @@ func (n *nonfungible) burnByKey(ic *interop.Context, key []byte) {
ts := n.TotalSupply(ic.DAO)
ts.Sub(ts, intOne)
n.setTotalSupply(ic.DAO, ts)
n.postTransfer(ic, &owner, nil, id)
n.postTransfer(ic, &owner, nil, id, stackitem.Null{})
}
func (n *nonfungible) transfer(ic *interop.Context, args []stackitem.Item) stackitem.Item {
@ -373,7 +375,7 @@ func (n *nonfungible) transfer(ic *interop.Context, args []stackitem.Item) stack
acc.Add(tokenID)
n.putAccountState(ic.DAO, key, acc)
}
n.postTransfer(ic, &from, &to, tokenID)
n.postTransfer(ic, &from, &to, tokenID, args[2])
return stackitem.NewBool(true)
}

View file

@ -270,11 +270,11 @@ func TestTransfer(t *testing.T) {
testNameServiceInvokeAux(t, bc, defaultNameServiceSysfee, from, "setRecord", stackitem.Null{},
"neo.com", int64(nnsrecords.A), "1.2.3.4")
testNameServiceInvokeAux(t, bc, defaultRegisterSysfee, from, "transfer",
nil, to.Contract.ScriptHash().BytesBE(), []byte("not.exists"))
nil, to.Contract.ScriptHash().BytesBE(), []byte("not.exists"), nil)
testNameServiceInvokeAux(t, bc, defaultRegisterSysfee, true, "transfer",
false, to.Contract.ScriptHash().BytesBE(), []byte("neo.com"))
false, to.Contract.ScriptHash().BytesBE(), []byte("neo.com"), nil)
testNameServiceInvokeAux(t, bc, defaultRegisterSysfee, from, "transfer",
true, to.Contract.ScriptHash().BytesBE(), []byte("neo.com"))
true, to.Contract.ScriptHash().BytesBE(), []byte("neo.com"), nil)
testNameServiceInvokeAux(t, bc, defaultNameServiceSysfee, from, "totalSupply", 1)
testNameServiceInvokeAux(t, bc, defaultNameServiceSysfee, from, "ownerOf",
to.Contract.ScriptHash().BytesBE(), []byte("neo.com"))
@ -282,9 +282,9 @@ func TestTransfer(t *testing.T) {
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs))
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs2))
testNameServiceInvokeAux(t, bc, defaultRegisterSysfee, to, "transfer",
nil, cs2.Hash.BytesBE(), []byte("neo.com"))
nil, cs2.Hash.BytesBE(), []byte("neo.com"), nil)
testNameServiceInvokeAux(t, bc, defaultRegisterSysfee, to, "transfer",
true, cs.Hash.BytesBE(), []byte("neo.com"))
true, cs.Hash.BytesBE(), []byte("neo.com"), nil)
testNameServiceInvokeAux(t, bc, defaultNameServiceSysfee, from, "totalSupply", 1)
testNameServiceInvokeAux(t, bc, defaultNameServiceSysfee, from, "ownerOf",
cs.Hash.BytesBE(), []byte("neo.com"))

View file

@ -67,9 +67,9 @@ func TokensOf(addr interop.Hash160) iterator.Iterator {
}
// Transfer represents `transfer` method of NameService native contract.
func Transfer(to interop.Hash160, tokenID string) bool {
func Transfer(to interop.Hash160, tokenID string, data interface{}) bool {
return contract.Call(interop.Hash160(Hash), "transfer",
contract.ReadStates|contract.States|contract.AllowNotify, to, tokenID).(bool)
contract.ReadStates|contract.States|contract.AllowNotify, to, tokenID, data).(bool)
}
// AddRoot represents `addRoot` method of NameService native contract.

View file

@ -45,11 +45,11 @@ func (c *Client) NEP11TokenInfo(tokenHash util.Uint160) (*wallet.Token, error) {
// on a given token to move the whole NEP11 token with the specified token ID to
// given account and sends it to the network returning just a hash of it.
func (c *Client) TransferNEP11(acc *wallet.Account, to util.Uint160,
tokenHash util.Uint160, tokenID string, gas int64, cosigners []SignerAccount) (util.Uint256, error) {
tokenHash util.Uint160, tokenID string, data interface{}, gas int64, cosigners []SignerAccount) (util.Uint256, error) {
if !c.initDone {
return util.Uint256{}, errNetworkNotInitialized
}
tx, err := c.CreateNEP11TransferTx(acc, tokenHash, gas, cosigners, to, tokenID)
tx, err := c.CreateNEP11TransferTx(acc, tokenHash, gas, cosigners, to, tokenID, data)
if err != nil {
return util.Uint256{}, err
}
@ -62,8 +62,8 @@ func (c *Client) TransferNEP11(acc *wallet.Account, to util.Uint160,
// of) NEP11 token with the specified token ID to given account and returns it.
// The returned transaction is not signed. CreateNEP11TransferTx is also a
// helper for TransferNEP11 and TransferNEP11D.
// `args` for TransferNEP11: to util.Uint160, tokenID string;
// `args` for TransferNEP11D: from, to util.Uint160, amount int64, tokenID string.
// `args` for TransferNEP11: to util.Uint160, tokenID string, data interface{};
// `args` for TransferNEP11D: from, to util.Uint160, amount int64, tokenID string, data interface{}.
func (c *Client) CreateNEP11TransferTx(acc *wallet.Account, tokenHash util.Uint160,
gas int64, cosigners []SignerAccount, args ...interface{}) (*transaction.Transaction, error) {
w := io.NewBufBinWriter()
@ -143,7 +143,7 @@ func (c *Client) NEP11NDOwnerOf(tokenHash util.Uint160, tokenID string) (util.Ui
// (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) TransferNEP11D(acc *wallet.Account, to util.Uint160,
tokenHash util.Uint160, amount int64, tokenID string, gas int64, cosigners []SignerAccount) (util.Uint256, error) {
tokenHash util.Uint160, amount int64, tokenID string, data interface{}, gas int64, cosigners []SignerAccount) (util.Uint256, error) {
if !c.initDone {
return util.Uint256{}, errNetworkNotInitialized
}
@ -151,7 +151,7 @@ func (c *Client) TransferNEP11D(acc *wallet.Account, to util.Uint160,
if err != nil {
return util.Uint256{}, fmt.Errorf("bad account address: %w", err)
}
tx, err := c.CreateNEP11TransferTx(acc, tokenHash, gas, cosigners, from, to, amount, tokenID)
tx, err := c.CreateNEP11TransferTx(acc, tokenHash, gas, cosigners, from, to, amount, tokenID, data)
if err != nil {
return util.Uint256{}, err
}

View file

@ -834,7 +834,7 @@ func TestClient_NEP11(t *testing.T) {
require.EqualValues(t, expected, p)
})
t.Run("Transfer", func(t *testing.T) {
_, err := c.TransferNEP11(wallet.NewAccountFromPrivateKey(testchain.PrivateKeyByID(0)), testchain.PrivateKeyByID(1).GetScriptHash(), h, "neo.com", 0, nil)
_, err := c.TransferNEP11(wallet.NewAccountFromPrivateKey(testchain.PrivateKeyByID(0)), testchain.PrivateKeyByID(1).GetScriptHash(), h, "neo.com", nil, 0, nil)
require.NoError(t, err)
})
}

View file

@ -31,6 +31,7 @@ var nep11Base = &Standard{
Parameters: []manifest.Parameter{
{Name: "to", Type: smartcontract.Hash160Type},
{Name: "tokenId", Type: smartcontract.ByteArrayType},
{Name: "data", Type: smartcontract.AnyType},
},
ReturnType: smartcontract.BoolType,
},
@ -112,6 +113,7 @@ var nep11Divisible = &Standard{
{Name: "to", Type: smartcontract.Hash160Type},
{Name: "amount", Type: smartcontract.IntegerType},
{Name: "tokenId", Type: smartcontract.ByteArrayType},
{Name: "data", Type: smartcontract.AnyType},
},
ReturnType: smartcontract.BoolType,
},

View file

@ -14,6 +14,7 @@ var nep11payable = &Standard{
{Name: "from", Type: smartcontract.Hash160Type},
{Name: "amount", Type: smartcontract.IntegerType},
{Name: "tokenid", Type: smartcontract.ByteArrayType},
{Name: "data", Type: smartcontract.AnyType},
},
ReturnType: smartcontract.VoidType,
}},