manifest: support NEP-24

Close #3451

Signed-off-by: Ekaterina Pavlova <ekt@morphbits.io>
This commit is contained in:
Ekaterina Pavlova 2024-11-08 10:21:28 +03:00
parent 54e3708566
commit b2bd8e4a0a
21 changed files with 1715 additions and 36 deletions

View file

@ -476,7 +476,7 @@ func TestAssistedRPCBindings(t *testing.T) {
tmpDir := t.TempDir()
e := testcli.NewExecutor(t, false)
var checkBinding = func(source string, hasDefinedHash bool, guessEventTypes bool, suffix ...string) {
var checkBinding = func(source, configFile, expectedFile string, hasDefinedHash, guessEventTypes bool, suffix ...string) {
testName := source
if len(suffix) != 0 {
testName += suffix[0]
@ -484,13 +484,20 @@ func TestAssistedRPCBindings(t *testing.T) {
testName += fmt.Sprintf(", predefined hash: %t", hasDefinedHash)
t.Run(testName, func(t *testing.T) {
outFile := filepath.Join(tmpDir, "out.go")
configFile := filepath.Join(source, "config.yml")
expectedFile := filepath.Join(source, "rpcbindings.out")
if len(suffix) != 0 {
configFile = filepath.Join(source, "config"+suffix[0]+".yml")
expectedFile = filepath.Join(source, "rpcbindings"+suffix[0]+".out")
} else if !hasDefinedHash {
expectedFile = filepath.Join(source, "rpcbindings_dynamic_hash.out")
if configFile == "" {
if len(suffix) != 0 {
configFile = filepath.Join(source, "config"+suffix[0]+".yml")
} else {
configFile = filepath.Join(source, "config.yml")
}
}
if expectedFile == "" {
expectedFile = filepath.Join(source, "rpcbindings.out")
if len(suffix) != 0 {
expectedFile = filepath.Join(source, "rpcbindings"+suffix[0]+".out")
} else if !hasDefinedHash {
expectedFile = filepath.Join(source, "rpcbindings_dynamic_hash.out")
}
}
manifestF := filepath.Join(tmpDir, "manifest.json")
bindingF := filepath.Join(tmpDir, "binding.yml")
@ -532,12 +539,18 @@ func TestAssistedRPCBindings(t *testing.T) {
}
for _, hasDefinedHash := range []bool{true, false} {
checkBinding(filepath.Join("testdata", "rpcbindings", "types"), hasDefinedHash, false)
checkBinding(filepath.Join("testdata", "rpcbindings", "structs"), hasDefinedHash, false)
checkBinding(filepath.Join("testdata", "rpcbindings", "types"), "", "", hasDefinedHash, false)
checkBinding(filepath.Join("testdata", "rpcbindings", "structs"), "", "", hasDefinedHash, false)
checkBinding(filepath.Join("testdata", "rpcbindings", "royalty"), "", "", hasDefinedHash, false)
}
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), true, false)
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), true, false, "_extended")
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), true, true, "_guessed")
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), "", "", true, false)
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), "", "", true, false, "_extended")
checkBinding(filepath.Join("testdata", "rpcbindings", "notifications"), "", "", true, true, "_guessed")
checkBinding(filepath.Join("..", "..", "examples", "nft-d"), filepath.Join("..", "..", "examples", "nft-d", "nft.yml"), filepath.Join("testdata", "rpcbindings", "nft-d", "rpcbindings_dynamic_hash.out"), false, false)
checkBinding(filepath.Join("..", "..", "examples", "nft-d"), filepath.Join("..", "..", "examples", "nft-d", "nft.yml"), filepath.Join("testdata", "rpcbindings", "nft-d", "rpcbindings.out"), true, true)
checkBinding(filepath.Join("..", "..", "examples", "nft-nd"), filepath.Join("..", "..", "examples", "nft-nd", "nft.yml"), filepath.Join("testdata", "rpcbindings", "nft-nd", "rpcbindings_dynamic_hash.out"), false, false)
checkBinding(filepath.Join("..", "..", "examples", "nft-nd"), filepath.Join("..", "..", "examples", "nft-nd", "nft.yml"), filepath.Join("testdata", "rpcbindings", "nft-nd", "rpcbindings.out"), true, true)
require.False(t, rewriteExpectedOutputs)
}

View file

@ -0,0 +1,146 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package nft contains RPC wrappers for NeoFS Object NFT contract.
package nft
import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep24"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// Hash contains contract hash.
var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep11.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep11.Actor
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep11.DivisibleReader
nep24.RoyaltyReader
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep11.DivisibleWriter
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
func NewReader(invoker Invoker) *ContractReader {
var hash = Hash
return &ContractReader{*nep11.NewDivisibleReader(invoker, hash), *nep24.NewRoyaltyReader(invoker, hash), invoker, hash}
}
// New creates an instance of Contract using Hash and the given Actor.
func New(actor Actor) *Contract {
var hash = Hash
var nep11dt = nep11.NewDivisible(actor, hash)
var nep24t = nep24.NewRoyaltyReader(actor, hash)
return &Contract{ContractReader{nep11dt.DivisibleReader, *nep24t, actor, hash}, nep11dt.DivisibleWriter, actor, hash}
}
// Destroy creates a transaction invoking `destroy` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Destroy() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "destroy")
}
// DestroyTransaction creates a transaction invoking `destroy` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DestroyTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "destroy")
}
// DestroyUnsigned creates a transaction invoking `destroy` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DestroyUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "destroy", nil)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(nef []byte, manifest []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", nef, manifest)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", nef, manifest)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, nef, manifest)
}
func (c *Contract) scriptForVerify() ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "verify")
}
// Verify creates a transaction invoking `verify` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Verify() (util.Uint256, uint32, error) {
script, err := c.scriptForVerify()
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// VerifyTransaction creates a transaction invoking `verify` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) VerifyTransaction() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// VerifyUnsigned creates a transaction invoking `verify` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) VerifyUnsigned() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}

View file

@ -0,0 +1,141 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package nft contains RPC wrappers for NeoFS Object NFT contract.
package nft
import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep24"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep11.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep11.Actor
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep11.DivisibleReader
nep24.RoyaltyReader
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep11.DivisibleWriter
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker.
func NewReader(invoker Invoker, hash util.Uint160) *ContractReader {
return &ContractReader{*nep11.NewDivisibleReader(invoker, hash), *nep24.NewRoyaltyReader(invoker, hash), invoker, hash}
}
// New creates an instance of Contract using provided contract hash and the given Actor.
func New(actor Actor, hash util.Uint160) *Contract {
var nep11dt = nep11.NewDivisible(actor, hash)
var nep24t = nep24.NewRoyaltyReader(actor, hash)
return &Contract{ContractReader{nep11dt.DivisibleReader, *nep24t, actor, hash}, nep11dt.DivisibleWriter, actor, hash}
}
// Destroy creates a transaction invoking `destroy` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Destroy() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "destroy")
}
// DestroyTransaction creates a transaction invoking `destroy` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DestroyTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "destroy")
}
// DestroyUnsigned creates a transaction invoking `destroy` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DestroyUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "destroy", nil)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(nef []byte, manifest []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", nef, manifest)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", nef, manifest)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, nef, manifest)
}
func (c *Contract) scriptForVerify() ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "verify")
}
// Verify creates a transaction invoking `verify` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Verify() (util.Uint256, uint32, error) {
script, err := c.scriptForVerify()
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// VerifyTransaction creates a transaction invoking `verify` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) VerifyTransaction() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// VerifyUnsigned creates a transaction invoking `verify` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) VerifyUnsigned() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}

