mirror of
https://github.com/nspcc-dev/neo-go.git
synced 2024-12-23 03:41:34 +00:00
smartcontract: initial rpcbinding implementation, fix #2705
It can do some unwrapping and reuse nepXX packages. It only uses manifest data at the moment, see #2767, #2768, #2769.
This commit is contained in:
parent
f0abc035af
commit
617c31093f
8 changed files with 1176 additions and 22 deletions
|
@ -7,39 +7,58 @@ import (
|
|||
|
||||
"github.com/nspcc-dev/neo-go/cli/cmdargs"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/binding"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"github.com/urfave/cli"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
var generatorFlags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Usage: "Configuration file to use",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "manifest, m",
|
||||
Usage: "Read contract manifest (*.manifest.json) file",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "out, o",
|
||||
Usage: "Output of the compiled contract",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "hash",
|
||||
Usage: "Smart-contract hash",
|
||||
},
|
||||
}
|
||||
|
||||
var generateWrapperCmd = cli.Command{
|
||||
Name: "generate-wrapper",
|
||||
Usage: "generate wrapper to use in other contracts",
|
||||
UsageText: "neo-go contract generate-wrapper --manifest <file.json> --out <file.go> --hash <hash>",
|
||||
Description: ``,
|
||||
Action: contractGenerateWrapper,
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "config, c",
|
||||
Usage: "Configuration file to use",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "manifest, m",
|
||||
Usage: "Read contract manifest (*.manifest.json) file",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "out, o",
|
||||
Usage: "Output of the compiled contract",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "hash",
|
||||
Usage: "Smart-contract hash",
|
||||
},
|
||||
},
|
||||
Flags: generatorFlags,
|
||||
}
|
||||
|
||||
var generateRPCWrapperCmd = cli.Command{
|
||||
Name: "generate-rpcwrapper",
|
||||
Usage: "generate RPC wrapper to use for data reads",
|
||||
UsageText: "neo-go contract generate-rpcwrapper --manifest <file.json> --out <file.go> --hash <hash>",
|
||||
Action: contractGenerateRPCWrapper,
|
||||
Flags: generatorFlags,
|
||||
}
|
||||
|
||||
// contractGenerateWrapper deploys contract.
|
||||
func contractGenerateWrapper(ctx *cli.Context) error {
|
||||
return contractGenerateSomething(ctx, binding.Generate)
|
||||
}
|
||||
|
||||
func contractGenerateRPCWrapper(ctx *cli.Context) error {
|
||||
return contractGenerateSomething(ctx, rpcbinding.Generate)
|
||||
}
|
||||
|
||||
// contractGenerateSomething reads generator parameters and calls the given callback.
|
||||
func contractGenerateSomething(ctx *cli.Context, cb func(binding.Config) error) error {
|
||||
if err := cmdargs.EnsureNone(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -74,7 +93,7 @@ func contractGenerateWrapper(ctx *cli.Context) error {
|
|||
|
||||
cfg.Output = f
|
||||
|
||||
err = binding.Generate(cfg)
|
||||
err = cb(cfg)
|
||||
if err != nil {
|
||||
return cli.NewExitError(fmt.Errorf("error during generation: %w", err), 1)
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package smartcontract
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -224,6 +225,7 @@ func TestGenerateValidPackageName(t *testing.T) {
|
|||
Name: "get",
|
||||
Parameters: []manifest.Parameter{},
|
||||
ReturnType: smartcontract.IntegerType,
|
||||
Safe: true,
|
||||
},
|
||||
)
|
||||
|
||||
|
@ -239,7 +241,7 @@ func TestGenerateValidPackageName(t *testing.T) {
|
|||
0xCA, 0xFE, 0xBA, 0xBE, 0xDE, 0xAD, 0xBE, 0xEF, 0x03, 0x04,
|
||||
}
|
||||
app := cli.NewApp()
|
||||
app.Commands = []cli.Command{generateWrapperCmd}
|
||||
app.Commands = []cli.Command{generateWrapperCmd, generateRPCWrapperCmd}
|
||||
require.NoError(t, app.Run([]string{"", "generate-wrapper",
|
||||
"--manifest", manifestFile,
|
||||
"--out", outFile,
|
||||
|
@ -261,9 +263,85 @@ const Hash = "\x04\x08\x15\x16\x23\x42\x43\x44\x00\x01\xca\xfe\xba\xbe\xde\xad\x
|
|||
|
||||
// Get invokes `+"`get`"+` method of contract.
|
||||
func Get() int {
|
||||
return neogointernal.CallWithToken(Hash, "get", int(contract.All)).(int)
|
||||
return neogointernal.CallWithToken(Hash, "get", int(contract.ReadOnly)).(int)
|
||||
}
|
||||
`, string(data))
|
||||
require.NoError(t, app.Run([]string{"", "generate-rpcwrapper",
|
||||
"--manifest", manifestFile,
|
||||
"--out", outFile,
|
||||
"--hash", "0x" + h.StringLE(),
|
||||
}))
|
||||
|
||||
data, err = os.ReadFile(outFile)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, `// Package myspacecontract contains RPC wrappers for My space contract contract.
|
||||
package myspacecontract
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// Hash contains contract hash.
|
||||
var Hash = util.Uint160{0x4, 0x8, 0x15, 0x16, 0x23, 0x42, 0x43, 0x44, 0x0, 0x1, 0xca, 0xfe, 0xba, 0xbe, 0xde, 0xad, 0xbe, 0xef, 0x3, 0x4}
|
||||
|
||||
// Invoker is used by ContractReader to call various safe methods.
|
||||
type Invoker interface {
|
||||
Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error)
|
||||
}
|
||||
|
||||
// ContractReader implements safe contract methods.
|
||||
type ContractReader struct {
|
||||
invoker Invoker
|
||||
}
|
||||
|
||||
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
|
||||
func NewReader(invoker Invoker) *ContractReader {
|
||||
return &ContractReader{invoker}
|
||||
}
|
||||
|
||||
|
||||
// Get invokes `+"`get`"+` method of contract.
|
||||
func (c *ContractReader) Get() (*big.Int, error) {
|
||||
return unwrap.BigInt(c.invoker.Call(Hash, "get"))
|
||||
}
|
||||
`, string(data))
|
||||
}
|
||||
|
||||
func TestGenerateRPCBindings(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
app := cli.NewApp()
|
||||
app.Commands = []cli.Command{generateWrapperCmd, generateRPCWrapperCmd}
|
||||
|
||||
outFile := filepath.Join(tmpDir, "out.go")
|
||||
require.NoError(t, app.Run([]string{"", "generate-rpcwrapper",
|
||||
"--manifest", filepath.Join("testdata", "nex", "nex.manifest.json"),
|
||||
"--out", outFile,
|
||||
"--hash", "0xa2a67f09e8cf22c6bfd5cea24adc0f4bf0a11aa8",
|
||||
}))
|
||||
|
||||
data, err := os.ReadFile(outFile)
|
||||
require.NoError(t, err)
|
||||
data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows.
|
||||
expected, err := os.ReadFile(filepath.Join("testdata", "nex", "nex.go"))
|
||||
require.NoError(t, err)
|
||||
expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows.
|
||||
require.Equal(t, string(expected), string(data))
|
||||
|
||||
require.NoError(t, app.Run([]string{"", "generate-rpcwrapper",
|
||||
"--manifest", filepath.Join("testdata", "nameservice", "nns.manifest.json"),
|
||||
"--out", outFile,
|
||||
"--hash", "0x50ac1c37690cc2cfc594472833cf57505d5f46de",
|
||||
}))
|
||||
|
||||
data, err = os.ReadFile(outFile)
|
||||
require.NoError(t, err)
|
||||
data = bytes.ReplaceAll(data, []byte("\r"), []byte{}) // Windows.
|
||||
expected, err = os.ReadFile(filepath.Join("testdata", "nameservice", "nns.go"))
|
||||
require.NoError(t, err)
|
||||
expected = bytes.ReplaceAll(expected, []byte("\r"), []byte{}) // Windows.
|
||||
require.Equal(t, string(expected), string(data))
|
||||
}
|
||||
|
||||
func TestGenerate_Errors(t *testing.T) {
|
||||
|
|
|
@ -182,6 +182,7 @@ func NewCommands() []cli.Command {
|
|||
Flags: deployFlags,
|
||||
},
|
||||
generateWrapperCmd,
|
||||
generateRPCWrapperCmd,
|
||||
{
|
||||
Name: "invokefunction",
|
||||
Usage: "invoke deployed contract on the blockchain",
|
||||
|
|
62
cli/smartcontract/testdata/nameservice/nns.go
vendored
Normal file
62
cli/smartcontract/testdata/nameservice/nns.go
vendored
Normal file
|
@ -0,0 +1,62 @@
|
|||
// Package nameservice contains RPC wrappers for NameService contract.
|
||||
package nameservice
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||
"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{0xde, 0x46, 0x5f, 0x5d, 0x50, 0x57, 0xcf, 0x33, 0x28, 0x47, 0x94, 0xc5, 0xcf, 0xc2, 0xc, 0x69, 0x37, 0x1c, 0xac, 0x50}
|
||||
|
||||
// Invoker is used by ContractReader to call various safe methods.
|
||||
type Invoker interface {
|
||||
nep11.Invoker
|
||||
Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error)
|
||||
}
|
||||
|
||||
// ContractReader implements safe contract methods.
|
||||
type ContractReader struct {
|
||||
nep11.NonDivisibleReader
|
||||
invoker Invoker
|
||||
}
|
||||
|
||||
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
|
||||
func NewReader(invoker Invoker) *ContractReader {
|
||||
return &ContractReader{*nep11.NewNonDivisibleReader(invoker, Hash), invoker}
|
||||
}
|
||||
|
||||
|
||||
// Roots invokes `roots` method of contract.
|
||||
func (c *ContractReader) Roots() (stackitem.Item, error) {
|
||||
return unwrap.Item(c.invoker.Call(Hash, "roots"))
|
||||
}
|
||||
|
||||
// GetPrice invokes `getPrice` method of contract.
|
||||
func (c *ContractReader) GetPrice(length *big.Int) (*big.Int, error) {
|
||||
return unwrap.BigInt(c.invoker.Call(Hash, "getPrice", length))
|
||||
}
|
||||
|
||||
// IsAvailable invokes `isAvailable` method of contract.
|
||||
func (c *ContractReader) IsAvailable(name string) (bool, error) {
|
||||
return unwrap.Bool(c.invoker.Call(Hash, "isAvailable", name))
|
||||
}
|
||||
|
||||
// GetRecord invokes `getRecord` method of contract.
|
||||
func (c *ContractReader) GetRecord(name string, type *big.Int) (string, error) {
|
||||
return unwrap.UTF8String(c.invoker.Call(Hash, "getRecord", name, type))
|
||||
}
|
||||
|
||||
// GetAllRecords invokes `getAllRecords` method of contract.
|
||||
func (c *ContractReader) GetAllRecords(name string) (stackitem.Item, error) {
|
||||
return unwrap.Item(c.invoker.Call(Hash, "getAllRecords", name))
|
||||
}
|
||||
|
||||
// Resolve invokes `resolve` method of contract.
|
||||
func (c *ContractReader) Resolve(name string, type *big.Int) (string, error) {
|
||||
return unwrap.UTF8String(c.invoker.Call(Hash, "resolve", name, type))
|
||||
}
|
441
cli/smartcontract/testdata/nameservice/nns.manifest.json
vendored
Normal file
441
cli/smartcontract/testdata/nameservice/nns.manifest.json
vendored
Normal file
|
@ -0,0 +1,441 @@
|
|||
{
|
||||
"abi" : {
|
||||
"events" : [
|
||||
{
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "from",
|
||||
"type" : "Hash160"
|
||||
},
|
||||
{
|
||||
"name" : "to",
|
||||
"type" : "Hash160"
|
||||
},
|
||||
{
|
||||
"name" : "amount",
|
||||
"type" : "Integer"
|
||||
},
|
||||
{
|
||||
"type" : "ByteArray",
|
||||
"name" : "tokenId"
|
||||
}
|
||||
],
|
||||
"name" : "Transfer"
|
||||
},
|
||||
{
|
||||
"parameters" : [
|
||||
{
|
||||
"type" : "String",
|
||||
"name" : "name"
|
||||
},
|
||||
{
|
||||
"type" : "Hash160",
|
||||
"name" : "oldAdmin"
|
||||
},
|
||||
{
|
||||
"type" : "Hash160",
|
||||
"name" : "newAdmin"
|
||||
}
|
||||
],
|
||||
"name" : "SetAdmin"
|
||||
},
|
||||
{
|
||||
"name" : "Renew",
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "name",
|
||||
"type" : "String"
|
||||
},
|
||||
{
|
||||
"type" : "Integer",
|
||||
"name" : "oldExpiration"
|
||||
},
|
||||
{
|
||||
"name" : "newExpiration",
|
||||
"type" : "Integer"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"methods" : [
|
||||
{
|
||||
"safe" : true,
|
||||
"parameters" : [],
|
||||
"name" : "symbol",
|
||||
"returntype" : "String",
|
||||
"offset" : 0
|
||||
},
|
||||
{
|
||||
"parameters" : [],
|
||||
"name" : "decimals",
|
||||
"returntype" : "Integer",
|
||||
"safe" : true,
|
||||
"offset" : 6
|
||||
},
|
||||
{
|
||||
"offset" : 8,
|
||||
"returntype" : "Integer",
|
||||
"name" : "totalSupply",
|
||||
"parameters" : [],
|
||||
"safe" : true
|
||||
},
|
||||
{
|
||||
"offset" : 53,
|
||||
"safe" : true,
|
||||
"parameters" : [
|
||||
{
|
||||
"type" : "ByteArray",
|
||||
"name" : "tokenId"
|
||||
}
|
||||
],
|
||||
"name" : "ownerOf",
|
||||
"returntype" : "Hash160"
|
||||
},
|
||||
{
|
||||
"safe" : true,
|
||||
"name" : "properties",
|
||||
"returntype" : "Map",
|
||||
"parameters" : [
|
||||
{
|
||||
"type" : "ByteArray",
|
||||
"name" : "tokenId"
|
||||
}
|
||||
],
|
||||
"offset" : 184
|
||||
},
|
||||
{
|
||||
"safe" : true,
|
||||
"returntype" : "Integer",
|
||||
"name" : "balanceOf",
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "owner",
|
||||
"type" : "Hash160"
|
||||
}
|
||||
],
|
||||
"offset" : 341
|
||||
},
|
||||
{
|
||||
"safe" : true,
|
||||
"returntype" : "InteropInterface",
|
||||
"name" : "tokens",
|
||||
"parameters" : [],
|
||||
"offset" : 453
|
||||
},
|
||||
{
|
||||
"safe" : true,
|
||||
"name" : "tokensOf",
|
||||
"returntype" : "InteropInterface",
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "owner",
|
||||
"type" : "Hash160"
|
||||
}
|
||||
],
|
||||
"offset" : 494
|
||||
},
|
||||
{
|
||||
"offset" : 600,
|
||||
"safe" : false,
|
||||
"parameters" : [
|
||||
{
|
||||
"type" : "Hash160",
|
||||
"name" : "to"
|
||||
},
|
||||
{
|
||||
"name" : "tokenId",
|
||||
"type" : "ByteArray"
|
||||
},
|
||||
{
|
||||
"name" : "data",
|
||||
"type" : "Any"
|
||||
}
|
||||
],
|
||||
"name" : "transfer",
|
||||
"returntype" : "Boolean"
|
||||
},
|
||||
{
|
||||
"name" : "update",
|
||||
"returntype" : "Void",
|
||||
"parameters" : [
|
||||
{
|
||||
"type" : "ByteArray",
|
||||
"name" : "nef"
|
||||
},
|
||||
{
|
||||
"name" : "manifest",
|
||||
"type" : "String"
|
||||
}
|
||||
],
|
||||
"safe" : false,
|
||||
"offset" : 1121
|
||||
},
|
||||
{
|
||||
"offset" : 1291,
|
||||
"returntype" : "Void",
|
||||
"name" : "addRoot",
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "root",
|
||||
"type" : "String"
|
||||
}
|
||||
],
|
||||
"safe" : false
|
||||
},
|
||||
{
|
||||
"offset" : 1725,
|
||||
"safe" : true,
|
||||
"parameters" : [],
|
||||
"returntype" : "InteropInterface",
|
||||
"name" : "roots"
|
||||
},
|
||||
{
|
||||
"offset" : 1757,
|
||||
"parameters" : [
|
||||
{
|
||||
"type" : "Array",
|
||||
"name" : "priceList"
|
||||
}
|
||||
],
|
||||
"name" : "setPrice",
|
||||
"returntype" : "Void",
|
||||
"safe" : false
|
||||
},
|
||||
{
|
||||
"offset" : 1952,
|
||||
"safe" : true,
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "length",
|
||||
"type" : "Integer"
|
||||
}
|
||||
],
|
||||
"name" : "getPrice",
|
||||
"returntype" : "Integer"
|
||||
},
|
||||
{
|
||||
"offset" : 2017,
|
||||
"safe" : true,
|
||||
"parameters" : [
|
||||
{
|
||||
"type" : "String",
|
||||
"name" : "name"
|
||||
}
|
||||
],
|
||||
"name" : "isAvailable",
|
||||
"returntype" : "Boolean"
|
||||
},
|
||||
{
|
||||
"offset" : 2405,
|
||||
"parameters" : [
|
||||
{
|
||||
"type" : "String",
|
||||
"name" : "name"
|
||||
},
|
||||
{
|
||||
"type" : "Hash160",
|
||||
"name" : "owner"
|
||||
}
|
||||
],
|
||||
"name" : "register",
|
||||
"returntype" : "Boolean",
|
||||
"safe" : false
|
||||
},
|
||||
{
|
||||
"name" : "renew",
|
||||
"returntype" : "Integer",
|
||||
"parameters" : [
|
||||
{
|
||||
"type" : "String",
|
||||
"name" : "name"
|
||||
}
|
||||
],
|
||||
"safe" : false,
|
||||
"offset" : 3113
|
||||
},
|
||||
{
|
||||
"offset" : 3123,
|
||||
"safe" : false,
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "name",
|
||||
"type" : "String"
|
||||
},
|
||||
{
|
||||
"name" : "years",
|
||||
"type" : "Integer"
|
||||
}
|
||||
],
|
||||
"name" : "renew",
|
||||
"returntype" : "Integer"
|
||||
},
|
||||
{
|
||||
"offset" : 3697,
|
||||
"parameters" : [
|
||||
{
|
||||
"type" : "String",
|
||||
"name" : "name"
|
||||
},
|
||||
{
|
||||
"name" : "admin",
|
||||
"type" : "Hash160"
|
||||
}
|
||||
],
|
||||
"name" : "setAdmin",
|
||||
"returntype" : "Void",
|
||||
"safe" : false
|
||||
},
|
||||
{
|
||||
"safe" : false,
|
||||
"returntype" : "Void",
|
||||
"name" : "setRecord",
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "name",
|
||||
"type" : "String"
|
||||
},
|
||||
{
|
||||
"type" : "Integer",
|
||||
"name" : "type"
|
||||
},
|
||||
{
|
||||
"name" : "data",
|
||||
"type" : "String"
|
||||
}
|
||||
],
|
||||
"offset" : 3921
|
||||
},
|
||||
{
|
||||
"name" : "getRecord",
|
||||
"returntype" : "String",
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "name",
|
||||
"type" : "String"
|
||||
},
|
||||
{
|
||||
"type" : "Integer",
|
||||
"name" : "type"
|
||||
}
|
||||
],
|
||||
"safe" : true,
|
||||
"offset" : 5877
|
||||
},
|
||||
{
|
||||
"returntype" : "InteropInterface",
|
||||
"name" : "getAllRecords",
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "name",
|
||||
"type" : "String"
|
||||
}
|
||||
],
|
||||
"safe" : true,
|
||||
"offset" : 6201
|
||||
},
|
||||
{
|
||||
"safe" : false,
|
||||
"returntype" : "Void",
|
||||
"name" : "deleteRecord",
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "name",
|
||||
"type" : "String"
|
||||
},
|
||||
{
|
||||
"type" : "Integer",
|
||||
"name" : "type"
|
||||
}
|
||||
],
|
||||
"offset" : 6281
|
||||
},
|
||||
{
|
||||
"offset" : 6565,
|
||||
"name" : "resolve",
|
||||
"returntype" : "String",
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "name",
|
||||
"type" : "String"
|
||||
},
|
||||
{
|
||||
"type" : "Integer",
|
||||
"name" : "type"
|
||||
}
|
||||
],
|
||||
"safe" : true
|
||||
},
|
||||
{
|
||||
"safe" : false,
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "data",
|
||||
"type" : "Any"
|
||||
},
|
||||
{
|
||||
"name" : "update",
|
||||
"type" : "Boolean"
|
||||
}
|
||||
],
|
||||
"name" : "_deploy",
|
||||
"returntype" : "Void",
|
||||
"offset" : 7152
|
||||
},
|
||||
{
|
||||
"offset" : 7514,
|
||||
"parameters" : [],
|
||||
"returntype" : "Void",
|
||||
"name" : "_initialize",
|
||||
"safe" : false
|
||||
}
|
||||
]
|
||||
},
|
||||
"supportedstandards" : [
|
||||
"NEP-11"
|
||||
],
|
||||
"permissions" : [
|
||||
{
|
||||
"contract" : "0x726cb6e0cd8628a1350a611384688911ab75f51b",
|
||||
"methods" : [
|
||||
"ripemd160"
|
||||
]
|
||||
},
|
||||
{
|
||||
"contract" : "0xacce6fd80d44e1796aa0c2c625e9e4e0ce39efc0",
|
||||
"methods" : [
|
||||
"atoi",
|
||||
"deserialize",
|
||||
"serialize",
|
||||
"stringSplit"
|
||||
]
|
||||
},
|
||||
{
|
||||
"contract" : "0xef4073a0f2b305a38ec4050e4d3d28bc40ea63f5",
|
||||
"methods" : [
|
||||
"getCommittee"
|
||||
]
|
||||
},
|
||||
{
|
||||
"contract" : "0xfffdc93764dbaddd97c48f252a53ea4643faa3fd",
|
||||
"methods" : [
|
||||
"getContract",
|
||||
"update"
|
||||
]
|
||||
},
|
||||
{
|
||||
"methods" : [
|
||||
"onNEP11Payment"
|
||||
],
|
||||
"contract" : "*"
|
||||
}
|
||||
],
|
||||
"features" : {},
|
||||
"name" : "NameService",
|
||||
"trusts" : [],
|
||||
"extra" : {
|
||||
"Author" : "The Neo Project",
|
||||
"Description" : "Neo Name Service",
|
||||
"Email" : "dev@neo.org"
|
||||
},
|
||||
"groups" : []
|
||||
}
|
52
cli/smartcontract/testdata/nex/nex.go
vendored
Normal file
52
cli/smartcontract/testdata/nex/nex.go
vendored
Normal file
|
@ -0,0 +1,52 @@
|
|||
// Package nextoken contains RPC wrappers for NEX Token contract.
|
||||
package nextoken
|
||||
|
||||
import (
|
||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"
|
||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// Hash contains contract hash.
|
||||
var Hash = util.Uint160{0xa8, 0x1a, 0xa1, 0xf0, 0x4b, 0xf, 0xdc, 0x4a, 0xa2, 0xce, 0xd5, 0xbf, 0xc6, 0x22, 0xcf, 0xe8, 0x9, 0x7f, 0xa6, 0xa2}
|
||||
|
||||
// Invoker is used by ContractReader to call various safe methods.
|
||||
type Invoker interface {
|
||||
nep17.Invoker
|
||||
Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error)
|
||||
}
|
||||
|
||||
// ContractReader implements safe contract methods.
|
||||
type ContractReader struct {
|
||||
nep17.TokenReader
|
||||
invoker Invoker
|
||||
}
|
||||
|
||||
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
|
||||
func NewReader(invoker Invoker) *ContractReader {
|
||||
return &ContractReader{*nep17.NewReader(invoker, Hash), invoker}
|
||||
}
|
||||
|
||||
|
||||
// Cap invokes `cap` method of contract.
|
||||
func (c *ContractReader) Cap() (*big.Int, error) {
|
||||
return unwrap.BigInt(c.invoker.Call(Hash, "cap"))
|
||||
}
|
||||
|
||||
// GetMinter invokes `getMinter` method of contract.
|
||||
func (c *ContractReader) GetMinter() (*keys.PublicKey, error) {
|
||||
return unwrap.PublicKey(c.invoker.Call(Hash, "getMinter"))
|
||||
}
|
||||
|
||||
// GetOwner invokes `getOwner` method of contract.
|
||||
func (c *ContractReader) GetOwner() (util.Uint160, error) {
|
||||
return unwrap.Uint160(c.invoker.Call(Hash, "getOwner"))
|
||||
}
|
||||
|
||||
// TotalMinted invokes `totalMinted` method of contract.
|
||||
func (c *ContractReader) TotalMinted() (*big.Int, error) {
|
||||
return unwrap.BigInt(c.invoker.Call(Hash, "totalMinted"))
|
||||
}
|
275
cli/smartcontract/testdata/nex/nex.manifest.json
vendored
Normal file
275
cli/smartcontract/testdata/nex/nex.manifest.json
vendored
Normal file
|
@ -0,0 +1,275 @@
|
|||
{
|
||||
"name" : "NEX Token",
|
||||
"abi" : {
|
||||
"events" : [
|
||||
{
|
||||
"parameters" : [
|
||||
{
|
||||
"type" : "Hash160",
|
||||
"name" : "from"
|
||||
},
|
||||
{
|
||||
"name" : "to",
|
||||
"type" : "Hash160"
|
||||
},
|
||||
{
|
||||
"name" : "amount",
|
||||
"type" : "Integer"
|
||||
}
|
||||
],
|
||||
"name" : "Transfer"
|
||||
},
|
||||
{
|
||||
"name" : "OnMint",
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "from",
|
||||
"type" : "Hash160"
|
||||
},
|
||||
{
|
||||
"type" : "Hash160",
|
||||
"name" : "to"
|
||||
},
|
||||
{
|
||||
"type" : "Integer",
|
||||
"name" : "amount"
|
||||
},
|
||||
{
|
||||
"name" : "swapId",
|
||||
"type" : "Integer"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"methods" : [
|
||||
{
|
||||
"parameters" : [],
|
||||
"offset" : 0,
|
||||
"name" : "_initialize",
|
||||
"safe" : false,
|
||||
"returntype" : "Void"
|
||||
},
|
||||
{
|
||||
"parameters" : [
|
||||
{
|
||||
"type" : "Any",
|
||||
"name" : "data"
|
||||
},
|
||||
{
|
||||
"name" : "isUpdate",
|
||||
"type" : "Boolean"
|
||||
}
|
||||
],
|
||||
"offset" : 3,
|
||||
"name" : "_deploy",
|
||||
"safe" : false,
|
||||
"returntype" : "Void"
|
||||
},
|
||||
{
|
||||
"parameters" : [
|
||||
{
|
||||
"type" : "Hash160",
|
||||
"name" : "holder"
|
||||
}
|
||||
],
|
||||
"offset" : 484,
|
||||
"returntype" : "Integer",
|
||||
"safe" : true,
|
||||
"name" : "balanceOf"
|
||||
},
|
||||
{
|
||||
"safe" : true,
|
||||
"returntype" : "Integer",
|
||||
"name" : "cap",
|
||||
"offset" : 1881,
|
||||
"parameters" : []
|
||||
},
|
||||
{
|
||||
"name" : "changeMinter",
|
||||
"safe" : false,
|
||||
"returntype" : "Void",
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "newMinter",
|
||||
"type" : "PublicKey"
|
||||
}
|
||||
],
|
||||
"offset" : 1678
|
||||
},
|
||||
{
|
||||
"parameters" : [
|
||||
{
|
||||
"type" : "Hash160",
|
||||
"name" : "newOwner"
|
||||
}
|
||||
],
|
||||
"offset" : 1659,
|
||||
"name" : "changeOwner",
|
||||
"safe" : false,
|
||||
"returntype" : "Void"
|
||||
},
|
||||
{
|
||||
"offset" : 466,
|
||||
"parameters" : [],
|
||||
"safe" : true,
|
||||
"name" : "decimals",
|
||||
"returntype" : "Integer"
|
||||
},
|
||||
{
|
||||
"returntype" : "Void",
|
||||
"safe" : false,
|
||||
"name" : "destroy",
|
||||
"parameters" : [],
|
||||
"offset" : 1194
|
||||
},
|
||||
{
|
||||
"safe" : true,
|
||||
"returntype" : "PublicKey",
|
||||
"name" : "getMinter",
|
||||
"offset" : 1671,
|
||||
"parameters" : []
|
||||
},
|
||||
{
|
||||
"parameters" : [],
|
||||
"offset" : 1652,
|
||||
"name" : "getOwner",
|
||||
"safe" : true,
|
||||
"returntype" : "Hash160"
|
||||
},
|
||||
{
|
||||
"name" : "maxSupply",
|
||||
"safe" : false,
|
||||
"returntype" : "Integer",
|
||||
"parameters" : [],
|
||||
"offset" : 468
|
||||
},
|
||||
{
|
||||
"offset" : 1222,
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "from",
|
||||
"type" : "Hash160"
|
||||
},
|
||||
{
|
||||
"name" : "to",
|
||||
"type" : "Hash160"
|
||||
},
|
||||
{
|
||||
"type" : "Integer",
|
||||
"name" : "amount"
|
||||
},
|
||||
{
|
||||
"name" : "swapId",
|
||||
"type" : "Integer"
|
||||
},
|
||||
{
|
||||
"name" : "signature",
|
||||
"type" : "Signature"
|
||||
},
|
||||
{
|
||||
"name" : "data",
|
||||
"type" : "Any"
|
||||
}
|
||||
],
|
||||
"safe" : false,
|
||||
"name" : "mint",
|
||||
"returntype" : "Void"
|
||||
},
|
||||
{
|
||||
"safe" : true,
|
||||
"name" : "symbol",
|
||||
"returntype" : "String",
|
||||
"parameters" : [],
|
||||
"offset" : 460
|
||||
},
|
||||
{
|
||||
"offset" : 1854,
|
||||
"parameters" : [],
|
||||
"name" : "totalMinted",
|
||||
"safe" : true,
|
||||
"returntype" : "Integer"
|
||||
},
|
||||
{
|
||||
"offset" : 478,
|
||||
"parameters" : [],
|
||||
"name" : "totalSupply",
|
||||
"safe" : true,
|
||||
"returntype" : "Integer"
|
||||
},
|
||||
{
|
||||
"offset" : 543,
|
||||
"parameters" : [
|
||||
{
|
||||
"name" : "from",
|
||||
"type" : "Hash160"
|
||||
},
|
||||
{
|
||||
"name" : "to",
|
||||
"type" : "Hash160"
|
||||
},
|
||||
{
|
||||
"type" : "Integer",
|
||||
"name" : "amount"
|
||||
},
|
||||
{
|
||||
"name" : "data",
|
||||
"type" : "Any"
|
||||
}
|
||||
],
|
||||
"name" : "transfer",
|
||||
"safe" : false,
|
||||
"returntype" : "Boolean"
|
||||
},
|
||||
{
|
||||
"offset" : 1205,
|
||||
"parameters" : [
|
||||
{
|
||||
"type" : "ByteArray",
|
||||
"name" : "nef"
|
||||
},
|
||||
{
|
||||
"name" : "manifest",
|
||||
"type" : "ByteArray"
|
||||
}
|
||||
],
|
||||
"safe" : false,
|
||||
"returntype" : "Void",
|
||||
"name" : "update"
|
||||
},
|
||||
{
|
||||
"offset" : 1717,
|
||||
"parameters" : [
|
||||
{
|
||||
"type" : "Integer",
|
||||
"name" : "newCap"
|
||||
}
|
||||
],
|
||||
"returntype" : "Void",
|
||||
"safe" : false,
|
||||
"name" : "updateCap"
|
||||
}
|
||||
]
|
||||
},
|
||||
"supportedstandards" : [
|
||||
"NEP-17"
|
||||
],
|
||||
"extra" : null,
|
||||
"trusts" : [],
|
||||
"features" : {},
|
||||
"groups" : [],
|
||||
"permissions" : [
|
||||
{
|
||||
"methods" : [
|
||||
"onNEP17Payment"
|
||||
],
|
||||
"contract" : "*"
|
||||
},
|
||||
{
|
||||
"methods" : [
|
||||
"update",
|
||||
"destroy"
|
||||
],
|
||||
"contract" : "0xfffdc93764dbaddd97c48f252a53ea4643faa3fd"
|
||||
}
|
||||
]
|
||||
}
|
226
pkg/smartcontract/rpcbinding/binding.go
Normal file
226
pkg/smartcontract/rpcbinding/binding.go
Normal file
|
@ -0,0 +1,226 @@
|
|||
package rpcbinding
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"text/template"
|
||||
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/binding"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest"
|
||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest/standard"
|
||||
)
|
||||
|
||||
const srcTmpl = `
|
||||
{{- define "METHOD" -}}
|
||||
// {{.Name}} {{.Comment}}
|
||||
func (c *ContractReader) {{.Name}}({{range $index, $arg := .Arguments -}}
|
||||
{{- if ne $index 0}}, {{end}}
|
||||
{{- .Name}} {{.Type}}
|
||||
{{- end}}) {{if .ReturnType }}({{ .ReturnType }}, error) {
|
||||
return unwrap.{{.CallFlag}}(c.invoker.Call(Hash, "{{ .NameABI }}"{{/* CallFlag field is used for function name */}}
|
||||
{{- range $arg := .Arguments -}}, {{.Name}}{{end}}))
|
||||
{{- else -}} (*result.Invoke, error) {
|
||||
c.invoker.Call(Hash, "{{ .NameABI }}"
|
||||
{{- range $arg := .Arguments -}}, {{.Name}}{{end}})
|
||||
{{- end}}
|
||||
}
|
||||
{{- end -}}
|
||||
// Package {{.PackageName}} contains RPC wrappers for {{.ContractName}} contract.
|
||||
package {{.PackageName}}
|
||||
|
||||
import (
|
||||
{{range $m := .Imports}} "{{ $m }}"
|
||||
{{end}})
|
||||
|
||||
// Hash contains contract hash.
|
||||
var Hash = {{ .Hash }}
|
||||
|
||||
// Invoker is used by ContractReader to call various safe methods.
|
||||
type Invoker interface {
|
||||
{{if or .IsNep11D .IsNep11ND}}nep11.Invoker
|
||||
{{end -}}
|
||||
{{if .IsNep17}}nep17.Invoker
|
||||
{{end -}}
|
||||
Call(contract util.Uint160, operation string, params ...interface{}) (*result.Invoke, error)
|
||||
}
|
||||
|
||||
// ContractReader implements safe contract methods.
|
||||
type ContractReader struct {
|
||||
{{if .IsNep11D}}nep11.DivisibleReader
|
||||
{{end -}}
|
||||
{{if .IsNep11ND}}nep11.NonDivisibleReader
|
||||
{{end -}}
|
||||
{{if .IsNep17}}nep17.TokenReader
|
||||
{{end -}}
|
||||
invoker Invoker
|
||||
}
|
||||
|
||||
// NewReader creates an instance of ContractReader using Hash and the given Invoker.
|
||||
func NewReader(invoker Invoker) *ContractReader {
|
||||
return &ContractReader{
|
||||
{{- if .IsNep11D}}*nep11.NewDivisibleReader(invoker, Hash), {{end}}
|
||||
{{- if .IsNep11ND}}*nep11.NewNonDivisibleReader(invoker, Hash), {{end}}
|
||||
{{- if .IsNep17}}*nep17.NewReader(invoker, Hash), {{end -}}
|
||||
invoker}
|
||||
}
|
||||
|
||||
{{range $m := .Methods}}
|
||||
{{template "METHOD" $m }}
|
||||
{{end}}`
|
||||
|
||||
var srcTemplate = template.Must(template.New("generate").Parse(srcTmpl))
|
||||
|
||||
type (
|
||||
ContractTmpl struct {
|
||||
binding.ContractTmpl
|
||||
IsNep11D bool
|
||||
IsNep11ND bool
|
||||
IsNep17 bool
|
||||
}
|
||||
)
|
||||
|
||||
// NewConfig initializes and returns a new config instance.
|
||||
func NewConfig() binding.Config {
|
||||
return binding.NewConfig()
|
||||
}
|
||||
|
||||
// Generate writes Go file containing smartcontract bindings to the `cfg.Output`.
|
||||
func Generate(cfg binding.Config) error {
|
||||
bctr, err := binding.TemplateFromManifest(cfg, scTypeToGo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ctr := scTemplateToRPC(cfg, bctr)
|
||||
|
||||
return srcTemplate.Execute(cfg.Output, ctr)
|
||||
}
|
||||
|
||||
func dropManifestMethods(meths []binding.MethodTmpl, manifested []manifest.Method) []binding.MethodTmpl {
|
||||
for _, m := range manifested {
|
||||
for i := 0; i < len(meths); i++ {
|
||||
if meths[i].NameABI == m.Name && len(meths[i].Arguments) == len(m.Parameters) {
|
||||
meths = append(meths[:i], meths[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
}
|
||||
return meths
|
||||
}
|
||||
|
||||
func dropStdMethods(meths []binding.MethodTmpl, std *standard.Standard) []binding.MethodTmpl {
|
||||
meths = dropManifestMethods(meths, std.Manifest.ABI.Methods)
|
||||
if std.Optional != nil {
|
||||
meths = dropManifestMethods(meths, std.Optional)
|
||||
}
|
||||
if std.Base != nil {
|
||||
return dropStdMethods(meths, std.Base)
|
||||
}
|
||||
return meths
|
||||
}
|
||||
|
||||
func scTypeToGo(name string, typ smartcontract.ParamType, overrides map[string]binding.Override) (string, string) {
|
||||
switch typ {
|
||||
case smartcontract.AnyType:
|
||||
return "interface{}", ""
|
||||
case smartcontract.BoolType:
|
||||
return "bool", ""
|
||||
case smartcontract.IntegerType:
|
||||
return "*big.Int", "math/big"
|
||||
case smartcontract.ByteArrayType:
|
||||
return "[]byte", ""
|
||||
case smartcontract.StringType:
|
||||
return "string", ""
|
||||
case smartcontract.Hash160Type:
|
||||
return "util.Uint160", "github.com/nspcc-dev/neo-go/pkg/util"
|
||||
case smartcontract.Hash256Type:
|
||||
return "util.Uint256", "github.com/nspcc-dev/neo-go/pkg/util"
|
||||
case smartcontract.PublicKeyType:
|
||||
return "*keys.PublicKey", "github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
||||
case smartcontract.SignatureType:
|
||||
return "[]byte", ""
|
||||
case smartcontract.ArrayType:
|
||||
return "[]interface{}", ""
|
||||
case smartcontract.MapType:
|
||||
return "*stackitem.Map", "github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||
case smartcontract.InteropInterfaceType:
|
||||
return "interface{}", ""
|
||||
case smartcontract.VoidType:
|
||||
return "", ""
|
||||
default:
|
||||
panic("unreachable")
|
||||
}
|
||||
}
|
||||
|
||||
func scTemplateToRPC(cfg binding.Config, bctr binding.ContractTmpl) ContractTmpl {
|
||||
var imports = make(map[string]struct{})
|
||||
var ctr = ContractTmpl{ContractTmpl: bctr}
|
||||
for i := range ctr.Imports {
|
||||
imports[ctr.Imports[i]] = struct{}{}
|
||||
}
|
||||
ctr.Hash = fmt.Sprintf("%#v", cfg.Hash)
|
||||
for _, std := range cfg.Manifest.SupportedStandards {
|
||||
if std == manifest.NEP11StandardName {
|
||||
imports["github.com/nspcc-dev/neo-go/pkg/rpcclient/nep11"] = struct{}{}
|
||||
if standard.ComplyABI(cfg.Manifest, standard.Nep11Divisible) == nil {
|
||||
ctr.Methods = dropStdMethods(ctr.Methods, standard.Nep11Divisible)
|
||||
ctr.IsNep11D = true
|
||||
} else if standard.ComplyABI(cfg.Manifest, standard.Nep11NonDivisible) == nil {
|
||||
ctr.Methods = dropStdMethods(ctr.Methods, standard.Nep11NonDivisible)
|
||||
ctr.IsNep11ND = true
|
||||
}
|
||||
break // Can't be NEP-17 at the same time.
|
||||
}
|
||||
if std == manifest.NEP17StandardName && standard.ComplyABI(cfg.Manifest, standard.Nep17) == nil {
|
||||
ctr.Methods = dropStdMethods(ctr.Methods, standard.Nep17)
|
||||
imports["github.com/nspcc-dev/neo-go/pkg/rpcclient/nep17"] = struct{}{}
|
||||
ctr.IsNep17 = true
|
||||
break // Can't be NEP-11 at the same time.
|
||||
}
|
||||
}
|
||||
for i := 0; i < len(ctr.Methods); i++ {
|
||||
abim := cfg.Manifest.ABI.GetMethod(ctr.Methods[i].NameABI, len(ctr.Methods[i].Arguments))
|
||||
if !abim.Safe {
|
||||
ctr.Methods = append(ctr.Methods[:i], ctr.Methods[i+1:]...)
|
||||
i--
|
||||
}
|
||||
}
|
||||
// We're misusing CallFlag field for function name here.
|
||||
for i := range ctr.Methods {
|
||||
switch ctr.Methods[i].ReturnType {
|
||||
case "interface{}":
|
||||
imports["github.com/nspcc-dev/neo-go/pkg/vm/stackitem"] = struct{}{}
|
||||
ctr.Methods[i].ReturnType = "stackitem.Item"
|
||||
ctr.Methods[i].CallFlag = "Item"
|
||||
case "bool":
|
||||
ctr.Methods[i].CallFlag = "Bool"
|
||||
case "*big.Int":
|
||||
ctr.Methods[i].CallFlag = "BigInt"
|
||||
case "string":
|
||||
ctr.Methods[i].CallFlag = "UTF8String"
|
||||
case "util.Uint160":
|
||||
ctr.Methods[i].CallFlag = "Uint160"
|
||||
case "util.Uint256":
|
||||
ctr.Methods[i].CallFlag = "Uint256"
|
||||
case "*keys.PublicKey":
|
||||
ctr.Methods[i].CallFlag = "PublicKey"
|
||||
case "[]byte":
|
||||
ctr.Methods[i].CallFlag = "Bytes"
|
||||
case "[]interface{}":
|
||||
imports["github.com/nspcc-dev/neo-go/pkg/vm/stackitem"] = struct{}{}
|
||||
ctr.Methods[i].ReturnType = "[]stackitem.Item"
|
||||
ctr.Methods[i].CallFlag = "Array"
|
||||
case "*stackitem.Map":
|
||||
ctr.Methods[i].CallFlag = "Map"
|
||||
}
|
||||
}
|
||||
|
||||
imports["github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"] = struct{}{}
|
||||
imports["github.com/nspcc-dev/neo-go/pkg/neorpc/result"] = struct{}{}
|
||||
ctr.Imports = ctr.Imports[:0]
|
||||
for imp := range imports {
|
||||
ctr.Imports = append(ctr.Imports, imp)
|
||||
}
|
||||
sort.Strings(ctr.Imports)
|
||||
return ctr
|
||||
}
|
Loading…
Reference in a new issue