Skip to content

Repository files navigation

Polkadot bridge SDK

Polkadot bridge SDK for multi-chain cross-chain token transfer.

You can integrate the amazing multi-chain bridge into your DApp with this SDK.

And you're welcome to add your parachain-adapter into the SDK.

Supported channels

Interlay:

channeltokens
acalaINTR IBTC
astarINTR IBTC
bifrostBNC VDOT
parallelINTR IBTC
polkadotDOT
polkadot asset hubUSDT
hydradxHDX IBTC

Kintsugi:

channeltokens
bifrostVKSM
heikoKBTC KINT
karuraKBTC KINT LKSM
kusama asset hubUSDT, KSM*
  • KSM can only be transferred from Kintsugi to Kusama Asset Hub. Transfers are not available in the other direction.

Polkadot:

channeltokens
polkadot asset hubDOT

Usage

Example: src/bridge.spec.ts

1. initiate the bridge SDK

/// import any parachain-adapters you want in your bridge.constavailableAdapters: Record<string,BaseCrossChainAdapter>={polkadot: newPolkadotAdapter(),// kusama: new KusamaAdapter(),acala: newAcalaAdapter(),// karura: new KaruraAdapter(),// statemine: new StatemineAdapter(),// bifrost: new BifrostAdapter(),// ...};/// create your bridge instance and pass the adapters to it.constbridge=newBridge({adapters: Object.values(availableAdapters),});

Then you can get the bridge routers:

constallRouters=bridge.router.getRouters();/// or the available routers (some may temporarily unavailable)constavailableRouters=bridge.router.getAvailableRouters();/// and get filtered routersconstdestChains=bridge.router.getDestinationChains({from: "interlay"});consttokens=bridge.router.getAvailableTokens({from: "interlay",to: "polkadot",});

2. network connection

You can use the ApiProvider of the SDK which can connect to all the parachains https://polkadot.js.org/apps supported, or you can use your own apiProvider.

import{ApiProvider}from"./api-provider";constprovider=newApiProvider();

Connect network and pass the ApiPromise | ApiRx into the adapters.

// list all available from-chainsconstchains=Object.keys(availableAdapters)asChainName[];// connect all adaptersconstconnected=awaitfirstValueFrom(provider.connectFromChain(chains,undefined));// and set `ApiPromise | ApiRx` for each adapterawaitPromise.all(chains.map((chain)=>availableAdapters[chain].setApi(provider.getApi(chain))));

3. token balance query & token transfer

/// balance queryconstbalance=awaitfirstValueFrom(adapter.subscribeTokenBalance(token,testAccount));/// and you may want to use the inputConfig provided by the SDK/// to limit user's transfer amount inputconstinputConfig=awaitfirstValueFrom(adapter.subscribeInputConfigs({to: toChain,
token,address: toAddress,
signer,}));console.log(inputConfig.minInput,inputConfig.maxInput,inputConfig.destFee,inputConfig.estimateFee,inputConfig.ss58Prefix);/// create tx & sendconsttx=adapter.createTx({amount: FixedPointNumber.fromInner("10000000000",10),to: "polkadot",token: "DOT",address: toAddress,signer: testAccount,});tx.signAndSend(keyPair,{tip: "0"},onStatusChangecCallback);

How to integrate your parachain into the bridge sdk

For Substrate parachains

1. Add parachain config

Add a new item in src/configs/chains/polkadot-chains.ts or src/configs/chains/kusama-chains.ts.

/// karura for example{karura: {id: 'karura',display: 'Karura',icon: 'https://resources.acala.network/networks/karura.png',paraChainId: 2000,ss58Prefix: 8}/// ...other parachains}

2. Create adapter for your parachain

Add a new adapter file in src/adapters/, and create your ParachainAdapter class extends BaseCrossChainAdapter.

Example: src/adapters/bifrost.ts

2.1 define tokens and routers
/// bifrost for exampleexportconstbifrostTokensConfig: Record<string,MultiChainToken>={BNC: {name: "BNC",symbol: "BNC",decimals: 12,ed: "10000000000"},VSKSM: {name: "VSKSM",symbol: "VSKSM",decimals: 12,ed: "100000000"},/// ...other tokens};exportconstbifrostRoutersConfig: Omit<CrossChainRouterConfigs,"from">[]=[/// router for token `BNC` from `bifrost` to `karura`,/// `xcm.fee` defines the XCM-Fee on karura,/// `xcm.weightLimit` defines the weightLimit value used creating Extrinsic.{to: "karura",token: "BNC",xcm: {fee: {token: "BNC",amount: "932400000"},weightLimit: "Unlimited",},},/// router for token `KUSD` from `bifrost` to `karura`{to: "karura",token: "KUSD",xcm: {fee: {token: "KUSD",amount: "3826597686"},weightLimit: "Unlimited",},},];
2.2 implement public method subscribeTokenBalance()

