diff --git a/cli/smartcontract/generate_test.go b/cli/smartcontract/generate_test.go index b274e99f3..67a46fc12 100644 --- a/cli/smartcontract/generate_test.go +++ b/cli/smartcontract/generate_test.go @@ -416,6 +416,7 @@ func TestAssistedRPCBindings(t *testing.T) { checkBinding(filepath.Join("testdata", "types")) checkBinding(filepath.Join("testdata", "structs")) + checkBinding(filepath.Join("testdata", "notifications")) require.False(t, rewriteExpectedOutputs) } diff --git a/cli/smartcontract/testdata/notifications/config.yml b/cli/smartcontract/testdata/notifications/config.yml new file mode 100644 index 000000000..5bb9b8554 --- /dev/null +++ b/cli/smartcontract/testdata/notifications/config.yml @@ -0,0 +1,7 @@ +name: "Notifications" +sourceurl: https://github.com/nspcc-dev/neo-go/ +events: + - name: "! complicated name %$#" + parameters: + - name: ! complicated param @#$% + type: String diff --git a/cli/smartcontract/testdata/notifications/notifications.go b/cli/smartcontract/testdata/notifications/notifications.go new file mode 100644 index 000000000..369d1cdb6 --- /dev/null +++ b/cli/smartcontract/testdata/notifications/notifications.go @@ -0,0 +1,7 @@ +package structs + +import "github.com/nspcc-dev/neo-go/pkg/interop/runtime" + +func Main() { + runtime.Notify("! complicated name %$#", "str1") +} diff --git a/cli/smartcontract/testdata/notifications/rpcbindings.out b/cli/smartcontract/testdata/notifications/rpcbindings.out new file mode 100644 index 000000000..aeeb3ccd7 --- /dev/null +++ b/cli/smartcontract/testdata/notifications/rpcbindings.out @@ -0,0 +1,748 @@ +// Package structs contains RPC wrappers for Notifications contract. +package structs + +import ( + "crypto/elliptic" + "errors" + "fmt" + "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/neorpc/result" + "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} + +// LedgerBlock is a contract-specific ledger.Block type used by its methods. +type LedgerBlock struct { + Hash util.Uint256 + Version *big.Int + PrevHash util.Uint256 + MerkleRoot util.Uint256 + Timestamp *big.Int + Nonce *big.Int + Index *big.Int + NextConsensus util.Uint160 + TransactionsLength *big.Int +} + +// LedgerBlockSR is a contract-specific ledger.BlockSR type used by its methods. +type LedgerBlockSR struct { + Hash util.Uint256 + Version *big.Int + PrevHash util.Uint256 + MerkleRoot util.Uint256 + Timestamp *big.Int + Nonce *big.Int + Index *big.Int + NextConsensus util.Uint160 + TransactionsLength *big.Int + PrevStateRoot util.Uint256 +} + +// LedgerTransaction is a contract-specific ledger.Transaction type used by its methods. +type LedgerTransaction struct { + Hash util.Uint256 + Version *big.Int + Nonce *big.Int + Sender util.Uint160 + SysFee *big.Int + NetFee *big.Int + ValidUntilBlock *big.Int + Script []byte +} + +// LedgerTransactionSigner is a contract-specific ledger.TransactionSigner type used by its methods. +type LedgerTransactionSigner struct { + Account util.Uint160 + Scopes *big.Int + AllowedContracts []util.Uint160 + AllowedGroups keys.PublicKeys + Rules []*LedgerWitnessRule +} + +// LedgerWitnessCondition is a contract-specific ledger.WitnessCondition type used by its methods. +type LedgerWitnessCondition struct { + Type *big.Int + Value any +} + +// LedgerWitnessRule is a contract-specific ledger.WitnessRule type used by its methods. +type LedgerWitnessRule struct { + Action *big.Int + Condition *LedgerWitnessCondition +} + +// ComplicatedNameEvent represents "! complicated name %$#" event emitted by the contract. +type ComplicatedNameEvent struct { + ComplicatedParam string +} + +// 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 +} + +// New creates an instance of Contract using Hash and the given Actor. +func New(actor Actor) *Contract { + return &Contract{actor} +} + +// Main creates a transaction invoking `main` 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) Main() (util.Uint256, uint32, error) { + return c.actor.SendCall(Hash, "main") +} + +// MainTransaction creates a transaction invoking `main` method of the contract. +// This transaction is signed, but not sent to the network, instead it's +// returned to the caller. +func (c *Contract) MainTransaction() (*transaction.Transaction, error) { + return c.actor.MakeCall(Hash, "main") +} + +// MainUnsigned creates a transaction invoking `main` 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) MainUnsigned() (*transaction.Transaction, error) { + return c.actor.MakeUnsignedCall(Hash, "main", nil) +} + +// itemToLedgerBlock converts stack item into *LedgerBlock. +func itemToLedgerBlock(item stackitem.Item, err error) (*LedgerBlock, error) { + if err != nil { + return nil, err + } + var res = new(LedgerBlock) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerBlock from the given stack item +// and returns an error if so. +func (res *LedgerBlock) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 9 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.PrevHash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PrevHash: %w", err) + } + + index++ + res.MerkleRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field MerkleRoot: %w", err) + } + + index++ + res.Timestamp, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Timestamp: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Index, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Index: %w", err) + } + + index++ + res.NextConsensus, 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 NextConsensus: %w", err) + } + + index++ + res.TransactionsLength, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field TransactionsLength: %w", err) + } + + return nil +} + +// itemToLedgerBlockSR converts stack item into *LedgerBlockSR. +func itemToLedgerBlockSR(item stackitem.Item, err error) (*LedgerBlockSR, error) { + if err != nil { + return nil, err + } + var res = new(LedgerBlockSR) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerBlockSR from the given stack item +// and returns an error if so. +func (res *LedgerBlockSR) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 10 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.PrevHash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PrevHash: %w", err) + } + + index++ + res.MerkleRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field MerkleRoot: %w", err) + } + + index++ + res.Timestamp, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Timestamp: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Index, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Index: %w", err) + } + + index++ + res.NextConsensus, 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 NextConsensus: %w", err) + } + + index++ + res.TransactionsLength, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field TransactionsLength: %w", err) + } + + index++ + res.PrevStateRoot, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field PrevStateRoot: %w", err) + } + + return nil +} + +// itemToLedgerTransaction converts stack item into *LedgerTransaction. +func itemToLedgerTransaction(item stackitem.Item, err error) (*LedgerTransaction, error) { + if err != nil { + return nil, err + } + var res = new(LedgerTransaction) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerTransaction from the given stack item +// and returns an error if so. +func (res *LedgerTransaction) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 8 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Hash, err = func (item stackitem.Item) (util.Uint256, error) { + b, err := item.TryBytes() + if err != nil { + return util.Uint256{}, err + } + u, err := util.Uint256DecodeBytesBE(b) + if err != nil { + return util.Uint256{}, err + } + return u, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field Hash: %w", err) + } + + index++ + res.Version, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Version: %w", err) + } + + index++ + res.Nonce, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Nonce: %w", err) + } + + index++ + res.Sender, 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 Sender: %w", err) + } + + index++ + res.SysFee, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field SysFee: %w", err) + } + + index++ + res.NetFee, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field NetFee: %w", err) + } + + index++ + res.ValidUntilBlock, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field ValidUntilBlock: %w", err) + } + + index++ + res.Script, err = arr[index].TryBytes() + if err != nil { + return fmt.Errorf("field Script: %w", err) + } + + return nil +} + +// itemToLedgerTransactionSigner converts stack item into *LedgerTransactionSigner. +func itemToLedgerTransactionSigner(item stackitem.Item, err error) (*LedgerTransactionSigner, error) { + if err != nil { + return nil, err + } + var res = new(LedgerTransactionSigner) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerTransactionSigner from the given stack item +// and returns an error if so. +func (res *LedgerTransactionSigner) FromStackItem(item stackitem.Item) error { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return errors.New("not an array") + } + if len(arr) != 5 { + return errors.New("wrong number of structure elements") + } + + var ( + index = -1 + err error + ) + index++ + res.Account, 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 Account: %w", err) + } + + index++ + res.Scopes, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Scopes: %w", err) + } + + index++ + res.AllowedContracts, err = func (item stackitem.Item) ([]util.Uint160, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]util.Uint160, len(arr)) + for i := range res { + res[i], 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[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field AllowedContracts: %w", err) + } + + index++ + res.AllowedGroups, err = func (item stackitem.Item) (keys.PublicKeys, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make(keys.PublicKeys, len(arr)) + for i := range res { + res[i], err = func (item stackitem.Item) (*keys.PublicKey, error) { + b, err := item.TryBytes() + if err != nil { + return nil, err + } + k, err := keys.NewPublicKeyFromBytes(b, elliptic.P256()) + if err != nil { + return nil, err + } + return k, nil + } (arr[i]) + if err != nil { + return nil, fmt.Errorf("item %d: %w", i, err) + } + } + return res, nil + } (arr[index]) + if err != nil { + return fmt.Errorf("field AllowedGroups: %w", err) + } + + index++ + res.Rules, err = func (item stackitem.Item) ([]*LedgerWitnessRule, error) { + arr, ok := item.Value().([]stackitem.Item) + if !ok { + return nil, errors.New("not an array") + } + res := make([]*LedgerWitnessRule, len(arr)) + for i := range res { + res[i], err = itemToLedgerWitnessRule(arr[i], 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 Rules: %w", err) + } + + return nil +} + +// itemToLedgerWitnessCondition converts stack item into *LedgerWitnessCondition. +func itemToLedgerWitnessCondition(item stackitem.Item, err error) (*LedgerWitnessCondition, error) { + if err != nil { + return nil, err + } + var res = new(LedgerWitnessCondition) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerWitnessCondition from the given stack item +// and returns an error if so. +func (res *LedgerWitnessCondition) 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.Type, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Type: %w", err) + } + + index++ + res.Value, err = arr[index].Value(), error(nil) + if err != nil { + return fmt.Errorf("field Value: %w", err) + } + + return nil +} + +// itemToLedgerWitnessRule converts stack item into *LedgerWitnessRule. +func itemToLedgerWitnessRule(item stackitem.Item, err error) (*LedgerWitnessRule, error) { + if err != nil { + return nil, err + } + var res = new(LedgerWitnessRule) + err = res.FromStackItem(item) + return res, err +} + +// FromStackItem retrieves fields of LedgerWitnessRule from the given stack item +// and returns an error if so. +func (res *LedgerWitnessRule) 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.Action, err = arr[index].TryInteger() + if err != nil { + return fmt.Errorf("field Action: %w", err) + } + + index++ + res.Condition, err = itemToLedgerWitnessCondition(arr[index], nil) + if err != nil { + return fmt.Errorf("field Condition: %w", err) + } + + return nil +} + +// ComplicatedNameEventsFromApplicationLog retrieves a set of all emitted events +// with "! complicated name %$#" name from the provided ApplicationLog. +func ComplicatedNameEventsFromApplicationLog(log *result.ApplicationLog) ([]*ComplicatedNameEvent, error) { + if log == nil { + return nil, errors.New("nil application log") + } + + var res []*ComplicatedNameEvent + for i, ex := range log.Executions { + for j, e := range ex.Events { + if e.Name != "! complicated name %$#" { + continue + } + event := new(ComplicatedNameEvent) + err := event.FromStackItem(e.Item) + if err != nil { + return nil, fmt.Errorf("failed to deserialize ComplicatedNameEvent from stackitem (execution %d, event %d): %w", i, j, err) + } + res = append(res, event) + } + } + + return res, nil +} + +// FromStackItem converts provided stackitem.Array to ComplicatedNameEvent and +// returns an error if so. +func (e *ComplicatedNameEvent) 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.ComplicatedParam, 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 ComplicatedParam: %w", err) + } + + return nil +} diff --git a/pkg/compiler/compiler.go b/pkg/compiler/compiler.go index e44aead0a..6a711921a 100644 --- a/pkg/compiler/compiler.go +++ b/pkg/compiler/compiler.go @@ -18,6 +18,7 @@ import ( "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest" "github.com/nspcc-dev/neo-go/pkg/smartcontract/manifest/standard" "github.com/nspcc-dev/neo-go/pkg/smartcontract/nef" + "github.com/nspcc-dev/neo-go/pkg/smartcontract/rpcbinding" "github.com/nspcc-dev/neo-go/pkg/util" "golang.org/x/tools/go/packages" "gopkg.in/yaml.v3" @@ -339,14 +340,17 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { cfg.NamedTypes = di.NamedTypes } for name, et := range o.DeclaredNamedTypes { - // TODO: handle name conflict + // TODO: handle name conflict (it can happen due to invalid user input e.g.) cfg.NamedTypes[name] = et } for _, e := range o.ContractEvents { + eStructName := rpcbinding.ToEventBindingName(e.Name) for _, p := range e.Parameters { + pStructName := rpcbinding.ToParameterBindingName(p.Name) // TODO: proper imports handling during bindings generation (see utf8 example). + // Probably, we should always add p type to the list of types. if p.ExtendedType != nil { - pName := e.Name + "." + p.Name + pName := eStructName + "." + pStructName cfg.Types[pName] = *p.ExtendedType } } @@ -354,6 +358,7 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { if o.GuessEventTypes { if len(di.EmittedEvents) > 0 { for eventName, eventUsages := range di.EmittedEvents { + eBindingName := rpcbinding.ToEventBindingName(eventName) // Take into account the first usage only. // TODO: extend it to the rest of invocations. for typeName, extType := range eventUsages[0].ExtTypes { @@ -361,9 +366,10 @@ func CompileAndSave(src string, o *Options) ([]byte, error) { cfg.NamedTypes[typeName] = extType } } + for _, p := range eventUsages[0].Params { - // TODO: prettify notification name in-place. - pname := eventName + "." + p.Name + pBindingName := rpcbinding.ToParameterBindingName(p.Name) + pname := eBindingName + "." + pBindingName if p.RealType.TypeName != "" { if _, ok := cfg.Overrides[pname]; !ok { cfg.Overrides[pname] = p.RealType diff --git a/pkg/smartcontract/rpcbinding/binding.go b/pkg/smartcontract/rpcbinding/binding.go index ba65361f1..074b41c49 100644 --- a/pkg/smartcontract/rpcbinding/binding.go +++ b/pkg/smartcontract/rpcbinding/binding.go @@ -19,10 +19,10 @@ import ( // ensure that this block has new line at the start and in the end of the block. const ( eventDefinition = `{{ define "EVENT" }} -// {{.Name}}Event represents "{{.ManifestName}}" event emitted by the contract. -type {{.Name}}Event struct { +// {{.Name}} represents "{{.ManifestName}}" event emitted by the contract. +type {{.Name}} struct { {{- range $index, $arg := .Parameters}} - {{toPascalCase .Name}} {{.Type}} + {{.Name}} {{.Type}} {{- end}} } {{ end }}` @@ -262,23 +262,23 @@ func (res *{{toTypeName $name}}) FromStackItem(item stackitem.Item) error { } {{ end -}} {{- range $e := .CustomEvents }} -// {{$e.Name}}EventsFromApplicationLog retrieves a set of all emitted events +// {{$e.Name}}sFromApplicationLog 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) { +func {{$e.Name}}sFromApplicationLog(log *result.ApplicationLog) ([]*{{$e.Name}}, error) { if log == nil { return nil, errors.New("nil application log") } - var res []*{{$e.Name}}Event + var res []*{{$e.Name}} for i, ex := range log.Executions { for j, e := range ex.Events { if e.Name != "{{$e.ManifestName}}" { continue } - event := new({{$e.Name}}Event) + event := new({{$e.Name}}) 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) + return nil, fmt.Errorf("failed to deserialize {{$e.Name}} from stackitem (execution %d, event %d): %w", i, j, err) } res = append(res, event) } @@ -287,9 +287,9 @@ func {{$e.Name}}EventsFromApplicationLog(log *result.ApplicationLog) ([]*{{$e.Na return res, nil } -// FromStackItem converts provided stackitem.Array to {{$e.Name}}Event and +// FromStackItem converts provided stackitem.Array to {{$e.Name}} and // returns an error if so. -func (e *{{$e.Name}}Event) FromStackItem(item *stackitem.Array) error { +func (e *{{$e.Name}}) FromStackItem(item *stackitem.Array) error { if item == nil { return errors.New("nil item") } @@ -307,9 +307,9 @@ func (e *{{$e.Name}}Event) FromStackItem(item *stackitem.Array) error { ) {{- range $p := $e.Parameters}} index++ - e.{{toPascalCase .Name}}, err = {{etTypeConverter .ExtType "arr[index]"}} + e.{{.Name}}, err = {{etTypeConverter .ExtType "arr[index]"}} if err != nil { - return fmt.Errorf("field {{toPascalCase .Name}}: %w", err) + return fmt.Errorf("field {{.Name}}: %w", err) } {{end}} {{- end}} @@ -427,9 +427,8 @@ func Generate(cfg binding.Config) error { r, _ := extendedTypeToGo(et, ctr.NamedTypes) return r }, - "toTypeName": toTypeName, - "cutPointer": cutPointer, - "toPascalCase": toPascalCase, + "toTypeName": toTypeName, + "cutPointer": cutPointer, }).Parse(srcTmpl)) return srcTemplate.Execute(cfg.Output, ctr) @@ -675,23 +674,18 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st imports["errors"] = struct{}{} } for _, abiEvent := range cfg.Manifest.ABI.Events { + eBindingName := ToEventBindingName(abiEvent.Name) eTmp := CustomEventTemplate{ - // TODO: proper event name is better to be set right into config binding in normal form. - Name: toPascalCase(abiEvent.Name), + Name: eBindingName, ManifestName: abiEvent.Name, } - var varnames = make(map[string]bool) for i := range abiEvent.Parameters { - name := abiEvent.Parameters[i].Name - fullPName := abiEvent.Name + "." + name + pBindingName := ToParameterBindingName(abiEvent.Parameters[i].Name) + fullPName := eBindingName + "." + pBindingName 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 @@ -699,12 +693,12 @@ func scTemplateToRPC(cfg binding.Config, ctr ContractTmpl, imports map[string]st ) if extType, ok = cfg.Types[fullPName]; !ok { extType = binding.ExtendedType{ - Base: abiEvent.Parameters[i].Type, + Base: abiEvent.Parameters[i].Type, // TODO: properly handle imports for this case (see utf8 example) } } eTmp.Parameters = append(eTmp.Parameters, EventParamTmpl{ ParamTmpl: binding.ParamTmpl{ - Name: name, + Name: pBindingName, Type: typeStr, }, ExtType: extType, @@ -849,6 +843,18 @@ func addIndent(str string, ind string) string { return strings.ReplaceAll(str, "\n", "\n"+ind) } +// ToEventBindingName converts event name specified in the contract manifest to +// a valid go exported event structure name. +func ToEventBindingName(eventName string) string { + return toPascalCase(eventName) + "Event" +} + +// ToParameterBindingName converts parameter name specified in the contract +// manifest to a valid go structure's exported field name. +func ToParameterBindingName(paramName string) string { + return toPascalCase(paramName) +} + // 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 {