rpcclient: use separate reader/writer structs in nep11 and nep17

Which greatly simplifies reuse of these packages (and they're expected to be
reused since real tokens implement standards and also add something of their
own) and allows to avoid effects like

  doc_test.go:68:28: ambiguous selector neoContract.BalanceOf

when neo.Contract is used. Avoids duplication in NEP-11 implementation as
well.
This commit is contained in:
Roman Khimov 2022-09-07 15:05:25 +03:00
parent 00a9376311
commit e1fe76137e
5 changed files with 41 additions and 48 deletions

View file

@ -63,7 +63,7 @@ type ContractReader struct {
// Contract provides full NEO interface, both safe and state-changing methods.
type Contract struct {
ContractReader
nep17.Token
nep17.TokenWriter
actor Actor
}
@ -102,7 +102,8 @@ func NewReader(invoker Invoker) *ContractReader {
// New creates an instance of Contract to perform state-changing actions in the
// NEO contract.
func New(actor Actor) *Contract {
return &Contract{*NewReader(actor), *nep17.New(actor, Hash), actor}
nep := nep17.New(actor, Hash)
return &Contract{ContractReader{nep.TokenReader, actor}, nep.TokenWriter, actor}
}
// GetAccountState returns current NEO balance state for the account which

View file

@ -51,12 +51,19 @@ type BaseReader struct {
hash util.Uint160
}
// BaseWriter is a transaction-creating interface for common divisible and
// non-divisible NEP-11 methods. It simplifies reusing this set of methods,
// but a complete Base is expected to be used in other packages.
type BaseWriter struct {
hash util.Uint160
actor Actor
}
// Base is a state-changing interface for common divisible and non-divisible NEP-11
// methods.
type Base struct {
BaseReader
actor Actor
BaseWriter
}
// TransferEvent represents a Transfer event as defined in the NEP-11 standard.
@ -83,7 +90,7 @@ func NewBaseReader(invoker Invoker, hash util.Uint160) *BaseReader {
// NewBase creates an instance of Base for contract with the given
// hash using the given actor.
func NewBase(actor Actor, hash util.Uint160) *Base {
return &Base{*NewBaseReader(actor, hash), actor}
return &Base{*NewBaseReader(actor, hash), BaseWriter{hash, actor}}
}
// Properties returns a set of token's properties such as name or URL. The map
@ -142,7 +149,7 @@ func (t *BaseReader) TokensOfExpanded(account util.Uint160, num int) ([][]byte,
// transaction if it's not true. It works for divisible NFTs only when there is
// one owner for the particular token. The returned values are transaction hash,
// its ValidUntilBlock value and an error if any.
func (t *Base) Transfer(to util.Uint160, id []byte, data interface{}) (util.Uint256, uint32, error) {
func (t *BaseWriter) Transfer(to util.Uint160, id []byte, data interface{}) (util.Uint256, uint32, error) {
script, err := t.transferScript(to, id, data)
if err != nil {
return util.Uint256{}, 0, err
@ -155,7 +162,7 @@ func (t *Base) Transfer(to util.Uint160, id []byte, data interface{}) (util.Uint
// transaction if it's not true. It works for divisible NFTs only when there is
// one owner for the particular token. This transaction is signed, but not sent
// to the network, instead it's returned to the caller.
func (t *Base) TransferTransaction(to util.Uint160, id []byte, data interface{}) (*transaction.Transaction, error) {
func (t *BaseWriter) TransferTransaction(to util.Uint160, id []byte, data interface{}) (*transaction.Transaction, error) {
script, err := t.transferScript(to, id, data)
if err != nil {
return nil, err
@ -168,7 +175,7 @@ func (t *Base) TransferTransaction(to util.Uint160, id []byte, data interface{})
// transaction if it's not true. It works for divisible NFTs only when there is
// one owner for the particular token. This transaction is not signed and just
// returned to the caller.
func (t *Base) TransferUnsigned(to util.Uint160, id []byte, data interface{}) (*transaction.Transaction, error) {
func (t *BaseWriter) TransferUnsigned(to util.Uint160, id []byte, data interface{}) (*transaction.Transaction, error) {
script, err := t.transferScript(to, id, data)
if err != nil {
return nil, err
@ -176,7 +183,7 @@ func (t *Base) TransferUnsigned(to util.Uint160, id []byte, data interface{}) (*
return t.actor.MakeUnsignedRun(script, nil)
}
func (t *Base) transferScript(params ...interface{}) ([]byte, error) {
func (t *BaseWriter) transferScript(params ...interface{}) ([]byte, error) {
return smartcontract.CreateCallWithAssertScript(t.hash, "transfer", params...)
}

View file

@ -18,7 +18,8 @@ type DivisibleReader struct {
// Divisible is a state-changing interface for divisible NEP-11 contract.
type Divisible struct {
Base
DivisibleReader
BaseWriter
}
// OwnerIterator is used for iterating over OwnerOf (for divisible NFTs) results.
@ -37,7 +38,7 @@ func NewDivisibleReader(invoker Invoker, hash util.Uint160) *DivisibleReader {
// NewDivisible creates an instance of Divisible for a contract
// with the given hash using the given actor.
func NewDivisible(actor Actor, hash util.Uint160) *Divisible {
return &Divisible{*NewBase(actor, hash)}
return &Divisible{*NewDivisibleReader(actor, hash), BaseWriter{hash, actor}}
}
// OwnerOf returns returns an iterator that allows to walk through all owners of
@ -66,24 +67,6 @@ func (t *DivisibleReader) BalanceOfD(owner util.Uint160, token []byte) (*big.Int
return unwrap.BigInt(t.invoker.Call(t.hash, "balanceOf", owner, token))
}
// OwnerOf is the same as (*DivisibleReader).OwnerOf.
func (t *Divisible) OwnerOf(token []byte) (*OwnerIterator, error) {
r := DivisibleReader{t.BaseReader}
return r.OwnerOf(token)
}
// OwnerOfExpanded is the same as (*DivisibleReader).OwnerOfExpanded.
func (t *Divisible) OwnerOfExpanded(token []byte, num int) ([]util.Uint160, error) {
r := DivisibleReader{t.BaseReader}
return r.OwnerOfExpanded(token, num)
}
// BalanceOfD is the same as (*DivisibleReader).BalanceOfD.
func (t *Divisible) BalanceOfD(owner util.Uint160, token []byte) (*big.Int, error) {
r := DivisibleReader{t.BaseReader}
return r.BalanceOfD(owner, token)
}
// TransferD is a divisible version of (*Base).Transfer, allowing to transfer a
// part of NFT. It creates and sends a transaction that performs a `transfer`
// method call using the given parameters and checks for this call result,

View file

@ -12,7 +12,8 @@ type NonDivisibleReader struct {
// NonDivisible is a state-changing interface for non-divisble NEP-11 contract.
type NonDivisible struct {
Base
NonDivisibleReader
BaseWriter
}
// NewNonDivisibleReader creates an instance of NonDivisibleReader for a contract
@ -24,16 +25,10 @@ func NewNonDivisibleReader(invoker Invoker, hash util.Uint160) *NonDivisibleRead
// NewNonDivisible creates an instance of NonDivisible for a contract
// with the given hash using the given actor.
func NewNonDivisible(actor Actor, hash util.Uint160) *NonDivisible {
return &NonDivisible{*NewBase(actor, hash)}
return &NonDivisible{*NewNonDivisibleReader(actor, hash), BaseWriter{hash, actor}}
}
// OwnerOf returns the owner of the given NFT.
func (t *NonDivisibleReader) OwnerOf(token []byte) (util.Uint160, error) {
return unwrap.Uint160(t.invoker.Call(t.hash, "ownerOf", token))
}
// OwnerOf is the same as (*NonDivisibleReader).OwnerOf.
func (t *NonDivisible) OwnerOf(token []byte) (util.Uint160, error) {
r := NonDivisibleReader{t.BaseReader}
return r.OwnerOf(token)
}

View file

@ -36,12 +36,19 @@ type TokenReader struct {
neptoken.Base
}
// TokenWriter contains NEP-17 token methods that change state. It's not meant
// to be used directly (Token that includes it is more convenient) and just
// separates one set of methods from another to simplify reusing this package
// for other contracts that extend NEP-17 interface.
type TokenWriter struct {
hash util.Uint160
actor Actor
}
// Token provides full NEP-17 interface, both safe and state-changing methods.
type Token struct {
TokenReader
hash util.Uint160
actor Actor
TokenWriter
}
// TransferEvent represents a Transfer event as defined in the NEP-17 standard.
@ -68,14 +75,14 @@ func NewReader(invoker Invoker, hash util.Uint160) *TokenReader {
// New creates an instance of Token for contract with the given hash
// using the given Actor.
func New(actor Actor, hash util.Uint160) *Token {
return &Token{*NewReader(actor, hash), hash, actor}
return &Token{*NewReader(actor, hash), TokenWriter{hash, actor}}
}
// Transfer creates and sends a transaction that performs a `transfer` method
// call using the given parameters and checks for this call result, failing the
// transaction if it's not true. The returned values are transaction hash, its
// ValidUntilBlock value and an error if any.
func (t *Token) Transfer(from util.Uint160, to util.Uint160, amount *big.Int, data interface{}) (util.Uint256, uint32, error) {
func (t *TokenWriter) Transfer(from util.Uint160, to util.Uint160, amount *big.Int, data interface{}) (util.Uint256, uint32, error) {
return t.MultiTransfer([]TransferParameters{{from, to, amount, data}})
}
@ -83,7 +90,7 @@ func (t *Token) Transfer(from util.Uint160, to util.Uint160, amount *big.Int, da
// call using the given parameters and checks for this call result, failing the
// transaction if it's not true. This transaction is signed, but not sent to the
// network, instead it's returned to the caller.
func (t *Token) TransferTransaction(from util.Uint160, to util.Uint160, amount *big.Int, data interface{}) (*transaction.Transaction, error) {
func (t *TokenWriter) TransferTransaction(from util.Uint160, to util.Uint160, amount *big.Int, data interface{}) (*transaction.Transaction, error) {
return t.MultiTransferTransaction([]TransferParameters{{from, to, amount, data}})
}
@ -91,11 +98,11 @@ func (t *Token) TransferTransaction(from util.Uint160, to util.Uint160, amount *
// call using the given parameters and checks for this call result, failing the
// transaction if it's not true. This transaction is not signed and just returned
// to the caller.
func (t *Token) TransferUnsigned(from util.Uint160, to util.Uint160, amount *big.Int, data interface{}) (*transaction.Transaction, error) {
func (t *TokenWriter) TransferUnsigned(from util.Uint160, to util.Uint160, amount *big.Int, data interface{}) (*transaction.Transaction, error) {
return t.MultiTransferUnsigned([]TransferParameters{{from, to, amount, data}})
}
func (t *Token) multiTransferScript(params []TransferParameters) ([]byte, error) {
func (t *TokenWriter) multiTransferScript(params []TransferParameters) ([]byte, error) {
if len(params) == 0 {
return nil, errors.New("at least one transfer parameter required")
}
@ -113,7 +120,7 @@ func (t *Token) multiTransferScript(params []TransferParameters) ([]byte, error)
// many times as needed (with ASSERTs added, so if any of these transfers fail
// whole transaction (with all transfers) fails). The values returned are the
// same as in Transfer.
func (t *Token) MultiTransfer(params []TransferParameters) (util.Uint256, uint32, error) {
func (t *TokenWriter) MultiTransfer(params []TransferParameters) (util.Uint256, uint32, error) {
script, err := t.multiTransferScript(params)
if err != nil {
return util.Uint256{}, 0, err
@ -123,7 +130,7 @@ func (t *Token) MultiTransfer(params []TransferParameters) (util.Uint256, uint32
// MultiTransferTransaction is similar to MultiTransfer, but returns the same values
// as TransferTransaction (signed transaction that is not yet sent).
func (t *Token) MultiTransferTransaction(params []TransferParameters) (*transaction.Transaction, error) {
func (t *TokenWriter) MultiTransferTransaction(params []TransferParameters) (*transaction.Transaction, error) {
script, err := t.multiTransferScript(params)
if err != nil {
return nil, err
@ -133,7 +140,7 @@ func (t *Token) MultiTransferTransaction(params []TransferParameters) (*transact
// MultiTransferUnsigned is similar to MultiTransfer, but returns the same values
// as TransferUnsigned (not yet signed transaction).
func (t *Token) MultiTransferUnsigned(params []TransferParameters) (*transaction.Transaction, error) {
func (t *TokenWriter) MultiTransferUnsigned(params []TransferParameters) (*transaction.Transaction, error) {
script, err := t.multiTransferScript(params)
if err != nil {
return nil, err