Implement the subscribeTokenBalance method so the bridge can query token balances.

/// 1. create `BifrostBalanceAdapter` extends `BalanceAdapter`.classBifrostBalanceAdapterextendsBalanceAdapter{privatestorages: ReturnType<typeofcreateBalanceStorages>;constructor({ api, chain, tokens }: BalanceAdapterConfigs){super({ api, chain, tokens });this.storages=createBalanceStorages(api);}publicsubscribeBalance(token: string,address: string): Observable<BalanceData>{/// ...balance queries}}/// 2. we use a `createBalanceStorages` function with acala `Storage` utils/// for token balance queries here.functioncreateBalanceStorages(api: AnyApi)=>{return{/// balances for native-token (BNC for bifrost)balances: (address: string)=>Storage.create<any>({
api,path: 'query.system.account',params: [address]}),/// assets for non-native-token (KUSD for bifrost)assets: (tokenId: string,address: string)=>Storage.create<any>({
api,path: 'query.tokens.accounts',params: [tokenId,address]})};};/// 3. implement the `subscribeTokenBalance` methodclassBaseBifrostAdapterextendsBaseCrossChainAdapter{privatebalanceAdapter?: BifrostBalanceAdapter;publicsubscribeTokenBalance(token: string,address: string): Observable<BalanceData>{returnthis.balanceAdapter.subscribeBalance(token,address);}}
2.3 implement public method subscribeMaxInput()

Implement the subscribeMaxInput method so the bridge can set transferable token amount limit.

/// maxInput = availableBalance - estimatedFee - existentialDepositclassBaseBifrostAdapterextendsBaseCrossChainAdapter{publicsubscribeMaxInput(token: string,address: string,to: ChainName): Observable<FN>{returncombineLatest({txFee:
token===this.balanceAdapter?.nativeToken ? this.estimateTxFee() : "0",balance: this.balanceAdapter.subscribeBalance(token,address).pipe(map((i)=>i.available)),}).pipe(map(({ balance, txFee })=>{consttokenMeta=this.balanceAdapter?.getToken(token);constfeeFactor=1.2;constfee=FN.fromInner(txFee,tokenMeta?.decimals).mul(newFN(feeFactor));returnbalance.minus(fee).minus(FN.fromInner(tokenMeta?.ed||"0",tokenMeta?.decimals));}));}}
2.4 implement public method createTx()

Implement the createTx method so the bridge can create the cross-chain transfer Extrinsic.

/// maxInput = availableBalance - estimatedFee - existentialDepositclassBaseBifrostAdapterextendsBaseCrossChainAdapter{publiccreateTx(params: CrossChainTransferParams):
|SubmittableExtrinsic<"promise",ISubmittableResult>|SubmittableExtrinsic<"rxjs",ISubmittableResult>{const{ address, amount, to, token }=params;consttoChain=chains[to];constaccountId=this.api?.createType("AccountId32",address).toHex();consttokenId=SUPPORTED_TOKENS[token];if(!tokenId){thrownewCurrencyNotFound(token);}returnthis.api.tx.xTokens.transfer(tokenId,amount.toChainData(),{V1: {parents: 1,interior: {X2: [{Parachain: toChain.paraChainId},{AccountId32: {id: accountId,network: "Any"}},],},},},this.getDestWeight(token,to)?.toString());}}
2.5 pass your routers config to your adapter
/// `chains.bifrost` is the config you added in step 1./// `bifrostRoutersConfig` & `bifrostTokensConfig` is the config you defined in step 2.1.exportclassBifrostAdapterextendsBaseBifrostAdapter{constructor(){super(chains.bifrost,bifrostRoutersConfig,bifrostTokensConfig);}}

And you are all set now!

Additional steps

You can import your ParachainAdapter into src/bridge.spec.ts to test your adapter.

run testing with yarn test.

And remember to run yarn lint before commit your code.

For EVM parachains

TODO

About

Polkadot bridge js SDK with XCM transfer.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages