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. // Contract provides full NEO interface, both safe and state-changing methods.
type Contract struct { type Contract struct {
ContractReader ContractReader
nep17.Token nep17.TokenWriter
actor Actor actor Actor
} }
@ -102,7 +102,8 @@ func NewReader(invoker Invoker) *ContractReader {
// New creates an instance of Contract to perform state-changing actions in the // New creates an instance of Contract to perform state-changing actions in the
// NEO contract. // NEO contract.
func New(actor Actor) *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 // GetAccountState returns current NEO balance state for the account which

View file

@ -51,12 +51,19 @@ type BaseReader struct {
hash util.Uint160 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 // Base is a state-changing interface for common divisible and non-divisible NEP-11
// methods. // methods.
type Base struct { type Base struct {
BaseReader BaseReader
BaseWriter
actor Actor
} }
// TransferEvent represents a Transfer event as defined in the NEP-11 standard. // 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 // NewBase creates an instance of Base for contract with the given
// hash using the given actor. // hash using the given actor.
func NewBase(actor Actor, hash util.Uint160) *Base { 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 // 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 // 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, // one owner for the particular token. The returned values are transaction hash,
// its ValidUntilBlock value and an error if any. // 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) script, err := t.transferScript(to, id, data)
if err != nil { if err != nil {
return util.Uint256{}, 0, err 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 // 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 // one owner for the particular token. This transaction is signed, but not sent
// to the network, instead it's returned to the caller. // 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) script, err := t.transferScript(to, id, data)
if err != nil { if err != nil {
return nil, err 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 // 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 // one owner for the particular token. This transaction is not signed and just
// returned to the caller. // 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) script, err := t.transferScript(to, id, data)
if err != nil { if err != nil {
return nil, err return nil, err
@ -176,7 +183,7 @@ func (t *Base) TransferUnsigned(to util.Uint160, id []byte, data interface{}) (*
return t.actor.MakeUnsignedRun(script, nil) 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...) 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. // Divisible is a state-changing interface for divisible NEP-11 contract.
type Divisible struct { type Divisible struct {
Base DivisibleReader
BaseWriter
} }
// OwnerIterator is used for iterating over OwnerOf (for divisible NFTs) results. // 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 // NewDivisible creates an instance of Divisible for a contract
// with the given hash using the given actor. // with the given hash using the given actor.
func NewDivisible(actor Actor, hash util.Uint160) *Divisible { 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 // 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)) 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 // 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` // 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, // 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. // NonDivisible is a state-changing interface for non-divisble NEP-11 contract.
type NonDivisible struct { type NonDivisible struct {
Base NonDivisibleReader
BaseWriter
} }
// NewNonDivisibleReader creates an instance of NonDivisibleReader for a contract // 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 // NewNonDivisible creates an instance of NonDivisible for a contract
// with the given hash using the given actor. // with the given hash using the given actor.
func NewNonDivisible(actor Actor, hash util.Uint160) *NonDivisible { 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. // OwnerOf returns the owner of the given NFT.
func (t *NonDivisibleReader) OwnerOf(token []byte) (util.Uint160, error) { func (t *NonDivisibleReader) OwnerOf(token []byte) (util.Uint160, error) {
return unwrap.Uint160(t.invoker.Call(t.hash, "ownerOf", token)) 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 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. // Token provides full NEP-17 interface, both safe and state-changing methods.
type Token struct { type Token struct {
TokenReader TokenReader
TokenWriter
hash util.Uint160
actor Actor
} }
// TransferEvent represents a Transfer event as defined in the NEP-17 standard. // 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 // New creates an instance of Token for contract with the given hash
// using the given Actor. // using the given Actor.
func New(actor Actor, hash util.Uint160) *Token { 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 // Transfer creates and sends a transaction that performs a `transfer` method
// call using the given parameters and checks for this call result, failing the // 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 // transaction if it's not true. The returned values are transaction hash, its
// ValidUntilBlock value and an error if any. // 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}}) 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 // 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 // transaction if it's not true. This transaction is signed, but not sent to the
// network, instead it's returned to the caller. // 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}}) 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 // 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 // transaction if it's not true. This transaction is not signed and just returned
// to the caller. // 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}}) 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 { if len(params) == 0 {
return nil, errors.New("at least one transfer parameter required") 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 // 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 // whole transaction (with all transfers) fails). The values returned are the
// same as in Transfer. // 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) script, err := t.multiTransferScript(params)
if err != nil { if err != nil {
return util.Uint256{}, 0, err 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 // MultiTransferTransaction is similar to MultiTransfer, but returns the same values
// as TransferTransaction (signed transaction that is not yet sent). // 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) script, err := t.multiTransferScript(params)
if err != nil { if err != nil {
return nil, err return nil, err
@ -133,7 +140,7 @@ func (t *Token) MultiTransferTransaction(params []TransferParameters) (*transact
// MultiTransferUnsigned is similar to MultiTransfer, but returns the same values // MultiTransferUnsigned is similar to MultiTransfer, but returns the same values
// as TransferUnsigned (not yet signed transaction). // 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) script, err := t.multiTransferScript(params)
if err != nil { if err != nil {
return nil, err return nil, err