github.com/status-im/status-go@v1.1.0/services/wallet/token/balance_persistence.go (about) 1 package token 2 3 import ( 4 "context" 5 "database/sql" 6 "math/big" 7 8 "github.com/ethereum/go-ethereum/common" 9 ) 10 11 type TokenMarketValues struct { 12 MarketCap float64 `json:"marketCap"` 13 HighDay float64 `json:"highDay"` 14 LowDay float64 `json:"lowDay"` 15 ChangePctHour float64 `json:"changePctHour"` 16 ChangePctDay float64 `json:"changePctDay"` 17 ChangePct24hour float64 `json:"changePct24hour"` 18 Change24hour float64 `json:"change24hour"` 19 Price float64 `json:"price"` 20 HasError bool `json:"hasError"` 21 } 22 23 type StorageToken struct { 24 Token 25 BalancesPerChain map[uint64]ChainBalance `json:"balancesPerChain"` 26 Description string `json:"description"` 27 AssetWebsiteURL string `json:"assetWebsiteUrl"` 28 BuiltOn string `json:"builtOn"` 29 MarketValuesPerCurrency map[string]TokenMarketValues `json:"marketValuesPerCurrency"` 30 } 31 32 type ChainBalance struct { 33 RawBalance string `json:"rawBalance"` 34 Balance *big.Float `json:"balance"` 35 Balance1DayAgo string `json:"balance1DayAgo"` 36 Address common.Address `json:"address"` 37 ChainID uint64 `json:"chainId"` 38 HasError bool `json:"hasError"` 39 } 40 41 type TokenBalancesStorage interface { 42 SaveTokens(tokens map[common.Address][]StorageToken) error 43 GetTokens() (map[common.Address][]StorageToken, error) 44 } 45 46 type Persistence struct { 47 db *sql.DB 48 } 49 50 func NewPersistence(db *sql.DB) *Persistence { 51 return &Persistence{db: db} 52 } 53 54 func (p *Persistence) SaveTokens(tokens map[common.Address][]StorageToken) (err error) { 55 tx, err := p.db.BeginTx(context.Background(), &sql.TxOptions{}) 56 if err != nil { 57 return 58 } 59 defer func() { 60 if err == nil { 61 err = tx.Commit() 62 return 63 } 64 // don't shadow original error 65 _ = tx.Rollback() 66 }() 67 68 for address, addressTokens := range tokens { 69 for _, t := range addressTokens { 70 for chainID, b := range t.BalancesPerChain { 71 if b.HasError { 72 continue 73 } 74 _, err = tx.Exec(`INSERT INTO token_balances(user_address,token_name,token_symbol,token_address,token_decimals,token_description,token_url,balance,raw_balance,chain_id) VALUES (?,?,?,?,?,?,?,?,?,?)`, address.Hex(), t.Name, t.Symbol, b.Address.Hex(), t.Decimals, t.Description, t.AssetWebsiteURL, b.Balance.String(), b.RawBalance, chainID) 75 if err != nil { 76 return err 77 } 78 } 79 80 } 81 } 82 83 return nil 84 } 85 86 func (p *Persistence) GetTokens() (map[common.Address][]StorageToken, error) { 87 rows, err := p.db.Query(`SELECT user_address, token_name, token_symbol, token_address, token_decimals, token_description, token_url, balance, raw_balance, chain_id FROM token_balances `) 88 if err != nil { 89 return nil, err 90 } 91 92 defer rows.Close() 93 94 acc := make(map[common.Address]map[string]StorageToken) 95 96 for rows.Next() { 97 var addressStr, balance, rawBalance, tokenAddress string 98 token := StorageToken{} 99 var chainID uint64 100 101 err := rows.Scan(&addressStr, &token.Name, &token.Symbol, &tokenAddress, &token.Decimals, &token.Description, &token.AssetWebsiteURL, &balance, &rawBalance, &chainID) 102 if err != nil { 103 return nil, err 104 } 105 106 token.Address = common.HexToAddress(tokenAddress) 107 token.ChainID = chainID 108 address := common.HexToAddress(addressStr) 109 110 if acc[address] == nil { 111 acc[address] = make(map[string]StorageToken) 112 } 113 114 if acc[address][token.Name].Name == "" { 115 token.BalancesPerChain = make(map[uint64]ChainBalance) 116 acc[address][token.Name] = token 117 } 118 119 tokenAcc := acc[address][token.Name] 120 121 balanceFloat := new(big.Float) 122 _, _, err = balanceFloat.Parse(balance, 10) 123 if err != nil { 124 return nil, err 125 } 126 127 tokenAcc.BalancesPerChain[chainID] = ChainBalance{ 128 RawBalance: rawBalance, 129 Balance: balanceFloat, 130 Address: common.HexToAddress(tokenAddress), 131 ChainID: chainID, 132 } 133 } 134 135 result := make(map[common.Address][]StorageToken) 136 137 for address, tks := range acc { 138 for _, t := range tks { 139 result[address] = append(result[address], t) 140 } 141 } 142 return result, nil 143 }