Skip to content

Commit

Permalink
CP-8996 Implement support for DeBank tokens (#1505)
Browse files Browse the repository at this point in the history
Signed-off-by: neven-s <[email protected]>
  • Loading branch information
neven-s authored Aug 21, 2024
1 parent e820cac commit 5b50e4d
Show file tree
Hide file tree
Showing 10 changed files with 296 additions and 76 deletions.
8 changes: 7 additions & 1 deletion packages/core-mobile/app/hooks/earn/useCChainBalance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { TokenWithBalanceEVM } from '@avalabs/vm-module-types'
import { isBitcoinChainId } from 'utils/network/isBitcoinNetwork'
import { isXPChain } from 'utils/network/isAvalancheNetwork'
import { coingeckoInMemoryCache } from 'utils/coingeckoInMemoryCache'
import Logger from 'utils/Logger'
import useCChainNetwork from './useCChainNetwork'

export const useCChainBalance = (): UseQueryResult<
Expand Down Expand Up @@ -44,7 +45,12 @@ export const useCChainBalance = (): UseQueryResult<
network: mapToVmNetwork(network),
storage: coingeckoInMemoryCache
})
return balancesResponse[addressC]?.[network.networkToken.symbol]
const cChainBalance = balancesResponse[addressC]
if (!cChainBalance || 'error' in cChainBalance) {
Logger.error('Failed to fetch c-chain balance', cChainBalance?.error)
return undefined
}
return cChainBalance[network.networkToken.symbol]
}
})
}
10 changes: 8 additions & 2 deletions packages/core-mobile/app/hooks/earn/useIssueDelegation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,16 @@ export const useIssueDelegation = (
storage: coingeckoInMemoryCache
})

const pChainBalance =
balancesResponse[pAddress]?.[network.networkToken.symbol]
const pChainBalanceResponse = balancesResponse[pAddress]
if (!pChainBalanceResponse || 'error' in pChainBalanceResponse) {
return Promise.reject(
`Failed to fetch cChain balance. ${pChainBalanceResponse?.error}`
)
}
const pChainBalance = pChainBalanceResponse[network.networkToken.symbol]
if (
pChainBalance === undefined ||
'error' in pChainBalance ||
!isTokenWithBalancePVM(pChainBalance)
) {
return Promise.reject('invalid balance type.')
Expand Down
15 changes: 13 additions & 2 deletions packages/core-mobile/app/hooks/earn/usePChainBalance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { mapToVmNetwork } from 'vmModule/utils/mapToVmNetwork'
import { TokenWithBalancePVM } from '@avalabs/vm-module-types'
import { coingeckoInMemoryCache } from 'utils/coingeckoInMemoryCache'
import { isTokenWithBalancePVM } from '@avalabs/avalanche-module'
import Logger from 'utils/Logger'

export const usePChainBalance = (): UseQueryResult<
TokenWithBalancePVM | undefined,
Expand All @@ -35,10 +36,20 @@ export const usePChainBalance = (): UseQueryResult<
network: mapToVmNetwork(network),
storage: coingeckoInMemoryCache
})
const pChainBalance =
balancesResponse[addressPVM]?.[network.networkToken.symbol]

const pChainBalanceResponse = balancesResponse[addressPVM]
if (!pChainBalanceResponse || 'error' in pChainBalanceResponse) {
Logger.error(
'Failed to fetch p-chain balance',
pChainBalanceResponse?.error
)
return
}
const pChainBalance = pChainBalanceResponse[network.networkToken.symbol]

