-
Notifications
You must be signed in to change notification settings - Fork 244
/
token.go
141 lines (123 loc) · 3.92 KB
/
token.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
package wallet
import (
"context"
"database/sql"
"errors"
"math/big"
"sync"
"time"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
"github.com/status-im/status-go/services/wallet/async"
"github.com/status-im/status-go/services/wallet/chain"
"github.com/status-im/status-go/services/wallet/ierc20"
)
var requestTimeout = 20 * time.Second
type Token struct {
Address common.Address `json:"address"`
Name string `json:"name"`
Symbol string `json:"symbol"`
Color string `json:"color"`
// Decimals defines how divisible the token is. For example, 0 would be
// indivisible, whereas 18 would allow very small amounts of the token
// to be traded.
Decimals uint `json:"decimals"`
ChainID uint64 `json:"chainId"`
}
type TokenManager struct {
db *sql.DB
}
func (tm *TokenManager) getTokens(chainID uint64) ([]*Token, error) {
tokensMap, ok := tokenStore[chainID]
if !ok {
return nil, errors.New("no tokens for this network")
}
res := make([]*Token, 0, len(tokensMap))
for _, token := range tokensMap {
res = append(res, token)
}
return res, nil
}
func (tm *TokenManager) getCustoms() ([]*Token, error) {
rows, err := tm.db.Query("SELECT address, name, symbol, decimals, color, network_id FROM tokens")
if err != nil {
return nil, err
}
defer rows.Close()
var rst []*Token
for rows.Next() {
token := &Token{}
err := rows.Scan(&token.Address, &token.Name, &token.Symbol, &token.Decimals, &token.Color, &token.ChainID)
if err != nil {
return nil, err
}
rst = append(rst, token)
}
return rst, nil
}
func (tm *TokenManager) upsertCustom(token Token) error {
insert, err := tm.db.Prepare("INSERT OR REPLACE INTO TOKENS (network_id, address, name, symbol, decimals, color) VALUES (?, ?, ?, ?, ?, ?)")
if err != nil {
return err
}
_, err = insert.Exec(token.ChainID, token.Address, token.Name, token.Symbol, token.Decimals, token.Color)
return err
}
func (tm *TokenManager) deleteCustom(chainID uint64, address common.Address) error {
_, err := tm.db.Exec(`DELETE FROM TOKENS WHERE address = ? and network_id = ?`, address, chainID)
return err
}
func (tm *TokenManager) getBalances(parent context.Context, clients []*chain.Client, accounts, tokens []common.Address) (map[common.Address]map[common.Address]*hexutil.Big, error) {
var (
group = async.NewAtomicGroup(parent)
mu sync.Mutex
response = map[common.Address]map[common.Address]*hexutil.Big{}
)
for _, client := range clients {
for tokenIdx := range tokens {
caller, err := ierc20.NewIERC20Caller(tokens[tokenIdx], client)
if err != nil {
return nil, err
}
for accountIdx := range accounts {
// Below, we set account and token from idx on purpose to avoid override
account := accounts[accountIdx]
token := tokens[tokenIdx]
group.Add(func(parent context.Context) error {
ctx, cancel := context.WithTimeout(parent, requestTimeout)
defer cancel()
balance, err := caller.BalanceOf(&bind.CallOpts{
Context: ctx,
}, account)
// We don't want to return an error here and prevent
// the rest from completing
if err != nil {
log.Error("can't fetch erc20 token balance", "account", account, "token", token, "error", err)
return nil
}
mu.Lock()
if _, ok := response[account]; !ok {
response[account] = map[common.Address]*hexutil.Big{}
}
if _, ok := response[account][token]; !ok {
zeroHex := hexutil.Big(*big.NewInt(0))
response[account][token] = &zeroHex
}
sum := big.NewInt(0).Add(response[account][token].ToInt(), balance)
sumHex := hexutil.Big(*sum)
response[account][token] = &sumHex
mu.Unlock()
return nil
})
}
}
}
select {
case <-group.WaitAsync():
case <-parent.Done():
return nil, parent.Err()
}
return response, group.Error()
}