-
Notifications
You must be signed in to change notification settings - Fork 243
/
commands.go
192 lines (159 loc) · 5.57 KB
/
commands.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
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
package collectibles
import (
"context"
"errors"
"time"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/rpc/network"
"github.com/status-im/status-go/services/wallet/async"
walletCommon "github.com/status-im/status-go/services/wallet/common"
"github.com/status-im/status-go/services/wallet/thirdparty"
"github.com/status-im/status-go/services/wallet/walletevent"
)
const (
fetchLimit = 50 // Limit number of collectibles we fetch per provider call
accountOwnershipUpdateInterval = 30 * time.Minute
)
// Fetches owned collectibles for all chainIDs and wallet addresses
type refreshOwnedCollectiblesCommand struct {
manager *Manager
ownershipDB *OwnershipDB
accountsDB *accounts.Database
walletFeed *event.Feed
networkManager *network.Manager
}
func newRefreshOwnedCollectiblesCommand(manager *Manager, ownershipDB *OwnershipDB, accountsDB *accounts.Database, walletFeed *event.Feed, networkManager *network.Manager) *refreshOwnedCollectiblesCommand {
return &refreshOwnedCollectiblesCommand{
manager: manager,
ownershipDB: ownershipDB,
accountsDB: accountsDB,
walletFeed: walletFeed,
networkManager: networkManager,
}
}
func (c *refreshOwnedCollectiblesCommand) Command() async.Command {
return async.InfiniteCommand{
Interval: accountOwnershipUpdateInterval,
Runable: c.Run,
}.Run
}
func (c *refreshOwnedCollectiblesCommand) Run(ctx context.Context) (err error) {
return c.updateOwnershipForAllAccounts(ctx)
}
func (c *refreshOwnedCollectiblesCommand) updateOwnershipForAllAccounts(ctx context.Context) error {
networks, err := c.networkManager.Get(false)
if err != nil {
return err
}
addresses, err := c.accountsDB.GetWalletAddresses()
if err != nil {
return err
}
areTestNetworksEnabled, err := c.accountsDB.GetTestNetworksEnabled()
if err != nil {
return err
}
start := time.Now()
group := async.NewGroup(ctx)
log.Debug("refreshOwnedCollectiblesCommand started")
for _, network := range networks {
if network.IsTest != areTestNetworksEnabled {
continue
}
for _, address := range addresses {
command := newLoadOwnedCollectiblesCommand(c.manager, c.ownershipDB, c.walletFeed, walletCommon.ChainID(network.ChainID), common.Address(address))
group.Add(command.Command())
}
}
select {
case <-ctx.Done():
return ctx.Err()
case <-group.WaitAsync():
}
log.Debug("refreshOwnedCollectiblesCommand finished", "in", time.Since(start))
return nil
}
// Fetches owned collectibles for a ChainID+OwnerAddress combination in chunks
// and updates the ownershipDB when all chunks are loaded
type loadOwnedCollectiblesCommand struct {
chainID walletCommon.ChainID
account common.Address
manager *Manager
ownershipDB *OwnershipDB
walletFeed *event.Feed
// Not to be set by the caller
partialOwnership []thirdparty.CollectibleUniqueID
err error
}
func newLoadOwnedCollectiblesCommand(manager *Manager, ownershipDB *OwnershipDB, walletFeed *event.Feed, chainID walletCommon.ChainID, account common.Address) *loadOwnedCollectiblesCommand {
return &loadOwnedCollectiblesCommand{
manager: manager,
ownershipDB: ownershipDB,
walletFeed: walletFeed,
chainID: chainID,
account: account,
}
}
func (c *loadOwnedCollectiblesCommand) Command() async.Command {
return c.Run
}
func (c *loadOwnedCollectiblesCommand) triggerEvent(eventType walletevent.EventType, chainID walletCommon.ChainID, account common.Address, message string) {
c.walletFeed.Send(walletevent.Event{
Type: eventType,
ChainID: uint64(chainID),
Accounts: []common.Address{
account,
},
Message: message,
})
}
func (c *loadOwnedCollectiblesCommand) Run(parent context.Context) (err error) {
log.Debug("start loadOwnedCollectiblesCommand", "chain", c.chainID, "account", c.account)
pageNr := 0
cursor := FetchFromStartCursor
c.triggerEvent(EventCollectiblesOwnershipUpdateStarted, c.chainID, c.account, "")
// Fetch collectibles in chunks
for {
if shouldCancel(parent) {
c.err = errors.New("context cancelled")
break
}
partialOwnership, err := c.manager.FetchCollectibleOwnershipByOwner(c.chainID, c.account, cursor, fetchLimit)
if err != nil {
log.Error("failed loadOwnedCollectiblesCommand", "chain", c.chainID, "account", c.account, "page", pageNr, "error", err)
c.err = err
break
}
log.Debug("partial loadOwnedCollectiblesCommand", "chain", c.chainID, "account", c.account, "page", pageNr, "found", len(partialOwnership.Collectibles), "collectibles")
c.partialOwnership = append(c.partialOwnership, partialOwnership.Collectibles...)
pageNr++
cursor = partialOwnership.NextCursor
if cursor == FetchFromStartCursor {
err = c.ownershipDB.Update(c.chainID, c.account, c.partialOwnership)
if err != nil {
log.Error("failed updating ownershipDB in loadOwnedCollectiblesCommand", "chain", c.chainID, "account", c.account, "error", err)
c.err = err
}
break
}
}
if c.err != nil {
c.triggerEvent(EventCollectiblesOwnershipUpdateFinishedWithError, c.chainID, c.account, c.err.Error())
} else {
c.triggerEvent(EventCollectiblesOwnershipUpdateFinished, c.chainID, c.account, "")
}
log.Debug("end loadOwnedCollectiblesCommand", "chain", c.chainID, "account", c.account)
return nil
}
// shouldCancel returns true if the context has been cancelled and task should be aborted
func shouldCancel(ctx context.Context) bool {
select {
case <-ctx.Done():
return true
default:
}
return false
}