/
divisible.go
147 lines (131 loc) · 5.58 KB
/
divisible.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package nep11
import (
"fmt"
"math/big"
"github.com/google/uuid"
"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/rpcclient/unwrap"
"github.com/nspcc-dev/neo-go/pkg/util"
)
// DivisibleReader is a reader interface for divisible NEP-11 contract.
type DivisibleReader struct {
BaseReader
}
// DivisibleWriter is a state-changing interface for divisible NEP-11 contract.
// It's mostly useful not directly, but as a reusable layer for higher-level
// structures.
type DivisibleWriter struct {
BaseWriter
}
// Divisible is a full reader interface for divisible NEP-11 contract.
type Divisible struct {
DivisibleReader
DivisibleWriter
}
// OwnerIterator is used for iterating over OwnerOf (for divisible NFTs) results.
type OwnerIterator struct {
client Invoker
session uuid.UUID
iterator result.Iterator
}
// NewDivisibleReader creates an instance of DivisibleReader for a contract
// with the given hash using the given invoker.
func NewDivisibleReader(invoker Invoker, hash util.Uint160) *DivisibleReader {
return &DivisibleReader{*NewBaseReader(invoker, hash)}
}
// 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{*NewDivisibleReader(actor, hash), DivisibleWriter{BaseWriter{hash, actor}}}
}
// OwnerOf returns returns an iterator that allows to walk through all owners of
// the given token. It depends on the server to provide proper session-based
// iterator, but can also work with expanded one.
func (t *DivisibleReader) OwnerOf(token []byte) (*OwnerIterator, error) {
sess, iter, err := unwrap.SessionIterator(t.invoker.Call(t.hash, "ownerOf", token))
if err != nil {
return nil, err
}
return &OwnerIterator{t.invoker, sess, iter}, nil
}
// OwnerOfExpanded uses the same NEP-11 method as OwnerOf, but can be useful if
// the server used doesn't support sessions and doesn't expand iterators. It
// creates a script that will get num of result items from the iterator right in
// the VM and return them to you. It's only limited by VM stack and GAS available
// for RPC invocations.
func (t *DivisibleReader) OwnerOfExpanded(token []byte, num int) ([]util.Uint160, error) {
return unwrap.ArrayOfUint160(t.invoker.CallAndExpandIterator(t.hash, "ownerOf", num, token))
}
// BalanceOfD is a BalanceOf for divisible NFTs, it returns the amount of token
// owned by a particular account.
func (t *DivisibleReader) BalanceOfD(owner util.Uint160, token []byte) (*big.Int, error) {
return unwrap.BigInt(t.invoker.Call(t.hash, "balanceOf", 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,
// failing the transaction if it's not true. The returned values are transaction
// hash, its ValidUntilBlock value and an error if any.
func (t *DivisibleWriter) TransferD(from util.Uint160, to util.Uint160, amount *big.Int, id []byte, data any) (util.Uint256, uint32, error) {
script, err := t.transferScript(from, to, amount, id, data)
if err != nil {
return util.Uint256{}, 0, err
}
return t.actor.SendRun(script)
}
// TransferDTransaction is a divisible version of (*Base).TransferTransaction,
// allowing to transfer a part of NFT. It creates 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. This transaction is signed,
// but not sent to the network, instead it's returned to the caller.
func (t *DivisibleWriter) TransferDTransaction(from util.Uint160, to util.Uint160, amount *big.Int, id []byte, data any) (*transaction.Transaction, error) {
script, err := t.transferScript(from, to, amount, id, data)
if err != nil {
return nil, err
}
return t.actor.MakeRun(script)
}
// TransferDUnsigned is a divisible version of (*Base).TransferUnsigned,
// allowing to transfer a part of NFT. It creates 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. This transaction is not
// signed and just returned to the caller.
func (t *DivisibleWriter) TransferDUnsigned(from util.Uint160, to util.Uint160, amount *big.Int, id []byte, data any) (*transaction.Transaction, error) {
script, err := t.transferScript(from, to, amount, id, data)
if err != nil {
return nil, err
}
return t.actor.MakeUnsignedRun(script, nil)
}
// Next returns the next set of elements from the iterator (up to num of them).
// It can return less than num elements in case iterator doesn't have that many
// or zero elements if the iterator has no more elements or the session is
// expired.
func (v *OwnerIterator) Next(num int) ([]util.Uint160, error) {
items, err := v.client.TraverseIterator(v.session, &v.iterator, num)
if err != nil {
return nil, err
}
res := make([]util.Uint160, len(items))
for i := range items {
b, err := items[i].TryBytes()
if err != nil {
return nil, fmt.Errorf("element %d is not a byte string: %w", i, err)
}
u, err := util.Uint160DecodeBytesBE(b)
if err != nil {
return nil, fmt.Errorf("element %d is not a uint160: %w", i, err)
}
res[i] = u
}
return res, nil
}
// Terminate closes the iterator session used by OwnerIterator (if it's
// session-based).
func (v *OwnerIterator) Terminate() error {
if v.iterator.ID == nil {
return nil
}
return v.client.TerminateSession(v.session)
}