From 8ece1cf2d8d6f86f4df7480aada8718f5f05912c Mon Sep 17 00:00:00 2001 From: "B. Yap" <2826165+b-yap@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:03:08 +0800 Subject: [PATCH] Add PEN and XLM tokens to Astar portal (#1348) * first iteration * add PendulumXcmRepository.ts * fix build issues * update PEN png * PEN is native, so use `getNativeBalance` * remove console.log * use '0' instead of 'PEN' * add XLM and EURC * use triple '=' * update symbols and issuers * remove EURC * remove console * xcPEN instead of PEN * Add refactoring * changing minimum amount --------- Co-authored-by: Marcel Ebert --- src/assets/img/token/PEN.svg | 23 ++++ src/assets/img/token/XLM.svg | 20 ++++ src/modules/xcm/index.ts | 9 ++ src/modules/xcm/tokens/index.ts | 20 ++++ .../config/xcm/XcmRepositoryConfiguration.ts | 2 + src/v2/models/XcmModels.ts | 2 + src/v2/repositories/implementations/index.ts | 1 + .../xcm/PendulumXcmRepository.ts | 106 ++++++++++++++++++ 8 files changed, 183 insertions(+) create mode 100644 src/assets/img/token/PEN.svg create mode 100644 src/assets/img/token/XLM.svg create mode 100644 src/v2/repositories/implementations/xcm/PendulumXcmRepository.ts diff --git a/src/assets/img/token/PEN.svg b/src/assets/img/token/PEN.svg new file mode 100644 index 000000000..7b8b9d536 --- /dev/null +++ b/src/assets/img/token/PEN.svg @@ -0,0 +1,23 @@ + + \ No newline at end of file diff --git a/src/assets/img/token/XLM.svg b/src/assets/img/token/XLM.svg new file mode 100644 index 000000000..dfd233676 --- /dev/null +++ b/src/assets/img/token/XLM.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + diff --git a/src/modules/xcm/index.ts b/src/modules/xcm/index.ts index 7fba14724..9a9a3b0b2 100644 --- a/src/modules/xcm/index.ts +++ b/src/modules/xcm/index.ts @@ -364,6 +364,15 @@ export let xcmChainObj: XcmChainObj = { subscan: 'https://hydration.subscan.io', isAstarNativeToken: true, }, + [Chain.PENDULUM]: { + name: Chain.PENDULUM, + relayChain: Chain.POLKADOT, + img: require('/src/assets/img/token/PEN.svg'), + parachainId: parachainIds.PENDULUM, + endpoints: ['wss://rpc-pendulum.prd.pendulumchain.tech:443'], + subscan: 'https://pendulum.subscan.io/', + isAstarNativeToken: false, + }, }; export const xcmChains = objToArray(xcmChainObj); diff --git a/src/modules/xcm/tokens/index.ts b/src/modules/xcm/tokens/index.ts index 6ab948205..34b6b6e16 100644 --- a/src/modules/xcm/tokens/index.ts +++ b/src/modules/xcm/tokens/index.ts @@ -175,6 +175,26 @@ export const xcmToken = { originChain: Chain.HYDRATION, minBridgeAmount: '5', }, + { + symbol: 'xcPEN', + isNativeToken: true, + assetId: '18446744073709551634', + originAssetId: 'PEN', + logo: require('/src/assets/img/token/PEN.svg'), + isXcmCompatible: true, + originChain: Chain.PENDULUM, + minBridgeAmount: '2', + }, + { + symbol: 'XLM.s', + isNativeToken: false, + assetId: '18446744073709551635', + originAssetId: 'XLM.s', + logo: require('/src/assets/img/token/XLM.svg'), + isXcmCompatible: true, + originChain: Chain.PENDULUM, + minBridgeAmount: '1', + }, ], [endpointKey.SHIDEN]: [ { diff --git a/src/v2/config/xcm/XcmRepositoryConfiguration.ts b/src/v2/config/xcm/XcmRepositoryConfiguration.ts index 362573077..f02e2d9b2 100644 --- a/src/v2/config/xcm/XcmRepositoryConfiguration.ts +++ b/src/v2/config/xcm/XcmRepositoryConfiguration.ts @@ -11,6 +11,7 @@ import { EquilibriumXcmRepository, UniqueXcmRepository, HydrationXcmRepository, + PendulumXcmRepository, } from 'src/v2/repositories/implementations'; import { Chain } from 'src/v2/models/XcmModels'; import { TypeMapping } from 'src/v2/config/types'; @@ -36,6 +37,7 @@ export const XcmRepositoryConfiguration: TypeMapping = { [Chain.EQUILIBRIUM]: EquilibriumXcmRepository, [Chain.UNIQUE]: UniqueXcmRepository, [Chain.HYDRATION]: HydrationXcmRepository, + [Chain.PENDULUM]: PendulumXcmRepository, }; export type AstarToken = 'ASTR' | 'SDN'; diff --git a/src/v2/models/XcmModels.ts b/src/v2/models/XcmModels.ts index 5dc33e7d0..ab71bc1b9 100644 --- a/src/v2/models/XcmModels.ts +++ b/src/v2/models/XcmModels.ts @@ -22,6 +22,7 @@ export enum Chain { EQUILIBRIUM = 'Equilibrium', UNIQUE = 'Unique', HYDRATION = 'Hydration', + PENDULUM = 'Pendulum', } export enum parachainIds { @@ -43,6 +44,7 @@ export enum parachainIds { EQUILIBRIUM = 2011, UNIQUE = 2037, HYDRATION = 2034, + PENDULUM = 2094, } export interface XcmChain { diff --git a/src/v2/repositories/implementations/index.ts b/src/v2/repositories/implementations/index.ts index c8699ef85..70e19a4f2 100644 --- a/src/v2/repositories/implementations/index.ts +++ b/src/v2/repositories/implementations/index.ts @@ -19,6 +19,7 @@ export * from './xcm/BifrostXcmRepository'; export * from './xcm/EquilibriumXcmRepository'; export * from './xcm/UniqueXcmRepository'; export * from './xcm/HydrationXcmRepository'; +export * from './xcm/PendulumXcmRepository'; export * from './NftRepository'; export * from './AccountUnificationRepository'; export * from './InflationRepository'; diff --git a/src/v2/repositories/implementations/xcm/PendulumXcmRepository.ts b/src/v2/repositories/implementations/xcm/PendulumXcmRepository.ts new file mode 100644 index 000000000..b1510ded5 --- /dev/null +++ b/src/v2/repositories/implementations/xcm/PendulumXcmRepository.ts @@ -0,0 +1,106 @@ +import { BN } from '@polkadot/util'; +import { XcmTokenInformation } from 'src/modules/xcm'; +import { container } from 'src/v2/common'; +import { ExtrinsicPayload, IApi, IApiFactory } from 'src/v2/integration'; +import { Asset } from 'src/v2/models'; +import { Symbols } from 'src/v2/symbols'; +import { XcmRepository } from '../XcmRepository'; +import { XcmChain } from 'src/v2/models/XcmModels'; +import { TokensAccounts } from 'src/v2/repositories/implementations/xcm/AcalaXcmRepository'; +import { decodeAddress } from '@polkadot/util-crypto'; + +// Mapping object for token IDs +const TOKEN_IDS: Record = { + PEN: 'Native', + 'XLM.s': { Stellar: 'StellarNative' }, +}; + +export class PendulumXcmRepository extends XcmRepository { + constructor() { + const defaultApi = container.get(Symbols.DefaultApi); + const apiFactory = container.get(Symbols.ApiFactory); + const registeredTokens = container.get(Symbols.RegisteredTokens); + super(defaultApi, apiFactory, registeredTokens); + } + + public async getTransferCall( + from: XcmChain, + to: XcmChain, + recipientAddress: string, + token: Asset, + amount: BN, + endpoint: string + ): Promise { + if (!to.parachainId) { + throw `Parachain id for ${to.name} is not defined`; + } + + const tokenData = TOKEN_IDS[token.originAssetId]; + if (!tokenData) { + throw `Token name for ${token.originAssetId} is not defined`; + } + + const version = 'V3'; + + const AccountId32 = { + id: decodeAddress(recipientAddress), + }; + + const destination = { + [version]: { + parents: '1', + interior: { + X2: [ + { + Parachain: to.parachainId, + }, + { + AccountId32, + }, + ], + }, + }, + }; + + const destWeight = { Unlimited: null }; + + return await this.buildTxCall( + from, + endpoint, + 'xTokens', + 'transfer', + tokenData, + amount, + destination, + destWeight + ); + } + + public async getTokenBalance( + address: string, + chain: XcmChain, + token: Asset, + isNativeToken: boolean, + endpoint: string + ): Promise { + const api = await this.apiFactory.get(endpoint); + + try { + if (isNativeToken) { + const nativeBalance = await this.getNativeBalance(address, chain, endpoint); + return nativeBalance.toString(); + } + + const currencyId = TOKEN_IDS[token.originAssetId]; + if (!currencyId) { + console.error(`Token name for ${token.originAssetId} is not defined`); + return '0'; + } + const bal = await api.query.tokens.accounts(address, currencyId); + return bal.free.toString(); + } catch (e) { + console.error(e); + return '0'; + } + } +}