Hop
Search…
Getting started
⚠️ JavaScript SDK is in beta

Install module

Using NPM:
1
npm install @hop-protocol/sdk
Copied!
Using Yarn:
1
yarn add @hop-protocol/sdk
Copied!

CDN

jsDeliver CDN
1
<script src="https://cdn.jsdelivr.net/npm/@hop-protocol/[email protected]/hop.js"></script>
Copied!
unpkg CDN
1
<script src="https://unpkg.com/@hop-protocol/[email protected]/hop.js"></script>
Copied!

Import module

Import as ES6 module (e.g. Using TypeScript, babel, webpack):
1
import { Hop } from '@hop-protocol/sdk'
Copied!
Import as commonJS module (e.g. Using Node.js directly or no ES6 modules):
1
const { Hop } = require('@hop-protocol/sdk')
Copied!

Instantiate SDK

1
import { Hop } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
Copied!
Supported network is only mainnetat the moment.

Instantiate with ethers Signer for sending transactions:

1
import { Hop } from '@hop-protocol/sdk'
2
import { Wallet, providers } from 'ethers'
3
4
const provider = new providers.getDefaultProvider('mainnet')
5
const signer = new Wallet(privateKey, provider)
6
const hop = new Hop('mainnet', signer)
Copied!
Example: Using window web3 provider as signer:
1
import { Hop } from '@hop-protocol/sdk'
2
import { Wallet } from 'ethers'
3
4
const provider = new ethers.providers.Web3Provider(window.ethereum, 'any')
5
const signer = provider.getSigner()
6
7
const hop = new Hop('mainnet', signer)
Copied!

SDK with Node.js

Basic example that instantiates sdk with signer
1
require('dotenv').config()
2
const { Hop } = require('@hop-protocol/sdk')
3
const { Wallet, providers } = require('ethers')
4
5
const privateKey = process.env.PRIVATE_KEY
6
const provider = new providers.getDefaultProvider('mainnet')
7
const signer = new Wallet(privateKey, provider)
8
9
const hop = new Hop('mainnet', signer)
10
console.log(hop.version)
Copied!
Create .env file
1
PRIVATE_KEY=<your private key>
Copied!
Running example
1
$ node example.js
2
0.0.1-beta.198
Copied!

Examples

Unit conversion using ethers

The SDK accepts amounts as inputs as big number and most SDK methods return outputs as big numbers, so it's important to know how to convert to and from big numbers.
1
import { parseUnits, formatUnits } from 'ethers/lib/utils'
2
3
const decimals = 6
4
5
const amountBN = parseUnits('1', decimals)
6
console.log(amountBN.toString()) // 1000000
7
8
const amount = formatUnits('1000000', decimals)
9
console.log(amount.toString()) // 1.0
Copied!

Token decimals

Symbol
Decimals
USDC
6
USDT
6
DAI
18
MATIC
18
ETH
18
WBTC
8
FRAX
18
HOP
18

Send tokens across chains

Note: when sending (or generally dealing with amounts in sdk), use values in big number format, e.g. 1 ETH = "1000000000000000000"

Send L1->L2

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).bridge('USDC')
5
6
// send 100 USDC tokens from Ethereum -> Polygon
7
const tx = await bridge.send('100000000', Chain.Ethereum, Chain.Polygon)
8
console.log(tx.hash)
Copied!
Note: there is no bonder fee when sending L1->L2 because transfers go through the native messaging bridge and don't require bonding.

Send L2->L1

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).bridge('USDC')
5
6
// send 100 USDC tokens from Polygon -> Ethereum
7
const tx = await bridge.send('100000000', Chain.Polygon, Chain.Ethereum)
8
console.log(tx.hash)
Copied!
Note: a bonder fee is required when sending L2->L1. It'll calculate it the bonder fee if don't is not explicitly specified. See specifying a custom fee for more info.

Send L2->L2

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).bridge('USDC')
5
6
// send 100 USDC tokens from Polygon -> Gnosis
7
const tx = await bridge.send('100000000', Chain.Polygon, Chain.Gnosis)
8
console.log(tx.hash)
Copied!
Note: a bonder fee is required when sending L2->L2. It'll calculate it the bonder fee if don't is not explicitly specified. See specifying a custom fee for more info.

Send ETH

Sending ETH is the same as sending any other ERC-20. The sdk handles inputting the tx value based on input amount.
Example
1
const bridge = hop.connect(signer).bridge('ETH')
2
3
// send 1 ETH from Polygon -> Gnosis
4
const tx = await bridge.send('1000000000000000000', Chain.Polygon, Chain.Gnosis)
Copied!

Get send calldata only

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).bridge('USDC')
5
6
// get tx calldata for sending 100 USDC tokens from Polygon -> Gnosis
7
const tx = await bridge.populateSendTx('100000000', Chain.Polygon, Chain.Gnosis)
8
console.log(tx) // { data: '0xdeace8f5000000000000000000000000000000000000000000000000000000000000000a0000000000000000000000002a6303e6b99d451df3566068ebb110708335658f00000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006227bdad00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000', to: '0x3666f603Cc164936C1b87e207F36BEBa4AC5f18a' }
Copied!

Using BigNumber as amount input

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
import { parseUnits } from 'ethers/lib/utils'
3
4
const hop = new Hop('mainnet')
5
const bridge = hop.connect(signer).bridge('USDC')
6
7
// send 100 USDC tokens from Ethereum -> Polygon
8
const decimals = 6
9
const amount = parseUnits('100', decimals)
10
const tx = await bridge.send(amount, Chain.Ethereum, Chain.Polygon)
11
console.log(tx.hash)
Copied!

Specifying custom bonder fee

By default, the sdk will use the recommended bonder fee if one is not specified. You can set a custom bonder fee in the bonderFee field. See getSendData(...) for more info on the fee breakdown.
1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).bridge('USDC')
5
6
const amountBN = '10000000'
7
const source = Chain.Polygon
8
const destination = Chain.Gnosis
9
10
// send 100 USDC tokens from Polygon -> Gnosis
11
const { totalFee } = await bridge.getSendData(amountBN, source, destination)
12
const tx = await bridge.send(amountBN, source, destination, {
13
bonderFee: totalFee
14
})
15
console.log(tx.hash)
Copied!

Sending to a custom recipient

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).bridge('USDC')
5
6
// send 100 USDC tokens from Polygon -> Gnosis
7
const tx = await bridge.send('100000000', Chain.Polygon, Chain.Gnosis, {
8
recipient: '0x123...'
9
})
10
console.log(tx.hash)
Copied!

Specifying custom min amount out

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).bridge('USDC')
5
6
// send 100 USDC tokens from Polygon -> Gnosis
7
const tx = await bridge.send('100000000', Chain.Polygon, Chain.Gnosis, {
8
amountOutMin: '99863953' // must take account fee and slippage
9
})
10
11
console.log(tx.hash)
Copied!
Note: The amountOutMin will be 0 if one is not specified. You can use the result from getSendData(...) to calculate the estimated amountOutMin value.

Specify custom deadline

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).bridge('USDC')
5
6
// send 100 USDC tokens from Polygon -> Gnosis
7
const deadline = Math.floor((Date.now() / 1000) + (24 * 60 * 60)) // expire after one day
8
const tx = await bridge.send('100000000', Chain.Polygon, Chain.Gnosis, {
9
deadline
10
})
11
12
console.log(tx.hash)
Copied!
Note: a deadline will be 7 days if one is not specified. The deadline is required for L2->L1 transfers and L2->L2 transfers. Additionally, destinationDeadline is also required for L2->L1 transfers and L2->L2 transfers to swap the hTokens for the canonical tokens at the destination.

Get approval address for sending over bridge

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
import { constants } from 'ethers'
3
4
const hop = new Hop('mainnet')
5
const bridge = hop.connect(signer).bridge('USDC')
6
7
// get address to approve for sending over Ethereum -> Polygon Hop bridge
8
const approvalAddress = await bridge.getSendApprovalAddress(Chain.Ethereum)
9
console.log(approvalAddress)
10
11
const token = bridge.getCanonicalToken(Chain.Ethereum)
12
const amountToApprove = constants.MaxUint256
13
const tx = await token.approve(approvalAddress, amountToApprove)
14
console.log(tx.hash)
Copied!

Get approval address for sending hTokens over bridge

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
import { constants } from 'ethers'
3
4
const hop = new Hop('mainnet')
5
const bridge = hop.connect(signer).bridge('USDC')
6
7
// get address to approve for sending over Ethereum -> Polygon Hop bridge
8
const isHToken = true
9
const approvalAddress = await bridge.getSendApprovalAddress(Chain.Ethereum, isHToken)
10
console.log(approvalAddress)
Copied!

Get allowance for bridge approval address

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
import { constants } from 'ethers'
3
4
const hop = new Hop('mainnet')
5
const bridge = hop.connect(signer).bridge('USDC')
6
7
const approvalAddress = await bridge.getSendApprovalAddress(Chain.Ethereum)
8
const token = bridge.getCanonicalToken(Chain.Ethereum)
9
const allowance = await token.allowance(approvalAddress)
10
console.log(allowance)
Copied!

Check allowance and send approval

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
import { constants } from 'ethers'
3
import { parseUnits, formatUnits } from 'ethers/lib/utils'
4
5
const hop = new Hop('mainnet')
6
const bridge = hop.connect(signer).bridge('USDC')
7
8
const approvalAddress = await bridge.getSendApprovalAddress(Chain.Ethereum)
9
const token = bridge.getCanonicalToken(Chain.Ethereum)
10
const allowance = await token.allowance(approvalAddress)
11
const transferAmount = parseUnits('1', 6) // 1 USDC
12
if (allowance.lt(transferAmount)) {
13
const tx = await token.approve(approvalAddress, transferAmount)
14
console.log(tx.hash)
15
await tx.wait()
16
}
17
// ...
Copied!

Send tokens over canonical bridge (deprecated)

Deposit tokens (L1 -> L2):

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).canonicalBridge('USDC', Chain.Gnosis)
5
6
// send 1 USDC tokens from Ethereum -> Gnosis
7
const tx = await bridge.deposit('100000000')
8
console.log(tx.hash)
Copied!

Withdraw tokens (L2 -> L1):

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).canonicalBridge('USDC', Chain.Gnosis)
5
6
// send 100 USDC tokens from Gnosis -> Ethereum
7
const tx = await bridge.withdraw('100000000')
8
console.log(tx.hash)
Copied!

Estimate tokens amount out from swap

Call this to estimate how many tokens the swap will provide (does not take account slippage or bonder fee):
1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).bridge('USDC')
5
6
// estimate tokens amount out for 100 USDC
7
const amountOut = await bridge.getAmountOut('1000000', Chain.Polygon, Chain.Gnosis)
8
console.log(amountOut) // 998608
Copied!

Estimate tokens that will be received at destination

Call this to estimate how you'll receive at the destination. This is what the UI uses to show user the estimated tokens amount out.
1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).bridge('USDC')
5
6
// get estimated tokens out when sending 100 USDC Polygon -> Gnosis
7
const { estimatedReceived } = await bridge.getSendData('10000000', Chain.Polygon, Chain.Gnosis)
8
console.log(estimatedReceived.toString()) // 98866001
Copied!
The estimatedReceived value takes account the bonder fee and destination transaction fee.

Estimate total bonder fee

The total bonder fee is bonderFee+ destinationTxFee
Make sure to use the total bonder fee when sending a transfer over the Hop bridge.
1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).bridge('USDC')
5
6
// estimate total bonder fee for sending 100 UDSC Polygon -> Gnosis
7
const totalBonderFee = await bridge.getTotalFee('10000000', Chain.Polygon, Chain.Gnosis)
8
console.log(toalBonderFee.toString()) // 998239
Copied!
The method bridge.getTotalFee(...) is a simple wrapper for const { totalFee } = await bridge.getSendData(...).
The following examples below are for retrieving the individual fees if you're interested in the breakdown of the total bonder fee.

Estimate base bonder fee:

The base bonder fee is the fee the bonder takes for fronting liquidity at the destination. The bonder receives this fee when bonding the transfer at the destination.
1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).bridge('USDC')
5
6
// estimate base bonder fee for sending 100 UDSC Polygon -> Gnosis
7
const totalBonderFee = await bridge.getBonderFee('10000000', Chain.Polygon, Chain.Gnosis)
8
console.log(toalBonderFee.toString()) // 1000000
Copied!

Estimate bonder destination transaction fee:

The bonder destination transaction fee is the regular chain transaction fee that the bonder pays to get transaction included in block. The sender of the transfer must compensate the bonder for this fee which is why this fee exists. The destination transaction fee returned here is in terms of the asset being transferred.
1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).bridge('USDC')
5
6
// estimate bonder destination tx fee for sending USDC Polygon -> Gnosis
7
const destinationTxFee = await bridge.getBonderFee(Chain.Polygon, Chain.Gnosis)
8
console.log(destinationTxFee.toString()) // 772
Copied!

