npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@interlay/bridge

v0.6.2

Published

polkawallet bridge sdk

Downloads

144

Readme

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

/// import any parachain-adapters you want in your bridge.
const availableAdapters: Record<string, BaseCrossChainAdapter> = {
  polkadot: new PolkadotAdapter(),
  // kusama: new KusamaAdapter(),
  acala: new AcalaAdapter(),
  // karura: new KaruraAdapter(),
  // statemine: new StatemineAdapter(),
  // bifrost: new BifrostAdapter(),
  // ...
};

/// create your bridge instance and pass the adapters to it.
const bridge = new Bridge({
  adapters: Object.values(availableAdapters),
});

Then you can get the bridge routers:

const allRouters = bridge.router.getRouters();
/// or the available routers (some may temporarily unavailable)
const availableRouters = bridge.router.getAvailableRouters();

/// and get filtered routers
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.

// list all available from-chains
const chains = Object.keys(availableAdapters) as ChainName[];

// connect all adapters
const connected = await firstValueFrom(
  provider.connectFromChain(chains, undefined)
);

// and set `ApiPromise | ApiRx` for each adapter
await Promise.all(
  chains.map((chain) => availableAdapters[chain].setApi(provider.getApi(chain)))
);

3. token balance query & token transfer

/// balance query
const balance = await firstValueFrom(
  adapter.subscribeTokenBalance(token, testAccount)
);

/// and you may want to use the inputConfig provided by the SDK
/// to limit user's transfer amount input
const inputConfig = await firstValueFrom(
  adapter.subscribeInputConfigs({
    to: toChain,
    token,
    address: toAddress,
    signer,
  })
);
console.log(
  inputConfig.minInput,
  inputConfig.maxInput,
  inputConfig.destFee,
  inputConfig.estimateFee,
  inputConfig.ss58Prefix
);

/// create tx & send
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 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 example
export const bifrostTokensConfig: Record<string, MultiChainToken> = {
  BNC: { name: "BNC", symbol: "BNC", decimals: 12, ed: "10000000000" },
  VSKSM: { name: "VSKSM", symbol: "VSKSM", decimals: 12, ed: "100000000" },
  /// ...other tokens
};
export const bifrostRoutersConfig: 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`.
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> {
    /// ...balance queries
  }
}
/// 2. we use a `createBalanceStorages` function with acala `Storage` utils
///    for token balance queries here.
function createBalanceStorages(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` method
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.

/// maxInput = availableBalance - estimatedFee - existentialDeposit
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.

/// maxInput = availableBalance - estimatedFee - existentialDeposit
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
/// `chains.bifrost` is the config you added in step 1.
/// `bifrostRoutersConfig` & `bifrostTokensConfig` is the config you defined in step 2.1.
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