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, SIGNATURE_TYPES } 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) => {
// Optional 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. First provider object should be a provider object with accounts information.

It can be a JSON RPC Provider object or HttpProvider object with an RPC URL also given that you have passed walletProvider object in the options and Biconomy support is present. Check Network Agnostic Support section.

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/hyphen";
​
let preTransferStatus = await hyphen.preDepositStatus({
tokenAddress: "", // Token address on fromChain 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 on target chain for given tokenn
} else if (preTransferStatus.code === RESPONSE_CODES.UNSUPPORTED_TOKEN) {
// ❌ Requested token is not supported on fromChain 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
* userAddress User address who is giving the approval
* infiniteApproval Boolean flag whether to give infinite approval for the selected token
* useBiconomy Boolean flag whether to use Biconomy for gasless transaction for approval
*/
let approveTx = await hyphen.approveERC20(tokenAddress, spender, amount,
userAddress, infiniteApproval, useBiconomy);
​
// 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 Hyphen.

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: "30000000000", //Amount to be transferred. Denoted in smallest unit eg in wei",
fromChainId: 137, // chainId of fromChain
toChainId: 1, // chainId of toChain
useBiconomy: true // OPTIONAL boolean flag specifying whether to use Biconomy for gas less transaction or not
});
​
// 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.

In the next section, we'll see how to listen for the status of our cross chain transfers once our deposit transaction is successful. Or what if you don't get the funds on the destination chain within 1 min after your successful deposit transaction.

Enable Network Agnostic Gasless Transactions

You can enable network agnostic gasless transactions using Biconomy so that user can make Approve and Deposit transactions on a chain e.g., Polygon while still keeping his wallet pointing to Ethereum chain.

To enable this, you need to pass 'walletProvider' object and 'biconomy' options in the second parameter of Hyphen constructor as mentioned below:

let hyphen = new Hyphen(<Provider Object 1>, {
...
walletProvider: <Provider Object 2>, // Pass this to enable network agnostic transfers
biconomy: {
enable: true,
debug: true,
apiKey: "Biconomy API Key for meta transaction support"
},
signatureType: SIGNATURE_TYPES.EIP712, // Optional. To specify signature types in case of meta transactions
...
});

Here, Provider Object 1 is the JSON RPC provider object pointing to the 'fromChain' where user will be depositing his tokens. It doesn't need to have any accounts information.

Provider Object 2 has to be a provider object from the connected user wallet. It doesn't matter on which network the User Wallet is because we'll be using this provider object to just get the user signatures to do gasless transactions on the chain pointed by Provider Object 1.

So when user initiates a deposit transaction using deposit method, Hyphen SDK will use Biconomy SDK to send the deposit transaction, internally Biconomy SDK will check the details of called method and smart contract from Biconomy Dashboard. This step is mandatory and the called method needs to be registered on the Biconomy Dashboard.

Once the details are matched with the Biconomy dashboard data, Biconomy SDK will ask the user for the signature using 'walletProvider' object passed and user will get a signature popup on his wallet.

Once user provides his signatures, transaction is routed via Biconomy and the transaction will be executed on the 'fromChain' pointed by Provider Object 1.