mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-11-29 23:33:37 +00:00
Merge pull request #1950 from nspcc-dev/nep11/data
*: add `data` to NEP11 Transfer
This commit is contained in:
commit
ba5273999f
18 changed files with 163 additions and 100 deletions
|
@ -4,11 +4,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"math/big"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/native/nativenames"
|
"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/encoding/address"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/trigger"
|
"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
|
// transfer: no id specified
|
||||||
e.In.WriteString(nftOwnerPass + "\r")
|
e.In.WriteString(nftOwnerPass + "\r")
|
||||||
e.RunWithError(t, cmdTransfer...)
|
e.RunWithError(t, cmdTransfer...)
|
||||||
cmdTransfer = append(cmdTransfer, "--id", string(tokenID))
|
|
||||||
|
|
||||||
// transfer: good
|
// transfer: good
|
||||||
e.In.WriteString(nftOwnerPass + "\r")
|
e.In.WriteString(nftOwnerPass + "\r")
|
||||||
e.Run(t, cmdTransfer...)
|
e.Run(t, append(cmdTransfer, "--id", string(tokenID))...)
|
||||||
e.checkTxPersisted(t)
|
e.checkTxPersisted(t)
|
||||||
|
|
||||||
// check balance after transfer
|
// check balance after transfer
|
||||||
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
|
e.Run(t, append(cmdCheckBalance, "--token", h.StringLE())...)
|
||||||
checkBalanceResult(t, nftOwnerAddr, "1") // tokenID1
|
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 {
|
func deployNFTContract(t *testing.T, e *executor) util.Uint160 {
|
||||||
|
|
|
@ -86,7 +86,7 @@ func TestNEP17Balance(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
e.checkNextLine(t, "^\\s*$")
|
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)
|
require.NoError(t, err)
|
||||||
e.checkNextLine(t, "^Account "+address.Uint160ToString(addr4))
|
e.checkNextLine(t, "^Account "+address.Uint160ToString(addr4))
|
||||||
e.checkEOF(t)
|
e.checkEOF(t)
|
||||||
|
|
12
cli/testdata/verify.go
vendored
12
cli/testdata/verify.go
vendored
|
@ -1,6 +1,9 @@
|
||||||
package testdata
|
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 {
|
func Verify() bool {
|
||||||
return true
|
return true
|
||||||
|
@ -8,3 +11,10 @@ func Verify() bool {
|
||||||
|
|
||||||
func OnNEP17Payment(from interop.Hash160, amount int, data interface{}) {
|
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)
|
||||||
|
}
|
||||||
|
|
11
cli/testdata/verify.yml
vendored
11
cli/testdata/verify.yml
vendored
|
@ -1 +1,12 @@
|
||||||
name: Test verify
|
name: Test verify
|
||||||
|
Events:
|
||||||
|
- name: OnNEP11Payment
|
||||||
|
parameters:
|
||||||
|
- name: from
|
||||||
|
type: Hash160
|
||||||
|
- name: amount
|
||||||
|
type: Integer
|
||||||
|
- name: tokenId
|
||||||
|
type: ByteArray
|
||||||
|
- name: data
|
||||||
|
type: Any
|
||||||
|
|
118
cli/testdata/wallet1_solo.json
vendored
118
cli/testdata/wallet1_solo.json
vendored
|
@ -1,84 +1,84 @@
|
||||||
{
|
{
|
||||||
"version": "3.0",
|
"scrypt" : {
|
||||||
"accounts": [
|
"r" : 8,
|
||||||
|
"p" : 8,
|
||||||
|
"n" : 16384
|
||||||
|
},
|
||||||
|
"version" : "3.0",
|
||||||
|
"accounts" : [
|
||||||
{
|
{
|
||||||
"address": "NTh9TnZTstvAePEYWDGLLxidBikJE24uTo",
|
"address" : "NTh9TnZTstvAePEYWDGLLxidBikJE24uTo",
|
||||||
"key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL",
|
"label" : "",
|
||||||
"label": "",
|
"lock" : false,
|
||||||
"contract": {
|
"contract" : {
|
||||||
"script": "DCECs2Ir9AF73+MXxYrtX0x1PyBrfbiWBG+n13S7xL9/jcJBdHR2qg==",
|
"parameters" : [
|
||||||
"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",
|
"label" : "",
|
||||||
"key": "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL",
|
"address" : "NgEisvCqr2h8wpRxQb7bVPWUZdbVCY8Uo6",
|
||||||
"label": "",
|
"isdefault" : false,
|
||||||
"contract": {
|
"key" : "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL",
|
||||||
"script": "EwwhAhA6f33QFlWFl/eWDSfFFqQ5T9loueZRVetLAT5AQEBuDCECp7xV/oaE4BGXaNEEujB5W9zIZhnoZK3SYVZyPtGFzWIMIQKzYiv0AXvf4xfFiu1fTHU/IGt9uJYEb6fXdLvEv3+NwgwhA9kMB99j5pDOd5EuEKtRrMlEtmhgI3tgjE+PgwnnHuaZFEF7zmyl",
|
"contract" : {
|
||||||
"parameters": [
|
"parameters" : [
|
||||||
{
|
{
|
||||||
"name": "parameter0",
|
"name" : "parameter0",
|
||||||
"type": "Signature"
|
"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,
|
"lock" : false
|
||||||
"isdefault": false
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"address": "NNudMSGzEoktFzdYGYoNb3bzHzbmM1genF",
|
"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",
|
|
||||||
"label" : "",
|
"label" : "",
|
||||||
"contract" : {
|
"contract" : {
|
||||||
"script" : "VwEAEdsgQFcAA0A=",
|
"parameters" : [
|
||||||
"deployed" : true,
|
{
|
||||||
"parameters" : []
|
"name" : "parameter0",
|
||||||
},
|
"type" : "Signature"
|
||||||
"lock" : false,
|
|
||||||
"isdefault" : false
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"scrypt": {
|
"deployed" : false,
|
||||||
"n": 16384,
|
"script" : "EQwhArNiK/QBe9/jF8WK7V9MdT8ga324lgRvp9d0u8S/f43CEUF7zmyl"
|
||||||
"r": 8,
|
|
||||||
"p": 8
|
|
||||||
},
|
},
|
||||||
"extra": {
|
"lock" : false,
|
||||||
"Tokens": null
|
"isdefault" : false,
|
||||||
|
"key" : "6PYL8Gnjsz4RBKX18jx5ZAQTDH7PKkZwEVjPKEkjNzCDNFE6TKZwaFLibL"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"contract" : {
|
||||||
|
"parameters" : [],
|
||||||
|
"script" : "VwEAEdsgQFcAA0BXAQR4eXp7VBTAcAwOT25ORVAxMVBheW1lbnRoUEGVAW9hIUA=",
|
||||||
|
"deployed" : true
|
||||||
|
},
|
||||||
|
"lock" : false,
|
||||||
|
"isdefault" : false,
|
||||||
|
"key" : "6PYVxVpeNjxGDFTTG61ARGy6mCz6fQsGm9qW2tsFW3ox1zM6KpCoWSE4PB",
|
||||||
|
"address" : "NaZjSxmRZ4ErG2QEXCQMjjJfvAxMPiutmi",
|
||||||
|
"label" : "acc"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"extra" : {
|
||||||
|
"Tokens" : null
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -80,7 +80,7 @@ func newNEP11Commands() []cli.Command {
|
||||||
{
|
{
|
||||||
Name: "transfer",
|
Name: "transfer",
|
||||||
Usage: "transfer NEP11 tokens",
|
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,
|
Action: transferNEP11,
|
||||||
Flags: transferFlags,
|
Flags: transferFlags,
|
||||||
Description: `Transfers specified NEP11 token with optional cosigners list attached to
|
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)
|
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")
|
gas := flags.Fixed8FromContext(ctx, "gas")
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
@ -247,9 +247,9 @@ func signAndSendNEP11Transfer(ctx *cli.Context, c *client.Client, acc *wallet.Ac
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("bad account address: %w", err), 1)
|
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 {
|
} 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 {
|
if err != nil {
|
||||||
return cli.NewExitError(err, 1)
|
return cli.NewExitError(err, 1)
|
||||||
|
|
|
@ -549,13 +549,13 @@ func transferNEP(ctx *cli.Context, standard string) error {
|
||||||
return cli.NewExitError(errors.New("token ID should be specified"), 1)
|
return cli.NewExitError(errors.New("token ID should be specified"), 1)
|
||||||
}
|
}
|
||||||
if amountArg == "" {
|
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))
|
amount, err := fixedn.FromString(amountArg, int(token.Decimals))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cli.NewExitError(fmt.Errorf("invalid amount: %w", err), 1)
|
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:
|
default:
|
||||||
return cli.NewExitError(fmt.Errorf("unsupported token standard %s", standard), 1)
|
return cli.NewExitError(fmt.Errorf("unsupported token standard %s", standard), 1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -181,10 +181,9 @@ func OwnerOf(token []byte) interop.Hash160 {
|
||||||
return getOwnerOf(ctx, token)
|
return getOwnerOf(ctx, token)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transfer token from its owner to another user, notice that it only has two
|
// 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 and RC1
|
// parameters because token owner can be deduced from token ID itself.
|
||||||
// implementation doesn't yet have 'data' parameter as in NEP-17 Transfer.
|
func Transfer(to interop.Hash160, token []byte, data interface{}) bool {
|
||||||
func Transfer(to interop.Hash160, token []byte) bool {
|
|
||||||
if len(to) != 20 {
|
if len(to) != 20 {
|
||||||
panic("invalid 'to' address")
|
panic("invalid 'to' address")
|
||||||
}
|
}
|
||||||
|
@ -212,15 +211,15 @@ func Transfer(to interop.Hash160, token []byte) bool {
|
||||||
setTokensOf(ctx, to, toksTo)
|
setTokensOf(ctx, to, toksTo)
|
||||||
setOwnerOf(ctx, token, to)
|
setOwnerOf(ctx, token, to)
|
||||||
}
|
}
|
||||||
postTransfer(owner, to, token)
|
postTransfer(owner, to, token, data)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
// postTransfer emits Transfer event and calls onNEP11Payment if needed.
|
// 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)
|
runtime.Notify("Transfer", from, to, 1, token)
|
||||||
if management.GetContract(to) != nil {
|
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++
|
total++
|
||||||
storage.Put(ctx, []byte(totalSupplyPrefix), 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
|
// Verify allows owner to manage contract's address, including earned GAS
|
||||||
|
|
|
@ -108,13 +108,13 @@ func TestOnPayableChecks(t *testing.T) {
|
||||||
t.Run("NEP-11, good", func(t *testing.T) {
|
t.Run("NEP-11, good", func(t *testing.T) {
|
||||||
src := `package payable
|
src := `package payable
|
||||||
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
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))
|
require.NoError(t, compileAndCheck(t, src))
|
||||||
})
|
})
|
||||||
t.Run("NEP-11, bad", func(t *testing.T) {
|
t.Run("NEP-11, bad", func(t *testing.T) {
|
||||||
src := `package payable
|
src := `package payable
|
||||||
import "github.com/nspcc-dev/neo-go/pkg/interop"
|
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))
|
require.Error(t, compileAndCheck(t, src))
|
||||||
})
|
})
|
||||||
t.Run("NEP-17, good", func(t *testing.T) {
|
t.Run("NEP-17, good", func(t *testing.T) {
|
||||||
|
|
|
@ -169,7 +169,7 @@ func TestNativeHelpersCompile(t *testing.T) {
|
||||||
{"properties", []string{`"neo.com"`}},
|
{"properties", []string{`"neo.com"`}},
|
||||||
{"tokens", nil},
|
{"tokens", nil},
|
||||||
{"tokensOf", []string{u160}},
|
{"tokensOf", []string{u160}},
|
||||||
{"transfer", []string{u160, `"neo.com"`}},
|
{"transfer", []string{u160, `"neo.com"`, "nil"}},
|
||||||
|
|
||||||
// name service
|
// name service
|
||||||
{"addRoot", []string{`"com"`}},
|
{"addRoot", []string{`"com"`}},
|
||||||
|
|
|
@ -319,7 +319,7 @@ func getTestContractState(bc *Blockchain) (*state.Contract, *state.Contract) {
|
||||||
emit.Opcodes(w.BinWriter, opcode.RET)
|
emit.Opcodes(w.BinWriter, opcode.RET)
|
||||||
onNEP11PaymentOff := w.Len()
|
onNEP11PaymentOff := w.Len()
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetCallingScriptHash)
|
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeGetCallingScriptHash)
|
||||||
emit.Int(w.BinWriter, 4)
|
emit.Int(w.BinWriter, 5)
|
||||||
emit.Opcodes(w.BinWriter, opcode.PACK)
|
emit.Opcodes(w.BinWriter, opcode.PACK)
|
||||||
emit.String(w.BinWriter, "LostPayment")
|
emit.String(w.BinWriter, "LostPayment")
|
||||||
emit.Syscall(w.BinWriter, interopnames.SystemRuntimeNotify)
|
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("from", smartcontract.Hash160Type),
|
||||||
manifest.NewParameter("amount", smartcontract.IntegerType),
|
manifest.NewParameter("amount", smartcontract.IntegerType),
|
||||||
manifest.NewParameter("tokenid", smartcontract.ByteArrayType),
|
manifest.NewParameter("tokenid", smartcontract.ByteArrayType),
|
||||||
|
manifest.NewParameter("data", smartcontract.AnyType),
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.VoidType,
|
ReturnType: smartcontract.VoidType,
|
||||||
},
|
},
|
||||||
|
|
|
@ -107,7 +107,8 @@ func newNonFungible(name string, id int32, symbol string, decimals byte) *nonfun
|
||||||
|
|
||||||
desc = newDescriptor("transfer", smartcontract.BoolType,
|
desc = newDescriptor("transfer", smartcontract.BoolType,
|
||||||
manifest.NewParameter("to", smartcontract.Hash160Type),
|
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 = newMethodAndPrice(n.transfer, 1<<17, callflag.States|callflag.AllowNotify)
|
||||||
md.StorageFee = 50
|
md.StorageFee = 50
|
||||||
n.AddMethod(md, desc)
|
n.AddMethod(md, desc)
|
||||||
|
@ -267,10 +268,10 @@ func (n *nonfungible) mint(ic *interop.Context, s nftTokenState) {
|
||||||
ts := n.TotalSupply(ic.DAO)
|
ts := n.TotalSupply(ic.DAO)
|
||||||
ts.Add(ts, intOne)
|
ts.Add(ts, intOne)
|
||||||
n.setTotalSupply(ic.DAO, ts)
|
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{
|
ne := state.NotificationEvent{
|
||||||
ScriptHash: n.Hash,
|
ScriptHash: n.Hash,
|
||||||
Name: "Transfer",
|
Name: "Transfer",
|
||||||
|
@ -298,6 +299,7 @@ func (n *nonfungible) postTransfer(ic *interop.Context, from, to *util.Uint160,
|
||||||
fromArg,
|
fromArg,
|
||||||
stackitem.NewBigInteger(intOne),
|
stackitem.NewBigInteger(intOne),
|
||||||
stackitem.NewByteArray(tokenID),
|
stackitem.NewByteArray(tokenID),
|
||||||
|
data,
|
||||||
}
|
}
|
||||||
if err := contract.CallFromNative(ic, n.Hash, cs, manifest.MethodOnNEP11Payment, args, false); err != nil {
|
if err := contract.CallFromNative(ic, n.Hash, cs, manifest.MethodOnNEP11Payment, args, false); err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@ -332,7 +334,7 @@ func (n *nonfungible) burnByKey(ic *interop.Context, key []byte) {
|
||||||
ts := n.TotalSupply(ic.DAO)
|
ts := n.TotalSupply(ic.DAO)
|
||||||
ts.Sub(ts, intOne)
|
ts.Sub(ts, intOne)
|
||||||
n.setTotalSupply(ic.DAO, ts)
|
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 {
|
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)
|
acc.Add(tokenID)
|
||||||
n.putAccountState(ic.DAO, key, acc)
|
n.putAccountState(ic.DAO, key, acc)
|
||||||
}
|
}
|
||||||
n.postTransfer(ic, &from, &to, tokenID)
|
n.postTransfer(ic, &from, &to, tokenID, args[2])
|
||||||
return stackitem.NewBool(true)
|
return stackitem.NewBool(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -270,11 +270,11 @@ func TestTransfer(t *testing.T) {
|
||||||
testNameServiceInvokeAux(t, bc, defaultNameServiceSysfee, from, "setRecord", stackitem.Null{},
|
testNameServiceInvokeAux(t, bc, defaultNameServiceSysfee, from, "setRecord", stackitem.Null{},
|
||||||
"neo.com", int64(nnsrecords.A), "1.2.3.4")
|
"neo.com", int64(nnsrecords.A), "1.2.3.4")
|
||||||
testNameServiceInvokeAux(t, bc, defaultRegisterSysfee, from, "transfer",
|
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",
|
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",
|
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, "totalSupply", 1)
|
||||||
testNameServiceInvokeAux(t, bc, defaultNameServiceSysfee, from, "ownerOf",
|
testNameServiceInvokeAux(t, bc, defaultNameServiceSysfee, from, "ownerOf",
|
||||||
to.Contract.ScriptHash().BytesBE(), []byte("neo.com"))
|
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, cs))
|
||||||
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs2))
|
require.NoError(t, bc.contracts.Management.PutContractState(bc.dao, cs2))
|
||||||
testNameServiceInvokeAux(t, bc, defaultRegisterSysfee, to, "transfer",
|
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",
|
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, "totalSupply", 1)
|
||||||
testNameServiceInvokeAux(t, bc, defaultNameServiceSysfee, from, "ownerOf",
|
testNameServiceInvokeAux(t, bc, defaultNameServiceSysfee, from, "ownerOf",
|
||||||
cs.Hash.BytesBE(), []byte("neo.com"))
|
cs.Hash.BytesBE(), []byte("neo.com"))
|
||||||
|
|
|
@ -67,9 +67,9 @@ func TokensOf(addr interop.Hash160) iterator.Iterator {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transfer represents `transfer` method of NameService native contract.
|
// 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",
|
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.
|
// AddRoot represents `addRoot` method of NameService native contract.
|
||||||
|
|
|
@ -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
|
// 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.
|
// given account and sends it to the network returning just a hash of it.
|
||||||
func (c *Client) TransferNEP11(acc *wallet.Account, to util.Uint160,
|
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 {
|
if !c.initDone {
|
||||||
return util.Uint256{}, errNetworkNotInitialized
|
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 {
|
if err != nil {
|
||||||
return util.Uint256{}, err
|
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.
|
// of) NEP11 token with the specified token ID to given account and returns it.
|
||||||
// The returned transaction is not signed. CreateNEP11TransferTx is also a
|
// The returned transaction is not signed. CreateNEP11TransferTx is also a
|
||||||
// helper for TransferNEP11 and TransferNEP11D.
|
// helper for TransferNEP11 and TransferNEP11D.
|
||||||
// `args` for TransferNEP11: to util.Uint160, tokenID string;
|
// `args` for TransferNEP11: to util.Uint160, tokenID string, data interface{};
|
||||||
// `args` for TransferNEP11D: from, to util.Uint160, amount int64, tokenID string.
|
// `args` for TransferNEP11D: from, to util.Uint160, amount int64, tokenID string, data interface{}.
|
||||||
func (c *Client) CreateNEP11TransferTx(acc *wallet.Account, tokenHash util.Uint160,
|
func (c *Client) CreateNEP11TransferTx(acc *wallet.Account, tokenHash util.Uint160,
|
||||||
gas int64, cosigners []SignerAccount, args ...interface{}) (*transaction.Transaction, error) {
|
gas int64, cosigners []SignerAccount, args ...interface{}) (*transaction.Transaction, error) {
|
||||||
w := io.NewBufBinWriter()
|
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
|
// (in FixedN format using contract's number of decimals) to given account and
|
||||||
// sends it to the network returning just a hash of it.
|
// sends it to the network returning just a hash of it.
|
||||||
func (c *Client) TransferNEP11D(acc *wallet.Account, to util.Uint160,
|
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 {
|
if !c.initDone {
|
||||||
return util.Uint256{}, errNetworkNotInitialized
|
return util.Uint256{}, errNetworkNotInitialized
|
||||||
}
|
}
|
||||||
|
@ -151,7 +151,7 @@ func (c *Client) TransferNEP11D(acc *wallet.Account, to util.Uint160,
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return util.Uint256{}, fmt.Errorf("bad account address: %w", err)
|
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 {
|
if err != nil {
|
||||||
return util.Uint256{}, err
|
return util.Uint256{}, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -834,7 +834,7 @@ func TestClient_NEP11(t *testing.T) {
|
||||||
require.EqualValues(t, expected, p)
|
require.EqualValues(t, expected, p)
|
||||||
})
|
})
|
||||||
t.Run("Transfer", func(t *testing.T) {
|
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)
|
require.NoError(t, err)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -31,6 +31,7 @@ var nep11Base = &Standard{
|
||||||
Parameters: []manifest.Parameter{
|
Parameters: []manifest.Parameter{
|
||||||
{Name: "to", Type: smartcontract.Hash160Type},
|
{Name: "to", Type: smartcontract.Hash160Type},
|
||||||
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
||||||
|
{Name: "data", Type: smartcontract.AnyType},
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.BoolType,
|
ReturnType: smartcontract.BoolType,
|
||||||
},
|
},
|
||||||
|
@ -112,6 +113,7 @@ var nep11Divisible = &Standard{
|
||||||
{Name: "to", Type: smartcontract.Hash160Type},
|
{Name: "to", Type: smartcontract.Hash160Type},
|
||||||
{Name: "amount", Type: smartcontract.IntegerType},
|
{Name: "amount", Type: smartcontract.IntegerType},
|
||||||
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
{Name: "tokenId", Type: smartcontract.ByteArrayType},
|
||||||
|
{Name: "data", Type: smartcontract.AnyType},
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.BoolType,
|
ReturnType: smartcontract.BoolType,
|
||||||
},
|
},
|
||||||
|
|
|
@ -14,6 +14,7 @@ var nep11payable = &Standard{
|
||||||
{Name: "from", Type: smartcontract.Hash160Type},
|
{Name: "from", Type: smartcontract.Hash160Type},
|
||||||
{Name: "amount", Type: smartcontract.IntegerType},
|
{Name: "amount", Type: smartcontract.IntegerType},
|
||||||
{Name: "tokenid", Type: smartcontract.ByteArrayType},
|
{Name: "tokenid", Type: smartcontract.ByteArrayType},
|
||||||
|
{Name: "data", Type: smartcontract.AnyType},
|
||||||
},
|
},
|
||||||
ReturnType: smartcontract.VoidType,
|
ReturnType: smartcontract.VoidType,
|
||||||
}},
|
}},
|
||||||
|
|
Loading…
Reference in a new issue