View file

@ -0,0 +1,249 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package nft contains RPC wrappers for HASHY NFT contract.
package nft
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep24"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
)
// Hash contains contract hash.
var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0}
// NftRoyaltyRecipientShare is a contract-specific nft.RoyaltyRecipientShare type used by its methods.
type NftRoyaltyRecipientShare struct {
Address util.Uint160
Share *big.Int
}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep11.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep11.Actor
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep11.NonDivisibleReader
nep24.RoyaltyReader
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep11.BaseWriter
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
func NewReader(invoker Invoker) *ContractReader {
var hash = Hash
return &ContractReader{*nep11.NewNonDivisibleReader(invoker, hash), *nep24.NewRoyaltyReader(invoker, hash), invoker, hash}
}
// New creates an instance of Contract using Hash and the given Actor.
func New(actor Actor) *Contract {
var hash = Hash
var nep11ndt = nep11.NewNonDivisible(actor, hash)
var nep24t = nep24.NewRoyaltyReader(actor, hash)
return &Contract{ContractReader{nep11ndt.NonDivisibleReader, *nep24t, actor, hash}, nep11ndt.BaseWriter, actor, hash}
}
// Destroy creates a transaction invoking `destroy` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Destroy() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "destroy")
}
// DestroyTransaction creates a transaction invoking `destroy` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DestroyTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "destroy")
}
// DestroyUnsigned creates a transaction invoking `destroy` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DestroyUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "destroy", nil)
}
func (c *Contract) scriptForSetRoyaltyInfo(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "setRoyaltyInfo", ctx, tokenID, recipients)
}
// SetRoyaltyInfo creates a transaction invoking `setRoyaltyInfo` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetRoyaltyInfo(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (util.Uint256, uint32, error) {
script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients)
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// SetRoyaltyInfoTransaction creates a transaction invoking `setRoyaltyInfo` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetRoyaltyInfoTransaction(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (*transaction.Transaction, error) {
script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients)
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// SetRoyaltyInfoUnsigned creates a transaction invoking `setRoyaltyInfo` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetRoyaltyInfoUnsigned(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (*transaction.Transaction, error) {
script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients)
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(nef []byte, manifest []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", nef, manifest)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", nef, manifest)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, nef, manifest)
}
func (c *Contract) scriptForVerify() ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "verify")
}
// Verify creates a transaction invoking `verify` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Verify() (util.Uint256, uint32, error) {
script, err := c.scriptForVerify()
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// VerifyTransaction creates a transaction invoking `verify` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) VerifyTransaction() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// VerifyUnsigned creates a transaction invoking `verify` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) VerifyUnsigned() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}
// itemToNftRoyaltyRecipientShare converts stack item into *NftRoyaltyRecipientShare.
// NULL item is returned as nil pointer without error.
func itemToNftRoyaltyRecipientShare(item stackitem.Item, err error) (*NftRoyaltyRecipientShare, error) {
if err != nil {
return nil, err
}
_, null := item.(stackitem.Null)
if null {
return nil, nil
}
var res = new(NftRoyaltyRecipientShare)
err = res.FromStackItem(item)
return res, err
}
// FromStackItem retrieves fields of NftRoyaltyRecipientShare from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
func (res *NftRoyaltyRecipientShare) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
res.Address, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Address: %w", err)
}
index++
res.Share, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Share: %w", err)
}
return nil
}

View file

@ -0,0 +1,244 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package nft contains RPC wrappers for HASHY NFT contract.
package nft
import (
"errors"
"fmt"
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep24"
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"math/big"
)
// NftRoyaltyRecipientShare is a contract-specific nft.RoyaltyRecipientShare type used by its methods.
type NftRoyaltyRecipientShare struct {
Address util.Uint160
Share *big.Int
}
// Invoker is used by ContractReader to call various safe methods.
type Invoker interface {
nep11.Invoker
}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
Invoker
nep11.Actor
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// ContractReader implements safe contract methods.
type ContractReader struct {
nep11.NonDivisibleReader
nep24.RoyaltyReader
invoker Invoker
hash util.Uint160
}
// Contract implements all contract methods.
type Contract struct {
ContractReader
nep11.BaseWriter
actor Actor
hash util.Uint160
}
// NewReader creates an instance of ContractReader using provided contract hash and the given Invoker.
func NewReader(invoker Invoker, hash util.Uint160) *ContractReader {
return &ContractReader{*nep11.NewNonDivisibleReader(invoker, hash), *nep24.NewRoyaltyReader(invoker, hash), invoker, hash}
}
// New creates an instance of Contract using provided contract hash and the given Actor.
func New(actor Actor, hash util.Uint160) *Contract {
var nep11ndt = nep11.NewNonDivisible(actor, hash)
var nep24t = nep24.NewRoyaltyReader(actor, hash)
return &Contract{ContractReader{nep11ndt.NonDivisibleReader, *nep24t, actor, hash}, nep11ndt.BaseWriter, actor, hash}
}
// Destroy creates a transaction invoking `destroy` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Destroy() (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "destroy")
}
// DestroyTransaction creates a transaction invoking `destroy` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) DestroyTransaction() (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "destroy")
}
// DestroyUnsigned creates a transaction invoking `destroy` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) DestroyUnsigned() (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "destroy", nil)
}
func (c *Contract) scriptForSetRoyaltyInfo(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "setRoyaltyInfo", ctx, tokenID, recipients)
}
// SetRoyaltyInfo creates a transaction invoking `setRoyaltyInfo` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) SetRoyaltyInfo(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (util.Uint256, uint32, error) {
script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients)
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// SetRoyaltyInfoTransaction creates a transaction invoking `setRoyaltyInfo` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) SetRoyaltyInfoTransaction(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (*transaction.Transaction, error) {
script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients)
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// SetRoyaltyInfoUnsigned creates a transaction invoking `setRoyaltyInfo` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) SetRoyaltyInfoUnsigned(ctx any, tokenID []byte, recipients []*NftRoyaltyRecipientShare) (*transaction.Transaction, error) {
script, err := c.scriptForSetRoyaltyInfo(ctx, tokenID, recipients)
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}
// Update creates a transaction invoking `update` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Update(nef []byte, manifest []byte) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "update", nef, manifest)
}
// UpdateTransaction creates a transaction invoking `update` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) UpdateTransaction(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "update", nef, manifest)
}
// UpdateUnsigned creates a transaction invoking `update` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) UpdateUnsigned(nef []byte, manifest []byte) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "update", nil, nef, manifest)
}
func (c *Contract) scriptForVerify() ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(c.hash, "verify")
}
// Verify creates a transaction invoking `verify` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) Verify() (util.Uint256, uint32, error) {
script, err := c.scriptForVerify()
if err != nil {
return util.Uint256{}, 0, err
}
return c.actor.SendRun(script)
}
// VerifyTransaction creates a transaction invoking `verify` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) VerifyTransaction() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeRun(script)
}
// VerifyUnsigned creates a transaction invoking `verify` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) VerifyUnsigned() (*transaction.Transaction, error) {
script, err := c.scriptForVerify()
if err != nil {
return nil, err
}
return c.actor.MakeUnsignedRun(script, nil)
}
// itemToNftRoyaltyRecipientShare converts stack item into *NftRoyaltyRecipientShare.
// NULL item is returned as nil pointer without error.
func itemToNftRoyaltyRecipientShare(item stackitem.Item, err error) (*NftRoyaltyRecipientShare, error) {
if err != nil {
return nil, err
}
_, null := item.(stackitem.Null)
if null {
return nil, nil
}
var res = new(NftRoyaltyRecipientShare)
err = res.FromStackItem(item)
return res, err
}
// FromStackItem retrieves fields of NftRoyaltyRecipientShare from the given
// [stackitem.Item] or returns an error if it's not possible to do to so.
func (res *NftRoyaltyRecipientShare) FromStackItem(item stackitem.Item) error {
arr, ok := item.Value().([]stackitem.Item)
if !ok {
return errors.New("not an array")
}
if len(arr) != 2 {
return errors.New("wrong number of structure elements")
}
var (
index = -1
err error
)
index++
res.Address, err = func(item stackitem.Item) (util.Uint160, error) {
b, err := item.TryBytes()
if err != nil {
return util.Uint160{}, err
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return util.Uint160{}, err
}
return u, nil
}(arr[index])
if err != nil {
return fmt.Errorf("field Address: %w", err)
}
index++
res.Share, err = arr[index].TryInteger()
if err != nil {
return fmt.Errorf("field Share: %w", err)
}
return nil
}

