smartconract: generate RPC binding wrappers for events
Signed-off-by: Anna Shaleva <shaleva.ann@nspcc.ru>
This commit is contained in:
parent
ae52b2c2fa
commit
044ae477ca
10 changed files with 896 additions and 44 deletions
96
cli/smartcontract/testdata/gas/gas.go
vendored
96
cli/smartcontract/testdata/gas/gas.go
vendored
|
@ -2,13 +2,25 @@
|
||||||
package gastoken
|
package gastoken
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"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/nep17"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Hash contains contract hash.
|
// Hash contains contract hash.
|
||||||
var Hash = util.Uint160{0xcf, 0x76, 0xe2, 0x8b, 0xd0, 0x6, 0x2c, 0x4a, 0x47, 0x8e, 0xe3, 0x55, 0x61, 0x1, 0x13, 0x19, 0xf3, 0xcf, 0xa4, 0xd2}
|
var Hash = util.Uint160{0xcf, 0x76, 0xe2, 0x8b, 0xd0, 0x6, 0x2c, 0x4a, 0x47, 0x8e, 0xe3, 0x55, 0x61, 0x1, 0x13, 0x19, 0xf3, 0xcf, 0xa4, 0xd2}
|
||||||
|
|
||||||
|
// TransferEvent represents "Transfer" event emitted by the contract.
|
||||||
|
type TransferEvent struct {
|
||||||
|
From util.Uint160
|
||||||
|
To util.Uint160
|
||||||
|
Amount *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
// Invoker is used by ContractReader to call various safe methods.
|
// Invoker is used by ContractReader to call various safe methods.
|
||||||
type Invoker interface {
|
type Invoker interface {
|
||||||
nep17.Invoker
|
nep17.Invoker
|
||||||
|
@ -44,3 +56,87 @@ func New(actor Actor) *Contract {
|
||||||
var nep17t = nep17.New(actor, Hash)
|
var nep17t = nep17.New(actor, Hash)
|
||||||
return &Contract{ContractReader{nep17t.TokenReader, actor}, nep17t.TokenWriter, actor}
|
return &Contract{ContractReader{nep17t.TokenReader, actor}, nep17t.TokenWriter, actor}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransferEventsFromApplicationLog retrieves a set of all emitted events
|
||||||
|
// with "Transfer" name from the provided ApplicationLog.
|
||||||
|
func TransferEventsFromApplicationLog(log *result.ApplicationLog) ([]*TransferEvent, error) {
|
||||||
|
if log == nil {
|
||||||
|
return nil, errors.New("nil application log")
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []*TransferEvent
|
||||||
|
for i, ex := range log.Executions {
|
||||||
|
for j, e := range ex.Events {
|
||||||
|
if e.Name != "Transfer" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
event := new(TransferEvent)
|
||||||
|
err := event.FromStackItem(e.Item)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to deserialize TransferEvent from stackitem (execution %d, event %d): %w", i, j, err)
|
||||||
|
}
|
||||||
|
res = append(res, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStackItem converts provided stackitem.Array to TransferEvent and
|
||||||
|
// returns an error if so.
|
||||||
|
func (e *TransferEvent) FromStackItem(item *stackitem.Array) error {
|
||||||
|
if item == nil {
|
||||||
|
return errors.New("nil item")
|
||||||
|
}
|
||||||
|
arr, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("not an array")
|
||||||
|
}
|
||||||
|
if len(arr) != 3 {
|
||||||
|
return errors.New("wrong number of structure elements")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
index = -1
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
index++
|
||||||
|
e.From, 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 From: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
e.To, 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 To: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
e.Amount, err = arr[index].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("field Amount: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
280
cli/smartcontract/testdata/nameservice/nns.go
vendored
280
cli/smartcontract/testdata/nameservice/nns.go
vendored
|
@ -2,6 +2,8 @@
|
||||||
package nameservice
|
package nameservice
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||||
|
@ -16,6 +18,28 @@ import (
|
||||||
// Hash contains contract hash.
|
// 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}
|
var Hash = util.Uint160{0xde, 0x46, 0x5f, 0x5d, 0x50, 0x57, 0xcf, 0x33, 0x28, 0x47, 0x94, 0xc5, 0xcf, 0xc2, 0xc, 0x69, 0x37, 0x1c, 0xac, 0x50}
|
||||||
|
|
||||||
|
// TransferEvent represents "Transfer" event emitted by the contract.
|
||||||
|
type TransferEvent struct {
|
||||||
|
From util.Uint160
|
||||||
|
To util.Uint160
|
||||||
|
Amount *big.Int
|
||||||
|
TokenId []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAdminEvent represents "SetAdmin" event emitted by the contract.
|
||||||
|
type SetAdminEvent struct {
|
||||||
|
Name string
|
||||||
|
OldAdmin util.Uint160
|
||||||
|
NewAdmin util.Uint160
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenewEvent represents "Renew" event emitted by the contract.
|
||||||
|
type RenewEvent struct {
|
||||||
|
Name string
|
||||||
|
OldExpiration *big.Int
|
||||||
|
NewExpiration *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
// Invoker is used by ContractReader to call various safe methods.
|
// Invoker is used by ContractReader to call various safe methods.
|
||||||
type Invoker interface {
|
type Invoker interface {
|
||||||
nep11.Invoker
|
nep11.Invoker
|
||||||
|
@ -320,3 +344,259 @@ func (c *Contract) DeleteRecordTransaction(name string, typev *big.Int) (*transa
|
||||||
func (c *Contract) DeleteRecordUnsigned(name string, typev *big.Int) (*transaction.Transaction, error) {
|
func (c *Contract) DeleteRecordUnsigned(name string, typev *big.Int) (*transaction.Transaction, error) {
|
||||||
return c.actor.MakeUnsignedCall(Hash, "deleteRecord", nil, name, typev)
|
return c.actor.MakeUnsignedCall(Hash, "deleteRecord", nil, name, typev)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransferEventsFromApplicationLog retrieves a set of all emitted events
|
||||||
|
// with "Transfer" name from the provided ApplicationLog.
|
||||||
|
func TransferEventsFromApplicationLog(log *result.ApplicationLog) ([]*TransferEvent, error) {
|
||||||
|
if log == nil {
|
||||||
|
return nil, errors.New("nil application log")
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []*TransferEvent
|
||||||
|
for i, ex := range log.Executions {
|
||||||
|
for j, e := range ex.Events {
|
||||||
|
if e.Name != "Transfer" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
event := new(TransferEvent)
|
||||||
|
err := event.FromStackItem(e.Item)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to deserialize TransferEvent from stackitem (execution %d, event %d): %w", i, j, err)
|
||||||
|
}
|
||||||
|
res = append(res, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStackItem converts provided stackitem.Array to TransferEvent and
|
||||||
|
// returns an error if so.
|
||||||
|
func (e *TransferEvent) FromStackItem(item *stackitem.Array) error {
|
||||||
|
if item == nil {
|
||||||
|
return errors.New("nil item")
|
||||||
|
}
|
||||||
|
arr, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("not an array")
|
||||||
|
}
|
||||||
|
if len(arr) != 4 {
|
||||||
|
return errors.New("wrong number of structure elements")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
index = -1
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
index++
|
||||||
|
e.From, 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 From: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
e.To, 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 To: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
e.Amount, err = arr[index].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("field Amount: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
e.TokenId, err = arr[index].TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("field TokenId: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetAdminEventsFromApplicationLog retrieves a set of all emitted events
|
||||||
|
// with "SetAdmin" name from the provided ApplicationLog.
|
||||||
|
func SetAdminEventsFromApplicationLog(log *result.ApplicationLog) ([]*SetAdminEvent, error) {
|
||||||
|
if log == nil {
|
||||||
|
return nil, errors.New("nil application log")
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []*SetAdminEvent
|
||||||
|
for i, ex := range log.Executions {
|
||||||
|
for j, e := range ex.Events {
|
||||||
|
if e.Name != "SetAdmin" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
event := new(SetAdminEvent)
|
||||||
|
err := event.FromStackItem(e.Item)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to deserialize SetAdminEvent from stackitem (execution %d, event %d): %w", i, j, err)
|
||||||
|
}
|
||||||
|
res = append(res, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStackItem converts provided stackitem.Array to SetAdminEvent and
|
||||||
|
// returns an error if so.
|
||||||
|
func (e *SetAdminEvent) FromStackItem(item *stackitem.Array) error {
|
||||||
|
if item == nil {
|
||||||
|
return errors.New("nil item")
|
||||||
|
}
|
||||||
|
arr, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("not an array")
|
||||||
|
}
|
||||||
|
if len(arr) != 3 {
|
||||||
|
return errors.New("wrong number of structure elements")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
index = -1
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
index++
|
||||||
|
e.Name, err = func (item stackitem.Item) (string, error) {
|
||||||
|
b, err := item.TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !utf8.Valid(b) {
|
||||||
|
return "", errors.New("not a UTF-8 string")
|
||||||
|
}
|
||||||
|
return string(b), nil
|
||||||
|
} (arr[index])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("field Name: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
e.OldAdmin, 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 OldAdmin: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
e.NewAdmin, 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 NewAdmin: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RenewEventsFromApplicationLog retrieves a set of all emitted events
|
||||||
|
// with "Renew" name from the provided ApplicationLog.
|
||||||
|
func RenewEventsFromApplicationLog(log *result.ApplicationLog) ([]*RenewEvent, error) {
|
||||||
|
if log == nil {
|
||||||
|
return nil, errors.New("nil application log")
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []*RenewEvent
|
||||||
|
for i, ex := range log.Executions {
|
||||||
|
for j, e := range ex.Events {
|
||||||
|
if e.Name != "Renew" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
event := new(RenewEvent)
|
||||||
|
err := event.FromStackItem(e.Item)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to deserialize RenewEvent from stackitem (execution %d, event %d): %w", i, j, err)
|
||||||
|
}
|
||||||
|
res = append(res, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStackItem converts provided stackitem.Array to RenewEvent and
|
||||||
|
// returns an error if so.
|
||||||
|
func (e *RenewEvent) FromStackItem(item *stackitem.Array) error {
|
||||||
|
if item == nil {
|
||||||
|
return errors.New("nil item")
|
||||||
|
}
|
||||||
|
arr, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("not an array")
|
||||||
|
}
|
||||||
|
if len(arr) != 3 {
|
||||||
|
return errors.New("wrong number of structure elements")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
index = -1
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
index++
|
||||||
|
e.Name, err = func (item stackitem.Item) (string, error) {
|
||||||
|
b, err := item.TryBytes()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
if !utf8.Valid(b) {
|
||||||
|
return "", errors.New("not a UTF-8 string")
|
||||||
|
}
|
||||||
|
return string(b), nil
|
||||||
|
} (arr[index])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("field Name: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
e.OldExpiration, err = arr[index].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("field OldExpiration: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
e.NewExpiration, err = arr[index].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("field NewExpiration: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
193
cli/smartcontract/testdata/nex/nex.go
vendored
193
cli/smartcontract/testdata/nex/nex.go
vendored
|
@ -2,17 +2,36 @@
|
||||||
package nextoken
|
package nextoken
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/crypto/keys"
|
"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/nep17"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
"github.com/nspcc-dev/neo-go/pkg/rpcclient/unwrap"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
"math/big"
|
"math/big"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Hash contains contract hash.
|
// 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}
|
var Hash = util.Uint160{0xa8, 0x1a, 0xa1, 0xf0, 0x4b, 0xf, 0xdc, 0x4a, 0xa2, 0xce, 0xd5, 0xbf, 0xc6, 0x22, 0xcf, 0xe8, 0x9, 0x7f, 0xa6, 0xa2}
|
||||||
|
|
||||||
|
// TransferEvent represents "Transfer" event emitted by the contract.
|
||||||
|
type TransferEvent struct {
|
||||||
|
From util.Uint160
|
||||||
|
To util.Uint160
|
||||||
|
Amount *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnMintEvent represents "OnMint" event emitted by the contract.
|
||||||
|
type OnMintEvent struct {
|
||||||
|
From util.Uint160
|
||||||
|
To util.Uint160
|
||||||
|
Amount *big.Int
|
||||||
|
SwapId *big.Int
|
||||||
|
}
|
||||||
|
|
||||||
// Invoker is used by ContractReader to call various safe methods.
|
// Invoker is used by ContractReader to call various safe methods.
|
||||||
type Invoker interface {
|
type Invoker interface {
|
||||||
nep17.Invoker
|
nep17.Invoker
|
||||||
|
@ -229,3 +248,177 @@ func (c *Contract) UpdateCapTransaction(newCap *big.Int) (*transaction.Transacti
|
||||||
func (c *Contract) UpdateCapUnsigned(newCap *big.Int) (*transaction.Transaction, error) {
|
func (c *Contract) UpdateCapUnsigned(newCap *big.Int) (*transaction.Transaction, error) {
|
||||||
return c.actor.MakeUnsignedCall(Hash, "updateCap", nil, newCap)
|
return c.actor.MakeUnsignedCall(Hash, "updateCap", nil, newCap)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TransferEventsFromApplicationLog retrieves a set of all emitted events
|
||||||
|
// with "Transfer" name from the provided ApplicationLog.
|
||||||
|
func TransferEventsFromApplicationLog(log *result.ApplicationLog) ([]*TransferEvent, error) {
|
||||||
|
if log == nil {
|
||||||
|
return nil, errors.New("nil application log")
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []*TransferEvent
|
||||||
|
for i, ex := range log.Executions {
|
||||||
|
for j, e := range ex.Events {
|
||||||
|
if e.Name != "Transfer" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
event := new(TransferEvent)
|
||||||
|
err := event.FromStackItem(e.Item)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to deserialize TransferEvent from stackitem (execution %d, event %d): %w", i, j, err)
|
||||||
|
}
|
||||||
|
res = append(res, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStackItem converts provided stackitem.Array to TransferEvent and
|
||||||
|
// returns an error if so.
|
||||||
|
func (e *TransferEvent) FromStackItem(item *stackitem.Array) error {
|
||||||
|
if item == nil {
|
||||||
|
return errors.New("nil item")
|
||||||
|
}
|
||||||
|
arr, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("not an array")
|
||||||
|
}
|
||||||
|
if len(arr) != 3 {
|
||||||
|
return errors.New("wrong number of structure elements")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
index = -1
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
index++
|
||||||
|
e.From, 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 From: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
e.To, 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 To: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
e.Amount, err = arr[index].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("field Amount: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// OnMintEventsFromApplicationLog retrieves a set of all emitted events
|
||||||
|
// with "OnMint" name from the provided ApplicationLog.
|
||||||
|
func OnMintEventsFromApplicationLog(log *result.ApplicationLog) ([]*OnMintEvent, error) {
|
||||||
|
if log == nil {
|
||||||
|
return nil, errors.New("nil application log")
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []*OnMintEvent
|
||||||
|
for i, ex := range log.Executions {
|
||||||
|
for j, e := range ex.Events {
|
||||||
|
if e.Name != "OnMint" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
event := new(OnMintEvent)
|
||||||
|
err := event.FromStackItem(e.Item)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to deserialize OnMintEvent from stackitem (execution %d, event %d): %w", i, j, err)
|
||||||
|
}
|
||||||
|
res = append(res, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStackItem converts provided stackitem.Array to OnMintEvent and
|
||||||
|
// returns an error if so.
|
||||||
|
func (e *OnMintEvent) FromStackItem(item *stackitem.Array) error {
|
||||||
|
if item == nil {
|
||||||
|
return errors.New("nil item")
|
||||||
|
}
|
||||||
|
arr, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("not an array")
|
||||||
|
}
|
||||||
|
if len(arr) != 4 {
|
||||||
|
return errors.New("wrong number of structure elements")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
index = -1
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
index++
|
||||||
|
e.From, 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 From: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
e.To, 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 To: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
e.Amount, err = arr[index].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("field Amount: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
index++
|
||||||
|
e.SwapId, err = arr[index].TryInteger()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("field SwapId: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
74
cli/smartcontract/testdata/verifyrpc/verify.go
vendored
74
cli/smartcontract/testdata/verifyrpc/verify.go
vendored
|
@ -2,14 +2,23 @@
|
||||||
package verify
|
package verify
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
"github.com/nspcc-dev/neo-go/pkg/core/transaction"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/neorpc/result"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/vm/stackitem"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Hash contains contract hash.
|
// 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}
|
var Hash = util.Uint160{0x33, 0x22, 0x11, 0x0, 0xff, 0xee, 0xdd, 0xcc, 0xbb, 0xaa, 0x99, 0x88, 0x77, 0x66, 0x55, 0x44, 0x33, 0x22, 0x11, 0x0}
|
||||||
|
|
||||||
|
// HelloWorldEvent represents "Hello world!" event emitted by the contract.
|
||||||
|
type HelloWorldEvent struct {
|
||||||
|
Args []any
|
||||||
|
}
|
||||||
|
|
||||||
// Actor is used by Contract to call state-changing methods.
|
// Actor is used by Contract to call state-changing methods.
|
||||||
type Actor interface {
|
type Actor interface {
|
||||||
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
|
MakeCall(contract util.Uint160, method string, params ...any) (*transaction.Transaction, error)
|
||||||
|
@ -67,3 +76,68 @@ func (c *Contract) VerifyUnsigned() (*transaction.Transaction, error) {
|
||||||
}
|
}
|
||||||
return c.actor.MakeUnsignedRun(script, nil)
|
return c.actor.MakeUnsignedRun(script, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HelloWorldEventsFromApplicationLog retrieves a set of all emitted events
|
||||||
|
// with "Hello world!" name from the provided ApplicationLog.
|
||||||
|
func HelloWorldEventsFromApplicationLog(log *result.ApplicationLog) ([]*HelloWorldEvent, error) {
|
||||||
|
if log == nil {
|
||||||
|
return nil, errors.New("nil application log")
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []*HelloWorldEvent
|
||||||
|
for i, ex := range log.Executions {
|
||||||
|
for j, e := range ex.Events {
|
||||||
|
if e.Name != "Hello world!" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
event := new(HelloWorldEvent)
|
||||||
|
err := event.FromStackItem(e.Item)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to deserialize HelloWorldEvent from stackitem (execution %d, event %d): %w", i, j, err)
|
||||||
|
}
|
||||||
|
res = append(res, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStackItem converts provided stackitem.Array to HelloWorldEvent and
|
||||||
|
// returns an error if so.
|
||||||
|
func (e *HelloWorldEvent) FromStackItem(item *stackitem.Array) error {
|
||||||
|
if item == nil {
|
||||||
|
return errors.New("nil item")
|
||||||
|
}
|
||||||
|
arr, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("not an array")
|
||||||
|
}
|
||||||
|
if len(arr) != 1 {
|
||||||
|
return errors.New("wrong number of structure elements")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
index = -1
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
index++
|
||||||
|
e.Args, err = func (item stackitem.Item) ([]any, error) {
|
||||||
|
arr, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("not an array")
|
||||||
|
}
|
||||||
|
res := make([]any, len(arr))
|
||||||
|
for i := range res {
|
||||||
|
res[i], err = arr[i].Value(), error(nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("item %d: %w", i, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
} (arr[index])
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("field Args: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
|
@ -110,7 +110,7 @@ type codegen struct {
|
||||||
docIndex map[string]int
|
docIndex map[string]int
|
||||||
|
|
||||||
// emittedEvents contains all events emitted by the contract.
|
// emittedEvents contains all events emitted by the contract.
|
||||||
emittedEvents map[string][][]string
|
emittedEvents map[string][]EmittedEventInfo
|
||||||
|
|
||||||
// invokedContracts contains invoked methods of other contracts.
|
// invokedContracts contains invoked methods of other contracts.
|
||||||
invokedContracts map[util.Uint160][]string
|
invokedContracts map[util.Uint160][]string
|
||||||
|
@ -2269,7 +2269,7 @@ func newCodegen(info *buildInfo, pkg *packages.Package) *codegen {
|
||||||
initEndOffset: -1,
|
initEndOffset: -1,
|
||||||
deployEndOffset: -1,
|
deployEndOffset: -1,
|
||||||
|
|
||||||
emittedEvents: make(map[string][][]string),
|
emittedEvents: make(map[string][]EmittedEventInfo),
|
||||||
invokedContracts: make(map[util.Uint160][]string),
|
invokedContracts: make(map[util.Uint160][]string),
|
||||||
sequencePoints: make(map[string][]DebugSeqPoint),
|
sequencePoints: make(map[string][]DebugSeqPoint),
|
||||||
}
|
}
|
||||||
|
|
|
@ -309,6 +309,22 @@ func CompileAndSave(src string, o *Options) ([]byte, error) {
|
||||||
if len(di.NamedTypes) > 0 {
|
if len(di.NamedTypes) > 0 {
|
||||||
cfg.NamedTypes = di.NamedTypes
|
cfg.NamedTypes = di.NamedTypes
|
||||||
}
|
}
|
||||||
|
if len(di.EmittedEvents) > 0 {
|
||||||
|
for eventName, eventUsages := range di.EmittedEvents {
|
||||||
|
for typeName, extType := range eventUsages[0].ExtTypes {
|
||||||
|
cfg.NamedTypes[typeName] = extType
|
||||||
|
}
|
||||||
|
for _, p := range eventUsages[0].Params {
|
||||||
|
pname := eventName + "." + p.Name
|
||||||
|
if p.RealType.TypeName != "" {
|
||||||
|
cfg.Overrides[pname] = p.RealType
|
||||||
|
}
|
||||||
|
if p.ExtendedType != nil {
|
||||||
|
cfg.Types[pname] = *p.ExtendedType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
data, err := yaml.Marshal(&cfg)
|
data, err := yaml.Marshal(&cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("can't marshal bindings configuration: %w", err)
|
return nil, fmt.Errorf("can't marshal bindings configuration: %w", err)
|
||||||
|
@ -366,24 +382,23 @@ func CreateManifest(di *DebugInfo, o *Options) (*manifest.Manifest, error) {
|
||||||
}
|
}
|
||||||
if !o.NoEventsCheck {
|
if !o.NoEventsCheck {
|
||||||
for name := range di.EmittedEvents {
|
for name := range di.EmittedEvents {
|
||||||
ev := m.ABI.GetEvent(name)
|
expected := m.ABI.GetEvent(name)
|
||||||
if ev == nil {
|
if expected == nil {
|
||||||
return nil, fmt.Errorf("event '%s' is emitted but not specified in manifest", name)
|
return nil, fmt.Errorf("event '%s' is emitted but not specified in manifest", name)
|
||||||
}
|
}
|
||||||
argsList := di.EmittedEvents[name]
|
for _, emitted := range di.EmittedEvents[name] {
|
||||||
for i := range argsList {
|
if len(emitted.Params) != len(expected.Parameters) {
|
||||||
if len(argsList[i]) != len(ev.Parameters) {
|
|
||||||
return nil, fmt.Errorf("event '%s' should have %d parameters but has %d",
|
return nil, fmt.Errorf("event '%s' should have %d parameters but has %d",
|
||||||
name, len(ev.Parameters), len(argsList[i]))
|
name, len(expected.Parameters), len(emitted.Params))
|
||||||
}
|
}
|
||||||
for j := range ev.Parameters {
|
for j := range expected.Parameters {
|
||||||
if ev.Parameters[j].Type == smartcontract.AnyType {
|
if expected.Parameters[j].Type == smartcontract.AnyType {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
expected := ev.Parameters[j].Type.String()
|
expectedT := expected.Parameters[j].Type
|
||||||
if argsList[i][j] != expected {
|
if emitted.Params[j].TypeSC != expectedT {
|
||||||
return nil, fmt.Errorf("event '%s' should have '%s' as type of %d parameter, "+
|
return nil, fmt.Errorf("event '%s' should have '%s' as type of %d parameter, "+
|
||||||
"got: %s", name, expected, j+1, argsList[i][j])
|
"got: %s", name, expectedT, j+1, emitted.Params[j].TypeSC)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,9 +29,14 @@ type DebugInfo struct {
|
||||||
// NamedTypes are exported structured types that have some name (even
|
// NamedTypes are exported structured types that have some name (even
|
||||||
// if the original structure doesn't) and a number of internal fields.
|
// if the original structure doesn't) and a number of internal fields.
|
||||||
NamedTypes map[string]binding.ExtendedType `json:"-"`
|
NamedTypes map[string]binding.ExtendedType `json:"-"`
|
||||||
Events []EventDebugInfo `json:"events"`
|
// Events are the events that contract is allowed to emit and that have to
|
||||||
// EmittedEvents contains events occurring in code.
|
// be presented in the resulting contract manifest and debug info file.
|
||||||
EmittedEvents map[string][][]string `json:"-"`
|
Events []EventDebugInfo `json:"events"`
|
||||||
|
// EmittedEvents contains events occurring in code, i.e. events emitted
|
||||||
|
// via runtime.Notify(...) call in the contract code if they have constant
|
||||||
|
// names and doesn't have ellipsis arguments. EmittedEvents are not related
|
||||||
|
// to the debug info and are aimed to serve bindings generation.
|
||||||
|
EmittedEvents map[string][]EmittedEventInfo `json:"-"`
|
||||||
// InvokedContracts contains foreign contract invocations.
|
// InvokedContracts contains foreign contract invocations.
|
||||||
InvokedContracts map[util.Uint160][]string `json:"-"`
|
InvokedContracts map[util.Uint160][]string `json:"-"`
|
||||||
// StaticVariables contains a list of static variable names and types.
|
// StaticVariables contains a list of static variable names and types.
|
||||||
|
@ -112,6 +117,14 @@ type DebugParam struct {
|
||||||
TypeSC smartcontract.ParamType `json:"-"`
|
TypeSC smartcontract.ParamType `json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EmittedEventInfo describes information about single emitted event got from
|
||||||
|
// the contract code. It has the map of extended types used as the parameters to
|
||||||
|
// runtime.Notify(...) call (if any) and the parameters info itself.
|
||||||
|
type EmittedEventInfo struct {
|
||||||
|
ExtTypes map[string]binding.ExtendedType
|
||||||
|
Params []DebugParam
|
||||||
|
}
|
||||||
|
|
||||||
func (c *codegen) saveSequencePoint(n ast.Node) {
|
func (c *codegen) saveSequencePoint(n ast.Node) {
|
||||||
name := "init"
|
name := "init"
|
||||||
if c.scope != nil {
|
if c.scope != nil {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"go/types"
|
"go/types"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
"github.com/nspcc-dev/neo-go/pkg/core/interop/runtime"
|
||||||
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/binding"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
"github.com/nspcc-dev/neo-go/pkg/smartcontract/callflag"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/util"
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
||||||
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
"github.com/nspcc-dev/neo-go/pkg/vm/emit"
|
||||||
|
@ -172,11 +173,21 @@ func (c *codegen) processNotify(f *funcScope, args []ast.Expr, hasEllipsis bool)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
params := make([]string, 0, len(args[1:]))
|
params := make([]DebugParam, 0, len(args[1:]))
|
||||||
vParams := make([]*stackitem.Type, 0, len(args[1:]))
|
vParams := make([]*stackitem.Type, 0, len(args[1:]))
|
||||||
|
// extMap holds the extended parameter types used for the given event call.
|
||||||
|
// It will be unified with the common extMap later during bindings config
|
||||||
|
// generation.
|
||||||
|
extMap := make(map[string]binding.ExtendedType)
|
||||||
for _, p := range args[1:] {
|
for _, p := range args[1:] {
|
||||||
st, vt, _, _ := c.scAndVMTypeFromExpr(p, nil)
|
st, vt, over, extT := c.scAndVMTypeFromExpr(p, extMap)
|
||||||
params = append(params, st.String())
|
params = append(params, DebugParam{
|
||||||
|
Name: "", // Parameter name will be filled in several lines below if the corresponding event exists in the buildinfo.options.
|
||||||
|
Type: vt.String(),
|
||||||
|
RealType: over,
|
||||||
|
ExtendedType: extT,
|
||||||
|
TypeSC: st,
|
||||||
|
})
|
||||||
vParams = append(vParams, &vt)
|
vParams = append(vParams, &vt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -187,36 +198,43 @@ func (c *codegen) processNotify(f *funcScope, args []ast.Expr, hasEllipsis bool)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
var eventFound bool
|
var eventFound bool
|
||||||
if c.buildInfo.options != nil && c.buildInfo.options.ContractEvents != nil && !c.buildInfo.options.NoEventsCheck {
|
if c.buildInfo.options != nil && c.buildInfo.options.ContractEvents != nil {
|
||||||
for _, e := range c.buildInfo.options.ContractEvents {
|
for _, e := range c.buildInfo.options.ContractEvents {
|
||||||
if e.Name == name && len(e.Parameters) == len(vParams) {
|
if e.Name == name && len(e.Parameters) == len(vParams) {
|
||||||
eventFound = true
|
eventFound = true
|
||||||
for i, scParam := range e.Parameters {
|
for i, scParam := range e.Parameters {
|
||||||
expectedType := scParam.Type.ConvertToStackitemType()
|
params[i].Name = scParam.Name
|
||||||
// No need to cast if the desired type is unknown.
|
if !c.buildInfo.options.NoEventsCheck {
|
||||||
if expectedType == stackitem.AnyT ||
|
expectedType := scParam.Type.ConvertToStackitemType()
|
||||||
// Do not cast if desired type is Interop, the actual type is likely to be Any, leave the resolving to runtime.Notify.
|
// No need to cast if the desired type is unknown.
|
||||||
expectedType == stackitem.InteropT ||
|
if expectedType == stackitem.AnyT ||
|
||||||
// No need to cast if actual parameter type matches the desired one.
|
// Do not cast if desired type is Interop, the actual type is likely to be Any, leave the resolving to runtime.Notify.
|
||||||
*vParams[i] == expectedType ||
|
expectedType == stackitem.InteropT ||
|
||||||
// expectedType doesn't contain Buffer anyway, but if actual variable type is Buffer,
|
// No need to cast if actual parameter type matches the desired one.
|
||||||
// then runtime.Notify will convert it to ByteArray automatically, thus no need to emit conversion code.
|
*vParams[i] == expectedType ||
|
||||||
(*vParams[i] == stackitem.BufferT && expectedType == stackitem.ByteArrayT) {
|
// expectedType doesn't contain Buffer anyway, but if actual variable type is Buffer,
|
||||||
vParams[i] = nil
|
// then runtime.Notify will convert it to ByteArray automatically, thus no need to emit conversion code.
|
||||||
} else {
|
(*vParams[i] == stackitem.BufferT && expectedType == stackitem.ByteArrayT) {
|
||||||
// For other cases the conversion code will be emitted using vParams...
|
vParams[i] = nil
|
||||||
vParams[i] = &expectedType
|
} else {
|
||||||
// ...thus, update emitted notification info in advance.
|
// For other cases the conversion code will be emitted using vParams...
|
||||||
params[i] = scParam.Type.String()
|
vParams[i] = &expectedType
|
||||||
|
// ...thus, update emitted notification info in advance.
|
||||||
|
params[i].Type = scParam.Type.String()
|
||||||
|
params[i].TypeSC = scParam.Type
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c.emittedEvents[name] = append(c.emittedEvents[name], params)
|
c.emittedEvents[name] = append(c.emittedEvents[name], EmittedEventInfo{
|
||||||
|
ExtTypes: extMap,
|
||||||
|
Params: params,
|
||||||
|
})
|
||||||
// Do not enforce perfect expected/actual events match on this step, the final
|
// Do not enforce perfect expected/actual events match on this step, the final
|
||||||
// check wil be performed after compilation if --no-events option is off.
|
// check wil be performed after compilation if --no-events option is off.
|
||||||
if eventFound {
|
if eventFound && !c.buildInfo.options.NoEventsCheck {
|
||||||
return vParams
|
return vParams
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -62,6 +62,7 @@ type (
|
||||||
// smartcontract. The map key has one of the following forms:
|
// smartcontract. The map key has one of the following forms:
|
||||||
// - `methodName` for method return value;
|
// - `methodName` for method return value;
|
||||||
// - `mathodName.paramName` for method's parameter value.
|
// - `mathodName.paramName` for method's parameter value.
|
||||||
|
// - `eventName.paramName` for event's parameter value.
|
||||||
Types map[string]ExtendedType `yaml:"types,omitempty"`
|
Types map[string]ExtendedType `yaml:"types,omitempty"`
|
||||||
Output io.Writer `yaml:"-"`
|
Output io.Writer `yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
"unicode"
|
||||||
|
|
||||||
"github.com/nspcc-dev/neo-go/pkg/smartcontract"
|
"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/binding"
|
||||||
|
@ -17,6 +18,15 @@ import (
|
||||||
// start and ends with a new line. On adding new block of code to the template, please,
|
// start and ends with a new line. On adding new block of code to the template, please,
|
||||||
// ensure that this block has new line at the start and in the end of the block.
|
// ensure that this block has new line at the start and in the end of the block.
|
||||||
const (
|
const (
|
||||||
|
eventDefinition = `{{ define "EVENT" }}
|
||||||
|
// {{.Name}}Event represents "{{.ManifestName}}" event emitted by the contract.
|
||||||
|
type {{.Name}}Event struct {
|
||||||
|
{{- range $index, $arg := .Parameters}}
|
||||||
|
{{toPascalCase .Name}} {{.Type}}
|
||||||
|
{{- end}}
|
||||||
|
}
|
||||||
|
{{ end }}`
|
||||||
|
|
||||||
safemethodDefinition = `{{ define "SAFEMETHOD" }}
|
safemethodDefinition = `{{ define "SAFEMETHOD" }}
|
||||||
// {{.Name}} {{.Comment}}
|
// {{.Name}} {{.Comment}}
|
||||||
func (c *ContractReader) {{.Name}}({{range $index, $arg := .Arguments -}}
|
func (c *ContractReader) {{.Name}}({{range $index, $arg := .Arguments -}}
|
||||||
|
@ -118,6 +128,7 @@ type {{toTypeName $name}} struct {
|
||||||
{{- end}}
|
{{- end}}
|
||||||
}
|
}
|
||||||
{{end}}
|
{{end}}
|
||||||
|
{{- range $e := .CustomEvents }}{{template "EVENT" $e }}{{ end -}}
|
||||||
{{- if .HasReader}}
|
{{- if .HasReader}}
|
||||||
// Invoker is used by ContractReader to call various safe methods.
|
// Invoker is used by ContractReader to call various safe methods.
|
||||||
type Invoker interface {
|
type Invoker interface {
|
||||||
|
@ -249,9 +260,65 @@ func (res *{{toTypeName $name}}) FromStackItem(item stackitem.Item) error {
|
||||||
{{- end}}
|
{{- end}}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
{{ end -}}
|
||||||
|
{{- range $e := .CustomEvents }}
|
||||||
|
// {{$e.Name}}EventsFromApplicationLog retrieves a set of all emitted events
|
||||||
|
// with "{{$e.ManifestName}}" name from the provided ApplicationLog.
|
||||||
|
func {{$e.Name}}EventsFromApplicationLog(log *result.ApplicationLog) ([]*{{$e.Name}}Event, error) {
|
||||||
|
if log == nil {
|
||||||
|
return nil, errors.New("nil application log")
|
||||||
|
}
|
||||||
|
|
||||||
|
var res []*{{$e.Name}}Event
|
||||||
|
for i, ex := range log.Executions {
|
||||||
|
for j, e := range ex.Events {
|
||||||
|
if e.Name != "{{$e.ManifestName}}" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
event := new({{$e.Name}}Event)
|
||||||
|
err := event.FromStackItem(e.Item)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to deserialize {{$e.Name}}Event from stackitem (execution %d, event %d): %w", i, j, err)
|
||||||
|
}
|
||||||
|
res = append(res, event)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromStackItem converts provided stackitem.Array to {{$e.Name}}Event and
|
||||||
|
// returns an error if so.
|
||||||
|
func (e *{{$e.Name}}Event) FromStackItem(item *stackitem.Array) error {
|
||||||
|
if item == nil {
|
||||||
|
return errors.New("nil item")
|
||||||
|
}
|
||||||
|
arr, ok := item.Value().([]stackitem.Item)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("not an array")
|
||||||
|
}
|
||||||
|
if len(arr) != {{len $e.Parameters}} {
|
||||||
|
return errors.New("wrong number of structure elements")
|
||||||
|
}
|
||||||
|
|
||||||
|
{{if len $e.Parameters}}var (
|
||||||
|
index = -1
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
{{- range $p := $e.Parameters}}
|
||||||
|
index++
|
||||||
|
e.{{toPascalCase .Name}}, err = {{etTypeConverter .ExtType "arr[index]"}}
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("field {{toPascalCase .Name}}: %w", err)
|
||||||
|
}
|
||||||
|
{{end}}
|
||||||
|
{{- end}}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
{{end -}}`
|
{{end -}}`
|
||||||
|
|
||||||
srcTmpl = bindingDefinition +
|
srcTmpl = bindingDefinition +
|
||||||
|
eventDefinition +
|
||||||
safemethodDefinition +
|
safemethodDefinition +
|
||||||
methodDefinition
|
methodDefinition
|
||||||
)
|
)
|
||||||
|
@ -260,8 +327,9 @@ type (
|
||||||
ContractTmpl struct {
|
ContractTmpl struct {
|
||||||
binding.ContractTmpl
|
binding.ContractTmpl
|
||||||
|
|
||||||
SafeMethods []SafeMethodTmpl
|
SafeMethods []SafeMethodTmpl
|
||||||
NamedTypes map[string]binding.ExtendedType
|
CustomEvents []CustomEventTemplate
|
||||||
|
NamedTypes map[string]binding.ExtendedType
|
||||||
|
|
||||||
IsNep11D bool
|
IsNep11D bool
|
||||||
IsNep11ND bool
|
IsNep11ND bool
|
||||||
|
@ -278,6 +346,25 @@ type (
|
||||||
ItemTo string
|
ItemTo string
|
||||||
ExtendedReturn binding.ExtendedType
|
ExtendedReturn binding.ExtendedType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CustomEventTemplate struct {
|
||||||
|
// Name is the event's name that will be used as the event structure name in
|
||||||
|
// the resulting RPC binding. It is a valid go structure name and may differ
|
||||||
|
// from ManifestName.
|
||||||
|
Name string
|
||||||
|
// ManifestName is the event's name declared in the contract manifest.
|
||||||
|
// It may contain any UTF8 character.
|
||||||
|
ManifestName string
|
||||||
|
Parameters []EventParamTmpl
|
||||||
|
}
|
||||||
|
|
||||||
|
EventParamTmpl struct {
|
||||||
|
binding.ParamTmpl
|
||||||
|
|
||||||
|
// ExtType holds the event parameter's type information provided by Manifest,
|
||||||
|
// i.e. simple types only.
|
||||||
|
ExtType binding.ExtendedType
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewConfig initializes and returns a new config instance.
|
// NewConfig initializes and returns a new config instance.
|
||||||
|
@ -328,7 +415,7 @@ func Generate(cfg binding.Config) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
ctr.ContractTmpl = binding.TemplateFromManifest(cfg, scTypeToGo)
|
ctr.ContractTmpl = binding.TemplateFromManifest(cfg, scTypeToGo)
|
||||||
ctr = scTemplateToRPC(cfg, ctr, imports)
|
ctr = scTemplateToRPC(cfg, ctr, imports, scTypeToGo)
|
||||||
ctr.NamedTypes = cfg.NamedTypes
|
ctr.NamedTypes = cfg.NamedTypes
|
||||||
|
|
||||||
var srcTemplate = template.Must(template.New("generate").Funcs(template.FuncMap{
|
var srcTemplate = template.Must(template.New("generate").Funcs(template.FuncMap{
|
||||||
|
@ -338,8 +425,9 @@ func Generate(cfg binding.Config) error {
|
||||||
r, _ := extendedTypeToGo(et, ctr.NamedTypes)
|
r, _ := extendedTypeToGo(et, ctr.NamedTypes)
|
||||||
return r
|
return r
|
||||||
},
|
},
|
||||||
"toTypeName": toTypeName,
|
"toTypeName": toTypeName,
|
||||||
"cutPointer": cutPointer,
|
"cutPointer": cutPointer,
|
||||||
|
"toPascalCase": toPascalCase,
|
||||||
}).Parse(srcTmpl))
|
}).Parse(srcTmpl))
|
||||||
|
|
||||||
return srcTemplate.Execute(cfg.Output, ctr)
|
return srcTemplate.Execute(cfg.Output, ctr)
|
||||||
|
@ -533,7 +621,7 @@ func scTypeToGo(name string, typ smartcontract.ParamType, cfg *binding.Config) (
|
||||||
return extendedTypeToGo(et, cfg.NamedTypes)
|
return extendedTypeToGo(et, cfg.NamedTypes)
|
||||||
}
|
}
|
||||||
|
|
||||||
func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]struct{}) ContractTmpl {
|
func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]struct{}, scTypeConverter func(string, smartcontract.ParamType, *binding.Config) (string, string)) ContractTmpl {
|
||||||
for i := range ctr.Imports {
|
for i := range ctr.Imports {
|
||||||
imports[ctr.Imports[i]] = struct{}{}
|
imports[ctr.Imports[i]] = struct{}{}
|
||||||
}
|
}
|
||||||
|
@ -564,6 +652,51 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st
|
||||||
if len(cfg.NamedTypes) > 0 {
|
if len(cfg.NamedTypes) > 0 {
|
||||||
imports["errors"] = struct{}{}
|
imports["errors"] = struct{}{}
|
||||||
}
|
}
|
||||||
|
for _, abiEvent := range cfg.Manifest.ABI.Events {
|
||||||
|
eTmp := CustomEventTemplate{
|
||||||
|
// TODO: proper event name is better to be set right into config binding in normal form.
|
||||||
|
Name: toPascalCase(abiEvent.Name),
|
||||||
|
ManifestName: abiEvent.Name,
|
||||||
|
}
|
||||||
|
var varnames = make(map[string]bool)
|
||||||
|
for i := range abiEvent.Parameters {
|
||||||
|
name := abiEvent.Parameters[i].Name
|
||||||
|
fullPName := abiEvent.Name + "." + name
|
||||||
|
typeStr, pkg := scTypeConverter(fullPName, abiEvent.Parameters[i].Type, &cfg)
|
||||||
|
if pkg != "" {
|
||||||
|
imports[pkg] = struct{}{}
|
||||||
|
}
|
||||||
|
for varnames[name] {
|
||||||
|
name = name + "_"
|
||||||
|
}
|
||||||
|
varnames[name] = true
|
||||||
|
|
||||||
|
var (
|
||||||
|
extType binding.ExtendedType
|
||||||
|
ok bool
|
||||||
|
)
|
||||||
|
if extType, ok = cfg.Types[fullPName]; !ok {
|
||||||
|
extType = binding.ExtendedType{
|
||||||
|
Base: abiEvent.Parameters[i].Type,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
eTmp.Parameters = append(eTmp.Parameters, EventParamTmpl{
|
||||||
|
ParamTmpl: binding.ParamTmpl{
|
||||||
|
Name: name,
|
||||||
|
Type: typeStr,
|
||||||
|
},
|
||||||
|
ExtType: extType,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
ctr.CustomEvents = append(ctr.CustomEvents, eTmp)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ctr.CustomEvents) > 0 {
|
||||||
|
imports["github.com/nspcc-dev/neo-go/pkg/neorpc/result"] = struct{}{}
|
||||||
|
imports["github.com/nspcc-dev/neo-go/pkg/vm/stackitem"] = struct{}{}
|
||||||
|
imports["fmt"] = struct{}{}
|
||||||
|
imports["errors"] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
for i := range ctr.SafeMethods {
|
for i := range ctr.SafeMethods {
|
||||||
switch ctr.SafeMethods[i].ReturnType {
|
switch ctr.SafeMethods[i].ReturnType {
|
||||||
|
@ -693,3 +826,32 @@ func toTypeName(s string) string {
|
||||||
func addIndent(str string, ind string) string {
|
func addIndent(str string, ind string) string {
|
||||||
return strings.ReplaceAll(str, "\n", "\n"+ind)
|
return strings.ReplaceAll(str, "\n", "\n"+ind)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// toPascalCase removes all non-unicode characters from the provided string and
|
||||||
|
// converts it to pascal case using space as delimiter.
|
||||||
|
func toPascalCase(s string) string {
|
||||||
|
var res string
|
||||||
|
ss := strings.Split(s, " ")
|
||||||
|
for i := range ss { // TODO: use DecodeRuneInString instead.
|
||||||
|
var word string
|
||||||
|
for _, ch := range ss[i] {
|
||||||
|
var ok bool
|
||||||
|
if len(res) == 0 && len(word) == 0 {
|
||||||
|
ok = unicode.IsLetter(ch)
|
||||||
|
} else {
|
||||||
|
ok = unicode.IsLetter(ch) || unicode.IsDigit(ch) || ch == '_'
|
||||||
|
}
|
||||||
|
if ok {
|
||||||
|
word += string(ch)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(word) > 0 {
|
||||||
|
res += upperFirst(word)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
func upperFirst(s string) string {
|
||||||
|
return strings.ToUpper(s[0:1]) + s[1:]
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue