Skip to content

Commit

Permalink
feat(sdk): Add support for abstracted assets selection ✨
Browse files Browse the repository at this point in the history
  • Loading branch information
michaeldev5 authored and dudo50 committed Nov 8, 2024
1 parent 0fd4655 commit b5ffed8
Show file tree
Hide file tree
Showing 153 changed files with 1,911 additions and 942 deletions.
17 changes: 9 additions & 8 deletions apps/playground/src/components/XcmTransfer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ import ErrorAlert from "./ErrorAlert";
import type { FormValuesTransformed } from "./TransferForm";
import TransferForm from "./TransferForm";
import { useDisclosure, useScrollIntoView } from "@mantine/hooks";
import type {
Extrinsic,
TCurrencyInput,
TMultiLocation,
TNode,
TNodePolkadotKusama,
import {
isForeignAsset,
type Extrinsic,
type TCurrencyInput,
type TMultiLocation,
type TNode,
type TNodePolkadotKusama,
} from "@paraspell/sdk";
import type { TPapiTransaction } from "@paraspell/sdk/papi";
import { getOtherAssets, isRelayChain } from "@paraspell/sdk/papi";
Expand Down Expand Up @@ -63,11 +64,11 @@ const XcmTransfer = () => {
};
}
} else if (currency) {
if (ethers.isAddress(currency.assetId)) {
if (isForeignAsset(currency) && ethers.isAddress(currency.assetId)) {
return { symbol: currency.symbol ?? "" };
}

if (!currency.assetId) {
if (!isForeignAsset(currency)) {
return { symbol: currency.symbol ?? "" };
}

Expand Down
6 changes: 3 additions & 3 deletions apps/playground/src/hooks/useCurrencyOptions.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { TAsset, TNodeWithRelayChains } from "@paraspell/sdk";
import { getSupportedAssets } from "@paraspell/sdk";
import { getSupportedAssets, isForeignAsset } from "@paraspell/sdk";
import { useMemo } from "react";

const useCurrencyOptions = (
Expand All @@ -20,7 +20,7 @@ const useCurrencyOptions = (
const currencyMap = useMemo(
() =>
supportedAssets.reduce((map: Record<string, TAsset>, asset) => {
const key = `${asset.symbol ?? "NO_SYMBOL"}-${asset.assetId ?? "NO_ID"}`;
const key = `${asset.symbol ?? "NO_SYMBOL"}-${isForeignAsset(asset) ? asset.assetId : "NO_ID"}`;
map[key] = asset;
return map;
}, {}),
Expand All @@ -31,7 +31,7 @@ const useCurrencyOptions = (
() =>
Object.keys(currencyMap).map((key) => ({
value: key,
label: `${currencyMap[key].symbol} - ${currencyMap[key].assetId ?? "Native"}`,
label: `${currencyMap[key].symbol} - ${isForeignAsset(currencyMap[key]) ? currencyMap[key].assetId : "Native"}`,
})),
[currencyMap],
);
Expand Down
14 changes: 9 additions & 5 deletions apps/playground/src/hooks/useRouterCurrencyOptions.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import type { TAsset, TNodeWithRelayChains } from "@paraspell/sdk";
import {
isForeignAsset,
type TAsset,
type TNodeWithRelayChains,
} from "@paraspell/sdk";
import { useMemo } from "react";
import type { TAutoSelect, TExchangeNode } from "@paraspell/xcm-router";
import {
Expand All @@ -24,7 +28,7 @@ const useRouterCurrencyOptions = (
const currencyFromMap = useMemo(
() =>
supportedAssetsFrom.reduce((map: Record<string, TAsset>, asset) => {
const key = `${asset.symbol ?? "NO_SYMBOL"}-${asset.assetId ?? "NO_ID"}`;
const key = `${asset.symbol ?? "NO_SYMBOL"}-${isForeignAsset(asset) ? asset.assetId : "NO_ID"}`;
map[key] = asset;
return map;
}, {}),
Expand All @@ -34,7 +38,7 @@ const useRouterCurrencyOptions = (
const currencyToMap = useMemo(
() =>
supportedAssetsTo.reduce((map: Record<string, TAsset>, asset) => {
const key = `${asset.symbol ?? "NO_SYMBOL"}-${asset.assetId ?? "NO_ID"}`;
const key = `${asset.symbol ?? "NO_SYMBOL"}-${isForeignAsset(asset) ? asset.assetId : "NO_ID"}`;
map[key] = asset;
return map;
}, {}),
Expand All @@ -45,7 +49,7 @@ const useRouterCurrencyOptions = (
() =>
Object.keys(currencyFromMap).map((key) => ({
value: key,
label: `${currencyFromMap[key].symbol} - ${currencyFromMap[key].assetId ?? "Native"}`,
label: `${currencyFromMap[key].symbol} - ${isForeignAsset(currencyFromMap[key]) ? currencyFromMap[key].assetId : "Native"}`,
})),
[currencyFromMap],
);
Expand All @@ -54,7 +58,7 @@ const useRouterCurrencyOptions = (
() =>
Object.keys(currencyToMap).map((key) => ({
value: key,
label: `${currencyToMap[key].symbol} - ${currencyToMap[key].assetId ?? "Native"}`,
label: `${currencyToMap[key].symbol} - ${isForeignAsset(currencyToMap[key]) ? currencyToMap[key].assetId : "Native"}`,
})),
[currencyToMap],
);
Expand Down
10 changes: 5 additions & 5 deletions apps/playground/src/routes/RouterTransferPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import type { MultiAddressStruct } from "@snowbridge/contract-types/dist/IGatewa
import { u8aToHex } from "@polkadot/util";
import { decodeAddress } from "@polkadot/keyring";
import { ApiPromise, WsProvider } from "@polkadot/api";
import type { TSerializedApiCall } from "@paraspell/sdk";
import { isForeignAsset, type TSerializedApiCall } from "@paraspell/sdk";
import { Web3 } from "web3";
import type { EIP6963ProviderDetail } from "../types";

Expand Down Expand Up @@ -173,12 +173,12 @@ const RouterTransferPage = () => {
.to(to)
.exchange(exchange)
.currencyFrom(
currencyFrom.assetId
isForeignAsset(currencyFrom)
? { id: currencyFrom.assetId }
: { symbol: currencyFrom.symbol ?? "" },
)
.currencyTo(
currencyTo.assetId
isForeignAsset(currencyTo)
? { id: currencyTo.assetId }
: { symbol: currencyTo.symbol ?? "" },
)
Expand Down Expand Up @@ -208,10 +208,10 @@ const RouterTransferPage = () => {
`${API_URL}/router`,
{
...formValues,
currencyFrom: currencyFrom.assetId
currencyFrom: isForeignAsset(currencyFrom)
? { id: currencyFrom.assetId }
: { symbol: currencyFrom.symbol ?? "" },
currencyTo: currencyTo.assetId
currencyTo: isForeignAsset(currencyTo)
? { id: currencyTo.assetId }
: { symbol: currencyTo.symbol ?? "" },
type: TransactionType[transactionType],
Expand Down
14 changes: 13 additions & 1 deletion codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ flag_management:
default_rules:
statuses:
- type: project
target: auto
target: 90%
threshold: 1%
- type: patch
target: 90%
Expand All @@ -28,5 +28,17 @@ flag_management:
- apps/visualizator-be
carryforward: true

coverage:
precision: 2
round: down
range: "90...100"
status:
project:
default:
target: 90%
patch:
default:
target: 90%

comment:
show_carryforward_flags: true
13 changes: 7 additions & 6 deletions packages/sdk/e2e/xcm-papi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { secp256k1 } from '@noble/curves/secp256k1'
import { keccak_256 } from '@noble/hashes/sha3'
import { mnemonicToSeedSync } from '@scure/bip39'
import { HDKey } from '@scure/bip32'
import { isForeignAsset } from '../src/utils/assets'

const MOCK_AMOUNT = 1000
const MOCK_ADDRESS = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'
Expand Down Expand Up @@ -63,7 +64,11 @@ const filteredNodes = NODE_NAMES_DOT_KSM.filter(
node !== 'Turing' &&
node !== 'Pendulum' &&
node !== 'Polkadex' &&
node !== 'Subsocial'
node !== 'Subsocial' &&
// has no assets
node !== 'Quartz' &&
node !== 'InvArchTinker' &&
node !== 'Unique'
)

const findTransferableNodeAndAsset = (
Expand Down Expand Up @@ -92,7 +97,7 @@ const findTransferableNodeAndAsset = (
return {
nodeTo,
asset: supportedAsset.symbol,
assetId: supportedAsset.assetId ?? null
assetId: isForeignAsset(supportedAsset) ? supportedAsset.assetId : null
}
}

Expand Down Expand Up @@ -149,10 +154,6 @@ describe.sequential('XCM - e2e', () => {
)
}

// const deriveEvm = ecdsaCreateDerive(miniSecret)
// const aliceKeyPairEvm = deriveEvm('//Alice//0')
// const evmSigner = getEvmEcdsaSigner(aliceKeyPairEvm.)

const seed = mnemonicToSeedSync(DEV_PHRASE)
const hdkey = HDKey.fromMasterSeed(seed)
const keyPair = hdkey.derive(`m/44'/60'/0'/0/0`)
Expand Down
11 changes: 8 additions & 3 deletions packages/sdk/e2e/xcm.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {
getSupportedAssets
} from '../src'
import { type ApiPromise } from '@polkadot/api'
import { isForeignAsset } from '../src/utils/assets'

const MOCK_AMOUNT = 1000
const MOCK_ADDRESS = '5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty'
Expand All @@ -38,11 +39,15 @@ const getAssetsForNode = (node: TNode): string[] => {

const filteredNodes = NODE_NAMES_DOT_KSM.filter(
node =>
node !== 'Quartz' &&
node !== 'Bitgreen' &&
node !== 'Bajun' &&
node !== 'CoretimeKusama' &&
node !== 'Polkadex'
node !== 'Polkadex' &&
// Has no assets
node !== 'Quartz' &&
node !== 'Pendulum' &&
node !== 'InvArchTinker' &&
node !== 'Unique'
)

const findTransferableNodeAndAsset = (
Expand Down Expand Up @@ -71,7 +76,7 @@ const findTransferableNodeAndAsset = (
return {
nodeTo,
asset: supportedAsset.symbol,
assetId: supportedAsset.assetId ?? null
assetId: isForeignAsset(supportedAsset) ? supportedAsset.assetId : null
}
}

Expand Down
1 change: 1 addition & 0 deletions packages/sdk/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"updateAssets": "node --loader ts-node/esm --experimental-specifier-resolution=node ./scripts/assets/updateAssets.ts",
"updatePallets": "node --loader ts-node/esm --experimental-specifier-resolution=node ./scripts/pallets/updatePallets.ts",
"updateEds": "node --loader ts-node/esm --experimental-specifier-resolution=node ./scripts/eds/updateEds.ts",
"checkDuplicateAssets": "node --loader ts-node/esm --experimental-specifier-resolution=node ./scripts/assets/checkDuplicates.ts",
"runAll": "pnpm compile && pnpm format:write && pnpm lint && pnpm test",
"test:e2e": "vitest run --config ./vitest.config.e2e.ts --sequence.concurrent"
},
Expand Down
76 changes: 76 additions & 0 deletions packages/sdk/scripts/assets/addAliases.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import type { TAssetJsonMap, TNode } from '../../src/types'
import { isForeignAsset } from '../../src/utils/assets'

function collectDuplicateSymbolsInChains(assetsMap: TAssetJsonMap): {
[node: string]: { [symbol: string]: string[] }
} {
const chainDuplicates: { [node: string]: { [symbol: string]: string[] } } = {}

for (const node in assetsMap) {
const nodeData = assetsMap[node as TNode]
const symbolToAssetIds: { [symbol: string]: Set<string> } = {}

const allAssets = [...(nodeData.nativeAssets || []), ...(nodeData.otherAssets || [])]
for (const asset of allAssets) {
const symbol = asset.symbol
const assetId = isForeignAsset(asset) ? asset.assetId : ''
if (symbol && assetId) {
if (!symbolToAssetIds[symbol]) {
symbolToAssetIds[symbol] = new Set()
}
symbolToAssetIds[symbol].add(assetId)
}
}

const duplicates: { [symbol: string]: string[] } = {}
for (const symbol in symbolToAssetIds) {
const assetIds = Array.from(symbolToAssetIds[symbol])
if (assetIds.length > 1) {
duplicates[symbol] = assetIds
}
}

if (Object.keys(duplicates).length > 0) {
chainDuplicates[node] = duplicates
}
}

return chainDuplicates
}

function assignAliasNumbers(assetIds: string[]): { [assetId: string]: number } {
const aliasMapping: { [assetId: string]: number } = {}
const sortedAssetIds = [...assetIds].sort()

sortedAssetIds.forEach((assetId, index) => {
aliasMapping[assetId] = index + 1
})

return aliasMapping
}

export function addAliasesToDuplicateSymbols(assetsMap: TAssetJsonMap): TAssetJsonMap {
const chainDuplicates = collectDuplicateSymbolsInChains(assetsMap)

for (const node in chainDuplicates) {
const duplicates = chainDuplicates[node]
for (const symbol in duplicates) {
const assetIds = duplicates[symbol]
const aliasNumbers = assignAliasNumbers(assetIds)

const nodeData = assetsMap[node as TNode]
const allAssets = [...(nodeData.nativeAssets || []), ...(nodeData.otherAssets || [])]

for (const asset of allAssets) {
if (asset.symbol === symbol && isForeignAsset(asset)) {
const aliasNumber = aliasNumbers[asset.assetId]
if (aliasNumber !== undefined) {
asset.alias = `${symbol}${aliasNumber}`
}
}
}
}
}

return assetsMap
}
39 changes: 39 additions & 0 deletions packages/sdk/scripts/assets/checkDuplicates.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import assetsMapJson from '../../src/maps/assets.json' assert { type: 'json' }
import type { TAssetJsonMap } from '../../src/types'

const assetsMap = assetsMapJson as TAssetJsonMap

const findDuplicates = () => {
Object.entries(assetsMap).forEach(([networkName, network]) => {
const assetSymbols = new Map<string, Record<string, number>>()

const addSymbol = (symbol: string | undefined, type: string) => {
if (!symbol) return // Skip if symbol is undefined
if (!assetSymbols.has(symbol)) {
assetSymbols.set(symbol, { nativeAssets: 0, otherAssets: 0 })
}
assetSymbols.get(symbol)![type] += 1
}

network.nativeAssets.forEach(asset => addSymbol(asset.symbol, 'nativeAssets'))
network.otherAssets.forEach(asset => addSymbol(asset.symbol, 'otherAssets'))

const duplicates = Array.from(assetSymbols.entries())
.filter(
([_, types]) =>
types.nativeAssets > 1 ||
types.otherAssets > 1 ||
(types.nativeAssets > 0 && types.otherAssets > 0)
)
.map(([symbol, counts]) => ({
symbol,
counts: `nativeAssets: ${counts.nativeAssets}, otherAssets: ${counts.otherAssets}`
}))

if (duplicates.length > 0) {
console.log(`Duplicates in ${networkName}:`, duplicates)
}
})
}

findDuplicates()
Loading

0 comments on commit b5ffed8

Please sign in to comment.