SDK

Javascript SDK for doing cross-chain transactions

Introduction

Hyphen SDK, is a javascript based SDK written in typescript that helps in integrating Hyphen services easily. It provides some helper methods that makes the Hyphen integration too easy. Even though you can just interact with LiquidityPoolManager smart contract directly and deposit the tokens using depositErc20 method from your DApp and do the deposit transactions but using SDK is recommended as it provides methods like checking available liquidity before doing the transaction, checking exit transaction status, checking approvals etc.

Lets get started

1. Installation

Via NPM
Via Yarn
Via NPM
npm install @biconomy/hyphen
Via Yarn
yarn add @biconomy/hyphen

2. Importing and Instantiation

HYPHEN accepts a provider object as first argument and an option object as second argument.

import { Hyphen } from "@biconomy/hyphen";
​
let hyphen = new Hyphen(<Provider Object>, {
debug: true, // If 'true', it prints debug logs on console window
environment: "test", // It can be "test" or "prod"
onFundsTransfered: (data) => {
// Callback method which will be called when funds transfer across
// chains will be completed
}
});

Note: <Provider Object> is the provider object exposed by your connected wallet. It can be window.ethereum for Metamask or portis.provider for Portis and so on. It should be a provider object with accounts information.

It should not be any RPC url or HttpProvider object with an RPC URL.

3. Initialisation

After the SDK object is instantiated you need to initialise the SDK by calling its init function. The initialisation is kept separated from the constructor to give flexibility to developer to use await statement for initialising the SDK.

await hyphen.init();

Keep your code in a try-catch block to catch any error that might be thrown from the SDK during instantiation or initialisation.

Time to do a Cross-Chain Transfer

Before we proceed lets define some terms which will be used frequently in this documentation

Terms

Definition

toChain

Target chain where the funds needs to be transferred.

fromChain

Source chain from where the funds needs to be transferred to toChain

toChainId

chain id of the target chain where funds needs to be transferred

fromChainId

chain id of the source chain from where funds needs to be transferred

LiquidityPoolManager

Smart Contract where liquidity is present. It is pre-deployed on each supported chains by Biconomy.

Now that we have initialised the SDK and defined the terms we can do the cross-chain transfer transaction. In order to do a cross-chain transfer some pre-checks needs to be done and some pre-actions needs to be taken if required.

SDK provides methods to do these pre-checks and do some pre-actions so you don't have to write extra code to do the same.

Pre-Checks

  • There should be enough liquidity on toChain where funds needs to be transferred.

  • Given token address and chain ids should be supported.

  • Enough approval should be given by the user to LiquidityPoolManager contract on fromChain for the given token which needs to be transferred.

These checks can be done using the method preDepositStatus that will check for a given fromChainId, toChainId, tokenAddress, userAddress and amount, wether there is enough liquidity on toChain, the given tokenAddress is supported, enough approval is given by the user to LiquidityPoolManager contract on fromChain and the networks are supported or not.

import { RESPONSE_CODES } from "@biconomy/inex";
​
let preTransferStatus = await hyphen.preDepositStatus({
tokenAddress: "", // Token address which needs to be transferred
amount: "", // Amount of tokens to be transferred in smallest unit eg wei
fromChainId: "" // Chain id from where tokens needs to be transferred
toChainId: "", // Chain id where tokens are supposed to be sent
userAddress: "" // User wallet address who want's to do the transfer
});
​
if (preTransferStatus.code === RESPONSE_CODES.OK) {
// βœ… ALL CHECKS PASSED. Proceed to do deposit transaction
} else if(preTransferStatus.code === RESPONSE_CODES.ALLOWANCE_NOT_GIVEN) {
// ❌ Not enough apporval from user address on LiquidityPoolManager contract on fromChain
let approveTx = await hyphen.approveERC20(tokenAddress,
preTransferStatus.depositContract, amount.toString());
​
// ⏱Wait for transaction to confirm, pass number of blocks to wait as param
await approveTx.wait(2);
// NOTE: Whenever there is a transaction done via SDK, all responses
// will be ethers.js compatible with an async wait() function that
// can be called with 'await' to wait for transaction confirmation.
// πŸ†—Now proceed to do the deposit transaction
} else if (preTransferStatus.code === RESPONSE_CODES.UNSUPPORTED_NETWORK) {
// ❌ Target chain id is not supported yet
} else if (preTransferStatus.code === RESPONSE_CODES.NO_LIQUIDITY) {
// ❌ No liquidity available onfor given tokenn
} else if (preTransferStatus.code === RESPONSE_CODES.UNSUPPORTED_TOKEN) {
// ❌ Requested token is not supported yet
} else {
// ❌ Any other unexpected error
}