View file

@ -0,0 +1,16 @@
name: Test royalty
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-24-Payable"]
events:
- name: RoyaltiesTransferred
parameters:
- name: royaltyToken
type: Hash160
- name: royaltyRecipient
type: Hash160
- name: buyer
type: Hash160
- name: tokenId
type: ByteArray
- name: amount
type: Integer

View file

@ -0,0 +1,13 @@
package royalty
import (
"github.com/nspcc-dev/neo-go/pkg/interop"
"github.com/nspcc-dev/neo-go/pkg/interop/native/std"
"github.com/nspcc-dev/neo-go/pkg/interop/runtime"
)
// RoyaltiesTransferred notifies about royalty payment. This method is called by marketplace
// contract when royalties are transferred.
func RoyaltiesTransferred(royaltyToken, royaltyRecipient, buyer interop.Hash160, tokenId []byte, amount int) {
runtime.Notify("RoyaltiesTransferred", royaltyToken, royaltyRecipient, buyer, std.Deserialize(tokenId), amount)
}

View file

@ -0,0 +1,57 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package royalty contains RPC wrappers for Test royalty contract.
package royalty
import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/util"
"math/big"
)
// Hash contains contract hash.
var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0}
// Actor is used by Contract to call state-changing methods.
type Actor interface {
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// Contract implements all contract methods.
type Contract struct {
actor Actor
hash util.Uint160
}
// New creates an instance of Contract using Hash and the given Actor.
func New(actor Actor) *Contract {
var hash = Hash
return &Contract{actor, hash}
}
// RoyaltiesTransferred creates a transaction invoking `royaltiesTransferred` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) RoyaltiesTransferred(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "royaltiesTransferred", royaltyToken, royaltyRecipient, buyer, tokenId, amount)
}
// RoyaltiesTransferredTransaction creates a transaction invoking `royaltiesTransferred` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) RoyaltiesTransferredTransaction(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "royaltiesTransferred", royaltyToken, royaltyRecipient, buyer, tokenId, amount)
}
// RoyaltiesTransferredUnsigned creates a transaction invoking `royaltiesTransferred` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) RoyaltiesTransferredUnsigned(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "royaltiesTransferred", nil, royaltyToken, royaltyRecipient, buyer, tokenId, amount)
}

View file

