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
All channels support transfers in both directions.
Interlay:
channel | tokens |
---|
acala | INTR IBTC |
astar | INTR IBTC |
bifrost | BNC VDOT |
parallel | INTR IBTC |
polkadot | DOT |
polkadot asset hub | USDT |
hydradx | HDX IBTC |
Kintsugi:
channel | tokens |
---|
bifrost | VKSM |
heiko | KBTC KINT |
karura | KBTC KINT LKSM |
kusama | KSM |
kusama asset hub | USDT |
Polkadot:
channel | tokens |
---|
polkadot asset hub | DOT |
Kusama:
channel | tokens |
---|
kusama asset hub | KSM |
Usage
Example: src/bridge.spec.ts
1. initiate the bridge SDK
const availableAdapters: Record<string, BaseCrossChainAdapter> = {
polkadot: new PolkadotAdapter(),
acala: new AcalaAdapter(),
};
const bridge = new Bridge({
adapters: Object.values(availableAdapters),
});
Then you can get the bridge routers:
const allRouters = bridge.router.getRouters();
const availableRouters = bridge.router.getAvailableRouters();
const destChains = bridge.router.getDestinationChains({ from: "interlay" });
const tokens = 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";
const provider = new ApiProvider();
Connect network and pass the ApiPromise | ApiRx
into the adapters.
const chains = Object.keys(availableAdapters) as ChainName[];
const connected = await firstValueFrom(
provider.connectFromChain(chains, undefined)
);
await Promise.all(
chains.map((chain) => availableAdapters[chain].setApi(provider.getApi(chain)))
);
3. token balance query & token transfer
const balance = await firstValueFrom(
adapter.subscribeTokenBalance(token, testAccount)
);
const inputConfig = await firstValueFrom(
adapter.subscribeInputConfigs({
to: toChain,
token,
address: toAddress,
signer,
})
);
console.log(
inputConfig.minInput,
inputConfig.maxInput,
inputConfig.destFee,
inputConfig.estimateFee,
inputConfig.ss58Prefix
);
const tx = 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: {
id: 'karura',
display: 'Karura',
icon: 'https://resources.acala.network/networks/karura.png',
paraChainId: 2000,
ss58Prefix: 8
}
}
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
export const bifrostTokensConfig: Record<string, MultiChainToken> = {
BNC: { name: "BNC", symbol: "BNC", decimals: 12, ed: "10000000000" },
VSKSM: { name: "VSKSM", symbol: "VSKSM", decimals: 12, ed: "100000000" },
};
export const bifrostRoutersConfig: Omit<CrossChainRouterConfigs, "from">[] = [
{
to: "karura",
token: "BNC",
xcm: {
fee: { token: "BNC", amount: "932400000" },
weightLimit: "Unlimited",
},
},
{
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.
class BifrostBalanceAdapter extends BalanceAdapter {
private storages: ReturnType<typeof createBalanceStorages>;
constructor ({ api, chain, tokens }: BalanceAdapterConfigs) {
super({ api, chain, tokens });
this.storages = createBalanceStorages(api);
}
public subscribeBalance (token: string, address: string): Observable<BalanceData> {
}
}
function createBalanceStorages(api: AnyApi) => {
return {
balances: (address: string) =>
Storage.create<any>({
api,
path: 'query.system.account',
params: [address]
}),
assets: (tokenId: string, address: string) =>
Storage.create<any>({
api,
path: 'query.tokens.accounts',
params: [tokenId, address]
})
};
};
class BaseBifrostAdapter extends BaseCrossChainAdapter {
private balanceAdapter?: BifrostBalanceAdapter;
public subscribeTokenBalance (token: string, address: string): Observable<BalanceData> {
return this.balanceAdapter.subscribeBalance(token, address);
}
}
2.3 implement public method subscribeMaxInput()
Implement the subscribeMaxInput
method so the bridge can set transferable token amount limit.
class BaseBifrostAdapter extends BaseCrossChainAdapter {
public subscribeMaxInput(
token: string,
address: string,
to: ChainName
): Observable<FN> {
return combineLatest({
txFee:
token === this.balanceAdapter?.nativeToken ? this.estimateTxFee() : "0",
balance: this.balanceAdapter
.subscribeBalance(token, address)
.pipe(map((i) => i.available)),
}).pipe(
map(({ balance, txFee }) => {
const tokenMeta = this.balanceAdapter?.getToken(token);
const feeFactor = 1.2;
const fee = FN.fromInner(txFee, tokenMeta?.decimals).mul(
new FN(feeFactor)
);
return balance
.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.
class BaseBifrostAdapter extends BaseCrossChainAdapter {
public createTx(
params: CrossChainTransferParams
):
| SubmittableExtrinsic<"promise", ISubmittableResult>
| SubmittableExtrinsic<"rxjs", ISubmittableResult> {
const { address, amount, to, token } = params;
const toChain = chains[to];
const accountId = this.api?.createType("AccountId32", address).toHex();
const tokenId = SUPPORTED_TOKENS[token];
if (!tokenId) {
throw new CurrencyNotFound(token);
}
return this.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
export class BifrostAdapter extends BaseBifrostAdapter {
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