Bridge tokens
The BRIDGE flow is a drop-in solution for web-based games and marketplaces that simplifies the process of moving tokens from and to the Immutable zkEVM network.
For additional questions, please contact our support team on Discord.
Bridge Overview
- deposit funds to Immutable zkEVM from Ethereum
- withdraw funds from Immutable zkEVM to Ethereum
Both deposits and withdrawals can be made across wallet addresses as the user chooses. For instance a user could deposit funds from their MetaMask wallet on Ethereum and choose to receive them in their Passport on Immutable zkEVM.
Once a deposit or withdrawal is made, there will be a processing time of up to ~20 mins. Users can then inspect the progress of their transactions by clicking the rocket icon. Transactions in progress will show for both the wallet address where funds are moving from as well as where they are moving to.
Withdrawals
Most withdrawals will be processed within the expected timeframe however there may be circumstances when withdrawals are delayed. If this occurs a user will need to claim their tokens on Ethereum.
1. Flow rate threshold exceeded - the withdrawal volume threshold of a particular token has been exceeded and so all subsequent withdrawals are delayed across all tokens.
2. Large withdrawal threshold exceeded - the withdrawal threshold for a particular token has been exceeded and so all subsequent withdrawals are delayed across all tokens
3. Withdrawal thresholds have not been defined for a specific token - all withdrawals in this token are delayed, withdrawals for other tokens are not affected.
If a withdrawal is delayed, it will be delayed for 24 hours before claiming can be done. It means that the funds have moved to Ethereum, however a separate transaction will need to be signed in order to claim the tokens. When claiming tokens on Ethereum (L1), any wallet can pay the gas required for the claim, however the destination wallet address of the withdrawal will always remain the same. During the claiming process, if a user does not have enough ETH to cover the gas fee, the user will be given the option to connect another wallet to pay the gas.
Getting started
Once you have completed the widget setup, use the WidgetsFactory to create a Bridge widget.
In order to mount the widget, call the mount()
function and pass in the id
attribute of the target element you wish to mount it to.
- React
- JavaScript
import { useEffect } from 'react';
import { checkout } from '@imtbl/sdk';
// Create a Checkout SDK instance
const checkoutSDK = new checkout.Checkout();
export function App() {
// Initialise the Commerce Widget
useEffect(() => {
(async () => {
// Create a factory
const factory = await checkoutSDK.widgets({
config: { theme: checkout.WidgetTheme.DARK, language: 'en' },
});
// Create a widget
const widget = factory.create(checkout.WidgetType.IMMUTABLE_COMMERCE);
// Mount a bridge flow, optionally pass any BridgeWidgetParams
widget.mount('mount-point', {
flow: checkout.CommerceFlowType.BRIDGE,
});
})();
}, []);
return <div id="mount-point" />;
}
<html>
<head>
<!-- Load the SDK from jsdelivr -->
<script src="https://cdn.jsdelivr.net/npm/@imtbl/sdk/dist/browser/checkout/sdk.js"></script>
</head>
<body>
<div id="mount-point"></div>
<script>
// Initialize Checkout SDK
var checkout;
(async function () {
checkout = new ImmutableCheckout.Checkout();
const factory = await checkout.widgets({
config: { theme: ImmutableCheckout.WidgetTheme.DARK },
});
const widget = factory.create(
ImmutableCheckout.WidgetType.IMMUTABLE_COMMERCE
);
widget.mount('mount-point', {
flow: ImmutableCheckout.CommerceFlowType.BRIDGE,
});
})();
</script>
</body>
</html>
Parameters
The mount()
function can also take in parameters to be passed into the widget.
Parameters are treated as transient and will be reset after the widget is unmounted.
Property | Type | Description |
---|---|---|
flow | CommerceFlowType.BRIDGE | The flow type to be used. |
tokenAddress | string | The address of the ERC20 contract to bridge. |
amount | string | The ERC20 token amount to bridge. |
walletProviderName | WalletProviderName | Enum representing the names of different wallet providers i.e PASSPORT or METAMASK |
showBackButton | boolean | Whether to show a back button on the first screen, on click triggers REQUEST_GO_BACK event |
- React
- JavaScript
import { checkout } from '@imtbl/sdk';
// @ts-ignore
const widget = factory.create(checkout.WidgetType.IMMUTABLE_COMMERCE, {
config: { theme: checkout.WidgetTheme.DARK },
});
// When calling the mount function,
// set flow to BRIDGE and pass in the parameters to use
widget.mount('mount-point', {
flow: checkout.CommerceFlowType.BRIDGE,
// set amount and token to bridge
amount: '100',
tokenAddress: '0x3B2d8A1931736Fc321C24864BceEe981B11c3c57', // USDC
});
// When calling the mount function,
// set flow to BRIDGE and pass in the parameters to use
widget.mount('mount-point', {
flow: checkout.CommerceFlowType.BRIDGE,
// set amount and token to bridge
amount: '100',
tokenAddress: '0x3B2d8A1931736Fc321C24864BceEe981B11c3c57', // USDC
});
Configuration
When you first create the widget, you can pass an optional configuration object to set it up. For example, passing in the theme will create the widget with that theme. If this is not passed the configuration will be set by default.
Configuration will persist after the widget is unmounted. You can always update a widget's configuration later by calling the update()
method.
Property | Description |
---|---|
BridgeWidgetConfiguration | The configuration type to be used with the Bridge widget. |
- React
- JavaScript
import { checkout } from '@imtbl/sdk';
// When creating the widget, pass in the configuration
// @ts-ignore
const widget = factory.create(checkout.WidgetType.IMMUTABLE_COMMERCE, {
config: {
language: 'en',
theme: checkout.WidgetTheme.DARK,
// bridge flow configuration options
BRIDGE: {},
},
});
// Update the widget config by calling update()
// @ts-ignore
widget.update({
config: {
theme: checkout.WidgetTheme.LIGHT,
BRIDGE: {},
},
});
import { checkout } from '@imtbl/sdk';
// When creating the widget, pass in the configuration
// @ts-ignore
const widget = factory.create(checkout.WidgetType.IMMUTABLE_COMMERCE, {
config: {
language: 'en',
theme: checkout.WidgetTheme.DARK,
// bridge flow configuration options
BRIDGE: {},
},
});
// Update the widget config by calling update()
// @ts-ignore
widget.update({
config: {
theme: checkout.WidgetTheme.LIGHT,
BRIDGE: {},
},
});
For more information on the configurations across all the Commerce Widgets (e.g. theme) review the Configuration section in our Setup page.
Events
The Commerce Widget emit events events when critical actions have been taken by the user or key states have been reached.
Below is a table outlining the key events associated with a BRIDGE
flow.
Event Type | Description | Event Payload |
---|---|---|
CommerceEventType.CLOSE | The user clicked the close button on the widget. This should usually be wired up to call the widget's unmount() function. | |
CommerceEventType.SUCCESS | The user has completed the flow successfully. | CommerceSuccessEvent |
CommerceEventType.FAILURE | There has been an error in the flow. | CommerceFailureEvent |
- React
- JavaScript
You can use the addListener()
function to tap into the events and provider handlers. Use the removeListener()
function to stop listening to that event.
import { checkout } from '@imtbl/sdk';
//@ts-ignore
const widget = factory.create(checkout.WidgetType.IMMUTABLE_COMMERCE, {
config: { theme: checkout.WidgetTheme.DARK },
});
// Add event listeners for the BRIDGE flow
widget.addListener(
checkout.CommerceEventType.SUCCESS,
(payload: checkout.CommerceSuccessEvent) => {
// narrow the event to a successfull swap event
if (payload.type === checkout.CommerceSuccessEventType.BRIDGE_SUCCESS) {
const { transactionHash } = payload.data;
console.log('successfull swap', transactionHash);
} else if (payload.type === checkout.CommerceSuccessEventType.BRIDGE_CLAIM_WITHDRAWAL_SUCCESS) {
const { transactionHash } = payload.data;
console.log('successful bridge claim withdrawal', transactionHash);
}
}
);
widget.addListener(
checkout.CommerceEventType.FAILURE,
(payload: checkout.CommerceFailureEvent) => {
// narrow the event to a failed bridge event
if (payload.type === checkout.CommerceFailureEventType.BRIDGE_FAILED) {
const { reason, timestamp } = payload.data;
console.log('bridge failed', reason, timestamp);
} else if (payload.type === checkout.CommerceFailureEventType.BRIDGE_CLAIM_WITHDRAWAL_FAILED) {
const { reason, timestamp } = payload.data;
console.log('bridge claim withdrawal failed', reason, timestamp);
}
}
);
widget.addListener(checkout.CommerceEventType.CLOSE, () => {
widget.unmount();
console.log('widget closed');
});
// Remove event listeners for the BRIDGE flow
widget.removeListener(checkout.CommerceEventType.SUCCESS);
widget.removeListener(checkout.CommerceEventType.FAILURE);
widget.removeListener(checkout.CommerceEventType.CLOSE);
You can use the addListener()
function to tap into the events and provider handlers. Use the removeListener()
function to stop listening to that event.
// Add event listeners for the BRIDGE flow
widget.addListener(checkout.CommerceEventType.SUCCESS, (payload) => {
// narrow the event to a successfull swap event
if (
payload.type === ImmutableCheckout.CommerceSuccessEventType.BRIDGE_SUCCESS
) {
const { transactionHash } = payload.data;
console.log('successfull swap', transactionHash);
} else if (
payload.type ===
ImmutableCheckout.CommerceSuccessEventType.BRIDGE_CLAIM_WITHDRAWAL_SUCCESS
) {
const { transactionHash } = payload.data;
console.log('successful bridge claim withdrawal', transactionHash);
}
});
widget.addListener(ImmutableCheckout.CommerceEventType.FAILURE, (payload) => {
// narrow the event to a successfull bridge event
if (
payload.type === ImmutableCheckout.CommerceFailureEventType.BRIDGE_FAILED
) {
const { reason, timestamp } = payload.data;
console.log('bridge failed', reason, timestamp);
} else if (
payload.type ===
ImmutableCheckout.CommerceFailureEventType.BRIDGE_CLAIM_WITHDRAWAL_FAILED
) {
const { reason, timestamp } = payload.data;
console.log('bridge claim withdrawal failed', reason, timestamp);
}
});
widget.addListener(ImmutableCheckout.CommerceEventType.CLOSE, () => {
widget.unmount();
console.log('widget closed');
});
// Remove event listeners for the BRIDGE flow
widget.removeListener(ImmutableCheckout.CommerceEventType.SUCCESS);
widget.removeListener(ImmutableCheckout.CommerceEventType.FAILURE);
widget.removeListener(ImmutableCheckout.CommerceEventType.CLOSE);
Sample code
This sample code gives you a good starting point for integrating the Bridge widget into your application and listening to its events.
- React
- JavaScript
import { useEffect, useState } from 'react';
import { checkout } from '@imtbl/sdk';
// create Checkout SDK
const checkoutSDK = new checkout.Checkout();
export function App() {
const [widget, setWidget] =
useState<checkout.Widget<typeof checkout.WidgetType.IMMUTABLE_COMMERCE>>();
// Initialise widget and mount a BRIDGE flow
useEffect(() => {
(async () => {
const factory = await checkoutSDK.widgets({
config: { theme: checkout.WidgetTheme.DARK, language: 'en' },
});
const checkoutWidget = factory.create(checkout.WidgetType.IMMUTABLE_COMMERCE);
setWidget(checkoutWidget);
})();
}, []);
// mount widget and add event listeners
useEffect(() => {
if (!widget) return;
// Bridge 100 USDC
widget.mount('mount-point', {
flow: checkout.CommerceFlowType.BRIDGE,
amount: '100',
tokenAddress: '0x3B2d8A1931736Fc321C24864BceEe981B11c3c57', // USDC
});
widget.addListener(
checkout.CommerceEventType.SUCCESS,
(payload: checkout.CommerceSuccessEvent) => {
const { type, data } = payload;
// detect successful bridge
if (type === checkout.CommerceSuccessEventType.BRIDGE_SUCCESS) {
console.log('successful bridge', data.transactionHash);
}
}
);
widget.addListener(
checkout.CommerceEventType.FAILURE,
(payload: checkout.CommerceFailureEvent) => {
const { type, data } = payload;
// detect when user fails to bridge tokens
if (type === checkout.CommerceFailureEventType.BRIDGE_FAILED) {
console.log('failed to bridge', data.reason);
}
}
);
// remove widget from view when closed
widget.addListener(checkout.CommerceEventType.CLOSE, () => {
widget.unmount();
});
// clean up event listeners
return () => {
widget.removeListener(checkout.CommerceEventType.SUCCESS);
widget.removeListener(checkout.CommerceEventType.CLOSE);
widget.removeListener(checkout.CommerceEventType.FAILURE);
};
}, [widget]);
return <div id="mount-point" />;
}
<html>
<head>
<!-- Load the SDK from jsdelivr -->
<script src="https://cdn.jsdelivr.net/npm/@imtbl/sdk/dist/browser/checkout/sdk.js"></script>
</head>
<body>
<div id="mount-point"></div>
<script>
// Initialize Checkout SDK
var checkout;
var web3Provider;
(async function () {
checkout = new ImmutableCheckout.Checkout();
const factory = await checkout.widgets({
config: { theme: ImmutableCheckout.WidgetTheme.DARK, language: 'en' },
});
const widget = widgets.create(
ImmutableCheckout.WidgetType.IMMUTABLE_COMMERCE,
{
config: {
WALLET: { showNetworkMenu: false },
},
}
);
// Bridge 100 USDC
widget.mount('mount-point', {
flow: checkout.CommerceFlowType.BRIDGE,
amount: '100',
tokenAddress: '0x3B2d8A1931736Fc321C24864BceEe981B11c3c57', // USDC
});
widget.addListener(
ImmutableCheckout.CommerceEventType.SUCCESS,
(payload) => {
const { type, data } = payload;
// detect successful bridge
if (
type === ImmutableCheckout.CommerceSuccessEventType.BRIDGE_SUCCESS
) {
console.log('successful bridge', data.transactionHash);
}
}
);
widget.addListener(
ImmutableCheckout.CommerceEventType.FAILURE,
(payload) => {
const { type, data } = payload;
// detect failed bridge
if (
type === ImmutableCheckout.CommerceFailureEventType.BRIDGE_FAILED
) {
console.log('failed to bridge', data.reason);
}
}
);
// remove widget from dom when closed
widget.addListener(ImmutableCheckout.CommerceEventType.CLOSE, () => {
widget.unmount();
});
// clean up event listeners
window.addEventListener('beforeunload', () => {
widget.removeListener(ImmutableCheckout.CommerceEventType.SUCCESS);
widget.removeListener(ImmutableCheckout.CommerceEventType.FAILURE);
widget.removeListener(ImmutableCheckout.CommerceEventType.CLOSE);
});
})();
</script>
</body>
</html>