github.com/status-im/status-go@v1.1.0/services/wallet/api.go (about) 1 package wallet 2 3 import ( 4 "context" 5 "encoding/hex" 6 "encoding/json" 7 "errors" 8 "fmt" 9 "math/big" 10 "strings" 11 "time" 12 13 "github.com/ethereum/go-ethereum/common" 14 "github.com/ethereum/go-ethereum/common/hexutil" 15 "github.com/ethereum/go-ethereum/ethclient" 16 "github.com/ethereum/go-ethereum/log" 17 gethrpc "github.com/ethereum/go-ethereum/rpc" 18 signercore "github.com/ethereum/go-ethereum/signer/core/apitypes" 19 abi_spec "github.com/status-im/status-go/abi-spec" 20 "github.com/status-im/status-go/account" 21 "github.com/status-im/status-go/eth-node/crypto" 22 "github.com/status-im/status-go/eth-node/types" 23 "github.com/status-im/status-go/params" 24 "github.com/status-im/status-go/rpc/network" 25 "github.com/status-im/status-go/services/typeddata" 26 "github.com/status-im/status-go/services/wallet/activity" 27 "github.com/status-im/status-go/services/wallet/collectibles" 28 wcommon "github.com/status-im/status-go/services/wallet/common" 29 "github.com/status-im/status-go/services/wallet/currency" 30 "github.com/status-im/status-go/services/wallet/history" 31 "github.com/status-im/status-go/services/wallet/onramp" 32 "github.com/status-im/status-go/services/wallet/requests" 33 "github.com/status-im/status-go/services/wallet/router" 34 "github.com/status-im/status-go/services/wallet/router/fees" 35 "github.com/status-im/status-go/services/wallet/router/pathprocessor" 36 "github.com/status-im/status-go/services/wallet/thirdparty" 37 "github.com/status-im/status-go/services/wallet/token" 38 "github.com/status-im/status-go/services/wallet/transfer" 39 "github.com/status-im/status-go/services/wallet/walletconnect" 40 "github.com/status-im/status-go/transactions" 41 ) 42 43 func NewAPI(s *Service) *API { 44 rpcClient := s.GetRPCClient() 45 transactor := s.GetTransactor() 46 tokenManager := s.GetTokenManager() 47 ensService := s.GetEnsService() 48 stickersService := s.GetStickersService() 49 featureFlags := s.FeatureFlags() 50 51 router := router.NewRouter(rpcClient, transactor, tokenManager, s.GetMarketManager(), s.GetCollectiblesService(), 52 s.GetCollectiblesManager(), ensService, stickersService) 53 54 transfer := pathprocessor.NewTransferProcessor(rpcClient, transactor) 55 router.AddPathProcessor(transfer) 56 57 erc721Transfer := pathprocessor.NewERC721Processor(rpcClient, transactor) 58 router.AddPathProcessor(erc721Transfer) 59 60 erc1155Transfer := pathprocessor.NewERC1155Processor(rpcClient, transactor) 61 router.AddPathProcessor(erc1155Transfer) 62 63 hop := pathprocessor.NewHopBridgeProcessor(rpcClient, transactor, tokenManager, rpcClient.NetworkManager) 64 router.AddPathProcessor(hop) 65 66 if featureFlags.EnableCelerBridge { 67 // TODO: Celar Bridge is out of scope for 2.30, check it thoroughly once we decide to include it again 68 cbridge := pathprocessor.NewCelerBridgeProcessor(rpcClient, transactor, tokenManager) 69 router.AddPathProcessor(cbridge) 70 } 71 72 paraswap := pathprocessor.NewSwapParaswapProcessor(rpcClient, transactor, tokenManager) 73 router.AddPathProcessor(paraswap) 74 75 ensRegister := pathprocessor.NewENSRegisterProcessor(rpcClient, transactor, ensService) 76 router.AddPathProcessor(ensRegister) 77 78 ensRelease := pathprocessor.NewENSReleaseProcessor(rpcClient, transactor, ensService) 79 router.AddPathProcessor(ensRelease) 80 81 ensPublicKey := pathprocessor.NewENSPublicKeyProcessor(rpcClient, transactor, ensService) 82 router.AddPathProcessor(ensPublicKey) 83 84 buyStickers := pathprocessor.NewStickersBuyProcessor(rpcClient, transactor, stickersService) 85 router.AddPathProcessor(buyStickers) 86 87 return &API{s, s.reader, router} 88 } 89 90 // API is class with methods available over RPC. 91 type API struct { 92 s *Service 93 reader *Reader 94 router *router.Router 95 } 96 97 func (api *API) StartWallet(ctx context.Context) error { 98 return api.reader.Start() 99 } 100 101 func (api *API) StopWallet(ctx context.Context) error { 102 api.router.Stop() 103 return api.s.Stop() 104 } 105 106 func (api *API) GetPairingsJSONFileContent() ([]byte, error) { 107 return api.s.keycardPairings.GetPairingsJSONFileContent() 108 } 109 110 func (api *API) SetPairingsJSONFileContent(content []byte) error { 111 return api.s.keycardPairings.SetPairingsJSONFileContent(content) 112 } 113 114 // Used by mobile 115 func (api *API) GetWalletToken(ctx context.Context, addresses []common.Address) (map[common.Address][]token.StorageToken, error) { 116 currency, err := api.s.accountsDB.GetCurrency() 117 if err != nil { 118 return nil, err 119 } 120 121 activeNetworks, err := api.s.rpcClient.NetworkManager.GetActiveNetworks() 122 if err != nil { 123 return nil, err 124 } 125 126 chainIDs := wcommon.NetworksToChainIDs(activeNetworks) 127 clients, err := api.s.rpcClient.EthClients(chainIDs) 128 if err != nil { 129 return nil, err 130 } 131 132 return api.reader.GetWalletToken(ctx, clients, addresses, currency) 133 } 134 135 // GetBalancesByChain return a map with key as chain id and value as map of account address and map of token address and balance 136 // [chainID][account][token]balance 137 func (api *API) GetBalancesByChain(ctx context.Context, chainIDs []uint64, addresses, tokens []common.Address) (map[uint64]map[common.Address]map[common.Address]*hexutil.Big, error) { 138 clients, err := api.s.rpcClient.EthClients(chainIDs) 139 if err != nil { 140 return nil, err 141 } 142 143 return api.s.tokenManager.GetBalancesByChain(ctx, clients, addresses, tokens) 144 } 145 146 func (api *API) FetchOrGetCachedWalletBalances(ctx context.Context, addresses []common.Address) (map[common.Address][]token.StorageToken, error) { 147 activeNetworks, err := api.s.rpcClient.NetworkManager.GetActiveNetworks() 148 if err != nil { 149 return nil, err 150 } 151 152 chainIDs := wcommon.NetworksToChainIDs(activeNetworks) 153 clients, err := api.s.rpcClient.EthClients(chainIDs) 154 if err != nil { 155 return nil, err 156 } 157 158 return api.reader.FetchOrGetCachedWalletBalances(ctx, clients, addresses, false) 159 } 160 161 type DerivedAddress struct { 162 Address common.Address `json:"address"` 163 PublicKey types.HexBytes `json:"public-key,omitempty"` 164 Path string `json:"path"` 165 HasActivity bool `json:"hasActivity"` 166 AlreadyCreated bool `json:"alreadyCreated"` 167 } 168 169 // @deprecated 170 func (api *API) CheckRecentHistory(ctx context.Context, addresses []common.Address) error { 171 return api.s.transferController.CheckRecentHistory([]uint64{api.s.rpcClient.UpstreamChainID}, addresses) 172 } 173 174 // @deprecated 175 func (api *API) CheckRecentHistoryForChainIDs(ctx context.Context, chainIDs []uint64, addresses []common.Address) error { 176 return api.s.transferController.CheckRecentHistory(chainIDs, addresses) 177 } 178 179 func hexBigToBN(hexBig *hexutil.Big) *big.Int { 180 var bN *big.Int 181 if hexBig != nil { 182 bN = hexBig.ToInt() 183 } 184 return bN 185 } 186 187 // @deprecated 188 // GetTransfersByAddress returns transfers for a single address 189 func (api *API) GetTransfersByAddress(ctx context.Context, address common.Address, toBlock, limit *hexutil.Big, fetchMore bool) ([]transfer.View, error) { 190 log.Debug("[WalletAPI:: GetTransfersByAddress] get transfers for an address", "address", address) 191 var intLimit = int64(1) 192 if limit != nil { 193 intLimit = limit.ToInt().Int64() 194 } 195 return api.s.transferController.GetTransfersByAddress(ctx, api.s.rpcClient.UpstreamChainID, address, hexBigToBN(toBlock), intLimit, fetchMore) 196 } 197 198 // @deprecated 199 // LoadTransferByHash loads transfer to the database 200 // Only used by status-mobile 201 func (api *API) LoadTransferByHash(ctx context.Context, address common.Address, hash common.Hash) error { 202 log.Debug("[WalletAPI:: LoadTransferByHash] get transfer by hash", "address", address, "hash", hash) 203 return api.s.transferController.LoadTransferByHash(ctx, api.s.rpcClient, address, hash) 204 } 205 206 // @deprecated 207 func (api *API) GetTransfersByAddressAndChainID(ctx context.Context, chainID uint64, address common.Address, toBlock, limit *hexutil.Big, fetchMore bool) ([]transfer.View, error) { 208 log.Debug("[WalletAPI:: GetTransfersByAddressAndChainIDs] get transfers for an address", "address", address) 209 return api.s.transferController.GetTransfersByAddress(ctx, chainID, address, hexBigToBN(toBlock), limit.ToInt().Int64(), fetchMore) 210 } 211 212 // @deprecated 213 func (api *API) GetTransfersForIdentities(ctx context.Context, identities []transfer.TransactionIdentity) ([]transfer.View, error) { 214 log.Debug("wallet.api.GetTransfersForIdentities", "identities.len", len(identities)) 215 216 return api.s.transferController.GetTransfersForIdentities(ctx, identities) 217 } 218 219 func (api *API) FetchDecodedTxData(ctx context.Context, data string) (*thirdparty.DataParsed, error) { 220 log.Debug("[Wallet: FetchDecodedTxData]") 221 222 return api.s.decoder.Decode(data) 223 } 224 225 // GetBalanceHistory retrieves token balance history for token identity on multiple chains 226 func (api *API) GetBalanceHistory(ctx context.Context, chainIDs []uint64, addresses []common.Address, tokenSymbol string, currencySymbol string, timeInterval history.TimeInterval) ([]*history.ValuePoint, error) { 227 log.Debug("wallet.api.GetBalanceHistory", "chainIDs", chainIDs, "address", addresses, "tokenSymbol", tokenSymbol, "currencySymbol", currencySymbol, "timeInterval", timeInterval) 228 229 var fromTimestamp uint64 230 now := uint64(time.Now().UTC().Unix()) 231 switch timeInterval { 232 case history.BalanceHistoryAllTime: 233 fromTimestamp = 0 234 case history.BalanceHistory1Year: 235 fallthrough 236 case history.BalanceHistory6Months: 237 fallthrough 238 case history.BalanceHistory1Month: 239 fallthrough 240 case history.BalanceHistory7Days: 241 fromTimestamp = now - history.TimeIntervalDurationSecs(timeInterval) 242 default: 243 return nil, fmt.Errorf("unknown time interval: %v", timeInterval) 244 } 245 246 return api.GetBalanceHistoryRange(ctx, chainIDs, addresses, tokenSymbol, currencySymbol, fromTimestamp, now) 247 } 248 249 // GetBalanceHistoryRange retrieves token balance history for token identity on multiple chains for a time range 250 // 'toTimestamp' is ignored for now, but will be used in the future to limit the range of the history 251 func (api *API) GetBalanceHistoryRange(ctx context.Context, chainIDs []uint64, addresses []common.Address, tokenSymbol string, currencySymbol string, fromTimestamp uint64, _ uint64) ([]*history.ValuePoint, error) { 252 log.Debug("wallet.api.GetBalanceHistoryRange", "chainIDs", chainIDs, "address", addresses, "tokenSymbol", tokenSymbol, "currencySymbol", currencySymbol, "fromTimestamp", fromTimestamp) 253 return api.s.history.GetBalanceHistory(ctx, chainIDs, addresses, tokenSymbol, currencySymbol, fromTimestamp) 254 } 255 256 func (api *API) GetTokenList(ctx context.Context) (*token.ListWrapper, error) { 257 log.Debug("call to get token list") 258 rst := api.s.tokenManager.GetList() 259 log.Debug("result from token list", "len", len(rst.Data)) 260 return rst, nil 261 } 262 263 // @deprecated 264 func (api *API) GetTokens(ctx context.Context, chainID uint64) ([]*token.Token, error) { 265 log.Debug("call to get tokens") 266 rst, err := api.s.tokenManager.GetTokens(chainID) 267 log.Debug("result from token store", "len", len(rst)) 268 return rst, err 269 } 270 271 // @deprecated 272 func (api *API) GetCustomTokens(ctx context.Context) ([]*token.Token, error) { 273 log.Debug("call to get custom tokens") 274 rst, err := api.s.tokenManager.GetCustoms(true) 275 log.Debug("result from database for custom tokens", "len", len(rst)) 276 return rst, err 277 } 278 279 func (api *API) DiscoverToken(ctx context.Context, chainID uint64, address common.Address) (*token.Token, error) { 280 log.Debug("call to get discover token") 281 token, err := api.s.tokenManager.DiscoverToken(ctx, chainID, address) 282 return token, err 283 } 284 285 func (api *API) AddCustomToken(ctx context.Context, token token.Token) error { 286 log.Debug("call to create or edit custom token") 287 if token.ChainID == 0 { 288 token.ChainID = api.s.rpcClient.UpstreamChainID 289 } 290 err := api.s.tokenManager.UpsertCustom(token) 291 log.Debug("result from database for create or edit custom token", "err", err) 292 return err 293 } 294 295 // @deprecated 296 func (api *API) DeleteCustomToken(ctx context.Context, address common.Address) error { 297 log.Debug("call to remove custom token") 298 err := api.s.tokenManager.DeleteCustom(api.s.rpcClient.UpstreamChainID, address) 299 log.Debug("result from database for remove custom token", "err", err) 300 return err 301 } 302 303 func (api *API) DeleteCustomTokenByChainID(ctx context.Context, chainID uint64, address common.Address) error { 304 log.Debug("call to remove custom token") 305 err := api.s.tokenManager.DeleteCustom(chainID, address) 306 log.Debug("result from database for remove custom token", "err", err) 307 return err 308 } 309 310 // @deprecated 311 // Not used by status-desktop anymore 312 func (api *API) GetPendingTransactions(ctx context.Context) ([]*transactions.PendingTransaction, error) { 313 log.Debug("wallet.api.GetPendingTransactions") 314 rst, err := api.s.pendingTxManager.GetAllPending() 315 log.Debug("wallet.api.GetPendingTransactions RESULT", "len", len(rst)) 316 return rst, err 317 } 318 319 // @deprecated 320 // Not used by status-desktop anymore 321 func (api *API) GetPendingTransactionsForIdentities(ctx context.Context, identities []transfer.TransactionIdentity) ( 322 result []*transactions.PendingTransaction, err error) { 323 324 log.Debug("wallet.api.GetPendingTransactionsForIdentities") 325 326 result = make([]*transactions.PendingTransaction, 0, len(identities)) 327 var pt *transactions.PendingTransaction 328 for _, identity := range identities { 329 pt, err = api.s.pendingTxManager.GetPendingEntry(identity.ChainID, identity.Hash) 330 result = append(result, pt) 331 } 332 333 log.Debug("wallet.api.GetPendingTransactionsForIdentities RES", "len", len(result)) 334 return 335 } 336 337 // @deprecated 338 // TODO - #11861: Remove this and replace with EventPendingTransactionStatusChanged event and Delete to confirm the transaction where it is needed 339 func (api *API) WatchTransactionByChainID(ctx context.Context, chainID uint64, transactionHash common.Hash) (err error) { 340 log.Debug("wallet.api.WatchTransactionByChainID", "chainID", chainID, "transactionHash", transactionHash) 341 defer func() { 342 log.Debug("wallet.api.WatchTransactionByChainID return", "err", err, "chainID", chainID, "transactionHash", transactionHash) 343 }() 344 345 return api.s.transactionManager.WatchTransaction(ctx, chainID, transactionHash) 346 } 347 348 func (api *API) GetCryptoOnRamps(ctx context.Context) ([]onramp.CryptoOnRamp, error) { 349 log.Debug("call to GetCryptoOnRamps") 350 return api.s.cryptoOnRampManager.GetProviders(ctx) 351 } 352 353 func (api *API) GetCryptoOnRampURL(ctx context.Context, providerID string, parameters onramp.Parameters) (string, error) { 354 log.Debug("call to GetCryptoOnRampURL") 355 return api.s.cryptoOnRampManager.GetURL(ctx, providerID, parameters) 356 } 357 358 /* 359 Collectibles API Start 360 */ 361 362 func (api *API) FetchCachedBalancesByOwnerAndContractAddress(ctx context.Context, chainID wcommon.ChainID, ownerAddress common.Address, contractAddresses []common.Address) (thirdparty.TokenBalancesPerContractAddress, error) { 363 log.Debug("call to FetchCachedBalancesByOwnerAndContractAddress") 364 365 return api.s.collectiblesManager.FetchCachedBalancesByOwnerAndContractAddress(ctx, chainID, ownerAddress, contractAddresses) 366 } 367 368 func (api *API) FetchBalancesByOwnerAndContractAddress(ctx context.Context, chainID wcommon.ChainID, ownerAddress common.Address, contractAddresses []common.Address) (thirdparty.TokenBalancesPerContractAddress, error) { 369 log.Debug("call to FetchBalancesByOwnerAndContractAddress") 370 371 return api.s.collectiblesManager.FetchBalancesByOwnerAndContractAddress(ctx, chainID, ownerAddress, contractAddresses) 372 } 373 374 func (api *API) GetCollectibleOwnership(id thirdparty.CollectibleUniqueID) ([]thirdparty.AccountBalance, error) { 375 return api.s.collectiblesManager.GetCollectibleOwnership(id) 376 } 377 378 func (api *API) RefetchOwnedCollectibles() error { 379 log.Debug("wallet.api.RefetchOwnedCollectibles") 380 381 api.s.collectibles.RefetchOwnedCollectibles() 382 return nil 383 } 384 385 func (api *API) GetOwnedCollectiblesAsync(requestID int32, chainIDs []wcommon.ChainID, addresses []common.Address, filter collectibles.Filter, offset int, limit int, dataType collectibles.CollectibleDataType, fetchCriteria collectibles.FetchCriteria) error { 386 log.Debug("wallet.api.GetOwnedCollectiblesAsync", "requestID", requestID, "chainIDs.count", len(chainIDs), "addr.count", len(addresses), "offset", offset, "limit", limit, "dataType", dataType, "fetchCriteria", fetchCriteria) 387 388 api.s.collectibles.GetOwnedCollectiblesAsync(requestID, chainIDs, addresses, filter, offset, limit, dataType, fetchCriteria) 389 return nil 390 } 391 392 func (api *API) GetCollectiblesByUniqueIDAsync(requestID int32, uniqueIDs []thirdparty.CollectibleUniqueID, dataType collectibles.CollectibleDataType) error { 393 log.Debug("wallet.api.GetCollectiblesByUniqueIDAsync", "requestID", requestID, "uniqueIDs.count", len(uniqueIDs), "dataType", dataType) 394 395 api.s.collectibles.GetCollectiblesByUniqueIDAsync(requestID, uniqueIDs, dataType) 396 return nil 397 } 398 399 func (api *API) FetchCollectionSocialsAsync(contractID thirdparty.ContractID) error { 400 log.Debug("wallet.api.FetchCollectionSocialsAsync", "contractID", contractID) 401 402 return api.s.collectiblesManager.FetchCollectionSocialsAsync(contractID) 403 } 404 405 func (api *API) GetCollectibleOwnersByContractAddress(ctx context.Context, chainID wcommon.ChainID, contractAddress common.Address) (*thirdparty.CollectibleContractOwnership, error) { 406 log.Debug("call to GetCollectibleOwnersByContractAddress") 407 return api.s.collectiblesManager.FetchCollectibleOwnersByContractAddress(ctx, chainID, contractAddress) 408 } 409 410 func (api *API) FetchCollectibleOwnersByContractAddress(ctx context.Context, chainID wcommon.ChainID, contractAddress common.Address) (*thirdparty.CollectibleContractOwnership, error) { 411 log.Debug("call to FetchCollectibleOwnersByContractAddress") 412 return api.s.collectiblesManager.FetchCollectibleOwnersByContractAddress(ctx, chainID, contractAddress) 413 } 414 415 func (api *API) SearchCollectibles(ctx context.Context, chainID wcommon.ChainID, text string, cursor string, limit int, providerID string) (*thirdparty.FullCollectibleDataContainer, error) { 416 log.Debug("call to SearchCollectibles") 417 return api.s.collectiblesManager.SearchCollectibles(ctx, chainID, text, cursor, limit, providerID) 418 } 419 420 func (api *API) SearchCollections(ctx context.Context, chainID wcommon.ChainID, text string, cursor string, limit int, providerID string) (*thirdparty.CollectionDataContainer, error) { 421 log.Debug("call to SearchCollections") 422 return api.s.collectiblesManager.SearchCollections(ctx, chainID, text, cursor, limit, providerID) 423 } 424 425 /* 426 Collectibles API End 427 */ 428 429 func (api *API) AddEthereumChain(ctx context.Context, network params.Network) error { 430 log.Debug("call to AddEthereumChain") 431 return api.s.rpcClient.NetworkManager.Upsert(&network) 432 } 433 434 func (api *API) DeleteEthereumChain(ctx context.Context, chainID uint64) error { 435 log.Debug("call to DeleteEthereumChain") 436 return api.s.rpcClient.NetworkManager.Delete(chainID) 437 } 438 439 func (api *API) GetEthereumChains(ctx context.Context) ([]*network.CombinedNetwork, error) { 440 log.Debug("call to GetEthereumChains") 441 return api.s.rpcClient.NetworkManager.GetCombinedNetworks() 442 } 443 444 // @deprecated 445 func (api *API) FetchPrices(ctx context.Context, symbols []string, currencies []string) (map[string]map[string]float64, error) { 446 log.Debug("call to FetchPrices") 447 return api.s.marketManager.FetchPrices(symbols, currencies) 448 } 449 450 // @deprecated 451 func (api *API) FetchMarketValues(ctx context.Context, symbols []string, currency string) (map[string]thirdparty.TokenMarketValues, error) { 452 log.Debug("call to FetchMarketValues") 453 return api.s.marketManager.FetchTokenMarketValues(symbols, currency) 454 } 455 456 func (api *API) GetHourlyMarketValues(ctx context.Context, symbol string, currency string, limit int, aggregate int) ([]thirdparty.HistoricalPrice, error) { 457 log.Debug("call to GetHourlyMarketValues") 458 return api.s.marketManager.FetchHistoricalHourlyPrices(symbol, currency, limit, aggregate) 459 } 460 461 func (api *API) GetDailyMarketValues(ctx context.Context, symbol string, currency string, limit int, allData bool, aggregate int) ([]thirdparty.HistoricalPrice, error) { 462 log.Debug("call to GetDailyMarketValues") 463 return api.s.marketManager.FetchHistoricalDailyPrices(symbol, currency, limit, allData, aggregate) 464 } 465 466 // @deprecated 467 func (api *API) FetchTokenDetails(ctx context.Context, symbols []string) (map[string]thirdparty.TokenDetails, error) { 468 log.Debug("call to FetchTokenDetails") 469 return api.s.marketManager.FetchTokenDetails(symbols) 470 } 471 472 func (api *API) GetSuggestedFees(ctx context.Context, chainID uint64) (*fees.SuggestedFeesGwei, error) { 473 log.Debug("call to GetSuggestedFees") 474 return api.router.GetFeesManager().SuggestedFeesGwei(ctx, chainID) 475 } 476 477 func (api *API) GetEstimatedLatestBlockNumber(ctx context.Context, chainID uint64) (uint64, error) { 478 log.Debug("call to GetEstimatedLatestBlockNumber, chainID:", chainID) 479 return api.s.blockChainState.GetEstimatedLatestBlockNumber(ctx, chainID) 480 } 481 482 func (api *API) GetTransactionEstimatedTime(ctx context.Context, chainID uint64, maxFeePerGas *big.Float) (fees.TransactionEstimation, error) { 483 log.Debug("call to getTransactionEstimatedTime") 484 return api.router.GetFeesManager().TransactionEstimatedTime(ctx, chainID, gweiToWei(maxFeePerGas)), nil 485 } 486 487 func gweiToWei(val *big.Float) *big.Int { 488 res, _ := new(big.Float).Mul(val, big.NewFloat(1000000000)).Int(nil) 489 return res 490 } 491 492 func (api *API) GetSuggestedRoutes(ctx context.Context, input *requests.RouteInputParams) (*router.SuggestedRoutes, error) { 493 log.Debug("call to GetSuggestedRoutes") 494 495 return api.router.SuggestedRoutes(ctx, input) 496 } 497 498 func (api *API) GetSuggestedRoutesAsync(ctx context.Context, input *requests.RouteInputParams) { 499 log.Debug("call to GetSuggestedRoutesAsync") 500 501 api.router.SuggestedRoutesAsync(input) 502 } 503 504 func (api *API) StopSuggestedRoutesAsyncCalculation(ctx context.Context) { 505 log.Debug("call to StopSuggestedRoutesAsyncCalculation") 506 507 api.router.StopSuggestedRoutesAsyncCalculation() 508 } 509 510 func (api *API) StopSuggestedRoutesCalculation(ctx context.Context) { 511 log.Debug("call to StopSuggestedRoutesCalculation") 512 513 api.router.StopSuggestedRoutesCalculation() 514 } 515 516 // Generates addresses for the provided paths, response doesn't include `HasActivity` value (if you need it check `GetAddressDetails` function) 517 func (api *API) GetDerivedAddresses(ctx context.Context, password string, derivedFrom string, paths []string) ([]*DerivedAddress, error) { 518 info, err := api.s.gethManager.AccountsGenerator().LoadAccount(derivedFrom, password) 519 if err != nil { 520 return nil, err 521 } 522 523 return api.getDerivedAddresses(info.ID, paths) 524 } 525 526 // Generates addresses for the provided paths derived from the provided mnemonic, response doesn't include `HasActivity` value (if you need it check `GetAddressDetails` function) 527 func (api *API) GetDerivedAddressesForMnemonic(ctx context.Context, mnemonic string, paths []string) ([]*DerivedAddress, error) { 528 mnemonicNoExtraSpaces := strings.Join(strings.Fields(mnemonic), " ") 529 530 info, err := api.s.gethManager.AccountsGenerator().ImportMnemonic(mnemonicNoExtraSpaces, "") 531 if err != nil { 532 return nil, err 533 } 534 535 return api.getDerivedAddresses(info.ID, paths) 536 } 537 538 // Generates addresses for the provided paths, response doesn't include `HasActivity` value (if you need it check `GetAddressDetails` function) 539 func (api *API) getDerivedAddresses(id string, paths []string) ([]*DerivedAddress, error) { 540 addedAccounts, err := api.s.accountsDB.GetActiveAccounts() 541 if err != nil { 542 return nil, err 543 } 544 545 info, err := api.s.gethManager.AccountsGenerator().DeriveAddresses(id, paths) 546 if err != nil { 547 return nil, err 548 } 549 550 derivedAddresses := make([]*DerivedAddress, 0) 551 for accPath, acc := range info { 552 553 derivedAddress := &DerivedAddress{ 554 Address: common.HexToAddress(acc.Address), 555 PublicKey: types.Hex2Bytes(acc.PublicKey), 556 Path: accPath, 557 } 558 559 for _, account := range addedAccounts { 560 if types.Address(derivedAddress.Address) == account.Address { 561 derivedAddress.AlreadyCreated = true 562 break 563 } 564 } 565 566 derivedAddresses = append(derivedAddresses, derivedAddress) 567 } 568 569 return derivedAddresses, nil 570 } 571 572 func (api *API) AddressExists(ctx context.Context, address types.Address) (bool, error) { 573 return api.s.accountsDB.AddressExists(address) 574 } 575 576 // AddressDetails returns details for passed params (passed address, chains to check, timeout for the call to complete) 577 // if chainIDs is empty, it will use all active chains 578 // if timeout is zero, it will wait until the call completes 579 // response doesn't include derivation path 580 func (api *API) AddressDetails(ctx context.Context, params *requests.AddressDetails) (*DerivedAddress, error) { 581 if err := params.Validate(); err != nil { 582 return nil, err 583 } 584 585 result := &DerivedAddress{ 586 Address: common.HexToAddress(params.Address), 587 } 588 addressExists, err := api.s.accountsDB.AddressExists(types.Address(result.Address)) 589 if err != nil { 590 return result, err 591 } 592 593 result.AlreadyCreated = addressExists 594 595 chainIDs := params.ChainIDs 596 if len(chainIDs) == 0 { 597 activeNetworks, err := api.s.rpcClient.NetworkManager.GetActiveNetworks() 598 if err != nil { 599 return nil, err 600 } 601 602 chainIDs = wcommon.NetworksToChainIDs(activeNetworks) 603 } 604 605 clients, err := api.s.rpcClient.EthClients(chainIDs) 606 if err != nil { 607 return nil, err 608 } 609 610 if params.TimeoutInMilliseconds > 0 { 611 var cancel context.CancelFunc 612 ctx, cancel = context.WithTimeout(ctx, time.Duration(params.TimeoutInMilliseconds)*time.Millisecond) 613 defer cancel() 614 } 615 616 for _, client := range clients { 617 balance, err := api.s.tokenManager.GetChainBalance(ctx, client, result.Address) 618 if err != nil { 619 if err != nil && errors.Is(err, context.DeadlineExceeded) { 620 return result, nil 621 } 622 return result, err 623 } 624 625 result.HasActivity = balance.Cmp(big.NewInt(0)) != 0 626 if result.HasActivity { 627 break 628 } 629 } 630 631 return result, nil 632 } 633 634 // @deprecated replaced by AddressDetails 635 // GetAddressDetails returns details for the passed address (response doesn't include derivation path) 636 func (api *API) GetAddressDetails(ctx context.Context, chainID uint64, address string) (*DerivedAddress, error) { 637 result := &DerivedAddress{ 638 Address: common.HexToAddress(address), 639 } 640 addressExists, err := api.s.accountsDB.AddressExists(types.Address(result.Address)) 641 if err != nil { 642 return result, err 643 } 644 645 result.AlreadyCreated = addressExists 646 647 chainClient, err := api.s.rpcClient.EthClient(chainID) 648 if err != nil { 649 return result, err 650 } 651 652 balance, err := api.s.tokenManager.GetChainBalance(ctx, chainClient, result.Address) 653 if err != nil { 654 return result, err 655 } 656 657 result.HasActivity = balance.Cmp(big.NewInt(0)) != 0 658 return result, nil 659 } 660 661 func (api *API) SignMessage(ctx context.Context, message types.HexBytes, address common.Address, password string) (string, error) { 662 log.Debug("[WalletAPI::SignMessage]", "message", message, "address", address) 663 664 selectedAccount, err := api.s.gethManager.VerifyAccountPassword(api.s.Config().KeyStoreDir, address.Hex(), password) 665 if err != nil { 666 return "", err 667 } 668 669 return api.s.transactionManager.SignMessage(message, selectedAccount) 670 } 671 672 func (api *API) BuildTransaction(ctx context.Context, chainID uint64, sendTxArgsJSON string) (response *transfer.TxResponse, err error) { 673 log.Debug("[WalletAPI::BuildTransaction]", "chainID", chainID, "sendTxArgsJSON", sendTxArgsJSON) 674 var params transactions.SendTxArgs 675 err = json.Unmarshal([]byte(sendTxArgsJSON), ¶ms) 676 if err != nil { 677 return nil, err 678 } 679 return api.s.transactionManager.BuildTransaction(chainID, params) 680 } 681 682 func (api *API) BuildRawTransaction(ctx context.Context, chainID uint64, sendTxArgsJSON string, signature string) (response *transfer.TxResponse, err error) { 683 log.Debug("[WalletAPI::BuildRawTransaction]", "chainID", chainID, "sendTxArgsJSON", sendTxArgsJSON, "signature", signature) 684 685 sig, err := hex.DecodeString(signature) 686 if err != nil { 687 return nil, err 688 } 689 690 var params transactions.SendTxArgs 691 err = json.Unmarshal([]byte(sendTxArgsJSON), ¶ms) 692 if err != nil { 693 return nil, err 694 } 695 696 return api.s.transactionManager.BuildRawTransaction(chainID, params, sig) 697 } 698 699 func (api *API) SendTransactionWithSignature(ctx context.Context, chainID uint64, txType transactions.PendingTrxType, 700 sendTxArgsJSON string, signature string) (hash types.Hash, err error) { 701 log.Debug("[WalletAPI::SendTransactionWithSignature]", "chainID", chainID, "txType", txType, "sendTxArgsJSON", sendTxArgsJSON, "signature", signature) 702 sig, err := hex.DecodeString(signature) 703 if err != nil { 704 return hash, err 705 } 706 707 var params transactions.SendTxArgs 708 err = json.Unmarshal([]byte(sendTxArgsJSON), ¶ms) 709 if err != nil { 710 return hash, err 711 } 712 return api.s.transactionManager.SendTransactionWithSignature(chainID, params, sig) 713 } 714 715 func (api *API) CreateMultiTransaction(ctx context.Context, multiTransactionCommand *transfer.MultiTransactionCommand, data []*pathprocessor.MultipathProcessorTxArgs, password string) (*transfer.MultiTransactionCommandResult, error) { 716 log.Debug("[WalletAPI:: CreateMultiTransaction] create multi transaction") 717 718 cmd, err := api.s.transactionManager.CreateMultiTransactionFromCommand(multiTransactionCommand, data) 719 if err != nil { 720 return nil, err 721 } 722 723 if password != "" { 724 selectedAccount, err := api.getVerifiedWalletAccount(multiTransactionCommand.FromAddress.Hex(), password) 725 if err != nil { 726 return nil, err 727 } 728 729 cmdRes, err := api.s.transactionManager.SendTransactions(ctx, cmd, data, api.router.GetPathProcessors(), selectedAccount) 730 if err != nil { 731 return nil, err 732 } 733 734 _, err = api.s.transactionManager.InsertMultiTransaction(cmd) 735 if err != nil { 736 log.Error("Failed to save multi transaction", "error", err) // not critical 737 } 738 739 return cmdRes, nil 740 } 741 742 return nil, api.s.transactionManager.SendTransactionForSigningToKeycard(ctx, cmd, data, api.router.GetPathProcessors()) 743 } 744 745 func (api *API) ProceedWithTransactionsSignatures(ctx context.Context, signatures map[string]transfer.SignatureDetails) (*transfer.MultiTransactionCommandResult, error) { 746 log.Debug("[WalletAPI:: ProceedWithTransactionsSignatures] sign with signatures and send multi transaction") 747 return api.s.transactionManager.ProceedWithTransactionsSignatures(ctx, signatures) 748 } 749 750 func (api *API) GetMultiTransactions(ctx context.Context, transactionIDs []wcommon.MultiTransactionIDType) ([]*transfer.MultiTransaction, error) { 751 log.Debug("wallet.api.GetMultiTransactions", "IDs.len", len(transactionIDs)) 752 return api.s.transactionManager.GetMultiTransactions(ctx, transactionIDs) 753 } 754 755 func (api *API) GetCachedCurrencyFormats() (currency.FormatPerSymbol, error) { 756 log.Debug("call to GetCachedCurrencyFormats") 757 return api.s.currency.GetCachedCurrencyFormats() 758 } 759 760 func (api *API) FetchAllCurrencyFormats() (currency.FormatPerSymbol, error) { 761 log.Debug("call to FetchAllCurrencyFormats") 762 return api.s.currency.FetchAllCurrencyFormats() 763 } 764 765 // @deprecated replaced by session APIs; see #12120 766 func (api *API) FilterActivityAsync(requestID int32, addresses []common.Address, chainIDs []wcommon.ChainID, filter activity.Filter, offset int, limit int) error { 767 log.Debug("wallet.api.FilterActivityAsync", "requestID", requestID, "addr.count", len(addresses), "chainIDs.count", len(chainIDs), "offset", offset, "limit", limit) 768 769 api.s.activity.FilterActivityAsync(requestID, addresses, chainIDs, filter, offset, limit) 770 return nil 771 } 772 773 // @deprecated replaced by session APIs; see #12120 774 func (api *API) CancelActivityFilterTask(requestID int32) error { 775 log.Debug("wallet.api.CancelActivityFilterTask", "requestID", requestID) 776 777 api.s.activity.CancelFilterTask(requestID) 778 return nil 779 } 780 781 func (api *API) StartActivityFilterSession(addresses []common.Address, chainIDs []wcommon.ChainID, filter activity.Filter, firstPageCount int) (activity.SessionID, error) { 782 log.Debug("wallet.api.StartActivityFilterSession", "addr.count", len(addresses), "chainIDs.count", len(chainIDs), "firstPageCount", firstPageCount) 783 784 return api.s.activity.StartFilterSession(addresses, chainIDs, filter, firstPageCount), nil 785 } 786 787 func (api *API) UpdateActivityFilterForSession(sessionID activity.SessionID, filter activity.Filter, firstPageCount int) error { 788 log.Debug("wallet.api.UpdateActivityFilterForSession", "sessionID", sessionID, "firstPageCount", firstPageCount) 789 790 return api.s.activity.UpdateFilterForSession(sessionID, filter, firstPageCount) 791 } 792 793 func (api *API) ResetActivityFilterSession(id activity.SessionID, firstPageCount int) error { 794 log.Debug("wallet.api.ResetActivityFilterSession", "id", id, "firstPageCount", firstPageCount) 795 796 return api.s.activity.ResetFilterSession(id, firstPageCount) 797 } 798 799 func (api *API) GetMoreForActivityFilterSession(id activity.SessionID, pageCount int) error { 800 log.Debug("wallet.api.GetMoreForActivityFilterSession", "id", id, "pageCount", pageCount) 801 802 return api.s.activity.GetMoreForFilterSession(id, pageCount) 803 } 804 805 func (api *API) StopActivityFilterSession(id activity.SessionID) { 806 log.Debug("wallet.api.StopActivityFilterSession", "id", id) 807 808 api.s.activity.StopFilterSession(id) 809 } 810 811 func (api *API) GetMultiTxDetails(ctx context.Context, multiTxID int) (*activity.EntryDetails, error) { 812 log.Debug("wallet.api.GetMultiTxDetails", "multiTxID", multiTxID) 813 814 return api.s.activity.GetMultiTxDetails(ctx, multiTxID) 815 } 816 817 func (api *API) GetTxDetails(ctx context.Context, id string) (*activity.EntryDetails, error) { 818 log.Debug("wallet.api.GetTxDetails", "id", id) 819 820 return api.s.activity.GetTxDetails(ctx, id) 821 } 822 823 func (api *API) GetRecipientsAsync(requestID int32, chainIDs []wcommon.ChainID, addresses []common.Address, offset int, limit int) (ignored bool, err error) { 824 log.Debug("wallet.api.GetRecipientsAsync", "addresses.len", len(addresses), "chainIDs.len", len(chainIDs), "offset", offset, "limit", limit) 825 826 ignored = api.s.activity.GetRecipientsAsync(requestID, chainIDs, addresses, offset, limit) 827 return ignored, err 828 } 829 830 func (api *API) GetOldestActivityTimestampAsync(requestID int32, addresses []common.Address) error { 831 log.Debug("wallet.api.GetOldestActivityTimestamp", "addresses.len", len(addresses)) 832 833 api.s.activity.GetOldestTimestampAsync(requestID, addresses) 834 return nil 835 } 836 837 func (api *API) GetActivityCollectiblesAsync(requestID int32, chainIDs []wcommon.ChainID, addresses []common.Address, offset int, limit int) error { 838 log.Debug("wallet.api.GetActivityCollectiblesAsync", "addresses.len", len(addresses), "chainIDs.len", len(chainIDs), "offset", offset, "limit", limit) 839 840 api.s.activity.GetActivityCollectiblesAsync(requestID, chainIDs, addresses, offset, limit) 841 842 return nil 843 } 844 845 func (api *API) FetchChainIDForURL(ctx context.Context, rpcURL string) (*big.Int, error) { 846 log.Debug("wallet.api.VerifyURL", "rpcURL", rpcURL) 847 848 rpcClient, err := gethrpc.Dial(rpcURL) 849 if err != nil { 850 return nil, fmt.Errorf("dial upstream server: %s", err) 851 } 852 client := ethclient.NewClient(rpcClient) 853 return client.ChainID(ctx) 854 } 855 856 func (api *API) getVerifiedWalletAccount(address, password string) (*account.SelectedExtKey, error) { 857 exists, err := api.s.accountsDB.AddressExists(types.HexToAddress(address)) 858 if err != nil { 859 log.Error("failed to query db for a given address", "address", address, "error", err) 860 return nil, err 861 } 862 863 if !exists { 864 log.Error("failed to get a selected account", "err", transactions.ErrInvalidTxSender) 865 return nil, transactions.ErrAccountDoesntExist 866 } 867 868 keyStoreDir := api.s.Config().KeyStoreDir 869 key, err := api.s.gethManager.VerifyAccountPassword(keyStoreDir, address, password) 870 if err != nil { 871 log.Error("failed to verify account", "account", address, "error", err) 872 return nil, err 873 } 874 875 return &account.SelectedExtKey{ 876 Address: key.Address, 877 AccountKey: key, 878 }, nil 879 } 880 881 // AddWalletConnectSession adds or updates a session wallet connect session 882 func (api *API) AddWalletConnectSession(ctx context.Context, session_json string) error { 883 log.Debug("wallet.api.AddWalletConnectSession", "rpcURL", len(session_json)) 884 return walletconnect.AddSession(api.s.db, api.s.config.Networks, session_json) 885 } 886 887 // DisconnectWalletConnectSession removes a wallet connect session 888 func (api *API) DisconnectWalletConnectSession(ctx context.Context, topic walletconnect.Topic) error { 889 log.Debug("wallet.api.DisconnectWalletConnectSession", "topic", topic) 890 return walletconnect.DisconnectSession(api.s.db, topic) 891 } 892 893 // GetWalletConnectActiveSessions returns all active wallet connect sessions 894 func (api *API) GetWalletConnectActiveSessions(ctx context.Context, validAtTimestamp int64) ([]walletconnect.DBSession, error) { 895 log.Debug("wallet.api.GetWalletConnectActiveSessions") 896 return walletconnect.GetActiveSessions(api.s.db, validAtTimestamp) 897 } 898 899 // GetWalletConnectDapps returns all active wallet connect dapps 900 // Active dApp are those having active sessions (not expired and not disconnected) 901 func (api *API) GetWalletConnectDapps(ctx context.Context, validAtTimestamp int64, testChains bool) ([]walletconnect.DBDApp, error) { 902 log.Debug("wallet.api.GetWalletConnectDapps", "validAtTimestamp", validAtTimestamp, "testChains", testChains) 903 return walletconnect.GetActiveDapps(api.s.db, validAtTimestamp, testChains) 904 } 905 906 // HashMessageEIP191 is used for hashing dApps requests for "personal_sign" and "eth_sign" 907 // in a safe manner following the EIP-191 version 0x45 for signing on the client side. 908 func (api *API) HashMessageEIP191(ctx context.Context, message types.HexBytes) types.Hash { 909 log.Debug("wallet.api.HashMessageEIP191", "len(data)", len(message)) 910 safeMsg := fmt.Sprintf("\x19Ethereum Signed Message:\n%d%s", len(message), string(message)) 911 return crypto.Keccak256Hash([]byte(safeMsg)) 912 } 913 914 // SignTypedDataV4 dApps use it to execute "eth_signTypedData_v4" requests 915 // the formatted typed data will be prefixed with \x19\x01 based on the EIP-712 916 // @deprecated 917 func (api *API) SignTypedDataV4(typedJson string, address string, password string) (types.HexBytes, error) { 918 log.Debug("wallet.api.SignTypedDataV4", "len(typedJson)", len(typedJson), "address", address, "len(password)", len(password)) 919 920 account, err := api.getVerifiedWalletAccount(address, password) 921 if err != nil { 922 return types.HexBytes{}, err 923 } 924 var typed signercore.TypedData 925 err = json.Unmarshal([]byte(typedJson), &typed) 926 if err != nil { 927 return types.HexBytes{}, err 928 } 929 930 // This is not used down the line but required by the typeddata.SignTypedDataV4 function call 931 chain := new(big.Int).SetUint64(api.s.config.NetworkID) 932 sig, err := typeddata.SignTypedDataV4(typed, account.AccountKey.PrivateKey, chain) 933 if err != nil { 934 return types.HexBytes{}, err 935 } 936 return types.HexBytes(sig), err 937 } 938 939 // SafeSignTypedDataForDApps is used to execute requests for "eth_signTypedData" 940 // if legacy is true else "eth_signTypedData_v4" 941 // the formatted typed data won't be prefixed in case of legacy calls, as the 942 // old dApps implementation expects 943 // the chain is validate for both cases 944 func (api *API) SafeSignTypedDataForDApps(typedJson string, address string, password string, chainID uint64, legacy bool) (types.HexBytes, error) { 945 log.Debug("wallet.api.SafeSignTypedDataForDApps", "len(typedJson)", len(typedJson), "address", address, "len(password)", len(password), "chainID", chainID, "legacy", legacy) 946 947 account, err := api.getVerifiedWalletAccount(address, password) 948 if err != nil { 949 return types.HexBytes{}, err 950 } 951 952 return walletconnect.SafeSignTypedDataForDApps(typedJson, account.AccountKey.PrivateKey, chainID, legacy) 953 } 954 955 func (api *API) RestartWalletReloadTimer(ctx context.Context) error { 956 return api.s.reader.Restart() 957 } 958 959 func (api *API) IsChecksumValidForAddress(address string) (bool, error) { 960 log.Debug("wallet.api.isChecksumValidForAddress", "address", address) 961 return abi_spec.CheckAddressChecksum(address) 962 }