Skip to content

Commit

Permalink
feat(sdk): Add asset search by multi-location ✨
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeldev5 committed Nov 11, 2024
1 parent a4db9c3 commit 68fc5fb
Show file tree
Hide file tree
Showing 16 changed files with 7,319 additions and 5,574 deletions.
133 changes: 38 additions & 95 deletions packages/sdk/scripts/assets/fetchAssets.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-unsafe-call */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import type { ApiPromise } from '@polkadot/api'
import type {
TForeignAsset,
TAssetJsonMap,
TMultiLocation,
TForeignAsset,
TNativeAsset,
TNode,
TNodeAssets,
TNodePolkadotKusama
} from '../../src/types'
import { getNode, getNodeEndpointOption } from '../../src/utils'
import { fetchTryMultipleProvidersWithTimeout } from '../scriptUtils'
import { nodeToQuery } from './nodeToQueryMap'
import { GLOBAL, nodeToQuery } from './nodeToQueryMap'
import { fetchBifrostAssets } from './fetchBifrostAssets'
import { fetchEthereumAssets } from './fetchEthereumAssets'
import { addAliasesToDuplicateSymbols } from './addAliases'
import { fetchOtherAssetsRegistry } from './fetchOtherAssetsRegistry'

const fetchNativeAssets = async (api: ApiPromise): Promise<TNativeAsset[]> => {
const propertiesRes = await api.rpc.system.properties()
Expand All @@ -30,9 +30,10 @@ const fetchNativeAssets = async (api: ApiPromise): Promise<TNativeAsset[]> => {
}))
}