Pre-Actions

If the approval for the token to be transferred is not given to LiquidityPoolManager contract on fromChain then first approval transaction needs to be done. You can directly interact with the token contract and call its approve or permit method but to make things simple SDK also provides a method to do this.

/**
* tokenAddress Token address whose approval needs to be given
* spender LiquidityPoolManager address on fromChain
* amount Amount to be transferred. It should be in the smallest unit on token eg in wei
*/
let approveTx = await hyphen.approveERC20(tokenAddress, spender, amount);
​
// Wait for 1 block confirmation
await approveTx.wait(1);

Once the approvals are checked and if required approval transaction is done. All you need to do is make only one deposit transaction on LiquidityPoolManager on fromChain and rest of the work will be done by Biconomy.

Lets do the Deposit Transaction

You can call the deposit method provided in the SDK to initiate a deposit transaction. Make sure to call preDepositStatus method before calling deposit method.

let depositTx = await hyphen.deposit({
sender: "User wallet address",
receiver: "Receiver address on toChain. Can be different than sender",
tokenAddress: "Address of the token on fromChain to be transferred",
depositContractAddress: "LiquidityPoolManager address on fromChain",
amount: "Amount to be transferred. Denoted in smallest unit eg in wei",
fromChainId: 137, // chainId of fromChain
toChainId: 1, // chainId of toChain
});
​
// Wait for 1 block confirmation
await depositTx.wait(1);

Deposit method checks if there's enough liquidity available on toChain for the given amount of tokens to be transferred considering all the pending transactions also for the same token. It also checks if there's enough approval given to LiquidityPoolManager contract by the sender address for the given amount of tokens to be transferred.

Once the deposit transaction is confirmed, rest of the transactions to make cross-chain transfer will be done by off-chain Executor nodes run by Biconomy. Now you can wait for the cross-chain transfer transaction to be done.

Listen for Cross-Chain Transfer Transaction

Once you have the deposit hash there are two ways of listening for cross-chain transfer transaction.

  1. One via SDK where you pass the callback method onFundsTransfered(data) in Hyphen constructor and let SDK call this method whenever a cross-chain transfer happens corresponding to the deposit transaction done by the user via SDK.

  2. Or you can use the SDK method checkDepositStatus({depositHash, fromChainId}) to check the status of cross-chain transfer for a given depositHash and fromChainId.

Via Callback Method in Contructor
Manually Checking Status via SDK
Via Callback Method in Contructor
let hyphen = new Hyphen(provider, {
// other options here
onFundsTransfered: (data) => {
/**
* data will contain following fields:
* fromChainId => Chain id from where funds were transferred
* toChainId => Chain id to where funds are transferred
* amount => Amount of tokens transferred in smallest unit of token
* tokenAddress => Address of token that was trasnferred
* depositHash => Deposit transacion hash on fromChainId
* exitHash => Transfer transaction hash on toChainId
**/
}
});
Manually Checking Status via SDK
import { RESPONSE_CODES } from "@biconomy/hyphen";
const EXIT_STATUS = {
PROCESSING: 1,
PROCESSED: 2,
FAILED: 3
}
​
let depsitHash = "";
let fromChainId = 80001;
​
// Assuming you already have the deposit transaction hash and the chain id
// where the transaction was done.
​
// Periodically call checkDepositStatus method unitll you get the response
const response = await hyphen.checkDepositStatus({
depositHash: depsitHash,
fromChainId: fromChainId
});
if(response && response.code === RESPONSE_CODES.SUCCESS) {
if(response.statusCode === EXIT_STATUS.PROCESSED && response.exitHash) {
// βœ… Cross-Chain transfer successfull
/**
* response will contain following fields:
*
* fromChainId => Chain id from where funds were transferred
* toChainId => Chain id to where funds are transferred
* amount => Amount of tokens transferred in smallest unit of token
* tokenAddress => Address of token that was trasnferred
* depositHash => Deposit transacion hash on fromChainId
* exitHash => Transfer transaction hash on toChainId
**/
}
}
​
​
​