@ -0,0 +1,53 @@
// Code generated by neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> [--hash <hash>] [--config <config>]; DO NOT EDIT.
// Package royalty contains RPC wrappers for Test royalty contract.
package royalty
import (
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
"github.com/nspcc-dev/neo-go/pkg/util"
"math/big"
)
// Actor is used by Contract to call state-changing methods.
type Actor interface {
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
MakeRun(script []byte) (*transaction.Transaction, error)
MakeUnsignedCall(contract util.Uint160, method string, attrs []transaction.Attribute, params ...any) (*transaction.Transaction, error)
MakeUnsignedRun(script []byte, attrs []transaction.Attribute) (*transaction.Transaction, error)
SendCall(contract util.Uint160, method string, params ...any) (util.Uint256, uint32, error)
SendRun(script []byte) (util.Uint256, uint32, error)
}
// Contract implements all contract methods.
type Contract struct {
actor Actor
hash util.Uint160
}
// New creates an instance of Contract using provided contract hash and the given Actor.
func New(actor Actor, hash util.Uint160) *Contract {
return &Contract{actor, hash}
}
// RoyaltiesTransferred creates a transaction invoking `royaltiesTransferred` method of the contract.
// This transaction is signed and immediately sent to the network.
// The values returned are its hash, ValidUntilBlock value and error if any.
func (c *Contract) RoyaltiesTransferred(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (util.Uint256, uint32, error) {
return c.actor.SendCall(c.hash, "royaltiesTransferred", royaltyToken, royaltyRecipient, buyer, tokenId, amount)
}
// RoyaltiesTransferredTransaction creates a transaction invoking `royaltiesTransferred` method of the contract.
// This transaction is signed, but not sent to the network, instead it's
// returned to the caller.
func (c *Contract) RoyaltiesTransferredTransaction(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeCall(c.hash, "royaltiesTransferred", royaltyToken, royaltyRecipient, buyer, tokenId, amount)
}
// RoyaltiesTransferredUnsigned creates a transaction invoking `royaltiesTransferred` method of the contract.
// This transaction is not signed, it's simply returned to the caller.
// Any fields of it that do not affect fees can be changed (ValidUntilBlock,
// Nonce), fee values (NetworkFee, SystemFee) can be increased as well.
func (c *Contract) RoyaltiesTransferredUnsigned(royaltyToken util.Uint160, royaltyRecipient util.Uint160, buyer util.Uint160, tokenId []byte, amount *big.Int) (*transaction.Transaction, error) {
return c.actor.MakeUnsignedCall(c.hash, "royaltiesTransferred", nil, royaltyToken, royaltyRecipient, buyer, tokenId, amount)
}

View file

@ -25,8 +25,8 @@ See the table below for the detailed examples description.
| [engine](engine) | This contract demonstrates how to use `runtime` interop package which implements an API for `System.Runtime.*` Neo system calls. Please, refer to the `runtime` [package documentation](../pkg/interop/doc.go) for details. |
| [events](events) | The contract shows how execution notifications with the different arguments types can be sent with the help of `runtime.Notify` function of the `runtime` interop package. Please, refer to the `runtime.Notify` [function documentation](../pkg/interop/runtime/runtime.go) for details. |
| [iterator](iterator) | This example describes a way to work with Neo iterators. Please, refer to the `iterator` [package documentation](../pkg/interop/iterator/iterator.go) for details. |
| [nft-d](nft-d) | NEP-11 divisible NFT. See NEP-11 token standard [specification](https://github.com/neo-project/proposals/blob/master/nep-11.mediawiki) for details. |
| [nft-nd](nft-nd) | NEP-11 non-divisible NFT. See NEP-11 token standard [specification](https://github.com/neo-project/proposals/blob/master/nep-11.mediawiki) for details. |
| [nft-d](nft-d) | NEP-11 divisible NFT. This contract implements the NEP-11 and the NEP-24 token standards. See NEP-11 token standard [specification](https://github.com/neo-project/proposals/blob/master/nep-11.mediawiki) and NEP-24 [specification](https://github.com/neo-project/proposals/blob/master/nep-24.mediawiki) for details. |
| [nft-nd](nft-nd) | NEP-11 non-divisible NFT. This contract implements the NEP-11 and the NEP-24 token standards. See NEP-11 token standard [specification](https://github.com/neo-project/proposals/blob/master/nep-11.mediawiki) and NEP-24 [specification](https://github.com/neo-project/proposals/blob/master/nep-24.mediawiki) for details. |
| [nft-nd-nns](nft-nd-nns) | Neo Name Service contract which is NEP-11 non-divisible NFT. The contract implements methods for Neo domain name system managing such as domains registration/transferring, records addition and names resolving. The package also contains tests implemented with [neotest](https://pkg.go.dev/github.com/nspcc-dev/neo-go/pkg/neotest). |
| [oracle](oracle) | Oracle demo contract exposing two methods that you can use to process URLs. It uses oracle native contract, see [interop package documentation](../pkg/interop/native/oracle/oracle.go) also. |
| [runtime](runtime) | This contract demonstrates how to use special `_initialize` and `_deploy` methods. See the [compiler documentation](../docs/compiler.md#vm-api-interop-layer ) for methods details. It also shows the pattern for checking owner witness inside the contract with the help of `runtime.CheckWitness` interop [function](../pkg/interop/runtime/runtime.go). |

View file

@ -425,3 +425,43 @@ func Update(nef, manifest []byte) {
}
management.Update(nef, manifest)
}
// RoyaltyRecipient contains information about the recipient and the royalty amount.
type RoyaltyRecipient struct {
Address interop.Hash160
Amount int
}
// RoyaltyInfo returns a list of royalty recipients and the corresponding royalty amounts.
func RoyaltyInfo(tokenID []byte, royaltyToken interop.Hash160, salePrice int) []RoyaltyRecipient {
if salePrice <= 0 {
panic("sale price must be positive")
}
executingHash := runtime.GetExecutingScriptHash()
if !royaltyToken.Equals(executingHash) {
panic("invalid royalty token")
}
ctx := storage.GetReadOnlyContext()
if !isTokenValid(ctx, tokenID) {
panic("unknown token")
}
ownerIter := ownersOf(ctx, tokenID)
var owners []interop.Hash160
for iterator.Next(ownerIter) {
owners = append(owners, iterator.Value(ownerIter).(interop.Hash160))
}
var (
recipients []RoyaltyRecipient
amount = salePrice / 10 / len(owners)
)
for _, owner := range owners {
recipients = append(recipients, RoyaltyRecipient{
Address: owner,
Amount: amount,
})
}
return recipients
}

View file

@ -1,7 +1,7 @@
name: "NeoFS Object NFT"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-11"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "properties", "tokens"]
supportedstandards: ["NEP-11", "NEP-24"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "properties", "tokens", "royaltyInfo"]
events:
- name: Transfer
parameters:

View file

@ -30,6 +30,8 @@ const (
accountPrefix = "a"
// tokenPrefix contains map from token id to it's owner.
tokenPrefix = "t"
// royaltyInfoPrefix contains map from token id to its royalty information.
royaltyInfoPrefix = "r"
)
var (
@ -285,3 +287,68 @@ func Properties(id []byte) map[string]string {
}
return result
}
// RoyaltyRecipient contains information about the recipient and the royalty amount.
type RoyaltyRecipient struct {
Address interop.Hash160
Amount int
}
// RoyaltyInfo returns a list of royalty recipients and the corresponding royalty amounts.
func RoyaltyInfo(tokenID []byte, royaltyToken interop.Hash160, salePrice int) []RoyaltyRecipient {
if salePrice <= 0 {
panic("sale price must be positive")
}
executingHash := runtime.GetExecutingScriptHash()
if !royaltyToken.Equals(executingHash) {
panic("invalid royalty token")
}
ctx := storage.GetReadOnlyContext()
owner := getOwnerOf(ctx, tokenID)
if owner == nil {
panic("invalid token ID")
}
royaltyInfoKey := append([]byte(royaltyInfoPrefix), tokenID...)
val := storage.Get(ctx, royaltyInfoKey)
var recipients []RoyaltyRecipient
if val == nil {
recipients = []RoyaltyRecipient{
{
Address: owner,
Amount: salePrice / 10,
},
}
} else {
bval := val.([]byte)
storedRecipients := std.Deserialize(bval)
st := storedRecipients.([]RoyaltyRecipientShare)
for _, r := range st {
recipients = append(recipients, RoyaltyRecipient{
Address: r.Address,
Amount: salePrice * r.Share / 100,
})
}
}
return recipients
}
// RoyaltyRecipientShare contains information about the recipient and the royalty share in percents.
type RoyaltyRecipientShare struct {
Address interop.Hash160
Share int
}
// SetRoyaltyInfo sets the royalty share for specified recipients on a given token ID.
// Only the token owner can set the royalty information.
func SetRoyaltyInfo(ctx storage.Context, tokenID []byte, recipients []RoyaltyRecipientShare) bool {
if !runtime.CheckWitness(getOwnerOf(ctx, tokenID)) {
return false
}
putKey := append([]byte(royaltyInfoPrefix), tokenID...)
storage.Put(ctx, putKey, std.Serialize(recipients))
return true
}

View file

@ -1,7 +1,7 @@
name: "HASHY NFT"
sourceurl: https://github.com/nspcc-dev/neo-go/
supportedstandards: ["NEP-11"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "tokens", "properties"]
supportedstandards: ["NEP-11", "NEP-24"]
safemethods: ["balanceOf", "decimals", "symbol", "totalSupply", "tokensOf", "ownerOf", "tokens", "properties", "royaltyInfo"]
events:
- name: Transfer
parameters:

View file

@ -0,0 +1,32 @@
package nep24_test
import (
"context"
"math/big"
"github.com/nspcc-dev/neo-go/pkg/rpcclient"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/invoker"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep24"
"github.com/nspcc-dev/neo-go/pkg/util"
)
func ExampleRoyaltyReader() {
// No error checking done at all, intentionally.
c, _ := rpcclient.New(context.Background(), "url", rpcclient.Options{})
// Safe methods are reachable with just an invoker, no need for an account there.
inv := invoker.New(c, nil)
// NEP-24 contract hash.
nep24Hash := util.Uint160{9, 8, 7}
// And a reader interface.
n24 := nep24.NewRoyaltyReader(inv, nep24Hash)
// Get the royalty information for a token.
tokenID := []byte("someTokenID")
royaltyToken := util.Uint160{1, 2, 3}
salePrice := big.NewInt(1000)
royaltyInfo, _ := n24.RoyaltyInfo(tokenID, royaltyToken, salePrice)
_ = royaltyInfo
}

View file

@ -0,0 +1,185 @@
/*
Package nep24 provides RPC wrappers for NEP-24 contracts.
All methods are safe (read-only) and encapsulated in the RoyaltyReader structure,
designed for managing NFT royalties and retrieving royalty information.
Refer to the nep11 package for basic NFT functionalities, while nep24 handles
royalty-related operations.
*/
package nep24
import (
"errors"
"fmt"
"math/big"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/rpcclient/neptoken"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
)
// RoyaltyRecipient contains information about the recipient and the royalty amount.
type RoyaltyRecipient struct {
Address util.Uint160
Amount *big.Int
}
// RoyaltiesTransferredEvent represents a RoyaltiesTransferred event as defined in
// the NEP-24 standard.
type RoyaltiesTransferredEvent struct {
RoyaltyToken util.Uint160
RoyaltyRecipient util.Uint160
Buyer util.Uint160
TokenID []byte
Amount *big.Int
}
// RoyaltyReader represents safe (read-only) methods of NEP-24 token. It can be
// used to query data about royalties.
type RoyaltyReader struct {
invoker neptoken.Invoker
hash util.Uint160
}
// NewRoyaltyReader returns a new RoyaltyReader instance.
func NewRoyaltyReader(invoker neptoken.Invoker, hash util.Uint160) *RoyaltyReader {
return &RoyaltyReader{
invoker: invoker,
hash: hash,
}
}
// RoyaltyInfo returns the royalty information for the given tokenID, royaltyToken,
// and salePrice.
func (c *RoyaltyReader) RoyaltyInfo(tokenID []byte, royaltyToken util.Uint160, salePrice *big.Int) ([]RoyaltyRecipient, error) {
res, err := c.invoker.Call(c.hash, "royaltyInfo", tokenID, royaltyToken, salePrice)
if err != nil {
return nil, err
}
if len(res.Stack) != 1 {
return nil, errors.New("invalid response: expected a single item on the stack")
}
rootItem, ok := res.Stack[0].Value().([]stackitem.Item)
if !ok {
return nil, errors.New("invalid response: expected an array of royalties")
}
var royalties []RoyaltyRecipient
for _, item := range rootItem {
royalty, ok := item.Value().([]stackitem.Item)
if !ok {
return nil, fmt.Errorf("invalid royalty structure: expected array of 2 items, got %d", len(royalty))
}
var recipient RoyaltyRecipient
err = recipient.FromStackItem(royalty)
if err != nil {
return nil, fmt.Errorf("failed to decode royalty detail: %w", err)
}
royalties = append(royalties, recipient)
}
return royalties, nil
}
// FromStackItem converts a stack item into a RoyaltyRecipient struct.
func (r *RoyaltyRecipient) FromStackItem(item []stackitem.Item) error {
if len(item) != 2 {
return fmt.Errorf("invalid royalty structure: expected 2 items, got %d", len(item))
}
recipientBytes, err := item[0].TryBytes()
if err != nil {
return fmt.Errorf("failed to decode recipient address: %w", err)
}
recipient, err := util.Uint160DecodeBytesBE(recipientBytes)
if err != nil {
return fmt.Errorf("invalid recipient address: %w", err)
}
amountBigInt, err := item[1].TryInteger()
if err != nil {
return fmt.Errorf("failed to decode royalty amount: %w", err)
}
if amountBigInt.Sign() < 0 {
return errors.New("negative royalty amount")
}
amount := big.NewInt(0).Set(amountBigInt)
r.Amount = amount
r.Address = recipient
return nil
}
// RoyaltiesTransferredEventsFromApplicationLog retrieves all emitted
// RoyaltiesTransferredEvents from the provided [result.ApplicationLog].
func RoyaltiesTransferredEventsFromApplicationLog(log *result.ApplicationLog) ([]*RoyaltiesTransferredEvent, error) {
if log == nil {
return nil, errors.New("nil application log")
}
var res []*RoyaltiesTransferredEvent
for i, ex := range log.Executions {
for j, e := range ex.Events {
if e.Name != "RoyaltiesTransferred" {
continue
}
event := new(RoyaltiesTransferredEvent)
err := event.FromStackItem(e.Item)
if err != nil {
return nil, fmt.Errorf("failed to decode event from stackitem (event #%d, execution #%d): %w", j, i, err)
}
res = append(res, event)
}
}
return res, nil
}
// FromStackItem converts a stack item into a RoyaltiesTransferredEvent struct.
func (e *RoyaltiesTransferredEvent) FromStackItem(item *stackitem.Array) error {
if item == nil {
return errors.New("nil item")
}
arr, ok := item.Value().([]stackitem.Item)
if !ok || len(arr) != 5 {
return errors.New("invalid event structure: expected array of 5 items")
}
b, err := arr[0].TryBytes()
if err != nil {
return fmt.Errorf("failed to decode RoyaltyToken: %w", err)
}
e.RoyaltyToken, err = util.Uint160DecodeBytesBE(b)
if err != nil {
return fmt.Errorf("invalid RoyaltyToken: %w", err)
}
b, err = arr[1].TryBytes()
if err != nil {
return fmt.Errorf("failed to decode RoyaltyRecipient: %w", err)
}
e.RoyaltyRecipient, err = util.Uint160DecodeBytesBE(b)
if err != nil {
return fmt.Errorf("invalid RoyaltyRecipient: %w", err)
}
b, err = arr[2].TryBytes()
if err != nil {
return fmt.Errorf("failed to decode Buyer: %w", err)
}
e.Buyer, err = util.Uint160DecodeBytesBE(b)
if err != nil {
return fmt.Errorf("invalid Buyer: %w", err)
}
e.TokenID, err = arr[3].TryBytes()
if err != nil {
return fmt.Errorf("failed to decode TokenID: %w", err)
}
e.Amount, err = arr[4].TryInteger()
if err != nil {
return fmt.Errorf("failed to decode Amount: %w", err)
}
return nil
}

View file

@ -0,0 +1,300 @@
package nep24
import (
"errors"
"fmt"
"math/big"
"testing"
"github.com/nspcc-dev/neo-go/pkg/core/state"
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
"github.com/nspcc-dev/neo-go/pkg/util"
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
"github.com/stretchr/testify/require"
)
type testAct struct {
err error
res *result.Invoke
}
func (t *testAct) Call(contract util.Uint160, operation string, params ...any) (*result.Invoke, error) {
return t.res, t.err
}
func TestRoyaltyReaderRoyaltyInfo(t *testing.T) {
ta := new(testAct)
rr := NewRoyaltyReader(ta, util.Uint160{1, 2, 3})
tokenID := []byte{1, 2, 3}
royaltyToken := util.Uint160{4, 5, 6}
salePrice := big.NewInt(1000)
ta.res = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make([]stackitem.Item{
stackitem.Make([]stackitem.Item{
stackitem.Make(util.Uint160{7, 8, 9}.BytesBE()),
stackitem.Make(big.NewInt(100)),
}),
stackitem.Make([]stackitem.Item{
stackitem.Make(util.Uint160{7, 8, 9}.BytesBE()),
stackitem.Make(big.NewInt(200)),
}),
}),
},
}
ri, err := rr.RoyaltyInfo(tokenID, royaltyToken, salePrice)
require.NoError(t, err)
require.Equal(t, []RoyaltyRecipient{
{
Address: util.Uint160{7, 8, 9},
Amount: big.NewInt(100),
},
{
Address: util.Uint160{7, 8, 9},
Amount: big.NewInt(200),
},
}, ri)
ta.err = errors.New("")
_, err = rr.RoyaltyInfo(tokenID, royaltyToken, salePrice)
require.Error(t, err)
ta.res = &result.Invoke{
State: "HALT",
Stack: []stackitem.Item{
stackitem.Make([]stackitem.Item{
stackitem.Make(util.Uint160{7, 8, 9}.BytesBE()),
}),
},
}
_, err = rr.RoyaltyInfo(tokenID, royaltyToken, salePrice)
require.Error(t, err)
}
func TestRoyaltyRecipient_FromStackItem(t *testing.T) {
tests := map[string]struct {
items []stackitem.Item
err error
expected RoyaltyRecipient
}{
"good": {
items: []stackitem.Item{
stackitem.Make(util.Uint160{7, 8, 9}.BytesBE()),
stackitem.Make(big.NewInt(100)),
},
err: nil,
expected: RoyaltyRecipient{
Address: util.Uint160{7, 8, 9},
Amount: big.NewInt(100),
},
},
"invalid number of items": {
items: []stackitem.Item{
stackitem.Make(util.Uint160{7, 8, 9}.BytesBE()),
},
err: fmt.Errorf("invalid royalty structure: expected 2 items, got 1"),
},
"invalid recipient size": {
items: []stackitem.Item{
stackitem.Make([]byte{1, 2}),
stackitem.Make(big.NewInt(100)),
},
err: fmt.Errorf("invalid recipient address: expected byte size of 20 got 2"),
},
"invalid recipient type": {
items: []stackitem.Item{
stackitem.Make([]int{7, 8, 9}),
stackitem.Make(big.NewInt(100)),
},
err: fmt.Errorf("failed to decode recipient address: invalid conversion: Array/ByteString"),
},
"invalid amount type": {
items: []stackitem.Item{
stackitem.Make(util.Uint160{7, 8, 9}.BytesBE()),
stackitem.Make([]int{7, 8, 9}),
},
err: fmt.Errorf("failed to decode royalty amount: invalid conversion: Array/Integer"),
},
"negative amount": {
items: []stackitem.Item{
stackitem.Make(util.Uint160{7, 8, 9}.BytesBE()),
stackitem.Make(big.NewInt(-100)),
},
err: fmt.Errorf("negative royalty amount"),
},
}
for name, tt := range tests {
t.Run(name, func(t *testing.T) {
var ri RoyaltyRecipient
err := ri.FromStackItem(tt.items)
if tt.err != nil {
require.EqualError(t, err, tt.err.Error())
} else {
require.NoError(t, err)
require.Equal(t, tt.expected, ri)
}
})
}
}
func TestRoyaltiesTransferredEventFromStackitem(t *testing.T) {
tests := []struct {
name string
item *stackitem.Array
expectErr bool
expected *RoyaltiesTransferredEvent
}{
{
name: "good",
item: stackitem.NewArray([]stackitem.Item{
stackitem.Make(util.Uint160{1, 2, 3}.BytesBE()), // RoyaltyToken
stackitem.Make(util.Uint160{4, 5, 6}.BytesBE()), // RoyaltyRecipient
stackitem.Make(util.Uint160{7, 8, 9}.BytesBE()), // Buyer
stackitem.Make([]byte{1, 2, 3}), // TokenID
stackitem.Make(big.NewInt(100)), // Amount
}),
expectErr: false,
expected: &RoyaltiesTransferredEvent{
RoyaltyToken: util.Uint160{1, 2, 3},
RoyaltyRecipient: util.Uint160{4, 5, 6},
Buyer: util.Uint160{7, 8, 9},
TokenID: []byte{1, 2, 3},
Amount: big.NewInt(100),
},
},
{
name: "invalid number of items",
item: stackitem.NewArray([]stackitem.Item{
stackitem.Make(util.Uint160{1, 2, 3}.BytesBE()), // Only one item
}),
expectErr: true,
},
{
name: "invalid recipient size",
item: stackitem.NewArray([]stackitem.Item{
stackitem.Make(util.Uint160{1, 2, 3}.BytesBE()), // RoyaltyToken
stackitem.Make([]byte{1, 2}), // Invalid RoyaltyRecipient
stackitem.Make(util.Uint160{7, 8, 9}.BytesBE()), // Buyer
stackitem.Make([]byte{1, 2, 3}), // TokenID
stackitem.Make(big.NewInt(100)), // Amount
}),
expectErr: true,
},
{
name: "invalid integer amount",
item: stackitem.NewArray([]stackitem.Item{
stackitem.Make(util.Uint160{1, 2, 3}.BytesBE()), // RoyaltyToken
stackitem.Make(util.Uint160{4, 5, 6}.BytesBE()), // RoyaltyRecipient
stackitem.Make(util.Uint160{7, 8, 9}.BytesBE()), // Buyer
stackitem.Make([]byte{1, 2, 3}), // TokenID
stackitem.Make(stackitem.NewStruct(nil)), // Invalid integer for Amount
}),
expectErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
event := new(RoyaltiesTransferredEvent)
err := event.FromStackItem(tt.item)
if tt.expectErr {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, tt.expected, event)
}
})
}
}
func TestRoyaltiesTransferredEventsFromApplicationLog(t *testing.T) {
createEvent := func(token, recipient, buyer util.Uint160, tokenID []byte, amount *big.Int) state.NotificationEvent {
return state.NotificationEvent{
ScriptHash: util.Uint160{1, 2, 3}, // Any contract address.
Name: "RoyaltiesTransferred",
Item: stackitem.NewArray([]stackitem.Item{
stackitem.Make(token.BytesBE()), // RoyaltyToken
stackitem.Make(recipient.BytesBE()), // RoyaltyRecipient
stackitem.Make(buyer.BytesBE()), // Buyer
stackitem.Make(tokenID), // TokenID
stackitem.Make(amount), // Amount
}),
}
}
tests := []struct {
name string
log *result.ApplicationLog
expectErr bool
expected []*RoyaltiesTransferredEvent
}{
{
name: "valid log with one event",
log: &result.ApplicationLog{
Executions: []state.Execution{
{
Events: []state.NotificationEvent{
createEvent(
util.Uint160{1, 2, 3}, // RoyaltyToken
util.Uint160{4, 5, 6}, // RoyaltyRecipient
util.Uint160{7, 8, 9}, // Buyer
[]byte{1, 2, 3}, // TokenID
big.NewInt(100), // Amount
),
},
},
},
},
expectErr: false,
expected: []*RoyaltiesTransferredEvent{
{
RoyaltyToken: util.Uint160{1, 2, 3},
RoyaltyRecipient: util.Uint160{4, 5, 6},
Buyer: util.Uint160{7, 8, 9},
TokenID: []byte{1, 2, 3},
Amount: big.NewInt(100),
},
},
},
{
name: "invalid event structure (missing fields)",
log: &result.ApplicationLog{
Executions: []state.Execution{
{
Events: []state.NotificationEvent{
{
Name: "RoyaltiesTransferred",
Item: stackitem.NewArray([]stackitem.Item{
stackitem.Make(util.Uint160{1, 2, 3}.BytesBE()), // RoyaltyToken
// Missing other fields
}),
},
},
},
},
},
expectErr: true,
},
{
name: "empty log",
log: &result.ApplicationLog{},
expectErr: false,
expected: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
events, err := RoyaltiesTransferredEventsFromApplicationLog(tt.log)
if tt.expectErr {
require.Error(t, err)
} else {
require.NoError(t, err)
require.Equal(t, tt.expected, events)
}
})
}
}

View file

@ -26,6 +26,11 @@ const (
NEP11Payable = "NEP-11-Payable"
// NEP17Payable represents the name of contract interface which can receive NEP-17 tokens.
NEP17Payable = "NEP-17-Payable"
// NEP24StandardName represents the name of the NEP-24 smart contract standard for NFT royalties.
NEP24StandardName = "NEP-24"
// NEP24Payable represents the name of the contract interface for handling royalty payments in accordance
// with the NEP-24 standard.
NEP24Payable = "NEP-24-Payable"
emptyFeatures = "{}"
)

View file

@ -23,6 +23,8 @@ var checks = map[string][]*Standard{
manifest.NEP17StandardName: {Nep17},
manifest.NEP11Payable: {Nep11Payable},
manifest.NEP17Payable: {Nep17Payable},
manifest.NEP24StandardName: {Nep24},
manifest.NEP24Payable: {Nep24Payable},
}
// Check checks if the manifest complies with all provided standards.

View file

@ -0,0 +1,51 @@
package standard
import (
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
)
// MethodRoyaltyInfo is the name of the method that returns royalty information.
const MethodRoyaltyInfo = "royaltyInfo"
// Nep24 is a NEP-24 Standard for NFT royalties.
var Nep24 = &Standard{
Manifest: manifest.Manifest{
ABI: manifest.ABI{
Methods: []manifest.Method{
{
Name: MethodRoyaltyInfo,
Parameters: []manifest.Parameter{
{Name: "tokenId", Type: smartcontract.ByteArrayType},
{Name: "royaltyToken", Type: smartcontract.Hash160Type},
{Name: "salePrice", Type: smartcontract.IntegerType},
},
ReturnType: smartcontract.ArrayType,
Safe: true,
},
},
},
},
Required: []string{manifest.NEP11StandardName},
}
// Nep24Payable contains an event that MUST be triggered after marketplaces
// transferring royalties to the royalty recipient if royaltyInfo method is implemented.
var Nep24Payable = &Standard{
Manifest: manifest.Manifest{
ABI: manifest.ABI{
Events: []manifest.Event{
{
Name: "RoyaltiesTransferred",
Parameters: []manifest.Parameter{
{Name: "royaltyToken", Type: smartcontract.Hash160Type},
{Name: "royaltyRecipient", Type: smartcontract.Hash160Type},
{Name: "buyer", Type: smartcontract.Hash160Type},
{Name: "tokenId", Type: smartcontract.ByteArrayType},
{Name: "amount", Type: smartcontract.IntegerType},
},
},
},
},
},
}

View file

@ -179,6 +179,8 @@ type ContractReader struct {
{{end -}}
{{if .IsNep17}}nep17.TokenReader
{{end -}}
{{if .IsNep24}}nep24.RoyaltyReader
{{end -}}
invoker Invoker
hash util.Uint160
}
@ -208,6 +210,7 @@ func NewReader(invoker Invoker{{- if not (len .Hash) -}}, hash util.Uint160{{- e
{{- if .IsNep11D}}*nep11.NewDivisibleReader(invoker, hash), {{end}}
{{- if .IsNep11ND}}*nep11.NewNonDivisibleReader(invoker, hash), {{end}}
{{- if .IsNep17}}*nep17.NewReader(invoker, hash), {{end -}}
{{- if .IsNep24}}*nep24.NewRoyaltyReader(invoker, hash), {{end -}}
invoker, hash}
}
{{end -}}
@ -223,11 +226,14 @@ func New(actor Actor{{- if not (len .Hash) -}}, hash util.Uint160{{- end -}}) *C
{{end -}}
{{if .IsNep17}}var nep17t = nep17.New(actor, hash)
{{end -}}
{{if .IsNep24}}var nep24t = nep24.NewRoyaltyReader(actor, hash)
{{end -}}
return &Contract{
{{- if .HasReader}}ContractReader{
{{- if .IsNep11D}}nep11dt.DivisibleReader, {{end -}}
{{- if .IsNep11ND}}nep11ndt.NonDivisibleReader, {{end -}}
{{- if .IsNep17}}nep17t.TokenReader, {{end -}}
{{- if .IsNep24}}*nep24t, {{end -}}
actor, hash}, {{end -}}
{{- if .IsNep11D}}nep11dt.DivisibleWriter, {{end -}}
{{- if .IsNep11ND}}nep11ndt.BaseWriter, {{end -}}
@ -349,9 +355,11 @@ type (
CustomEvents []CustomEventTemplate
NamedTypes []binding.ExtendedType
IsNep11D bool
IsNep11ND bool
IsNep17 bool
IsNep11D bool
IsNep11ND bool
IsNep17 bool
IsNep24 bool
IsNep24Payable bool
HasReader bool
HasWriter bool
@ -404,27 +412,52 @@ func Generate(cfg binding.Config) error {
// Strip standard methods from NEP-XX packages.
for _, std := range cfg.Manifest.SupportedStandards {
if std == manifest.NEP11StandardName {
switch std {
case manifest.NEP11StandardName:
imports["github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"] = struct{}{}
if standard.ComplyABI(cfg.Manifest, standard.Nep11Divisible) == nil {
mfst.ABI.Methods = dropStdMethods(mfst.ABI.Methods, standard.Nep11Divisible)
ctr.IsNep11D = true
} else if standard.ComplyABI(cfg.Manifest, standard.Nep11NonDivisible) == nil {
mfst.ABI.Methods = dropStdMethods(mfst.ABI.Methods, standard.Nep11NonDivisible)
ctr.IsNep11ND = true
}
mfst.ABI.Events = dropStdEvents(mfst.ABI.Events, standard.Nep11Base)
break // Can't be NEP-17 at the same time.
}
if std == manifest.NEP17StandardName && standard.ComplyABI(cfg.Manifest, standard.Nep17) == nil {
mfst.ABI.Methods = dropStdMethods(mfst.ABI.Methods, standard.Nep17)
imports["github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"] = struct{}{}
ctr.IsNep17 = true
mfst.ABI.Events = dropStdEvents(mfst.ABI.Events, standard.Nep17)
break // Can't be NEP-11 at the same time.
case manifest.NEP17StandardName:
if standard.ComplyABI(cfg.Manifest, standard.Nep17) == nil {
imports["github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"] = struct{}{}
ctr.IsNep17 = true
}
case manifest.NEP24StandardName:
if standard.ComplyABI(cfg.Manifest, standard.Nep24) == nil {
imports["github.com/nspcc-dev/neo-go/pkg/rpcclient/nep24"] = struct{}{}
ctr.IsNep24 = true
}
case manifest.NEP24Payable:
if standard.ComplyABI(cfg.Manifest, standard.Nep24Payable) == nil {
ctr.IsNep24Payable = true
}
}
}
if ctr.IsNep11D {
mfst.ABI.Methods = dropStdMethods(mfst.ABI.Methods, standard.Nep11Divisible)
}
if ctr.IsNep11ND {
mfst.ABI.Methods = dropStdMethods(mfst.ABI.Methods, standard.Nep11NonDivisible)
}
if ctr.IsNep11D || ctr.IsNep11ND {
mfst.ABI.Events = dropStdEvents(mfst.ABI.Events, standard.Nep11Base)
}
if ctr.IsNep17 {
mfst.ABI.Methods = dropStdMethods(mfst.ABI.Methods, standard.Nep17)
mfst.ABI.Events = dropStdEvents(mfst.ABI.Events, standard.Nep17)
}
if ctr.IsNep24 {
mfst.ABI.Methods = dropStdMethods(mfst.ABI.Methods, standard.Nep24)
cfg = dropNep24Types(cfg)
}
if ctr.IsNep24Payable {
mfst.ABI.Events = dropStdEvents(mfst.ABI.Events, standard.Nep24Payable)
}
// OnNepXXPayment handlers normally can't be called directly.
if standard.ComplyABI(cfg.Manifest, standard.Nep11Payable) == nil {
mfst.ABI.Methods = dropStdMethods(mfst.ABI.Methods, standard.Nep11Payable)
@ -519,6 +552,38 @@ func dropStdEvents(events []manifest.Event, std *standard.Standard) []manifest.E
return events
}
// dropNep24Types removes NamedTypes of NEP-24 from the config if they are used only from the methods of the standard.
func dropNep24Types(cfg binding.Config) binding.Config {
var targetTypeName string
// Find structure returned by standard.MethodRoyaltyInfo method
// and remove it from binding.Config.NamedTypes as it will be imported from nep24 package.
if royaltyInfo, ok := cfg.Types[standard.MethodRoyaltyInfo]; ok && royaltyInfo.Value != nil {
returnType, exists := cfg.NamedTypes[royaltyInfo.Value.Name]
if !exists || returnType.Fields == nil || len(returnType.Fields) != 2 ||
returnType.Fields[0].ExtendedType.Base != smartcontract.Hash160Type ||
returnType.Fields[1].ExtendedType.Base != smartcontract.IntegerType {
return cfg
}
targetTypeName = royaltyInfo.Value.Name
} else {
return cfg
}
found := false
for _, typeDef := range cfg.Types {
if typeDef.Value != nil && typeDef.Value.Name == targetTypeName {
if found {
return cfg
}
found = true
}
}
if found {
delete(cfg.NamedTypes, targetTypeName)
}
return cfg
}
func extendedTypeToGo(et binding.ExtendedType, named map[string]binding.ExtendedType) (string, string) {
switch et.Base {
case smartcontract.AnyType:
@ -834,7 +899,7 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st
imports["github.com/nspcc-dev/neo-go/pkg/util"] = struct{}{}
if len(ctr.SafeMethods) > 0 {
imports["github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"] = struct{}{}
if !(ctr.IsNep17 || ctr.IsNep11D || ctr.IsNep11ND) {
if !(ctr.IsNep17 || ctr.IsNep11D || ctr.IsNep11ND || ctr.IsNep24) {
imports["github.com/nspcc-dev/neo-go/pkg/neorpc/result"] = struct{}{}
}
}
@ -844,7 +909,7 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st
if len(ctr.Methods) > 0 || ctr.IsNep17 || ctr.IsNep11D || ctr.IsNep11ND {
ctr.HasWriter = true
}
if len(ctr.SafeMethods) > 0 || ctr.IsNep17 || ctr.IsNep11D || ctr.IsNep11ND {
if len(ctr.SafeMethods) > 0 || ctr.IsNep17 || ctr.IsNep11D || ctr.IsNep11ND || ctr.IsNep24 {
ctr.HasReader = true
}
ctr.Imports = ctr.Imports[:0]