if (
pChainBalance === undefined ||
'error' in pChainBalance ||
!isTokenWithBalancePVM(pChainBalance)
) {
return
Expand Down
19 changes: 15 additions & 4 deletions packages/core-mobile/app/services/balance/BalanceService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import SentryWrapper from 'services/sentry/SentryWrapper'
import { Transaction } from '@sentry/types'
import type {
NetworkContractToken,
TokenWithBalance
TokenWithBalance,
Error
} from '@avalabs/vm-module-types'
import ModuleManager from 'vmModule/ModuleManager'
import { mapToVmNetwork } from 'vmModule/utils/mapToVmNetwork'
Expand All @@ -15,7 +16,7 @@ export type BalancesForAccount = {
accountIndex: number
chainId: number
accountAddress: string
tokens: TokenWithBalance[]
tokens: (TokenWithBalance | Error)[]
}

export class BalanceService {
Expand All @@ -36,6 +37,7 @@ export class BalanceService {
.setContext('svc.balance.get_for_account')
.executeAsync(async () => {
const accountAddress = getAddressByNetwork(account, network)

const module = await ModuleManager.loadModuleByNetwork(network)
const balancesResponse = await module.getBalances({
customTokens,
Expand All @@ -44,11 +46,20 @@ export class BalanceService {
network: mapToVmNetwork(network),
storage: coingeckoInMemoryCache
})
const balances = Object.values(balancesResponse[accountAddress] ?? [])
const balances = balancesResponse[accountAddress] ?? {}
if ('error' in balances) {
return {
accountIndex: account.index,
chainId: network.chainId,
tokens: [],
accountAddress
}
}

return {
accountIndex: account.index,
chainId: network.chainId,
tokens: balances,
tokens: Object.values(balances),
accountAddress
}
})
Expand Down
17 changes: 14 additions & 3 deletions packages/core-mobile/app/services/earn/importP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,9 +103,20 @@ const getUnlockedUnstakedAmount = async ({
network: mapToVmNetwork(network),
storage: coingeckoInMemoryCache
})
const pChainBalance =
balancesResponse[addressPVM]?.[network.networkToken.symbol]
if (pChainBalance === undefined || !isTokenWithBalancePVM(pChainBalance)) {
const pChainBalanceResponse = balancesResponse[addressPVM]
if (!pChainBalanceResponse || 'error' in pChainBalanceResponse) {
Logger.error(
'Failed to fetch p-chain balance',
pChainBalanceResponse?.error
)
return
}
const pChainBalance = pChainBalanceResponse[network.networkToken.symbol]
if (
pChainBalance === undefined ||
'error' in pChainBalance ||
!isTokenWithBalancePVM(pChainBalance)
) {
return
}
return pChainBalance.balancePerType.unlockedUnstaked
Expand Down
83 changes: 82 additions & 1 deletion packages/core-mobile/app/services/network/NetworkService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@ import {
BITCOIN_TEST_NETWORK,
ChainId,
Network,
NetworkToken,
NetworkVMType
} from '@avalabs/core-chains-sdk'
import SentryWrapper from 'services/sentry/SentryWrapper'
import { Transaction } from '@sentry/types'
import { avaxSerial } from '@avalabs/avalanchejs'
import { TransactionResponse } from 'ethers'
import { Networks } from 'store/network/types'
import { ChainID, Networks } from 'store/network/types'
import Config from 'react-native-config'
import Logger from 'utils/Logger'
import { DebankNetwork } from 'services/network/types'
import { addIdToPromise, settleAllIdPromises } from '@avalabs/evm-module'
import { getBitcoinProvider, getEvmProvider } from './utils/providerUtils'
import { NETWORK_P, NETWORK_P_TEST, NETWORK_X, NETWORK_X_TEST } from './consts'

Expand All @@ -26,11 +29,13 @@ if (!Config.PROXY_URL)
class NetworkService {
async getNetworks(): Promise<Networks> {
const erc20Networks = await this.fetchERC20Networks()
const deBankNetworks = await this.fetchDeBankNetworks()

delete erc20Networks[ChainId.AVALANCHE_LOCAL_ID]

return {
...erc20Networks,
...deBankNetworks,
[ChainId.BITCOIN]: BITCOIN_NETWORK,
[ChainId.BITCOIN_TESTNET]: BITCOIN_TEST_NETWORK,
[ChainId.AVALANCHE_P]: this.getAvalancheNetworkP(false),
Expand Down Expand Up @@ -120,6 +125,7 @@ class NetworkService {
getAvalancheNetworkX(isDeveloperMode: boolean): Network {
return isDeveloperMode ? NETWORK_X_TEST : NETWORK_X
}

/**
* Returns the provider used by Avalanche X/P/CoreEth chains.
* Using either X or P Network will result in same provider.
Expand All @@ -138,6 +144,81 @@ class NetworkService {
return acc
}, {} as Networks)
}

private async fetchDeBankNetworks(): Promise<Networks> {
const response = await fetch(
`${Config.PROXY_URL}/proxy/debank/v1/chain/list`
)
if (!response.ok) {
throw Error('fetchDeBankNetworks failed: ' + response.statusText)
}
const deBankNetworks: DebankNetwork[] = await response.json()

const networks = deBankNetworks
.filter(network =>
['arb', 'bsc', 'op', 'matic', 'base'].includes(network.id)
)
.reduce(
(acc, network) => {
acc[network.community_id] = {
platformChainId: network.id,
chainId: network.community_id,
chainName: network.name,
logoUri: network.logo_url,
vmName: NetworkVMType.EVM,
vmId: network.id,
isTestnet: false,
networkToken: {} as NetworkToken,
nativeTokenId: network.native_token_id
} as Network & { nativeTokenId: string }
return acc
},
{} as {
[chainId: ChainID]: Network & { nativeTokenId: string }
}
)

//fetch info about native tokens
const promises = Object.keys(networks).map(chainId => {
const network = networks[Number(chainId)]
if (!network) {
return Promise.reject('invalid chain id: ' + chainId)
}
return addIdToPromise(
(async () => {
const tokenResponse = await fetch(
`${Config.PROXY_URL}/proxy/debank/v1/token?chain_id=${chainId}&id=${network.nativeTokenId}`
)
if (!tokenResponse.ok) {
throw Error('Failed to fetch debank/v1/token')
}
return await tokenResponse.json() //as DeBankToken
})(),
chainId
)
})

const nativeTokenInfos = await settleAllIdPromises(promises)
const networksWithToken: Networks = {}
for (const chainId in nativeTokenInfos) {
const nativeTokenInfo = nativeTokenInfos[chainId]
if (!nativeTokenInfo || 'error' in nativeTokenInfo) {
continue
}
const { nativeTokenId, ...networkWithToken } = {
...networks[Number(chainId)],
networkToken: {
symbol: nativeTokenInfo.symbol,
logoUri: nativeTokenInfo.logo_url,
decimals: nativeTokenInfo.decimals,
name: nativeTokenInfo.name
} as NetworkToken
}
networksWithToken[Number(chainId)] = networkWithToken as Network
}

return networksWithToken
}
}

export default new NetworkService()
11 changes: 11 additions & 0 deletions packages/core-mobile/app/services/network/types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Hex } from '@avalabs/vm-module-types'

export type DebankNetwork = {
community_id: number
id: string
is_support_pre_exec: boolean
logo_url: string
name: string
native_token_id: string
wrapped_token_id: Hex
}
38 changes: 23 additions & 15 deletions packages/core-mobile/app/store/balance/listeners.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ import {
setBalances,
setStatus
} from './slice'
import { Balances, QueryStatus } from './types'
import { Balances, LocalTokenWithBalance, QueryStatus } from './types'

/**
* In production:
Expand Down Expand Up @@ -281,26 +281,34 @@ const fetchBalanceForAccounts = async (
}

const { accountIndex, chainId, tokens } = result.value
const balances = {
dataAccurate: true,
accountIndex,
chainId,
tokens: [] as LocalTokenWithBalance[]
}

const tokensWithBalance = tokens.map(token => {
balances.tokens = tokens.reduce((tokenBalance, token) => {
if ('error' in token) {
balances.dataAccurate = false
return tokenBalance
}
if (isPChain(chainId)) {
return { ...token, localId: AVAX_P_ID }
return [...tokenBalance, { ...token, localId: AVAX_P_ID }]
}
if (isXChain(chainId)) {
return { ...token, localId: AVAX_X_ID }
}
return {
...token,
localId: getLocalTokenId(token)
return [...tokenBalance, { ...token, localId: AVAX_X_ID }]
}
})
return [
...tokenBalance,
{
...token,
localId: getLocalTokenId(token)
}
]
}, [] as LocalTokenWithBalance[])

acc[getKey(chainId, accountIndex)] = {
dataAccurate: true,
accountIndex,
chainId,
tokens: tokensWithBalance
}
acc[getKey(chainId, accountIndex)] = balances
return acc
}, {})
}
Expand Down
22 changes: 11 additions & 11 deletions packages/core-mobile/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,20 @@
"gen:glacierApi": "npx openapi-zod-client 'https://glacier-api-dev.avax.network/api-json' -o './app/utils/network/glacierApi.client.ts'"
},
"dependencies": {
"@avalabs/avalanche-module": "0.1.9",
"@avalabs/avalanche-module": "0.1.10",
"@avalabs/avalanchejs": "4.0.5",
"@avalabs/bitcoin-module": "0.1.9",
"@avalabs/bitcoin-module": "0.1.10",
"@avalabs/bridge-unified": "2.1.0",
"@avalabs/core-bridge-sdk": "3.1.0-alpha.1",
"@avalabs/core-chains-sdk": "3.1.0-alpha.1",
"@avalabs/core-coingecko-sdk": "3.1.0-alpha.1",
"@avalabs/core-utils-sdk": "3.1.0-alpha.1",
"@avalabs/core-wallets-sdk": "3.1.0-alpha.1",
"@avalabs/evm-module": "0.1.9",
"@avalabs/glacier-sdk": "3.1.0-alpha.1",
"@avalabs/core-bridge-sdk": "3.1.0-alpha.0",
"@avalabs/core-chains-sdk": "3.1.0-alpha.0",
"@avalabs/core-coingecko-sdk": "3.1.0-alpha.0",
"@avalabs/core-utils-sdk": "3.1.0-alpha.0",
"@avalabs/core-wallets-sdk": "3.1.0-alpha.0",
"@avalabs/evm-module": "0.1.10",
"@avalabs/glacier-sdk": "3.1.0-alpha.0",
"@avalabs/k2-mobile": "workspace:*",
"@avalabs/types": "3.1.0-alpha.1",
"@avalabs/vm-module-types": "0.1.9",
"@avalabs/types": "3.1.0-alpha.0",
"@avalabs/vm-module-types": "0.1.10",
"@blockaid/client": "0.10.0",
"@coinbase/cbpay-js": "2.2.1",
"@cubist-labs/cubesigner-sdk": "0.3.28",
Expand Down
Loading

0 comments on commit 5b50e4d

Please sign in to comment.