Get all send data info:

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).bridge('USDC')
5
6
// get all send data info for sending 100 USDC Polygon -> Gnosis
7
const sendData = await bridge.getSendData('10000000', Chain.Polygon, Chain.Gnosis)
8
console.log(sendData)
9
/*
10
{ amountOut: BigNumber { _hex: '0x05f3cd91', _isBigNumber: true },
11
rate: 0.998639,
12
priceImpact: -0.06402805611223007,
13
requiredLiquidity: BigNumber { _hex: '0x05f7a763', _isBigNumber: true },
14
lpFees: BigNumber { _hex: '0x013880', _isBigNumber: true },
15
adjustedBonderFee: BigNumber { _hex: '0x0f386a', _isBigNumber: true },
16
adjustedDestinationTxFee: BigNumber { _hex: '0x01d6', _isBigNumber: true },
17
totalFee: BigNumber { _hex: '0x0f3a40', _isBigNumber: true },
18
estimatedReceived: BigNumber { _hex: '0x05e49351', _isBigNumber: true } }
19
}
20
*/
Copied!
Return values of getSendData
Property
Type
Description
amountOut
BigNumber
The estimated amount out but without taking account slippage or fees. Use the value estimatedReceived if you're looking for the final estimated out amount which takes account fees.
rate
Number
The ratio between amountIn and amountOut
priceImpact
Number
Price impact percentage. Depositing underpooled assets will give you bonus LP tokens. Depositing overpooled assets will give you less LP tokens (shown as negative price impact).
requiredLiquidity
BigNumber
The amount of required liquidity the bonder must have in order for this transfer to go through. Use getFrontendAvailableLiquidity(...) to check total available liquidity before sending.
lpFees
BigNumber
The AMM swap fees for amountIn
adjustedBonderFee
BigNumber
Small fee bonder takes for fronting liquidity at the destination when bonding transfer. There is a bonderFee going L2->L2 or L2->L1. There is no bonder fee going L1->L2.
adjustedDestinationTxFee
BigNumber
The destination transaction fee that the bonder has to pay when bonding transfer. The sender pays this fee in terms of the token being sent.
totalFee
BigNumber
The total fee consists of bonderFee + destinationTxFee. Use this totalFee value for the bonderFee parameter when sending transfer going L2->L2 or L2->L1.
estimatedReceived
BigNumber
The estimated amount that will be received at destination when slippage and all fees (bonderFee + destinationTxfee) are accounted for. This is what the UI displays.
The getSendData logic can viewed in github here if you would like to know how it works or implement in another language.

Get available liquidity for transfer

A transfer can only be bonded if the availale liquidity amount is greater than or equal to the transfer amount. The transfer bond at the destination may be delayed if it is sent when there is low or no available liquidity.
1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).bridge('USDC')
5
6
// get total available liquidity for Polygon -> Gnosis
7
const availableLiquidity = await bridge.getFrontendAvailableLiquidity(Chain.Polygon, Chain.Gnosis)
8
console.log(availableLiquidity.toString()) // 38142941773
Copied!

Get pool deposit price impact percent

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
import { parseUnits, formatUnits } from 'ethers/lib/utils'
3
4
const hop = new Hop('mainnet')
5
const bridge = await hop.bridge('USDC').connect(signer)
6
const amm = bridge.getAmm(Chain.Polygon)
7
const canonicalTokenAmount = parseUnits('100', 6) // 100 USDC
8
const hTokenAmount = parseUnits('1', 6) // 1 hUSDC
9
const impactBn = await amm.getPriceImpact(canonicalTokenAmount, hTokenAmount)
10
const impactPercent = ((formatUnits(impactBn.toString(), 18)) * 100).toFixed(4)
11
console.log(`${impactPercent}%`) // 0.0489%
Copied!
Depositing underpooled assets will give you bonus LP tokens. Depositing overpooled assets will give you less LP tokens (shown as negative price impact).

Watch for receipt events

1
import { Hop, Chain } from '@hop-protocol/sdk'
2
3
const hop = new Hop('mainnet')
4
const bridge = hop.connect(signer).bridge('USDC')
5
const tx = await bridge.send('1000000', Chain.Ethereum, Chain.Gnosis)
6
7
hop
8
.watch(tx.hash, Token.USDC, Chain.Ethereum, Chain.Gnosis)
9
.on('receipt', data => {
10
const { receipt, chain } = data
11
console.log(receipt, chain)
12
})
13
})
Copied!
Note: You can also utilize TheGraph for quering for transaction receipts at the destination. See Hop TheGraph docs.

Set custom provider RPC URLs

1
import { providers } from 'ethers'
2
import { Hop } from '@hop-protocol/sdk'
3
4
const hop = new Hop('mainnet')
5
6
hop.setChainProviders({
7
ethereum: new providers.StaticJsonRpcProvider('https://mainnet.infura.io/v3/84842078b09946638c03157f83405213'),
8
polygon: new providers.StaticJsonRpcProvider('https://polygon-rpc.com'),
9
gnosis: new providers.StaticJsonRpcProvider('https://rpc.gnosischain.com'),
10
optimism: new providers.StaticJsonRpcProvider('https://mainnet.optimism.io'),
11
arbitrum: new providers.StaticJsonRpcProvider('https://arb1.arbitrum.io/rpc'),
12
})
Copied!

More examples

If you'd like to see more examples or have any feedback, message us on Discord!

SDK API Reference

Contract addresses

Last modified 10d ago