2019-06-14 10:16:30 +00:00
package wallet
import (
"context"
2022-05-18 11:31:45 +00:00
"math/big"
"strings"
2022-11-15 12:14:41 +00:00
"time"
2019-06-14 10:16:30 +00:00
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/log"
2022-05-18 11:31:45 +00:00
"github.com/status-im/status-go/eth-node/types"
2022-01-12 20:04:43 +00:00
"github.com/status-im/status-go/params"
2023-04-21 11:59:29 +00:00
"github.com/status-im/status-go/services/wallet/activity"
2022-09-13 07:10:59 +00:00
"github.com/status-im/status-go/services/wallet/bridge"
2023-02-17 14:11:07 +00:00
"github.com/status-im/status-go/services/wallet/currency"
2022-11-15 12:14:41 +00:00
"github.com/status-im/status-go/services/wallet/history"
2023-01-25 18:28:51 +00:00
"github.com/status-im/status-go/services/wallet/thirdparty"
2023-02-21 09:05:16 +00:00
"github.com/status-im/status-go/services/wallet/thirdparty/opensea"
2022-09-13 07:10:59 +00:00
"github.com/status-im/status-go/services/wallet/token"
2021-09-09 14:28:54 +00:00
"github.com/status-im/status-go/services/wallet/transfer"
2023-05-11 07:50:07 +00:00
2023-06-08 23:52:45 +00:00
wcommon "github.com/status-im/status-go/services/wallet/common"
2019-07-02 07:28:57 +00:00
)
2019-06-14 10:16:30 +00:00
func NewAPI ( s * Service ) * API {
2022-06-09 13:09:56 +00:00
router := NewRouter ( s )
2022-12-01 09:19:32 +00:00
return & API { s , s . reader , router }
2019-06-14 10:16:30 +00:00
}
// API is class with methods available over RPC.
type API struct {
2022-06-09 13:09:56 +00:00
s * Service
reader * Reader
router * Router
2022-05-10 07:48:05 +00:00
}
2022-12-01 09:19:32 +00:00
func ( api * API ) StartWallet ( ctx context . Context ) error {
return api . reader . Start ( )
2022-05-10 07:48:05 +00:00
}
2022-11-15 12:14:41 +00:00
func ( api * API ) StopWallet ( ctx context . Context ) error {
return api . s . Stop ( )
}
2022-12-02 10:55:44 +00:00
func ( api * API ) GetWalletToken ( ctx context . Context , addresses [ ] common . Address ) ( map [ common . Address ] [ ] Token , error ) {
return api . reader . GetWalletToken ( ctx , addresses )
2019-06-14 10:16:30 +00:00
}
2023-04-25 12:00:17 +00:00
func ( api * API ) GetCachedWalletTokensWithoutMarketData ( ctx context . Context ) ( map [ common . Address ] [ ] Token , error ) {
return api . reader . GetCachedWalletTokensWithoutMarketData ( )
}
2022-05-18 11:31:45 +00:00
type DerivedAddress struct {
Address common . Address ` json:"address" `
2023-05-16 10:48:00 +00:00
PublicKey types . HexBytes ` json:"public-key,omitempty" `
2022-05-18 11:31:45 +00:00
Path string ` json:"path" `
HasActivity bool ` json:"hasActivity" `
AlreadyCreated bool ` json:"alreadyCreated" `
}
2021-02-11 15:20:06 +00:00
// SetInitialBlocksRange sets initial blocks range
func ( api * API ) SetInitialBlocksRange ( ctx context . Context ) error {
2021-09-22 17:49:20 +00:00
return api . s . transferController . SetInitialBlocksRange ( [ ] uint64 { api . s . rpcClient . UpstreamChainID } )
2021-09-09 14:28:54 +00:00
}
func ( api * API ) SetInitialBlocksRangeForChainIDs ( ctx context . Context , chainIDs [ ] uint64 ) error {
return api . s . transferController . SetInitialBlocksRange ( chainIDs )
}
func ( api * API ) CheckRecentHistory ( ctx context . Context , addresses [ ] common . Address ) error {
2021-09-22 17:49:20 +00:00
return api . s . transferController . CheckRecentHistory ( [ ] uint64 { api . s . rpcClient . UpstreamChainID } , addresses )
2021-09-09 14:28:54 +00:00
}
func ( api * API ) CheckRecentHistoryForChainIDs ( ctx context . Context , chainIDs [ ] uint64 , addresses [ ] common . Address ) error {
return api . s . transferController . CheckRecentHistory ( chainIDs , addresses )
2021-02-11 15:20:06 +00:00
}
2022-10-18 13:27:44 +00:00
func hexBigToBN ( hexBig * hexutil . Big ) * big . Int {
var bN * big . Int
if hexBig != nil {
bN = hexBig . ToInt ( )
}
return bN
}
2020-02-07 10:26:00 +00:00
// GetTransfersByAddress returns transfers for a single address
2021-09-09 14:28:54 +00:00
func ( api * API ) GetTransfersByAddress ( ctx context . Context , address common . Address , toBlock , limit * hexutil . Big , fetchMore bool ) ( [ ] transfer . View , error ) {
2021-11-24 12:59:45 +00:00
log . Debug ( "[WalletAPI:: GetTransfersByAddress] get transfers for an address" , "address" , address )
2022-10-18 13:27:44 +00:00
var intLimit = int64 ( 1 )
if limit != nil {
intLimit = limit . ToInt ( ) . Int64 ( )
}
return api . s . transferController . GetTransfersByAddress ( ctx , api . s . rpcClient . UpstreamChainID , address , hexBigToBN ( toBlock ) , intLimit , fetchMore )
2021-09-09 14:28:54 +00:00
}
status-im/status-react#9203 Faster tx fetching with less request
*** How it worked before this PR on multiaccount creation:
- On multiacc creation we scanned chain for eth and erc20 transfers. For
each address of a new empty multiaccount this scan required
1. two `eth_getBalance` requests to find out that there is no any
balance change between zero and the last block, for eth transfers
2. and `chain-size/100000` (currently ~100) `eth_getLogs` requests,
for erc20 transfers
- For some reason we scanned an address of the chat account as well, and
also accounts were not deduplicated. So even for an empty multiacc we
scanned chain twice for each chat and main wallet addresses, in result
app had to execute about 400 requests.
- As mentioned above, `eth_getBalance` requests were used to check if
there were any eth transfers, and that caused empty history in case
if user already used all available eth (so that both zero and latest
blocks show 0 eth for an address). There might have been transactions
but we wouldn't fetch/show them.
- There was no upper limit for the number of rpc requests during the
scan, so it could require indefinite number of requests; the scanning
algorithm was written so that we persisted the whole history of
transactions or tried to scan form the beginning again in case of
failure, giving up only after 10 minutes of failures. In result
addresses with sufficient number of transactions would never be fully
scanned and during these 10 minutes app could use gigabytes of
internet data.
- Failures were caused by `eth_getBlockByNumber`/`eth_getBlockByHash`
requests. These requests return significantly bigger responses than
`eth_getBalance`/`eth_transactionsCount` and it is likely that
execution of thousands of them in parallel caused failures for
accounts with hundreds of transactions. Even for an account with 12k
we could successfully determine blocks with transaction in a few
minutes using `eth_getBalance` requests, but `eth_getBlock...`
couldn't be processed for this acc.
- There was no caching for for `eth_getBalance` requests, and this
caused in average 3-4 times more such requests than is needed.
*** How it works now on multiaccount creation:
- On multiacc creation we scan chain for last ~30 eth transactions and
then check erc20 in the range where these eth transactions were found.
For an empty address in multiacc this means:
1. two `eth_getBalance` transactions to determine that there was no
balance change between zero and the last block; two
`eth_transactionsCount` requests to determine there are no outgoing
transactions for this address; total 4 requests for eth transfers
2. 20 `eth_getLogs` for erc20 transfers. This number can be lowered,
but that's not a big deal
- Deduplication of addresses is added and also we don't scan chat
account, so a new multiacc requires ~25 (we also request latest block
number and probably execute a few other calls) request to determine
that multiacc is empty (comparing to ~400 before)
- In case if address contains transactions we:
1. determine the range which contains 20-25 outgoing eth/erc20
transactions. This usually requires up to 10 `eth_transactionCount`
requests
2. then we scan chain for eth transfers using `eth_getBalance` and
`eth_transactionCount` (for double checking zero balances)
3. we make sure that we do not scan db for more than 30 blocks with
transfers. That's important for accounts with mostly incoming
transactions, because the range found on the first step might
contain any number of incoming transfers, but only 20-25 outgoing
transactions
4. when we found ~30 blocks in a given range, we update initial
range `from` block using the oldest found block
5. and now we scan db for erc20transfers using `eth_getLogs`
`oldest-found-eth-block`-`latest-block`, we make not more than 20 calls
6. when all blocks which contain incoming/outgoing transfers for a
given address are found, we save these blocks to db and mark that
transfers from these blocks are still to be fetched
7. Then we select latest ~30 (the number can be adjusted) blocks from
these which were found and fetch transfers, this requires 3-4
requests per transfer.
8. we persist scanned range so that we know were to start next time
9. we dispatch an event which tells client that transactions are found
10. client fetches latest 20 transfers
- when user presses "fetch more" button we check if app's db contains next
20 transfers, if not we scan chain again and return transfers after
small fixes
2019-12-18 11:01:46 +00:00
2021-11-24 12:59:45 +00:00
// LoadTransferByHash loads transfer to the database
2023-06-02 20:08:45 +00:00
// Only used by status-mobile
2021-11-24 12:59:45 +00:00
func ( api * API ) LoadTransferByHash ( ctx context . Context , address common . Address , hash common . Hash ) error {
log . Debug ( "[WalletAPI:: LoadTransferByHash] get transfer by hash" , "address" , address , "hash" , hash )
return api . s . transferController . LoadTransferByHash ( ctx , api . s . rpcClient , address , hash )
}
2021-09-09 14:28:54 +00:00
func ( api * API ) GetTransfersByAddressAndChainID ( ctx context . Context , chainID uint64 , address common . Address , toBlock , limit * hexutil . Big , fetchMore bool ) ( [ ] transfer . View , error ) {
2022-07-04 07:48:30 +00:00
log . Debug ( "[WalletAPI:: GetTransfersByAddressAndChainIDs] get transfers for an address" , "address" , address )
2022-10-18 13:27:44 +00:00
return api . s . transferController . GetTransfersByAddress ( ctx , chainID , address , hexBigToBN ( toBlock ) , limit . ToInt ( ) . Int64 ( ) , fetchMore )
2021-09-09 14:28:54 +00:00
}
2023-04-21 11:59:29 +00:00
func ( api * API ) GetTransfersForIdentities ( ctx context . Context , identities [ ] transfer . TransactionIdentity ) ( [ ] transfer . View , error ) {
2023-06-13 09:25:23 +00:00
log . Debug ( "wallet.api.GetTransfersForIdentities" , "identities.len" , len ( identities ) )
2023-04-21 11:59:29 +00:00
return api . s . transferController . GetTransfersForIdentities ( ctx , identities )
}
2023-05-25 13:12:54 +00:00
func ( api * API ) FetchDecodedTxData ( ctx context . Context , data string ) ( * thirdparty . DataParsed , error ) {
log . Debug ( "[Wallet: FetchDecodedTxData]" )
return api . s . decoder . Run ( data )
}
2023-05-08 06:02:00 +00:00
// Deprecated: GetCachedBalances is deprecated. Use GetTokensBalances instead
func ( api * API ) GetCachedBalances ( ctx context . Context , addresses [ ] common . Address ) ( [ ] transfer . BlockView , error ) {
2021-09-22 17:49:20 +00:00
return api . s . transferController . GetCachedBalances ( ctx , api . s . rpcClient . UpstreamChainID , addresses )
2021-09-09 14:28:54 +00:00
}
2023-05-08 06:02:00 +00:00
// Deprecated: GetCachedBalances is deprecated. Use GetTokensBalancesForChainIDs instead
func ( api * API ) GetCachedBalancesbyChainID ( ctx context . Context , chainID uint64 , addresses [ ] common . Address ) ( [ ] transfer . BlockView , error ) {
2021-09-09 14:28:54 +00:00
return api . s . transferController . GetCachedBalances ( ctx , chainID , addresses )
}
status-im/status-react#9203 Faster tx fetching with less request
*** How it worked before this PR on multiaccount creation:
- On multiacc creation we scanned chain for eth and erc20 transfers. For
each address of a new empty multiaccount this scan required
1. two `eth_getBalance` requests to find out that there is no any
balance change between zero and the last block, for eth transfers
2. and `chain-size/100000` (currently ~100) `eth_getLogs` requests,
for erc20 transfers
- For some reason we scanned an address of the chat account as well, and
also accounts were not deduplicated. So even for an empty multiacc we
scanned chain twice for each chat and main wallet addresses, in result
app had to execute about 400 requests.
- As mentioned above, `eth_getBalance` requests were used to check if
there were any eth transfers, and that caused empty history in case
if user already used all available eth (so that both zero and latest
blocks show 0 eth for an address). There might have been transactions
but we wouldn't fetch/show them.
- There was no upper limit for the number of rpc requests during the
scan, so it could require indefinite number of requests; the scanning
algorithm was written so that we persisted the whole history of
transactions or tried to scan form the beginning again in case of
failure, giving up only after 10 minutes of failures. In result
addresses with sufficient number of transactions would never be fully
scanned and during these 10 minutes app could use gigabytes of
internet data.
- Failures were caused by `eth_getBlockByNumber`/`eth_getBlockByHash`
requests. These requests return significantly bigger responses than
`eth_getBalance`/`eth_transactionsCount` and it is likely that
execution of thousands of them in parallel caused failures for
accounts with hundreds of transactions. Even for an account with 12k
we could successfully determine blocks with transaction in a few
minutes using `eth_getBalance` requests, but `eth_getBlock...`
couldn't be processed for this acc.
- There was no caching for for `eth_getBalance` requests, and this
caused in average 3-4 times more such requests than is needed.
*** How it works now on multiaccount creation:
- On multiacc creation we scan chain for last ~30 eth transactions and
then check erc20 in the range where these eth transactions were found.
For an empty address in multiacc this means:
1. two `eth_getBalance` transactions to determine that there was no
balance change between zero and the last block; two
`eth_transactionsCount` requests to determine there are no outgoing
transactions for this address; total 4 requests for eth transfers
2. 20 `eth_getLogs` for erc20 transfers. This number can be lowered,
but that's not a big deal
- Deduplication of addresses is added and also we don't scan chat
account, so a new multiacc requires ~25 (we also request latest block
number and probably execute a few other calls) request to determine
that multiacc is empty (comparing to ~400 before)
- In case if address contains transactions we:
1. determine the range which contains 20-25 outgoing eth/erc20
transactions. This usually requires up to 10 `eth_transactionCount`
requests
2. then we scan chain for eth transfers using `eth_getBalance` and
`eth_transactionCount` (for double checking zero balances)
3. we make sure that we do not scan db for more than 30 blocks with
transfers. That's important for accounts with mostly incoming
transactions, because the range found on the first step might
contain any number of incoming transfers, but only 20-25 outgoing
transactions
4. when we found ~30 blocks in a given range, we update initial
range `from` block using the oldest found block
5. and now we scan db for erc20transfers using `eth_getLogs`
`oldest-found-eth-block`-`latest-block`, we make not more than 20 calls
6. when all blocks which contain incoming/outgoing transfers for a
given address are found, we save these blocks to db and mark that
transfers from these blocks are still to be fetched
7. Then we select latest ~30 (the number can be adjusted) blocks from
these which were found and fetch transfers, this requires 3-4
requests per transfer.
8. we persist scanned range so that we know were to start next time
9. we dispatch an event which tells client that transactions are found
10. client fetches latest 20 transfers
- when user presses "fetch more" button we check if app's db contains next
20 transfers, if not we scan chain again and return transfers after
small fixes
2019-12-18 11:01:46 +00:00
2021-09-09 14:28:54 +00:00
// GetTokensBalances return mapping of token balances for every account.
func ( api * API ) GetTokensBalances ( ctx context . Context , accounts , addresses [ ] common . Address ) ( map [ common . Address ] map [ common . Address ] * hexutil . Big , error ) {
2023-03-24 08:38:27 +00:00
chainClients , err := api . s . rpcClient . EthClients ( [ ] uint64 { api . s . rpcClient . UpstreamChainID } )
2019-06-14 10:16:30 +00:00
if err != nil {
return nil , err
}
2023-03-24 08:38:27 +00:00
return api . s . tokenManager . GetBalances ( ctx , chainClients , accounts , addresses )
2019-06-14 10:16:30 +00:00
}
2019-07-02 07:28:57 +00:00
2021-09-09 14:28:54 +00:00
func ( api * API ) GetTokensBalancesForChainIDs ( ctx context . Context , chainIDs [ ] uint64 , accounts , addresses [ ] common . Address ) ( map [ common . Address ] map [ common . Address ] * hexutil . Big , error ) {
2023-02-20 09:32:45 +00:00
clients , err := api . s . rpcClient . EthClients ( chainIDs )
2021-09-09 14:28:54 +00:00
if err != nil {
return nil , err
2019-07-02 07:28:57 +00:00
}
2022-09-13 07:10:59 +00:00
return api . s . tokenManager . GetBalances ( ctx , clients , accounts , addresses )
2019-07-02 07:28:57 +00:00
}
2019-12-10 17:31:08 +00:00
2022-11-15 12:14:41 +00:00
func ( api * API ) UpdateVisibleTokens ( ctx context . Context , symbols [ ] string ) error {
api . s . history . UpdateVisibleTokens ( symbols )
return nil
}
// GetBalanceHistory retrieves token balance history for token identity on multiple chains
2023-01-25 18:28:51 +00:00
func ( api * API ) GetBalanceHistory ( ctx context . Context , chainIDs [ ] uint64 , address common . Address , tokenSymbol string , currencySymbol string , timeInterval history . TimeInterval ) ( [ ] * history . ValuePoint , error ) {
2022-11-15 12:14:41 +00:00
endTimestamp := time . Now ( ) . UTC ( ) . Unix ( )
2023-01-25 18:28:51 +00:00
return api . s . history . GetBalanceHistory ( ctx , chainIDs , address , tokenSymbol , currencySymbol , endTimestamp , timeInterval )
2022-10-18 13:27:44 +00:00
}
2022-09-13 07:10:59 +00:00
func ( api * API ) GetTokens ( ctx context . Context , chainID uint64 ) ( [ ] * token . Token , error ) {
2022-01-14 09:21:00 +00:00
log . Debug ( "call to get tokens" )
2022-09-13 07:10:59 +00:00
rst , err := api . s . tokenManager . GetTokens ( chainID )
2022-01-14 09:21:00 +00:00
log . Debug ( "result from token store" , "len" , len ( rst ) )
return rst , err
}
2022-09-13 07:10:59 +00:00
func ( api * API ) GetCustomTokens ( ctx context . Context ) ( [ ] * token . Token , error ) {
2019-12-10 17:31:08 +00:00
log . Debug ( "call to get custom tokens" )
2022-09-13 07:10:59 +00:00
rst , err := api . s . tokenManager . GetCustoms ( )
2019-12-10 17:31:08 +00:00
log . Debug ( "result from database for custom tokens" , "len" , len ( rst ) )
return rst , err
}
2022-09-13 07:10:59 +00:00
func ( api * API ) DiscoverToken ( ctx context . Context , chainID uint64 , address common . Address ) ( * token . Token , error ) {
2022-04-04 16:54:44 +00:00
log . Debug ( "call to get discover token" )
2022-09-13 07:10:59 +00:00
token , err := api . s . tokenManager . DiscoverToken ( ctx , chainID , address )
2022-04-04 16:54:44 +00:00
return token , err
}
2022-09-13 07:10:59 +00:00
func ( api * API ) GetVisibleTokens ( chainIDs [ ] uint64 ) ( map [ uint64 ] [ ] * token . Token , error ) {
2022-04-13 07:55:38 +00:00
log . Debug ( "call to get visible tokens" )
2022-09-13 07:10:59 +00:00
rst , err := api . s . tokenManager . GetVisible ( chainIDs )
2022-04-13 07:55:38 +00:00
log . Debug ( "result from database for visible tokens" , "len" , len ( rst ) )
return rst , err
}
func ( api * API ) ToggleVisibleToken ( ctx context . Context , chainID uint64 , address common . Address ) ( bool , error ) {
log . Debug ( "call to toggle visible tokens" )
2022-09-13 07:10:59 +00:00
err := api . s . tokenManager . Toggle ( chainID , address )
2022-04-13 07:55:38 +00:00
if err != nil {
return false , err
}
return true , nil
}
2022-09-13 07:10:59 +00:00
func ( api * API ) AddCustomToken ( ctx context . Context , token token . Token ) error {
2019-12-10 17:31:08 +00:00
log . Debug ( "call to create or edit custom token" )
2021-09-09 14:28:54 +00:00
if token . ChainID == 0 {
2021-09-22 17:49:20 +00:00
token . ChainID = api . s . rpcClient . UpstreamChainID
2021-09-09 14:28:54 +00:00
}
2022-09-13 07:10:59 +00:00
err := api . s . tokenManager . UpsertCustom ( token )
2019-12-10 17:31:08 +00:00
log . Debug ( "result from database for create or edit custom token" , "err" , err )
return err
}
func ( api * API ) DeleteCustomToken ( ctx context . Context , address common . Address ) error {
log . Debug ( "call to remove custom token" )
2022-09-13 07:10:59 +00:00
err := api . s . tokenManager . DeleteCustom ( api . s . rpcClient . UpstreamChainID , address )
2021-09-09 14:28:54 +00:00
log . Debug ( "result from database for remove custom token" , "err" , err )
return err
}
func ( api * API ) DeleteCustomTokenByChainID ( ctx context . Context , chainID uint64 , address common . Address ) error {
log . Debug ( "call to remove custom token" )
2022-09-13 07:10:59 +00:00
err := api . s . tokenManager . DeleteCustom ( chainID , address )
2019-12-10 17:31:08 +00:00
log . Debug ( "result from database for remove custom token" , "err" , err )
return err
}
2020-09-14 13:39:24 +00:00
2022-05-10 07:48:05 +00:00
func ( api * API ) GetSavedAddresses ( ctx context . Context ) ( [ ] SavedAddress , error ) {
2021-09-10 18:08:22 +00:00
log . Debug ( "call to get saved addresses" )
2023-03-06 13:26:46 +00:00
rst , err := api . s . savedAddressesManager . GetSavedAddresses ( )
2021-09-10 18:08:22 +00:00
log . Debug ( "result from database for saved addresses" , "len" , len ( rst ) )
return rst , err
}
func ( api * API ) AddSavedAddress ( ctx context . Context , sa SavedAddress ) error {
log . Debug ( "call to create or edit saved address" )
2022-09-14 10:46:11 +00:00
_ , err := api . s . savedAddressesManager . UpdateMetadataAndUpsertSavedAddress ( sa )
2021-09-10 18:08:22 +00:00
log . Debug ( "result from database for create or edit saved address" , "err" , err )
return err
}
2023-03-06 13:26:46 +00:00
func ( api * API ) DeleteSavedAddress ( ctx context . Context , address common . Address , ens string , isTest bool ) error {
2021-09-10 18:08:22 +00:00
log . Debug ( "call to remove saved address" )
2023-03-06 13:26:46 +00:00
_ , err := api . s . savedAddressesManager . DeleteSavedAddress ( address , ens , isTest , uint64 ( time . Now ( ) . Unix ( ) ) )
2021-09-10 18:08:22 +00:00
log . Debug ( "result from database for remove saved address" , "err" , err )
return err
}
2023-02-15 12:28:19 +00:00
func ( api * API ) GetPendingTransactions ( ctx context . Context ) ( [ ] * transfer . PendingTransaction , error ) {
2020-09-14 13:39:24 +00:00
log . Debug ( "call to get pending transactions" )
2023-02-15 12:28:19 +00:00
rst , err := api . s . transactionManager . GetAllPending ( [ ] uint64 { api . s . rpcClient . UpstreamChainID } )
2021-09-09 14:28:54 +00:00
log . Debug ( "result from database for pending transactions" , "len" , len ( rst ) )
return rst , err
}
2023-02-15 12:28:19 +00:00
func ( api * API ) GetPendingTransactionsByChainIDs ( ctx context . Context , chainIDs [ ] uint64 ) ( [ ] * transfer . PendingTransaction , error ) {
2021-09-09 14:28:54 +00:00
log . Debug ( "call to get pending transactions" )
2023-02-15 12:28:19 +00:00
rst , err := api . s . transactionManager . GetAllPending ( chainIDs )
2020-09-14 13:39:24 +00:00
log . Debug ( "result from database for pending transactions" , "len" , len ( rst ) )
return rst , err
}
2023-04-21 11:59:29 +00:00
func ( api * API ) GetPendingTransactionsForIdentities ( ctx context . Context , identities [ ] transfer . TransactionIdentity ) ( result [ ] * transfer . PendingTransaction , err error ) {
log . Debug ( "call to GetPendingTransactionsForIdentities" )
result = make ( [ ] * transfer . PendingTransaction , 0 , len ( identities ) )
var pt * transfer . PendingTransaction
for _ , identity := range identities {
2023-05-11 07:50:07 +00:00
pt , err = api . s . transactionManager . GetPendingEntry ( uint64 ( identity . ChainID ) , identity . Hash )
2023-04-21 11:59:29 +00:00
result = append ( result , pt )
}
log . Debug ( "result from GetPendingTransactionsForIdentities" , "len" , len ( result ) )
return
}
2023-02-15 12:28:19 +00:00
func ( api * API ) GetPendingOutboundTransactionsByAddress ( ctx context . Context , address common . Address ) ( [ ] * transfer . PendingTransaction , error ) {
2021-09-09 14:28:54 +00:00
log . Debug ( "call to get pending outbound transactions by address" )
2023-02-15 12:28:19 +00:00
rst , err := api . s . transactionManager . GetPendingByAddress ( [ ] uint64 { api . s . rpcClient . UpstreamChainID } , address )
2021-09-09 14:28:54 +00:00
log . Debug ( "result from database for pending transactions by address" , "len" , len ( rst ) )
return rst , err
}
2023-02-15 12:28:19 +00:00
func ( api * API ) GetPendingOutboundTransactionsByAddressAndChainID ( ctx context . Context , chainIDs [ ] uint64 , address common . Address ) ( [ ] * transfer . PendingTransaction , error ) {
2021-09-09 14:28:54 +00:00
log . Debug ( "call to get pending outbound transactions by address" )
2023-02-15 12:28:19 +00:00
rst , err := api . s . transactionManager . GetPendingByAddress ( chainIDs , address )
2020-09-14 13:39:24 +00:00
log . Debug ( "result from database for pending transactions by address" , "len" , len ( rst ) )
return rst , err
}
2023-02-15 12:28:19 +00:00
func ( api * API ) StorePendingTransaction ( ctx context . Context , trx transfer . PendingTransaction ) error {
2020-09-14 13:39:24 +00:00
log . Debug ( "call to create or edit pending transaction" )
2021-09-09 14:28:54 +00:00
if trx . ChainID == 0 {
2021-09-22 17:49:20 +00:00
trx . ChainID = api . s . rpcClient . UpstreamChainID
2021-09-09 14:28:54 +00:00
}
2023-02-15 12:28:19 +00:00
err := api . s . transactionManager . AddPending ( trx )
2020-09-14 13:39:24 +00:00
log . Debug ( "result from database for creating or editing a pending transaction" , "err" , err )
return err
}
func ( api * API ) DeletePendingTransaction ( ctx context . Context , transactionHash common . Hash ) error {
log . Debug ( "call to remove pending transaction" )
2023-02-15 12:28:19 +00:00
err := api . s . transactionManager . DeletePending ( api . s . rpcClient . UpstreamChainID , transactionHash )
2020-09-14 13:39:24 +00:00
log . Debug ( "result from database for remove pending transaction" , "err" , err )
return err
}
2020-09-11 06:07:40 +00:00
2021-09-09 14:28:54 +00:00
func ( api * API ) DeletePendingTransactionByChainID ( ctx context . Context , chainID uint64 , transactionHash common . Hash ) error {
log . Debug ( "call to remove pending transaction" )
2023-02-15 12:28:19 +00:00
err := api . s . transactionManager . DeletePending ( chainID , transactionHash )
2021-09-09 14:28:54 +00:00
log . Debug ( "result from database for remove pending transaction" , "err" , err )
2020-09-11 06:07:40 +00:00
return err
}
2021-01-26 13:00:32 +00:00
2021-03-18 15:14:00 +00:00
func ( api * API ) WatchTransaction ( ctx context . Context , transactionHash common . Hash ) error {
2023-02-20 09:32:45 +00:00
chainClient , err := api . s . rpcClient . EthClient ( api . s . rpcClient . UpstreamChainID )
2021-09-09 14:28:54 +00:00
if err != nil {
return err
2021-03-18 15:14:00 +00:00
}
2023-02-15 12:28:19 +00:00
return api . s . transactionManager . Watch ( ctx , transactionHash , chainClient )
2021-03-18 15:14:00 +00:00
}
2021-04-01 09:04:47 +00:00
2021-09-09 14:28:54 +00:00
func ( api * API ) WatchTransactionByChainID ( ctx context . Context , chainID uint64 , transactionHash common . Hash ) error {
2023-02-20 09:32:45 +00:00
chainClient , err := api . s . rpcClient . EthClient ( chainID )
2021-04-01 09:04:47 +00:00
if err != nil {
return err
}
2023-02-15 12:28:19 +00:00
return api . s . transactionManager . Watch ( ctx , transactionHash , chainClient )
2021-09-09 14:28:54 +00:00
}
2021-05-20 12:11:18 +00:00
2021-09-09 14:28:54 +00:00
func ( api * API ) GetCryptoOnRamps ( ctx context . Context ) ( [ ] CryptoOnRamp , error ) {
return api . s . cryptoOnRampManager . Get ( )
2021-05-20 12:11:18 +00:00
}
2021-08-20 19:53:24 +00:00
2023-02-28 03:10:38 +00:00
func ( api * API ) GetOpenseaCollectionsByOwner ( ctx context . Context , chainID uint64 , owner common . Address ) ( [ ] opensea . OwnedCollection , error ) {
2021-08-20 19:53:24 +00:00
log . Debug ( "call to get opensea collections" )
2023-03-21 13:52:14 +00:00
return api . s . collectiblesManager . FetchAllCollectionsByOwner ( chainID , owner )
2021-08-20 19:53:24 +00:00
}
2023-03-06 16:15:48 +00:00
// Kept for compatibility with mobile app
2023-02-21 09:05:16 +00:00
func ( api * API ) GetOpenseaAssetsByOwnerAndCollection ( ctx context . Context , chainID uint64 , owner common . Address , collectionSlug string , limit int ) ( [ ] opensea . Asset , error ) {
2023-03-06 16:15:48 +00:00
container , err := api . GetOpenseaAssetsByOwnerAndCollectionWithCursor ( ctx , chainID , owner , collectionSlug , "" , limit )
if err != nil {
return nil , err
}
return container . Assets , nil
}
func ( api * API ) GetOpenseaAssetsByOwnerAndCollectionWithCursor ( ctx context . Context , chainID uint64 , owner common . Address , collectionSlug string , cursor string , limit int ) ( * opensea . AssetContainer , error ) {
2021-08-20 19:53:24 +00:00
log . Debug ( "call to get opensea assets" )
2023-03-21 13:52:14 +00:00
return api . s . collectiblesManager . FetchAllAssetsByOwnerAndCollection ( chainID , owner , collectionSlug , cursor , limit )
2023-03-06 16:15:48 +00:00
}
func ( api * API ) GetOpenseaAssetsByOwnerWithCursor ( ctx context . Context , chainID uint64 , owner common . Address , cursor string , limit int ) ( * opensea . AssetContainer , error ) {
log . Debug ( "call to FetchAllAssetsByOwner" )
2023-03-21 13:52:14 +00:00
return api . s . collectiblesManager . FetchAllAssetsByOwner ( chainID , owner , cursor , limit )
2021-08-20 19:53:24 +00:00
}
2021-09-09 14:28:54 +00:00
2023-03-17 21:20:31 +00:00
func ( api * API ) GetOpenseaAssetsByOwnerAndContractAddressWithCursor ( ctx context . Context , chainID uint64 , owner common . Address , contractAddresses [ ] common . Address , cursor string , limit int ) ( * opensea . AssetContainer , error ) {
log . Debug ( "call to GetOpenseaAssetsByOwnerAndContractAddressWithCursor" )
2023-03-21 13:52:14 +00:00
return api . s . collectiblesManager . FetchAllAssetsByOwnerAndContractAddress ( chainID , owner , contractAddresses , cursor , limit )
2023-03-17 21:20:31 +00:00
}
2023-03-21 13:52:14 +00:00
func ( api * API ) GetOpenseaAssetsByNFTUniqueID ( ctx context . Context , chainID uint64 , uniqueIDs [ ] thirdparty . NFTUniqueID , limit int ) ( * opensea . AssetContainer , error ) {
2023-02-28 03:10:38 +00:00
log . Debug ( "call to GetOpenseaAssetsByNFTUniqueID" )
2023-03-21 13:52:14 +00:00
return api . s . collectiblesManager . FetchAssetsByNFTUniqueID ( chainID , uniqueIDs , limit )
2023-02-28 03:10:38 +00:00
}
2023-04-17 11:42:01 +00:00
func ( api * API ) GetCollectibleOwnersByContractAddress ( chainID uint64 , contractAddress common . Address ) ( * thirdparty . NFTContractOwnership , error ) {
log . Debug ( "call to GetCollectibleOwnersByContractAddress" )
return api . s . collectiblesManager . FetchNFTOwnersByContractAddress ( chainID , contractAddress )
}
2022-01-12 20:04:43 +00:00
func ( api * API ) AddEthereumChain ( ctx context . Context , network params . Network ) error {
2021-09-09 14:28:54 +00:00
log . Debug ( "call to AddEthereumChain" )
2021-09-22 17:49:20 +00:00
return api . s . rpcClient . NetworkManager . Upsert ( & network )
2021-09-09 14:28:54 +00:00
}
func ( api * API ) DeleteEthereumChain ( ctx context . Context , chainID uint64 ) error {
log . Debug ( "call to DeleteEthereumChain" )
2021-09-22 17:49:20 +00:00
return api . s . rpcClient . NetworkManager . Delete ( chainID )
2021-09-09 14:28:54 +00:00
}
2022-01-12 20:04:43 +00:00
func ( api * API ) GetEthereumChains ( ctx context . Context , onlyEnabled bool ) ( [ ] * params . Network , error ) {
2021-09-09 14:28:54 +00:00
log . Debug ( "call to GetEthereumChains" )
2021-09-22 17:49:20 +00:00
return api . s . rpcClient . NetworkManager . Get ( onlyEnabled )
2021-09-09 14:28:54 +00:00
}
2022-03-23 08:35:58 +00:00
2023-01-12 17:54:14 +00:00
func ( api * API ) FetchPrices ( ctx context . Context , symbols [ ] string , currencies [ ] string ) ( map [ string ] map [ string ] float64 , error ) {
2022-03-23 08:35:58 +00:00
log . Debug ( "call to FetchPrices" )
2023-02-21 09:05:16 +00:00
return api . s . marketManager . FetchPrices ( symbols , currencies )
2023-01-19 14:49:48 +00:00
}
2023-02-21 09:05:16 +00:00
func ( api * API ) FetchMarketValues ( ctx context . Context , symbols [ ] string , currency string ) ( map [ string ] thirdparty . TokenMarketValues , error ) {
2022-08-23 08:46:15 +00:00
log . Debug ( "call to FetchMarketValues" )
2023-02-21 09:05:16 +00:00
return api . s . marketManager . FetchTokenMarketValues ( symbols , currency )
2022-08-23 08:46:15 +00:00
}
2023-02-21 09:05:16 +00:00
func ( api * API ) GetHourlyMarketValues ( ctx context . Context , symbol string , currency string , limit int , aggregate int ) ( [ ] thirdparty . HistoricalPrice , error ) {
2022-10-10 19:02:04 +00:00
log . Debug ( "call to GetHourlyMarketValues" )
2023-02-21 09:05:16 +00:00
return api . s . marketManager . FetchHistoricalHourlyPrices ( symbol , currency , limit , aggregate )
2022-10-10 19:02:04 +00:00
}
2023-02-21 09:05:16 +00:00
func ( api * API ) GetDailyMarketValues ( ctx context . Context , symbol string , currency string , limit int , allData bool , aggregate int ) ( [ ] thirdparty . HistoricalPrice , error ) {
2022-10-10 19:02:04 +00:00
log . Debug ( "call to GetDailyMarketValues" )
2023-02-21 09:05:16 +00:00
return api . s . marketManager . FetchHistoricalDailyPrices ( symbol , currency , limit , allData , aggregate )
2022-10-10 19:02:04 +00:00
}
2023-02-21 09:05:16 +00:00
func ( api * API ) FetchTokenDetails ( ctx context . Context , symbols [ ] string ) ( map [ string ] thirdparty . TokenDetails , error ) {
2022-08-23 08:46:15 +00:00
log . Debug ( "call to FetchTokenDetails" )
2023-02-21 09:05:16 +00:00
return api . s . marketManager . FetchTokenDetails ( symbols )
2022-08-23 08:46:15 +00:00
}
2022-03-29 21:12:05 +00:00
func ( api * API ) GetSuggestedFees ( ctx context . Context , chainID uint64 ) ( * SuggestedFees , error ) {
log . Debug ( "call to GetSuggestedFees" )
return api . s . feesManager . suggestedFees ( ctx , chainID )
}
2022-05-18 11:31:45 +00:00
2022-09-13 07:10:59 +00:00
func ( api * API ) GetTransactionEstimatedTime ( ctx context . Context , chainID uint64 , maxFeePerGas * big . Float ) ( TransactionEstimation , error ) {
2022-07-12 12:25:32 +00:00
log . Debug ( "call to getTransactionEstimatedTime" )
return api . s . feesManager . transactionEstimatedTime ( ctx , chainID , maxFeePerGas ) , nil
}
2022-11-23 17:49:23 +00:00
func ( api * API ) GetSuggestedRoutes (
ctx context . Context ,
sendType SendType ,
account common . Address ,
amountIn * hexutil . Big ,
tokenSymbol string ,
disabledFromChainIDs ,
disabledToChaindIDs ,
preferedChainIDs [ ] uint64 ,
gasFeeMode GasFeeMode ,
fromLockedAmount map [ uint64 ] * hexutil . Big ,
) ( * SuggestedRoutes , error ) {
2022-06-09 13:09:56 +00:00
log . Debug ( "call to GetSuggestedRoutes" )
2022-11-23 17:49:23 +00:00
return api . router . suggestedRoutes ( ctx , sendType , account , amountIn . ToInt ( ) , tokenSymbol , disabledFromChainIDs , disabledToChaindIDs , preferedChainIDs , gasFeeMode , fromLockedAmount )
2022-06-09 13:09:56 +00:00
}
2023-03-20 07:39:33 +00:00
// Generates addresses for the provided paths, response doesn't include `HasActivity` value (if you need it check `GetAddressDetails` function)
func ( api * API ) GetDerivedAddresses ( ctx context . Context , password string , derivedFrom string , paths [ ] string ) ( [ ] * DerivedAddress , error ) {
2023-01-31 12:16:05 +00:00
info , err := api . s . gethManager . AccountsGenerator ( ) . LoadAccount ( derivedFrom , password )
if err != nil {
return nil , err
}
2023-03-20 07:39:33 +00:00
return api . getDerivedAddresses ( info . ID , paths )
2023-01-31 12:16:05 +00:00
}
2023-03-20 07:39:33 +00:00
// Generates addresses for the provided paths derived from the provided mnemonic, response doesn't include `HasActivity` value (if you need it check `GetAddressDetails` function)
func ( api * API ) GetDerivedAddressesForMnemonic ( ctx context . Context , mnemonic string , paths [ ] string ) ( [ ] * DerivedAddress , error ) {
2022-05-18 11:31:45 +00:00
mnemonicNoExtraSpaces := strings . Join ( strings . Fields ( mnemonic ) , " " )
info , err := api . s . gethManager . AccountsGenerator ( ) . ImportMnemonic ( mnemonicNoExtraSpaces , "" )
if err != nil {
return nil , err
}
2023-03-20 07:39:33 +00:00
return api . getDerivedAddresses ( info . ID , paths )
2022-05-18 11:31:45 +00:00
}
2023-03-20 07:39:33 +00:00
// Generates addresses for the provided paths, response doesn't include `HasActivity` value (if you need it check `GetAddressDetails` function)
func ( api * API ) getDerivedAddresses ( id string , paths [ ] string ) ( [ ] * DerivedAddress , error ) {
addedAccounts , err := api . s . accountsDB . GetAccounts ( )
2022-05-18 11:31:45 +00:00
if err != nil {
2023-03-20 07:39:33 +00:00
return nil , err
2022-05-18 11:31:45 +00:00
}
2023-03-20 07:39:33 +00:00
info , err := api . s . gethManager . AccountsGenerator ( ) . DeriveAddresses ( id , paths )
2022-05-18 11:31:45 +00:00
if err != nil {
2023-03-20 07:39:33 +00:00
return nil , err
2022-05-18 11:31:45 +00:00
}
2022-10-21 08:01:31 +00:00
2023-03-20 07:39:33 +00:00
derivedAddresses := make ( [ ] * DerivedAddress , 0 )
for accPath , acc := range info {
derivedAddress := & DerivedAddress {
2023-05-16 10:48:00 +00:00
Address : common . HexToAddress ( acc . Address ) ,
PublicKey : types . Hex2Bytes ( acc . PublicKey ) ,
Path : accPath ,
2023-03-20 07:39:33 +00:00
}
for _ , account := range addedAccounts {
if types . Address ( derivedAddress . Address ) == account . Address {
derivedAddress . AlreadyCreated = true
break
}
}
derivedAddresses = append ( derivedAddresses , derivedAddress )
}
2022-10-21 08:01:31 +00:00
return derivedAddresses , nil
}
2023-05-25 09:53:15 +00:00
func ( api * API ) AddressExists ( ctx context . Context , address types . Address ) ( bool , error ) {
return api . s . accountsDB . AddressExists ( address )
}
2023-03-20 07:39:33 +00:00
// Returns details for the passed address (response doesn't include derivation path)
2023-05-16 08:18:56 +00:00
func ( api * API ) GetAddressDetails ( ctx context . Context , chainID uint64 , address string ) ( * DerivedAddress , error ) {
2022-10-21 08:01:31 +00:00
commonAddr := common . HexToAddress ( address )
addressExists , err := api . s . accountsDB . AddressExists ( types . Address ( commonAddr ) )
if err != nil {
2023-05-25 09:53:15 +00:00
return nil , err
2022-10-21 08:01:31 +00:00
}
2022-05-18 11:31:45 +00:00
2023-05-16 08:18:56 +00:00
chainClient , err := api . s . rpcClient . EthClient ( chainID )
if err != nil {
2023-05-25 09:53:15 +00:00
return nil , err
2023-05-16 08:18:56 +00:00
}
2022-05-18 11:31:45 +00:00
2023-05-16 08:18:56 +00:00
balance , err := api . s . tokenManager . GetChainBalance ( ctx , chainClient , commonAddr )
2022-05-18 11:31:45 +00:00
if err != nil {
2023-05-25 09:53:15 +00:00
return nil , err
2022-05-18 11:31:45 +00:00
}
2023-05-25 09:53:15 +00:00
return & DerivedAddress {
2022-10-21 08:01:31 +00:00
Address : commonAddr ,
2022-05-18 11:31:45 +00:00
Path : "" ,
AlreadyCreated : addressExists ,
2023-05-25 09:53:15 +00:00
HasActivity : balance . Cmp ( big . NewInt ( 0 ) ) != 0 ,
} , nil
2022-05-18 11:31:45 +00:00
}
2023-06-12 10:49:32 +00:00
func ( api * API ) CreateMultiTransaction ( ctx context . Context , multiTransactionCommand * transfer . MultiTransactionCommand , data [ ] * bridge . TransactionBridge , password string ) ( * transfer . MultiTransactionCommandResult , error ) {
2022-07-15 08:53:56 +00:00
log . Debug ( "[WalletAPI:: CreateMultiTransaction] create multi transaction" )
2023-06-12 10:49:32 +00:00
return api . s . transactionManager . CreateMultiTransactionFromCommand ( ctx , multiTransactionCommand , data , api . router . bridges , password )
2022-07-15 08:53:56 +00:00
}
2023-01-12 17:54:14 +00:00
2023-03-01 19:35:19 +00:00
func ( api * API ) GetMultiTransactions ( ctx context . Context , transactionIDs [ ] transfer . MultiTransactionIDType ) ( [ ] * transfer . MultiTransaction , error ) {
2023-06-13 09:25:23 +00:00
log . Debug ( "wallet.api.GetMultiTransactions" , "IDs.len" , len ( transactionIDs ) )
2023-03-01 19:35:19 +00:00
return api . s . transactionManager . GetMultiTransactions ( ctx , transactionIDs )
}
2023-02-17 14:11:07 +00:00
func ( api * API ) GetCachedCurrencyFormats ( ) ( currency . FormatPerSymbol , error ) {
log . Debug ( "call to GetCachedCurrencyFormats" )
return api . s . currency . GetCachedCurrencyFormats ( )
2023-01-12 17:54:14 +00:00
}
2023-02-17 14:11:07 +00:00
func ( api * API ) FetchAllCurrencyFormats ( ) ( currency . FormatPerSymbol , error ) {
log . Debug ( "call to FetchAllCurrencyFormats" )
return api . s . currency . FetchAllCurrencyFormats ( )
2023-01-12 17:54:14 +00:00
}
2023-04-21 11:59:29 +00:00
2023-06-08 23:52:45 +00:00
func ( api * API ) FilterActivityAsync ( ctx context . Context , addresses [ ] common . Address , chainIDs [ ] wcommon . ChainID , filter activity . Filter , offset int , limit int ) error {
2023-06-13 09:25:23 +00:00
log . Debug ( "wallet.api.FilterActivityAsync" , "addr.count" , len ( addresses ) , "chainIDs.count" , len ( chainIDs ) , "offset" , offset , "limit" , limit )
2023-06-08 23:52:45 +00:00
return api . s . activity . FilterActivityAsync ( ctx , addresses , chainIDs , filter , offset , limit )
2023-04-21 11:59:29 +00:00
}
2023-06-15 17:00:40 +00:00
type GetAllRecipientsResponse struct {
Addresses [ ] common . Address ` json:"addresses" `
HasMore bool ` json:"hasMore" `
}
func ( api * API ) GetAllRecipients ( ctx context . Context , offset int , limit int ) ( result * GetAllRecipientsResponse , err error ) {
2023-06-13 09:25:23 +00:00
log . Debug ( "wallet.api.GetAllRecipients" , "offset" , offset , "limit" , limit )
2023-06-15 17:00:40 +00:00
result = & GetAllRecipientsResponse { }
result . Addresses , result . HasMore , err = activity . GetRecipients ( ctx , api . s . db , offset , limit )
return result , err
}
2023-06-21 08:01:31 +00:00
func ( api * API ) GetOldestActivityTimestamp ( ctx context . Context , addresses [ ] common . Address ) ( timestamp int64 , err error ) {
log . Debug ( "wallet.api.GetOldestActivityTimestamp" , "addresses.len" , len ( addresses ) )
return activity . GetOldestTimestamp ( ctx , api . s . db , addresses )
}