Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CP-8996 Implement support for DeBank tokens #1505

Merged
merged 6 commits into from
Aug 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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> {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the logic in this function is because Glacier doesn't provide info about these chains, so here i'm translating deBank's chain info to our Network format.

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)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing stops us from including all networks, but ticket requested only these 5

)
.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,
atn4z7 marked this conversation as resolved.
Show resolved Hide resolved
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
Loading