const fetchOtherAssets = async (api: ApiPromise, query: string) => {
const fetchOtherAssets = async (api: ApiPromise, query: string): Promise<TForeignAsset[]> => {
const [module, section] = query.split('.')
const res = await api.query[module][section].entries()

return res
.map(
([
Expand Down Expand Up @@ -147,38 +148,6 @@ const fetchOtherAssetsAmplitude = async (api: ApiPromise, query: string) => {
)
}

const fetchOtherAssetsCentrifuge = async (api: ApiPromise, query: string) => {
const [module, section] = query.split('.')
const res = await api.query[module][section].entries()
return res
.filter(
([
{
args: [era]
}
]) => era.toHuman() !== 'Native'
)
.map(
([
{
args: [era]
},
value
]) => {
const { symbol, decimals } = value.toHuman() as any
const eraObj = era as any
return {
assetId:
eraObj.type === 'Tranche'
? Object.values(era.toHuman() ?? {})[0][0].replaceAll(',', '')
: Object.values(era.toHuman() ?? {})[0].replaceAll(',', ''),
symbol,
decimals: +decimals
}
}
)
}

const fetchOtherAssetsInnerType = async (api: ApiPromise, query: string) => {
const [module, section] = query.split('.')
const symbolsResponse = await api.query[module][section].entries()
Expand Down Expand Up @@ -273,16 +242,16 @@ const fetchNativeAsset = async (api: ApiPromise): Promise<string> => {
return symbols[0]
}

const fetchMultiLocations = async (api: ApiPromise): Promise<TMultiLocation[]> => {
const res = await api.query.foreignAssets.asset.entries()
return res.map(
([
{
args: [era]
}
]) => era.toJSON()
) as unknown as TMultiLocation[]
}
// const fetchMultiLocations = async (api: ApiPromise): Promise<TMultiLocation[]> => {
// const res = await api.query.foreignAssets.asset.entries()
// return res.map(
// ([
// {
// args: [era]
// }
// ]) => era.toJSON()
// ) as unknown as TMultiLocation[]
// }

const fetchNodeAssets = async (
node: TNodePolkadotKusama,
Expand All @@ -291,13 +260,6 @@ const fetchNodeAssets = async (
): Promise<Partial<TNodeAssets>> => {
const nativeAssetSymbol = await fetchNativeAsset(api)

// Different format of data
if (node === 'Acala' || node === 'Karura') {
const assets = await fetchAssetsType2(api, query!)
await api.disconnect()
return { ...assets, nativeAssetSymbol }
}

if (node === 'Polkadex') {
const nativeAssets = (await fetchNativeAssets(api)) ?? []
const otherAssets = await fetchAssetIdsOnly(api, query!)
Expand All @@ -319,31 +281,6 @@ const fetchNodeAssets = async (
nativeAssetSymbol
}
}

if (node === 'Pioneer') {
const { otherAssets } = await fetchAssetsType2(api, query!)
const nativeAssets = (await fetchNativeAssets(api)) ?? []
await api.disconnect()
return {
nativeAssets,
otherAssets,
nativeAssetSymbol
}
}

// Different format of data
if (node === 'Centrifuge' || node === 'Altair') {
const nativeAssets = (await fetchNativeAssets(api)) ?? []
const otherAssets = query ? await fetchOtherAssetsCentrifuge(api, query) : []
await api.disconnect()
return {
nativeAssets,
otherAssets,
nativeAssetSymbol
}
}

// Different format of data
if (node === 'Amplitude') {
const nativeAssets = (await fetchNativeAssets(api)) ?? []
const otherAssets = query ? await fetchOtherAssetsAmplitude(api, query) : []
Expand All @@ -366,6 +303,7 @@ const fetchNodeAssets = async (
nativeAssetSymbol
}
}

if (node === 'Picasso' || node === 'ComposableFinance') {
const nativeAssets = (await fetchNativeAssets(api)) ?? []
const otherAssets = query ? await fetchOtherAssetsInnerType(api, query) : []
Expand All @@ -378,7 +316,7 @@ const fetchNodeAssets = async (
}

if (node === 'BifrostPolkadot' || node === 'BifrostKusama') {
const { nativeAssets, otherAssets } = await fetchBifrostAssets(api, query ?? '')
const { nativeAssets, otherAssets } = await fetchBifrostAssets(node)
await api.disconnect()
return {
nativeAssets,
Expand All @@ -387,22 +325,21 @@ const fetchNodeAssets = async (
}
}

if (node === 'AssetHubPolkadot' || node === 'AssetHubKusama') {
const nativeAssets = (await fetchNativeAssets(api)) ?? []
const otherAssets = query ? await fetchOtherAssets(api, query) : []
const multiLocations = await fetchMultiLocations(api)
await api.disconnect()
return {
nativeAssets,
otherAssets,
nativeAssetSymbol,
multiLocations
}
}

const nativeAssets = (await fetchNativeAssets(api)) ?? []

const otherAssets = query ? await fetchOtherAssets(api, query) : []
let otherAssets: TForeignAsset[]

try {
otherAssets =
query === GLOBAL
? await fetchOtherAssetsRegistry(node)
: typeof query === 'string'
? await fetchOtherAssets(api, query)
: []
} catch (e) {
console.warn(`Failed to fetch other assets for ${node}: ${e.message}`)
otherAssets = []
}

await api.disconnect()

Expand All @@ -417,6 +354,7 @@ export const fetchAllNodesAssets = async (assetsMapJson: any) => {
const output: TAssetJsonMap = JSON.parse(JSON.stringify(assetsMapJson))
for (const [node, query] of Object.entries(nodeToQuery)) {
const nodeName = node as TNode

console.log(`Fetching assets for ${nodeName}...`)

let newData
Expand All @@ -425,9 +363,14 @@ export const fetchAllNodesAssets = async (assetsMapJson: any) => {
newData = await fetchEthereumAssets()
output[nodeName] = newData
} else {
// if (nodeName !== 'Zeitgeist') {
// continue
// }

newData = await fetchTryMultipleProvidersWithTimeout(nodeName as TNodePolkadotKusama, api =>
fetchNodeAssets(nodeName as TNodePolkadotKusama, api, query)
)

const isError = newData === null
const oldData = output[nodeName] ?? null
const paraId = getNodeEndpointOption(nodeName as TNodePolkadotKusama)?.paraId
Expand Down Expand Up @@ -457,7 +400,7 @@ export const fetchAllNodesAssets = async (assetsMapJson: any) => {
nativeAssetSymbol: newData?.nativeAssetSymbol ?? '',
nativeAssets: combinedNativeAssets,
otherAssets: combinedOtherAssets,
multiLocations: newData?.multiLocations ?? []
...(newData?.multiLocations?.length ? { multiLocations: newData.multiLocations } : {})
}
}
}
Expand Down
62 changes: 17 additions & 45 deletions packages/sdk/scripts/assets/fetchBifrostAssets.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,27 @@
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
/* eslint-disable @typescript-eslint/no-explicit-any */
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
import type { ApiPromise } from '@polkadot/api'
import type { TForeignAsset, TNativeAsset } from '../../src/types'
import type { StorageKey } from '@polkadot/types'
import type { AnyTuple, Codec } from '@polkadot/types/types'
import type { TForeignAsset, TNativeAsset, TNode } from '../../src/types'
import { fetchAssets } from './fetchOtherAssetsRegistry'

export const fetchBifrostAssets = async (
api: ApiPromise,
query: string
node: TNode
): Promise<{
nativeAssets: TNativeAsset[]
otherAssets: TForeignAsset[]
}> => {
const [module, section] = query.split('.')
const res = await api.query[module][section].entries()

const filterAssets = (tokenTypes: string[]) =>
res.filter(
([
{
args: [era]
}
]) => {
const tokenType = Object.keys(era.toHuman() ?? {})[0].toLowerCase()
return tokenTypes.includes(tokenType)
}
)

const mapAssets = (assets: [StorageKey<AnyTuple>, Codec][], isNative: boolean) =>
assets.map(([_key, value]) => {
const val = value.toHuman() as any
return isNative
? { symbol: val.symbol, decimals: +val.decimals }
: {
assetId: Object.values(_key.args[0].toHuman() ?? {})[0],
symbol: val.symbol,
decimals: +val.decimals
}
})

const nativeAssets = mapAssets(
filterAssets(['token', 'vtoken', 'native']),
true
) as TNativeAsset[]

const otherAssets = mapAssets(
filterAssets(['token2', 'vtoken2', 'vstoken2']),
false
) as TForeignAsset[]
const assets = await fetchAssets(node)

const nativeAssets: TNativeAsset[] = assets
.filter(item => Object.keys(item.asset)[0] === 'Native')
.map(item => ({
symbol: item.symbol,
decimals: item.decimals
}))
const otherAssets = assets
.filter(item => Object.keys(item.asset)[0] !== 'Native')
.map(item => ({
assetId: Object.values(item.asset)[0] as string,
symbol: item.symbol,
decimals: item.decimals
}))
return {
nativeAssets,
otherAssets
Expand Down
50 changes: 50 additions & 0 deletions packages/sdk/scripts/assets/fetchOtherAssetsRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import type { TAssetJsonMap, TForeignAsset, TNode } from '../../src/types'
import { getNode } from '../../src/utils'
import type { TRegistryAssets } from './fetchXcmRegistry'
import { fetchXcmRegistry } from './fetchXcmRegistry'

import assetsMapJson from '../../src/maps/assets.json' assert { type: 'json' }
const assetsMap = assetsMapJson as TAssetJsonMap

export const fetchAssets = async (node: TNode): Promise<TRegistryAssets[]> => {
const data = await fetchXcmRegistry()

const paraId = assetsMap[node].paraId
const relay = getNode(node).type

const isAssetHub = node === 'AssetHubPolkadot' || node === 'AssetHubKusama'

const assets = data[isAssetHub ? 'assets' : 'xcAssets'][relay].find(
item => item.paraID === paraId
)?.data

if (!assets) {
throw new Error(`No assets found for ${node}`)
}

return isAssetHub ? assets.filter(asset => !!asset.xcmInteriorKey) : assets
}

export const fetchOtherAssetsRegistry = async (node: TNode): Promise<TForeignAsset[]> => {
const assets = await fetchAssets(node)

const isAssetHub = node === 'AssetHubPolkadot' || node === 'AssetHubKusama'

return assets
.filter(item => item.currencyID !== 'Native')
.map(item => {
const assetField = item.currencyID ?? item.asset
const id =
typeof assetField === 'string' ? assetField : (Object.values(assetField)[0] as string)
const sanitizedId = id.replace(',', '')
return {
assetId: sanitizedId,
symbol: item.symbol,
decimals: item.decimals,
multiLocation: item.xcmV1MultiLocation,
...(isAssetHub && item.xcmInteriorKey
? { xcmInterior: JSON.parse(item.xcmInteriorKey) as object }
: {})
}
})
}
33 changes: 33 additions & 0 deletions packages/sdk/scripts/assets/fetchXcmRegistry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import axios from 'axios'

export type TAssets = {
polkadot: TRegistryAssetData[]
kusama: TRegistryAssetData[]
}

export type TRegistryAssetData = {
relayChain: string
paraID: number
id: string
xcAssetCnt: string
data: TRegistryAssets[]
}

export type TRegistryAssets = {
paraID: number
relayChain: string
nativeChainID: string | null
symbol: string
decimals: number
xcmV1MultiLocation: object
asset: object | string
currencyID: string | undefined
xcmInteriorKey: string
}

export const fetchXcmRegistry = async (): Promise<{ xcAssets: TAssets; assets: TAssets }> => {
const response = await axios.get<{ xcAssets: TAssets; assets: TAssets }>(
'https://cdn.jsdelivr.net/gh/colorfulnotion/xcm-global-registry/metadata/xcmgar.json'
)
return response.data
}
Loading

0 comments on commit 68fc5fb

Please sign in to comment.