# DepositAddress
Source: https://docs.layerswap.io/DepositAddress
# Integrate Layerswap
Source: https://docs.layerswap.io/Integrate
Layerswap provides a prebuilt, optimized gateway that lets anyone easily and securely process transactions across multiple networks – overcoming liquidity fragmentation and complex bridging steps.
You can utilize Layerswap's transacting infrastructure in four main ways:
} href="/integration/UI/Widget/Quickstart">
Layerswap Widget is a ready-to-use UI toolkit that lets you launch an in-app cross-chain bridging and swapping solution in minutes. It's fully customizable and seamlessly integrates with your app environment.
Layerswap API is set to enable fast and reliable crypto transfers for end-users directly in your app environment. It provides extensive customization options tailored to meet your specific requirements and objectives.
Layerswap iFrame allows embeding Layerswap directly in your app and instantly getting an in-app bridge with adjustments to fit your specific use case.
You can redirect users from your Web or Mobile app to Layerswap and customize their journey by providing [configurations](/integration/UI/Configurations).
# Playground
Source: https://docs.layerswap.io/Playground
# Partner Dashboard
Source: https://docs.layerswap.io/api-keys
Learn how to register in the Partner Dashboard, create an organization, and obtain API keys for Mainnet and Testnet environments
For integrating Layerswap, partners should register in the Dashboard and set up an organization.
Each partner account can create an organization and app(s) within that organization. The organization serves as a logical grouping for multiple associated apps. The app stores all the partner-related information like API keys for programmatic access, webhooks, and other configurations that are needed for the integration and customizations.
### Register as a Partner
1. Go to [https://layerswap.io/dashboard](https://layerswap.io/dashboard) and register
2. Create an organization & app under the organization
3. Go to the newly created app and copy the API key
### Enviorments
Dashboard will give you two types of API keys: Mainnet and Testnet. Respectively, if you use the Mainnet API key, you will get only mainnet networks, and the same applies to testnets.
# Depository
Source: https://docs.layerswap.io/api-reference/depository
Fund a Layerswap swap by calling an on-chain contract instead of sending to a generated deposit address — ideal for smart-contract, server, and programmable wallets.
## Overview
The **Depository** is a Layerswap-operated smart contract that acts as an on-chain entry point for
funding a swap. Your wallet calls a single method on the Depository contract; it records the deposit,
tags it with the identifier of your swap, and forwards the funds to the solver that fills it.
Layerswap watches for the resulting `Deposited` event and progresses your swap automatically.
This is the recommended funding path when you are **integrating Layerswap as an aggregator**, or when
funding from a **smart-contract wallet, server wallet, or any programmable wallet** (e.g.
[Privy](/recipes/privy-wallets), Safe, account-abstraction wallets).
Layerswap encodes the call for you. When you create a swap with `use_depository: true`, the deposit
action includes the contract address (`to_address`) and the fully encoded `call_data` — so the
simplest path is to submit `call_data` as-is (plus a token approval for ERC20). The individual
arguments, including the swap `id` and `receiver` that Layerswap assigns, are also returned in
`encoded_args` if you'd rather build or verify the call yourself.
### Depository vs. deposit address vs. direct transfer
| Funding method | How you fund | Best for |
| ------------------------------------------------- | --------------------------------------------------- | ------------------------------------------------------------------------------------- |
| **Depository** (`use_depository: true`) | Call a contract method with pre-encoded `call_data` | Aggregators; smart-contract wallets; deterministic target; batching approve + deposit |
| **Deposit address** (`use_deposit_address: true`) | Send funds to a generated address | Exchanges, custodial flows, users sending from anywhere |
| **Direct transfer** (default) | Transfer to the solver's address | Simple EOA wallet transfers |
The Depository and deposit-address methods are **mutually exclusive** — setting both
`use_depository: true` and `use_deposit_address: true` is rejected.
## Supported networks
* **EVM** chains where a Depository contract is deployed. Native and ERC20 deposits are both supported.
* **Tron** — **TRC20 tokens only**. Native TRX deposits via the Depository are not supported.
If you request `use_depository: true` on a network that does not have a Depository deployed, swap
creation fails with an "unsupported depository" error. The set of enabled networks grows over time —
always rely on the `to_address` returned by the API rather than hard-coding addresses (see
[Contract addresses](#contract-addresses)).
## How it works
1. **Create the swap** with `use_depository: true`.
2. **Fetch the deposit actions** (returned inline on swap creation, or via
[`GET /swaps/{swapId}/deposit_actions`](/api-reference/swaps/get-deposit-actions)). The action's
`to_address` is the Depository contract and `call_data` is the encoded deposit call.
3. **For ERC20 only:** approve the Depository contract (`to_address`) to spend the token amount.
4. **Submit the transaction** to `to_address` with the returned `call_data` (and `value` for native
deposits — see below).
5. Layerswap detects the on-chain `Deposited` event, correlates it to your swap via the embedded
`id`, and completes delivery on the destination chain.
## The contract
The Depository exposes two deposit methods:
```solidity theme={null}
// Native asset (ETH, etc.) — send the deposit amount as msg.value
function depositNative(bytes32 id, address receiver) external payable;
// ERC20 / TRC20 — requires prior approval of `amount` to this contract
function depositERC20(bytes32 id, address token, address receiver, uint256 amount) external;
```
On a successful deposit the contract emits:
```solidity theme={null}
event Deposited(
bytes32 indexed id, // identifies your swap; pre-encoded by Layerswap
address indexed token, // the deposited token (address(0) for native deposits)
address indexed receiver, // the solver that fills your swap
uint256 amount
);
```
## Using the Depository via the API
### 1. Create the swap
```bash cURL theme={null}
curl -X POST https://api.layerswap.io/api/v2/swaps \
-H "X-LS-APIKEY: $LAYERSWAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source_network": "ETHEREUM_MAINNET",
"source_token": "USDC",
"destination_network": "ARBITRUM_MAINNET",
"destination_token": "USDC",
"destination_address": "0xYourRecipient",
"amount": 100,
"use_depository": true
}'
```
See the [Create Swap](/api-reference/swaps/create-swap) endpoint for the full request schema.
### 2. Read the deposit action
The response includes a `deposit_actions` array. For a Depository swap the relevant fields are:
| Field | Meaning |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------ |
| `to_address` | The Depository contract address on the source chain — your transaction target **and** the ERC20 approval target |
| `call_data` | The fully encoded `depositNative` / `depositERC20` call — submit as-is |
| `amount` / `amount_in_base_units` | For **native** deposits: the value to send (`msg.value`), in decimal and base units. For **ERC20** deposits both are `0` |
| `token` | The token being deposited (`symbol`, `contract`, `decimals`) |
| `fee_token` | The gas asset on the network |
| `encoded_args` | The individual decoded arguments, in order, for inspection |
| `gas_limit` | Suggested gas limit |
| `order` | Execution order when an action has multiple steps |
```json theme={null}
{
"order": 0,
"type": "transfer",
"to_address": "0x",
"call_data": "0x",
"amount": 0,
"amount_in_base_units": "0",
"token": { "symbol": "USDC", "contract": "0x", "decimals": 6 },
"fee_token": { "symbol": "ETH", "contract": null, "decimals": 18 },
"encoded_args": [
"0x0000…",
"0x",
"0x",
"0x"
],
"gas_limit": "120000"
}
```
For ERC20 the top-level `amount` is `0` (no native value is sent). The token and amount live inside
`call_data` / `encoded_args`, so you **must approve first** — see below.
```json theme={null}
{
"order": 0,
"type": "transfer",
"to_address": "0x",
"call_data": "0x",
"amount": 0.05,
"amount_in_base_units": "50000000000000000",
"token": { "symbol": "ETH", "contract": null, "decimals": 18 },
"fee_token": { "symbol": "ETH", "contract": null, "decimals": 18 },
"encoded_args": [
"0x0000…",
"0x"
],
"gas_limit": "45000"
}
```
For native deposits, send `amount_in_base_units` as the transaction `value`.
### 3. Approve (ERC20 only)
Before calling `depositERC20`, approve the Depository contract (`to_address`) to spend the deposit
amount. Native deposits skip this step.
### 4. Submit the deposit transaction
```ts theme={null}
import { createWalletClient, custom, parseAbi } from "viem";
const action = swap.deposit_actions[0];
const wallet = createWalletClient({ transport: custom(window.ethereum) });
// ERC20 only: approve the Depository to spend the token
await wallet.writeContract({
address: action.token.contract, // source_token contract
abi: parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]),
functionName: "approve",
args: [action.to_address, depositAmountInBaseUnits],
});
// Submit the prepared deposit call exactly as returned by the API
await wallet.sendTransaction({
to: action.to_address,
data: action.call_data,
value: BigInt(action.amount_in_base_units), // 0 for ERC20, the deposit amount for native
});
```
The swap `id` and the `receiver` are assigned by Layerswap — use the exact values from the deposit
action. Submitting `call_data` as-is is the safest option; if you instead rebuild the call from
`encoded_args`, don't alter those two values, or Layerswap won't be able to match the on-chain
deposit to your swap.
## Deposit detection
The `id` argument encoded in `call_data` ties your on-chain deposit to your swap. Layerswap monitors
the `Deposited` event and matches its `id` back to your swap, then fills the destination side. From
this point the swap follows the normal [swap lifecycle](/api-reference/swap-lifecycle).
## Contract addresses
The **authoritative** Depository address for a given swap is always the `to_address` returned in the
deposit action — read it per swap rather than caching a global value, since deployments are added
over time and a few chains use a different address.
Most EVM chains share a single deterministically-deployed Depository address. A small number of
chains (e.g. those where deterministic deployment wasn't possible) use a chain-specific address. In
all cases, trust the API response.
## Common errors
| Situation | What happens |
| --------------------------------------------------------------- | ------------------------------------------------------------------------------ |
| `use_depository: true` on a network with no Depository deployed | Swap creation fails (unsupported depository) |
| `use_depository: true` **and** `use_deposit_address: true` | Rejected — the two methods are mutually exclusive |
| ERC20 deposit without sufficient approval to `to_address` | On-chain revert; approve the Depository first |
| Deposit to a `receiver` that isn't the one Layerswap assigned | On-chain revert (`NotWhitelisted`); use the `receiver` from the deposit action |
## Related
* [Privy wallets recipe](/recipes/privy-wallets) — end-to-end Depository flow with a Privy server wallet
* [Get deposit actions](/api-reference/swaps/get-deposit-actions) — full response schema
* [Deposit Widget](/integration/UI/Widget/DepositWidget) — drop-in UI that handles this for you
* [Security](/security) — Depository audit report
# Gasless Swaps
Source: https://docs.layerswap.io/api-reference/gasless-swaps
Let users fund a swap by signing an off-chain message instead of sending an on-chain transaction — Layerswap's paymaster broadcasts the deposit and pays the gas.
## Overview
With a **gasless swap**, the user never broadcasts a deposit transaction and never needs the network's
native token for gas. Instead they **sign a single off-chain message** (an [EIP-712](https://eips.ethereum.org/EIPS/eip-712)
typed-data payload), and Layerswap's **paymaster** broadcasts the on-chain deposit on their behalf and
pays the gas. From the deposit onward the swap follows the normal [swap lifecycle](/api-reference/swap-lifecycle).
This is ideal when the user holds only the token they want to swap (e.g. USDC) and no native gas token,
or when you want to remove the "top up for gas first" step from your flow entirely.
Gasless is a **funding method**, alongside the [Depository](/api-reference/depository), a generated
deposit address, and a direct transfer. You opt into it per swap with `use_gasless: true` — and, so the
quote reflects it, on the quote and limit endpoints as well (see [Request a gasless quote](#request-a-gasless-quote)).
| Funding method | How the user funds | Native gas needed? |
| ------------------------------------------------- | ---------------------------------------------------------- | ----------------------- |
| **Gasless** (`use_gasless: true`) | Signs an off-chain EIP-712 message; the paymaster deposits | **No** — paymaster pays |
| **Depository** (`use_depository: true`) | Calls a contract with pre-encoded `call_data` | Yes |
| **Deposit address** (`use_deposit_address: true`) | Sends funds to a generated address | Yes |
| **Direct transfer** (default) | Transfers to the solver's address | Yes |
## Supported gasless route types
Whether a token can be deposited gaslessly depends on the **signature standard** its contract
implements. Layerswap auto-detects this per token and picks one:
| Standard | How it works | Notes |
| ----------------------------------------- | ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| **EIP-3009** (`receiveWithAuthorization`) | One signature authorizes a pull from the signer to the paymaster — no prior approval. | Preferred when available (e.g. USDC-style tokens). |
| **ERC-2612** (`permit`) | The signature grants an allowance; the paymaster then pulls the funds. | EOA signers only (standard `permit` is `v,r,s` ecrecover). |
| **Permit2** | — | **Not supported.** It requires a one-time on-chain approval, so it can't be fully gasless; these tokens report as non-gasless. |
**You do not need to know which standard a token uses.** The gasless deposit action returns a ready-to-sign
`typed_data` payload with the correct EIP-712 domain, types, and message for that token — you sign it as-is.
Nonces and expiries are handled by Layerswap.
### Which tokens are gasless
A token is gasless-capable when its `supports_gasless_deposit` flag is `true` in the quote /
[networks](/networks-tokens) responses, and the network has a paymaster + [Depository](/api-reference/depository)
deployed. Gasless is **EVM-only** today. Always read `supports_gasless_deposit` at runtime rather than
hard-coding a token list — the set grows over time.
## Request a gasless quote
Gasless isn't free to the sender: Layerswap fronts the source-chain gas and recovers it as a fee folded
into the quote's `blockchain_fee` / `total_fee` (and therefore deducted from `receive_amount`). Because of
that, **you must pass `use_gasless=true` on the quote and limit endpoints** — otherwise you'll get a
non-gasless quote whose `receive_amount` is too high for a swap you intend to complete gaslessly.
`use_gasless` is supported on:
* `GET /api/v2/quote`
* `GET /api/v2/detailed_quote`
* `GET /api/v2/limits`
```bash Gasless quote theme={null}
curl "https://api.layerswap.io/api/v2/quote?\
source_network=ETHEREUM_MAINNET&source_token=USDC&\
destination_network=ARBITRUM_MAINNET&destination_token=ETH&\
amount=100&use_gasless=true" \
-H "X-LS-APIKEY: $LAYERSWAP_API_KEY"
```
```bash Regular quote (for comparison) theme={null}
curl "https://api.layerswap.io/api/v2/quote?\
source_network=ETHEREUM_MAINNET&source_token=USDC&\
destination_network=ARBITRUM_MAINNET&destination_token=ETH&\
amount=100&use_gasless=false" \
-H "X-LS-APIKEY: $LAYERSWAP_API_KEY"
```
The gasless request returns a larger `blockchain_fee` (and correspondingly smaller `receive_amount`) — the
difference is the paymaster's deposit gas. See [Fees](/fees) for how fees are composed.
Quote and create must agree. A swap created **without** `use_gasless: true` cannot be completed gaslessly
(there's no signature action to authorize). Conversely, if you create with `use_gasless: true` the swap is
priced with the gasless fee regardless of what you passed to the quote — so quote it gasless too, or the
`receive_amount` you showed the user won't match.
## Integration flow
Pass `use_gasless: true` in the create request. The swap is priced with the gasless fee and marked as
a gasless deposit.
Call `GET /api/v2/swaps/{swapId}/deposit_actions?source_address=`. For a gasless swap this
returns a **`sign`** action containing the `typed_data` to sign. The `source_address` is required — it's
the wallet that holds the funds and will sign (it is not stored at create time).
Have the wallet sign the returned `typed_data` with `eth_signTypedData_v4`.
`POST /api/v2/swaps/{swapId}/authorize` with the `signature` and `signer_address`. Layerswap verifies
the signature, then the paymaster broadcasts the deposit and pays the gas.
Poll `GET /api/v2/swaps/{swapId}/authorize` for the authorization status and, once published, the
on-chain transaction. The swap itself then progresses through the normal lifecycle.
### 1. Create the swap
```bash theme={null}
curl -X POST https://api.layerswap.io/api/v2/swaps \
-H "X-LS-APIKEY: $LAYERSWAP_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source_network": "ETHEREUM_MAINNET",
"source_token": "USDC",
"destination_network": "ARBITRUM_MAINNET",
"destination_token": "ETH",
"destination_address": "0xYourRecipient",
"amount": 100,
"use_gasless": true
}'
```
See [Create Swap](/api-reference/swaps/create-swap) for the full schema. You don't need to set
`use_depository` — gasless settles through the Depository automatically. Do **not** combine `use_gasless`
with `use_deposit_address`.
### 2. Fetch the deposit actions
```bash theme={null}
curl "https://api.layerswap.io/api/v2/swaps/$SWAP_ID/deposit_actions?source_address=$SIGNER_ADDRESS" \
-H "X-LS-APIKEY: $LAYERSWAP_API_KEY"
```
For a gasless swap the action's `type` is **`sign`**:
| Field | Meaning |
| --------------------------------- | ---------------------------------------------------------------- |
| `type` | `"sign"` — the user signs `typed_data`; no transaction to submit |
| `typed_data` | The full EIP-712 payload to sign with `eth_signTypedData_v4` |
| `to_address` | The paymaster (the `to` / `spender` inside the signed message) |
| `amount` / `amount_in_base_units` | The deposit amount being authorized |
| `token` | The token being deposited (`symbol`, `contract`, `decimals`) |
| `fee_token` | The network's gas asset (paid by the paymaster) |
| `valid_after` / `valid_before` | The signed authorization's validity window (Unix seconds) |
| `nonce` | The authorization nonce (managed by Layerswap) |
```json theme={null}
{
"order": 0,
"type": "sign",
"to_address": "0x",
"amount": 100,
"amount_in_base_units": "100000000",
"token": { "symbol": "USDC", "contract": "0x", "decimals": 6 },
"fee_token": { "symbol": "ETH", "contract": null, "decimals": 18 },
"valid_after": 0,
"valid_before": 1893456000,
"nonce": "0x",
"typed_data": {
"types": { "EIP712Domain": [ /* ... */ ], "ReceiveWithAuthorization": [ /* ... */ ] },
"primaryType": "ReceiveWithAuthorization",
"domain": { "name": "USD Coin", "version": "2", "chainId": "1", "verifyingContract": "0x" },
"message": { "from": "0x", "to": "0x", "value": "100000000", "validAfter": "0", "validBefore": "1893456000", "nonce": "0x" }
}
}
```
For an ERC-2612 token the `typed_data` `primaryType` is `Permit` with a `Permit` type block instead of
`ReceiveWithAuthorization` — but you handle both the same way: pass `typed_data` straight to
`eth_signTypedData_v4`.
### 3. Sign and submit
```ts theme={null}
import { createWalletClient, custom } from "viem";
const action = swap.deposit_actions.find(a => a.type === "sign");
const wallet = createWalletClient({ transport: custom(window.ethereum) });
// Sign the EIP-712 payload exactly as returned
const signature = await window.ethereum.request({
method: "eth_signTypedData_v4",
params: [signerAddress, JSON.stringify(action.typed_data)],
});
// Submit — the paymaster broadcasts the deposit and pays gas
await fetch(`https://api.layerswap.io/api/v2/swaps/${swapId}/authorize`, {
method: "POST",
headers: { "X-LS-APIKEY": apiKey, "Content-Type": "application/json" },
body: JSON.stringify({ signature, signer_address: signerAddress }),
});
```
### 4. Track the authorization
`GET /api/v2/swaps/{swapId}/authorize` returns:
```json theme={null}
{ "status": "Published", "transaction": { "transaction_id": "0x...", "status": "Completed" } }
```
| `status` | Meaning |
| -------------- | ----------------------------------------------------------------------------------- |
| `Initiated` | The sign action was issued; awaiting / holding the signature |
| `Published` | The paymaster's deposit transaction has been broadcast on-chain |
| `Completed` | The deposit transaction confirmed — the swap proceeds normally |
| `Expired` | The signed window lapsed before broadcast — fetch fresh deposit actions and re-sign |
| `Insufficient` | The signer's balance no longer covers the deposit amount |
| `Rejected` | Signature verification failed |
## Important nuances
* **Indicate gasless on the quote *and* limit endpoints.** `use_gasless=true` on `/quote`,
`/detailed_quote`, and `/limits` makes them include the gasless fee. Omitting it yields a non-gasless
quote with an inflated `receive_amount`.
* **`source_address` is required** when fetching deposit actions for a gasless swap — it's the signer, and
it isn't persisted at create time.
* **The signature standard is transparent.** Sign the returned `typed_data`; you never choose or construct
EIP-3009 vs ERC-2612 yourself, and you don't manage nonces.
* **Authorizations expire** (a fixed validity window, \~30 minutes). If a swap sits unsigned past the window,
re-fetch the deposit actions to get a fresh payload and sign again.
* **One authorization per swap.** A submitted signature is single-use per swap; it can be refreshed only
after it expires.
* **Balance is checked** at authorize time and again before broadcast. If the signer can no longer cover
the amount the authorization is marked `Insufficient` and nothing is broadcast.
* **Wallet support.** EOA (ECDSA) signers are supported everywhere. Smart-contract / account-abstraction
wallets are verified via [EIP-1271](https://eips.ethereum.org/EIPS/eip-1271) and work on EIP-3009 tokens
whose contract supports contract-signature validation; ERC-2612 is EOA-only. If a wallet can't sign
gaslessly for a given token, fall back to a normal (non-gasless) deposit.
## Common errors
| Situation | What happens |
| ------------------------------------------------------------------- | ---------------------------------------------------------------------------- |
| `POST .../authorize` on a swap not created with `use_gasless: true` | Rejected — "no authorization request found; fetch the deposit actions first" |
| Fetching gasless deposit actions without `source_address` | The sign action can't be built |
| Signature that doesn't recover to `signer_address` | Rejected (`400`) at authorize |
| Signer balance below the deposit amount | `400` at authorize; status `Insufficient` if it drops later |
| Authorization submitted after `valid_before` | Expired — re-fetch deposit actions and re-sign |
| `use_gasless` on a token/network without gasless support | `supports_gasless_deposit` is `false`; use another funding method |
## Related
* [Depository](/api-reference/depository) — the settlement contract gasless deposits flow through
* [Fees](/fees) — how the gasless fee fits into the quote
* [Get deposit actions](/api-reference/swaps/get-deposit-actions) — full response schema
* [Swap lifecycle](/api-reference/swap-lifecycle) — what happens after the deposit
# Health Check
Source: https://docs.layerswap.io/api-reference/health/health-check
https://api.layerswap.io/swagger/v2/swagger.json get /api/v2/health
Verifies that the API is running and operational.
# Refunds
Source: https://docs.layerswap.io/api-reference/refunds
Learn how Layerswap handles refunds
When a swap cannot be completed after the user has sent funds, Layerswap automatically initiates a refund. The refund is always processed on the **source chain** in the **source token**. Gas fees for processing the refund transaction are deducted from the refund amount.
## When does a refund happen?
Common scenarios that trigger a refund:
* **Quote expiration:** The user's deposit arrived after the original quote expired, and Layerswap could not obtain a valid new quote.
* **Insufficient liquidity:** The solver does not have enough liquidity to complete the transaction.
* **Provider execution failure:** A swap provider (e.g. a DEX) encountered an error during execution.
* **Destination chain unavailability:** The destination chain is unavailable due to an RPC outage or chain reorganization.
## Refund statuses
When a refund is initiated, the swap transitions to `pending_refund`. Once the refund transaction is confirmed on-chain, the status changes to `refunded`. See the [Swap lifecycle](/api-reference/swap-lifecycle) for the full status flow.
## Refund address
You can provide a `refund_address` when creating a swap. This must be a valid address on the source network. If the swap route involves a swap provider, the refund address is **required**.
## Identifying a refund transaction
A refunded swap will have a transaction with `type: "refund"` in its `transactions` array:
```bash theme={null}
curl -X GET \
'https://api.layerswap.io/api/v2/swaps/d0050d05-4e75-4e9c-8b89-c1c8cbee4a62?exclude_deposit_actions=true' \
-H 'accept: application/json' \
-H 'X-LS-APIKEY: bwDJw8c1mesRyWfO3WrOB7iE48xAkVEI5QWlgnNFHnwH/4W+zHOcRoM5D3Sne3eCXRqUzHTMXBt0hrd+lO4ASw'
```
The response will contain the swap with `status: "refunded"` and a `refund` transaction in the `transactions` array:
```json theme={null}
{
"data": {
"swap": {
"id": "d0050d05-4e75-4e9c-8b89-c1c8cbee4a62",
"status": "refunded",
"source_network": { "name": "ETHEREUM_MAINNET", "display_name": "Ethereum" },
"source_token": { "symbol": "mUSD", "display_asset": "MetaMask USD" },
"destination_network": { "name": "ETHEREUM_MAINNET", "display_name": "Ethereum" },
"destination_token": { "symbol": "ETH", "display_asset": "ETH" },
"requested_amount": 0.052854,
"transactions": [
{
"from": "0x425ce7a885c77b6b417e886ae542318250628a9d",
"to": "0x08b00ceee2fb66029b53d76110b19eeaabfd1e65",
"transaction_hash": "0x01885c930c698370b82f2614692eab5305e3d493238eb9750c88f80e172d0884",
"amount": 0.052854,
"type": "input",
"status": "completed",
"token": { "symbol": "mUSD" },
"network": { "name": "ETHEREUM_MAINNET" }
},
{
"from": "0x08b00ceee2fb66029b53d76110b19eeaabfd1e65",
"to": "0x425ce7a885c77b6b417e886ae542318250628a9d",
"transaction_hash": "0xa8a71bd2093c09b254f870fdeef38639f5ce712645f2c425acedb655616967ef",
"amount": 0.052854,
"type": "refund",
"status": "completed",
"token": { "symbol": "mUSD" },
"network": { "name": "ETHEREUM_MAINNET" }
}
]
}
}
}
```
The `refund` transaction shows the on-chain transfer back to the user in the original source token.
# Swap lifecycle
Source: https://docs.layerswap.io/api-reference/swap-lifecycle
Understand the different statuses a swap goes through from creation to completion or refund.
```mermaid theme={null}
%%{init: {'theme': 'base', 'themeVariables': {'background': '#1a1a2e', 'lineColor': '#94a3b8', 'fontSize': '14px', 'edgeLabelBackground': 'transparent', 'tertiaryTextColor': '#ffffff'}}}%%
graph TD
l1([Swap created]) --> B([user_transfer_pending])
B --> l2([Deposit confirmed]) --> D([ls_transfer_pending])
B --> l3([No deposit in 6h]) --> C([expired])
D --> l4([Success]) --> E([completed])
D --> l5([Below minimum]) --> F([failed])
D --> l6([Execution failed]) --> R{refund_address?}
R --> |Yes| G([pending_refund])
R -.-> |No| M([Retries])
G --> l7([Confirmed]) --> H([refunded])
classDef label fill:none,color:#cbd5e1,stroke:#334155,stroke-width:1px
class l1,l2,l3,l4,l5,l6,l7 label
style R fill:#475569,color:#fff,stroke:#475569
style M fill:#6b7280,color:#fff,stroke:#6b7280
style B fill:#0891b2,color:#fff,stroke:#0891b2
style D fill:#ca8a04,color:#fff,stroke:#ca8a04
style E fill:#16a34a,color:#fff,stroke:#16a34a
style C fill:#6b7280,color:#fff,stroke:#6b7280
style F fill:#dc2626,color:#fff,stroke:#dc2626
style G fill:#9333ea,color:#fff,stroke:#9333ea
style H fill:#9333ea,color:#fff,stroke:#9333ea
```
## Flow
### 1. `user_transfer_pending`
Swap is created. Layerswap is waiting for the user to complete the transaction in the source network.
* If no deposit arrives within **6 hours** → `expired`
* Once the deposit is confirmed with enough confirmations → `ls_transfer_pending`
### 2. `ls_transfer_pending`
Layerswap is processing the swap. If something goes wrong during execution:
* If deposited amount is **below the minimum** → `failed`
* If the swap cannot be completed for any other reason (e.g. price moved beyond slippage tolerance) → Layerswap **retries automatically**.
* **With `refund_address`** → if retries are exhausted, moves to `pending_refund`
* **Without `refund_address`** → Layerswap keeps retrying. The swap remains in `ls_transfer_pending` until resolved.
* If execution succeeds → `completed`
### 3. `completed`
Funds are delivered to the destination address.
### 4. `pending_refund` → `refunded`
Refund is sent to `refund_address` on the source chain in the source token, **minus gas fees**. If the refund amount is less than the gas fee, no refund is issued. See the [Refunds](/api-reference/refunds) page for details.
## What can go wrong
| Scenario | With refund\_address | Without refund\_address |
| ------------------------------------------------ | ---------------------- | ----------------------- |
| Deposited less than minimum | failed | failed |
| No liquidity or route available across providers | Retries, then refunded | Retries |
| Slippage tolerance cannot be met | Retries, then refunded | Retries |
| Blockchain issue across multiple RPCs | Retries, then refunded | Retries |
| DEX or DEX aggregator issue | Retries, then refunded | Retries |
# Authorize Gasless Deposit
Source: https://docs.layerswap.io/api-reference/swaps/authorize-gasless-deposit
https://api.layerswap.io/swagger/v2/swagger.json post /api/v2/swaps/{swapId}/authorize
Submits a signed EIP-3009 authorization for a gasless deposit and starts the paymaster-sponsored deposit workflow.
# Create Swap
Source: https://docs.layerswap.io/api-reference/swaps/create-swap
https://api.layerswap.io/swagger/v2/swagger.json post /api/v2/swaps
Creates a new swap based on the provided request. Network parameters accept either network names (e.g. ETHEREUM_MAINNET) or numeric chain IDs for EVM networks (e.g. 1). Token parameters accept either asset names (e.g. USDC, ETH) or token contract addresses (e.g. 0xa0b8...). For native tokens via contract address, use the network's zero address (e.g. 0x0000000000000000000000000000000000000000 for EVM, 11111111111111111111111111111111 for Solana).
# Get All Swaps
Source: https://docs.layerswap.io/api-reference/swaps/get-all-swaps
https://api.layerswap.io/swagger/v2/swagger.json get /api/v2/swaps
Retrieves a list of all swaps.
# Get Deposit Actions
Source: https://docs.layerswap.io/api-reference/swaps/get-deposit-actions
https://api.layerswap.io/swagger/v2/swagger.json get /api/v2/swaps/{swapId}/deposit_actions
Retrieves the deposit actions for a specific swap.
# Get Destinations
Source: https://docs.layerswap.io/api-reference/swaps/get-destinations
https://api.layerswap.io/swagger/v2/swagger.json get /api/v2/destinations
Retrieves all available destination routes. Network parameters accept either network names (e.g. ETHEREUM_MAINNET) or numeric chain IDs for EVM networks (e.g. 1). Token parameters accept either asset names (e.g. USDC, ETH) or token contract addresses (e.g. 0xa0b8...). For native tokens via contract address, use the network's zero address (e.g. 0x0000000000000000000000000000000000000000 for EVM, 11111111111111111111111111111111 for Solana).
# Get Detailed Quote
Source: https://docs.layerswap.io/api-reference/swaps/get-detailed-quote
https://api.layerswap.io/swagger/v2/swagger.json get /api/v2/detailed_quote
Retrieves a swap quote based on the provided route request. Network parameters accept either network names (e.g. ETHEREUM_MAINNET) or numeric chain IDs for EVM networks (e.g. 1). Token parameters accept either asset names (e.g. USDC, ETH) or token contract addresses (e.g. 0xa0b8...). For native tokens via contract address, use the network's zero address (e.g. 0x0000000000000000000000000000000000000000 for EVM, 11111111111111111111111111111111 for Solana).
# Get Gasless Authorization Status
Source: https://docs.layerswap.io/api-reference/swaps/get-gasless-authorization-status
https://api.layerswap.io/swagger/v2/swagger.json get /api/v2/swaps/{swapId}/authorize
Returns the status of a gasless deposit authorization and, once published, the on-chain transaction.
# Get Networks
Source: https://docs.layerswap.io/api-reference/swaps/get-networks
https://api.layerswap.io/swagger/v2/swagger.json get /api/v2/networks
Retrieves a list of available networks with their tokens.
# Get Quote
Source: https://docs.layerswap.io/api-reference/swaps/get-quote
https://api.layerswap.io/swagger/v2/swagger.json get /api/v2/quote
Retrieves a swap quote based on the provided route request. Network parameters accept either network names (e.g. ETHEREUM_MAINNET) or numeric chain IDs for EVM networks (e.g. 1). Token parameters accept either asset names (e.g. USDC, ETH) or token contract addresses (e.g. 0xa0b8...). For native tokens via contract address, use the network's zero address (e.g. 0x0000000000000000000000000000000000000000 for EVM, 11111111111111111111111111111111 for Solana).
# Get Sources
Source: https://docs.layerswap.io/api-reference/swaps/get-sources
https://api.layerswap.io/swagger/v2/swagger.json get /api/v2/sources
Retrieves all available source routes. Network parameters accept either network names (e.g. ETHEREUM_MAINNET) or numeric chain IDs for EVM networks (e.g. 1). Token parameters accept either asset names (e.g. USDC, ETH) or token contract addresses (e.g. 0xa0b8...). For native tokens via contract address, use the network's zero address (e.g. 0x0000000000000000000000000000000000000000 for EVM, 11111111111111111111111111111111 for Solana).
# Get Swap By Transaction Hash
Source: https://docs.layerswap.io/api-reference/swaps/get-swap-by-transaction-hash
https://api.layerswap.io/swagger/v2/swagger.json get /api/v2/swaps/by_transaction_hash/{transactionHash}
Retrieves the details of a swap associated with the given on-chain transaction hash.
# Get Swap Details
Source: https://docs.layerswap.io/api-reference/swaps/get-swap-details
https://api.layerswap.io/swagger/v2/swagger.json get /api/v2/swaps/{swapId}
Retrieves the details of a specific swap by its ID.
# Get Swap Route Limits
Source: https://docs.layerswap.io/api-reference/swaps/get-swap-route-limits
https://api.layerswap.io/swagger/v2/swagger.json get /api/v2/limits
Retrieves the limits for swap route. Network parameters accept either network names (e.g. ETHEREUM_MAINNET) or numeric chain IDs for EVM networks (e.g. 1). Token parameters accept either asset names (e.g. USDC, ETH) or token contract addresses (e.g. 0xa0b8...). For native tokens via contract address, use the network's zero address (e.g. 0x0000000000000000000000000000000000000000 for EVM, 11111111111111111111111111111111 for Solana).
# Get Transaction Status
Source: https://docs.layerswap.io/api-reference/swaps/get-transaction-status
https://api.layerswap.io/swagger/v2/swagger.json get /api/v2/transaction_status
Retrieves the status of a transaction by its ID.
# Speed up deposit
Source: https://docs.layerswap.io/api-reference/swaps/speed-up-deposit
https://api.layerswap.io/swagger/v2/swagger.json post /api/v2/swaps/{swapId}/deposit_speedup
Speed up deposit detection with deposit transaction hash.
# Webhooks
Source: https://docs.layerswap.io/api-reference/webhook
Describes the mainnet and tesnet enviorments
### Setup
Layerswap provides a webhook configuration functionality so that Partners can receive notifications on any swap status change. Webhooks can be set up per partner application. In order to configure a webhook the following steps must be performed:
1. Login to [https://layerswap.io/dashboard](https://layerswap.io/dashboard)
2. Select the organization/app
3. From left pane select Webhooks, then provide the URL where the webhook should be received and click Create Webhook
4. The newly created webhook should appear under the app
5. Copy the webhook secret for future verification
### Verification
Layerswaps uses Svix for Webhook Management. In order to verify an incoming webhook from Svix please refer to [https://docs.svix.com/receiving/verifying-payloads/how](https://docs.svix.com/receiving/verifying-payloads/how). Use the webhook secret from the dashboard as a Svix secret.
For the structure of the swap notification, please refer to the Swap Data object. Please note that the complete information webhook will be sent only when the swap status is completed. For other statuses, notification will include a subset of this information (whatever is available at that time).
# Brand Assets
Source: https://docs.layerswap.io/brand-assets
Download the Layerswap logo, symbol, and brand colors
Use these assets when referencing Layerswap in articles, partner integrations, or marketing materials. Please don't recolor, distort, or add effects to the logo.
## Logo with backgrounds
## Colors
| Name | Hex |
| ------------------- | --------- |
| **Pink** | `#FF3272` |
| **Background Blue** | `#0B1123` |
# Fees
Source: https://docs.layerswap.io/fees
Understand how Layerswap fees are structured and how to retrieve them via the API
### Fee breakdown
Layerswap fees are designed to be transparent and predictable. Every quote includes a detailed cost breakdown so you know exactly what you're paying for. The total cost of a transfer is made up of three components:
| Component | Description |
| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Layerswap fees** | A percentage-based service fee defined per route (e.g. 0.05%). This is Layerswap's commission for facilitating the transfer. |
| **Bridge expenses** | Covers gas and network costs on the source and destination chains. This is typically a fixed USD amount, periodically updated to reflect current network conditions. On multi-provider routes, an external bridge may add a percentage-based component. |
| **Market impact** | The price difference caused by on-chain swaps when the route involves a DEX. For same-token transfers (e.g. USDC → USDC) this is typically \$0. |
The price impact breakdown is visible in the app by hovering over the fee indicator before confirming a transfer.
### How fees are calculated
**Layerswap fees** are calculated as a percentage of the transfer amount. The percentage varies by route — each source/destination pair has its own rate. Some routes may also include a small fixed USD component on top of the percentage.
**Bridge expenses** are derived from the actual gas costs observed on recent transactions for that route. They cover:
* The output transaction fee on the destination network
* Sweeping fees (when deposit addresses are used)
* Refuel fees (if native gas is sent to the destination address)
**Market impact** only applies when the route involves an on-chain swap (e.g. bridging ETH on Arbitrum to USDC on Base). For direct token routes the market impact is \$0.
### Multi-provider routes
Layerswap can chain its own liquidity with external bridges — CCTP, LayerZero, Axelar, CCIP, or native rollup bridges — to serve routes that aren't covered by direct liquidity alone.
On these **multi-provider routes**, the bridge expenses may include costs from the external bridge, and the transfer time depends on the underlying protocol. For example, a route that uses CCTP + Layerswap may take around 20 minutes, while a direct Layerswap route typically completes in seconds.
The availability of fast, direct routes depends on where liquidity is positioned at any given moment.
### Fetching fees via the API
To get the fee breakdown for a **specific amount**, use the [`/v2/quote`](/api-reference/swaps/get-quote) endpoint:
```bash theme={null}
curl "https://api.layerswap.io/api/v2/quote?\
source_network=ETHEREUM_MAINNET&\
source_token=USDC&\
destination_network=ARBITRUM_MAINNET&\
destination_token=USDC&\
amount=1000"
```
The response includes:
```jsonc theme={null}
{
"quote": {
// blockchain_fee + service_fee
"total_fee": 0.55,
// total fee in USD
"total_fee_in_usd": 0.55,
// gas & network costs (bridge expenses)
"blockchain_fee": 0.05,
// Layerswap's commission
"service_fee": 0.50,
// estimated amount at destination
"receive_amount": 999.45,
// minimum after slippage tolerance
"min_receive_amount": 994.46,
...
}
}
```
To see fees **across all possible amounts** for a route, use the [`/v2/detailed_quote`](/api-reference/swaps/get-detailed-quote) endpoint instead — it returns the total percentage fee and fixed fee components, along with calculated fees at the minimum and maximum transfer amounts.
# API Integration
Source: https://docs.layerswap.io/integration/API
## Overview
Layerswap API is set to enable fast and reliable crypto token swaps across networks
Layerswap handles millions of dollars in transactions every day across multiple networks. We ensure quick and reliable crypto transfers, giving the freedom to move crypto anywhere.
## Quickstart
Get supported sources/networks, available routes and swap fees.
Create Swap using the Layerswap API.
Layerswap will monitor the source network for a transaction and will try to match it with the corresponding swap
Once the transaction is matched and added to the swap, Layerswap will initiate a counterparty transaction to the destination\_address
Poll via Get Swap endpoint to see if the matching transaction Input was added to the swap
If the webhook is configured, Layerswap will deliver a swap status update notification to the partner-specified URL
# Configurations
Source: https://docs.layerswap.io/integration/UI/Configurations
Set the initial flow shown by the widget. Accepted values:
* `swap` — Cross-chain Swap flow (default)
* `cex` — Deposit from CEX flow
* `deposit` — Easy Deposit flow. See the dedicated [Easy Deposit](/integration/UI/Widget/EasyDeposit) page.
Source network or exchange you want the users to transfer from, for example IMMUTABLEX\_MAINNET.
Destination network or exchange you want the users to transfer from, for example ETHEREUM\_MAINNET.
The asset that you want to be preselected in the "From" field. NOTE: available assets depend on the selected network, for example, the asset IMX is only available in the IMMUTABLEX\_MAINNET network.
The asset that you want to be preselected in the "To" field. NOTE: available assets depend on the selected network, for example, the asset IMX is only available in the IMMUTABLEX\_MAINNET network.
Use to pre-fill the amount field. Users will still be able to change it in the UI. The parameter will be skipped if the asset parameter was not passed.
The destination address the funds should reach at the end of the swap.
If set to true, destination address will be hidden in the UI.
If set to true, the source will be hidden in the UI.
If set to true, the source will be locked for editing.
If set to true, the destination will be hidden in the UI.
If set to true, the destination will be locked for editing.
If set to true, the source asset will be locked for editing.
If set to true, the destination asset will be locked for editing.
If set to true, refuel feature will be disabled and not shown in the UI.
Clinet Id from partner setup. And if the destAddress parameter is provided, the Partner's logo will be shown next to the address.
A unique ID representing the user's transfer session in the partner's system. This can later be used to query the status of the transfer and will be included in the webhook notifications sent to the partner. Refer to the API Reference section to learn more.
User account name or address that will be shown as a source. hideFrom should be set to true
Use to replace Swap now button text. For example, it can be changed to Deposit, Withdraw, Transfer or anything relevant to your app.
# Hosted Page
Source: https://docs.layerswap.io/integration/UI/HostedPage
## Layerswap-hosted page
The Layerswap-hosted page is the simplest integration method that allows you to redirect users from your Web or Mobile app directly to Layerswap. You can customize the user journey by providing URL parameters that configure the swap experience.
This approach requires no code integration and is perfect for quick implementations or mobile apps where you want to leverage Layerswap's full-featured interface.
Click to see a live example of the hosted page with pre-configured parameters
## Basic Implementation
Redirect users to the Layerswap hosted app with customization parameters:
```javascript theme={null}
https://layerswap.io/app/?
to=ETHEREUM_MAINNET
&destAddress=0x0000000000000000000000000000000000000000
&toAsset=USDC
&actionButtonText=Deposit
```
## How It Works
1. **Construct the URL**: Build a URL to `https://layerswap.io/app/` with query parameters
2. **Add Parameters**: Include any [configuration parameters](/integration/UI/Configurations) you need
3. **Redirect Users**: Send users to the constructed URL from your application
## Example: Simple Link
The simplest implementation is using a standard HTML link:
```html theme={null}
Deposit to Starknet
```
When the `destAddress` parameter is included in the URL, a warning will be displayed in the UI and the user must confirm the destination address before proceeding with the transaction. This is a security measure to ensure users are aware of where their funds will be sent.
**If you want to bypass the destination address confirmation flow**, you must integrate Layerswap via the [Widget](/integration/UI/Widget/Quickstart) instead of the hosted page approach.
## Example: Mobile Deep Linking
For mobile applications, you can use deep linking or custom URL schemes:
```swift theme={null}
// iOS Swift example
let baseURL = "https://layerswap.io/app/"
var components = URLComponents(string: baseURL)
components?.queryItems = [
URLQueryItem(name: "to", value: "STARKNET_MAINNET"),
URLQueryItem(name: "from", value: "ETHEREUM_MAINNET"),
URLQueryItem(name: "destAddress", value: userAddress),
URLQueryItem(name: "asset", value: "ETH")
]
if let url = components?.url {
UIApplication.shared.open(url)
}
```
# UI Integration Overview
Source: https://docs.layerswap.io/integration/UI/IntegrationOverview
Ready-to-use UI toolkit for integrating cross-chain swaps
## Overview
The Layerswap UI integration is the fastest way to let your users bridge, swap, and deposit crypto from 90+ sources directly inside your app without building custom infrastructure.
Choose between two simple options — a React Widget or an embeddable iFrame — to enable your users to seamlessly bridge assets across blockchains or deposit directly from centralized exchanges into on-chain wallets. A wide range of customization options are available allowing you to tailor the look and feel to your app’s design and environment.
### Key Highlights
The UI Integration provides:
* A fully-functioning app for bridging and swapping across all the Layerswap supported chains.
* Seperate flows for deposits from CEXes and swaps across chains, and ability to hide one or the other.
* Modular wallet management that can be extended or overriden depending on your integration needs.
* Ability to set defaults: tabs, source chain, destination chain, tokens, destination address.
* Access to widget events to receive swap status updates and handle errors through custom callbacks.
* Wide range of customization options for configuring themes, settings, UI elements and a Playground to preview the updates before going live.
### Ideal For
* dApps, NFT marketplaces, wallets, gaming and DeFi platforms that want a fast solution for enabling cross-chain swaps.
* Projects looking to accept deposits from any ecosystem without building bridging logic.
* Platform interested in onboarding users from Centralized Exchanges in an easy way.
### Get Started
} href="/integration/UI/Widget/Quickstart">
Get started with the Layerswap Widget
Setup the Layerswap iFrame
# Framework Compatibility
Source: https://docs.layerswap.io/integration/UI/Widget/Compatability
Technical requirements and configurations for integrating the Layerswap Widget
The Layerswap Widget is compatible with modern React frameworks. Each framework requires specific configurations to handle Node.js polyfills and package transpilation.
## Framework Configurations
### Next.js (App Router & Page Router)
Next.js requires package transpilation and webpack configuration.
```ts next.config.ts theme={null}
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
webpack: (config) => {
config.resolve.fallback = { fs: false, net: false, tls: false }
config.externals.push('pino-pretty', 'lokijs', 'encoding')
return config
},
transpilePackages: [
'@layerswap/widget',
'@layerswap/wallet-evm',
'@layerswap/wallet-bitcoin',
'@layerswap/wallet-fuel',
'@layerswap/wallet-paradex',
'@layerswap/wallet-starknet',
'@layerswap/wallet-svm',
'@layerswap/wallet-ton',
'@layerswap/wallet-tron',
'@layerswap/wallet-imtbl-x',
'@layerswap/wallet-imtbl-passport',
'@layerswap/wallet-module-zksync',
'@layerswap/wallet-module-loopring',
'@layerswap/wallets'
],
};
export default nextConfig;
```
Next.js App Router Example
***
### Vite
Vite requires Node.js polyfills for browser compatibility.
**Install polyfill plugin:**
```bash theme={null}
npm install -D vite-plugin-node-polyfills
```
**Configuration:**
```ts vite.config.ts theme={null}
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
import { nodePolyfills } from 'vite-plugin-node-polyfills'
export default defineConfig({
plugins: [react(), nodePolyfills()],
esbuild: {
target: 'esnext',
},
})
```
Vite Example
***
### React Router 7
React Router 7 requires global polyfills, SSR configuration, and dependency optimization.
**Install dependencies:**
```bash theme={null}
npm install buffer process stream-browserify
npm install -D vite-tsconfig-paths
```
**Global polyfills in root:**
```tsx app/root.tsx theme={null}
import {Buffer} from 'buffer'
import process from 'process';
import stream from 'stream-browserify';
globalThis.Buffer = Buffer;
globalThis.process = process;
if (typeof globalThis.stream === 'undefined') {
(globalThis as any).stream = stream;
}
// ... rest of your root component
```
**Vite configuration:**
```ts vite.config.ts theme={null}
import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
export default defineConfig({
plugins: [reactRouter(), tsconfigPaths()],
resolve: {
alias: {
"react-router-dom": "react-router",
"stream": "stream-browserify",
},
},
define: {
global: "globalThis",
},
ssr: {
noExternal: [
"@layerswap/widget",
"@layerswap/wallet-evm",
"@layerswap/wallet-svm",
"@layerswap/wallet-bitcoin",
"@layerswap/wallet-starknet",
"@layerswap/wallets",
"@layerswap/wallet-fuel",
"@layerswap/wallet-ton",
"@layerswap/wallet-paradex",
"@layerswap/wallet-imtbl-x",
"@layerswap/wallet-imtbl-passport",
"@layerswap/wallet-module-zksync",
"@layerswap/wallet-module-loopring",
"@layerswap/wallet-tron",
"js-sha3"
],
},
optimizeDeps: {
include: [
"@layerswap/widget",
"@layerswap/wallet-evm",
"@layerswap/wallet-svm",
"@layerswap/wallet-bitcoin",
"@layerswap/wallet-starknet",
"@layerswap/wallets",
"@layerswap/wallet-fuel",
"@layerswap/wallet-ton",
"@layerswap/wallet-paradex",
"@layerswap/wallet-imtbl-x",
"@layerswap/wallet-imtbl-passport",
"@layerswap/wallet-module-zksync",
"@layerswap/wallet-module-loopring",
"@layerswap/wallet-tron",
"js-sha3"
],
},
});
```
React Router 7 Example
***
## Wallet Library Integrations
The widget is compatible with popular wallet connection libraries:
| Library | Example | Notes |
| --------------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| **Default Providers** | [View](https://github.com/layerswap/layerswapapp/tree/main/examples/nextjs-app-router) | Built-in support for all networks via `@layerswap/wallets` |
| **RainbowKit** | [View](https://github.com/layerswap/layerswapapp/tree/main/examples/nextjs-rainbowkit) | Popular wallet UI library with wagmi |
| **Reown AppKit** | [View](https://github.com/layerswap/layerswapapp/tree/main/examples/nextjs-reown) | WalletConnect's official React integration |
| **Dynamic** | [View](https://github.com/layerswap/layerswapapp/tree/main/examples/nextjs-dynamic) | Enterprise wallet connection solution |
***
## All Examples
| Framework | Example | Key Configuration |
| -------------------- | --------------------------------------------------------------------------------------- | --------------------------------------- |
| Next.js App Router | [View](https://github.com/layerswap/layerswapapp/tree/main/examples/nextjs-app-router) | `transpilePackages` + webpack fallbacks |
| Next.js Page Router | [View](https://github.com/layerswap/layerswapapp/tree/main/examples/nextjs-page-router) | `transpilePackages` + webpack fallbacks |
| Vite | [View](https://github.com/layerswap/layerswapapp/tree/main/examples/vite) | `vite-plugin-node-polyfills` |
| React Router 7 | [View](https://github.com/layerswap/layerswapapp/tree/main/examples/react-router-7) | Global polyfills + SSR config |
| Next.js + RainbowKit | [View](https://github.com/layerswap/layerswapapp/tree/main/examples/nextjs-rainbowkit) | Next.js config + wagmi setup |
| Next.js + Reown | [View](https://github.com/layerswap/layerswapapp/tree/main/examples/nextjs-reown) | Next.js config + Reown AppKit |
| Next.js + Dynamic | [View](https://github.com/layerswap/layerswapapp/tree/main/examples/nextjs-dynamic) | Next.js config + Dynamic SDK |
All examples include working implementations with proper TypeScript configurations.
# Color Customization
Source: https://docs.layerswap.io/integration/UI/Widget/Customization/Colors
Complete guide to customizing widget colors and generating color palettes
## Overview
Color customization is the foundation of matching the Layerswap Widget to your branding. The widget supports comprehensive color theming including primary and secondary palettes, status colors, and text colors.
## Generating Color Palettes
Creating consistent color shades (100-900) for your theme is easy with **Tailwind Shades**:
Generate color palettes with 10 shades from a single base color. Ideal for creating consistent primary and secondary color schemes.
### How to Use Tailwind Shades
1. Visit [tailwindshades.app](https://tailwindshades.app/)
2. Enter your brand's primary color (hex, RGB, or HSL)
3. The tool generates 10 shades (100-900) plus a DEFAULT shade
4. Copy the generated colors and convert to Layerswap format
### Converting Tailwind Colors to Layerswap Format
Tailwind Shades provides colors in various formats. For Layerswap, use the **`RGB values without the rgb() wrapper`**:
**Tailwind Shades Output:**
```
rgb(99, 102, 241)
```
**Layerswap Format:**
```typescript theme={null}
'99, 102, 241'
```
**Example:**
```typescript theme={null}
// From Tailwind Shades
const tailwindColor = {
100: 'rgb(224, 231, 255)',
200: 'rgb(199, 210, 254)',
500: 'rgb(99, 102, 241)',
// ... etc
}
// Convert to Layerswap format
const layerswapPrimary = {
'100': '224, 231, 255',
'200': '199, 210, 254',
'500': '99, 102, 241',
// ... etc
}
```
## Color Types
### ThemeColor Type
Primary and secondary colors follow the `ThemeColor` structure with 10 shades:
```typescript theme={null}
export type ThemeColor = {
DEFAULT: string;
100: string; // Lightest
200: string;
300: string;
400: string;
500: string; // Base shade
600: string;
700: string;
800: string;
900: string; // Darkest
text: string; // Text color for this palette
}
```
### StatusColor Type
Status colors (warning, error, success) use a simpler structure:
```typescript theme={null}
export type StatusColor = {
Foreground: string; // Text color
Background: string; // Background color
}
```
## Primary Colors
Primary color palette used for main UI elements. Includes 10 shades (100-900), a DEFAULT value, and a text color.
**Example:**
```typescript theme={null}
primary: {
DEFAULT: '99, 102, 241', // Main primary color
'100': '224, 231, 255', // Lightest shade
'200': '199, 210, 254',
'300': '165, 180, 252',
'400': '129, 140, 248',
'500': '99, 102, 241', // Base shade
'600': '79, 70, 229',
'700': '67, 56, 202',
'800': '55, 48, 163',
'900': '49, 46, 129', // Darkest shade
'text': '255, 255, 255', // Text color on primary backgrounds
}
```
**Usage:**
* Main action buttons
* Links and interactive elements
* Primary highlights and accents
* Progress indicators
## Secondary Colors
Secondary color palette used for backgrounds, cards, and supporting UI elements. Follows the same structure as primary colors.
**Example:**
```typescript theme={null}
secondary: {
DEFAULT: '30, 41, 59',
'100': '241, 245, 249', // Lightest (for light themes)
'200': '226, 232, 240',
'300': '203, 213, 225',
'400': '148, 163, 184',
'500': '100, 116, 139',
'600': '71, 85, 105',
'700': '51, 65, 85',
'800': '30, 41, 59', // Darkest (for dark themes)
'900': '15, 23, 42',
'text': '148, 163, 184', // Text color on secondary backgrounds
}
```
**Usage:**
* Card backgrounds
* Panel surfaces
* Secondary text and labels
* Borders and dividers
## Tertiary Color
Tertiary color used for borders, dividers, and subtle UI elements. Single RGB value.
**Example:**
```typescript theme={null}
tertiary: '148, 163, 184'
```
**Usage:**
* Borders
* Dividers
* Input field outlines
* Decorative elements
## Button Text Color
Text color specifically for buttons. Overrides the primary text color for button labels.
**Example:**
```typescript theme={null}
buttonTextColor: '255, 255, 255'
```
## Status Colors
Status colors are used for displaying different states like warnings, errors, and success messages. Each status color has foreground (text) and background values.
### Warning Colors
Warning color with foreground (text) and background values.
```typescript theme={null}
warning: {
Foreground: '234, 179, 8', // Warning text color (yellow/amber)
Background: '254, 252, 232' // Warning background color (light yellow)
}
```
### Error Colors
Error color with foreground (text) and background values.
```typescript theme={null}
error: {
Foreground: '239, 68, 68', // Error text color (red)
Background: '254, 242, 242' // Error background color (light red)
}
```
### Success Colors
Success color with foreground (text) and background values.
```typescript theme={null}
success: {
Foreground: '34, 197, 94', // Success text color (green)
Background: '240, 253, 244' // Success background color (light green)
}
```
## Color Format Requirements
**Important**: All colors must be in RGB format **without** the `rgb()` wrapper. Use `'99, 102, 241'` instead of `'rgb(99, 102, 241)'`.
The widget internally handles the color formatting, so you only need to provide the numeric RGB values as strings.
**Correct Format:**
```typescript theme={null}
primary: {
DEFAULT: '99, 102, 241',
'500': '99, 102, 241',
// ...
}
```
**Incorrect Format:**
```typescript theme={null}
primary: {
DEFAULT: 'rgb(99, 102, 241)', // ❌ Don't include rgb()
'500': '#6366f1', // ❌ Don't use hex
// ...
}
```
## Complete Color Configuration Example
```typescript theme={null}
const colorTheme = {
// Primary palette
primary: {
DEFAULT: '99, 102, 241',
'100': '224, 231, 255',
'200': '199, 210, 254',
'300': '165, 180, 252',
'400': '129, 140, 248',
'500': '99, 102, 241',
'600': '79, 70, 229',
'700': '67, 56, 202',
'800': '55, 48, 163',
'900': '49, 46, 129',
'text': '255, 255, 255',
},
// Secondary palette
secondary: {
DEFAULT: '30, 41, 59',
'100': '241, 245, 249',
'200': '226, 232, 240',
'300': '203, 213, 225',
'400': '148, 163, 184',
'500': '100, 116, 139',
'600': '71, 85, 105',
'700': '51, 65, 85',
'800': '30, 41, 59',
'900': '15, 23, 42',
'text': '148, 163, 184',
},
// Single colors
tertiary: '148, 163, 184',
buttonTextColor: '255, 255, 255',
// Status colors
warning: {
Foreground: '234, 179, 8',
Background: '254, 252, 232'
},
error: {
Foreground: '239, 68, 68',
Background: '254, 242, 242'
},
success: {
Foreground: '34, 197, 94',
Background: '240, 253, 244'
}
};
```
## Next Steps
Configure borders, headers, and other theme elements
Browse complete theme examples
Experiment with colors in real-time
Create color schemes with Tailwind Shades
# Customization Overview
Source: https://docs.layerswap.io/integration/UI/Widget/Customization/CustomizationIntroduction
How to customize the Layerswap Widget appearance and functionalities
## Overview
The Layerswap Widget offers extensive customization options, allowing you to tailor both the appearance and functionalities to match your application's needs. Customize colors, layouts, header visibility, border radius, and more to create a seamless and branded experience.
## Interactive Playground
Experiment with all customization options in real-time. Test colors, layouts, styles, and configurations with live preview before implementing in your application.
## Quick Start
Here's a basic example of customizing the widget with a custom theme:
```typescript theme={null}
import { LayerswapProvider, Swap } from '@layerswap/widget';
import { createEVMProvider } from '@layerswap/wallet-evm';
export default function App() {
const customTheme = {
primary: {
DEFAULT: '99, 102, 241',
'500': '99, 102, 241',
'text': '255, 255, 255',
},
secondary: {
DEFAULT: '30, 41, 59',
'500': '30, 41, 59',
'text': '148, 163, 184',
}
};
return (
);
}
```
## Customization Options
The following customization options are available for integrators:
### Colors & Themes
Customize the entire color palette of the widget to match your brand. Learn more in the [Color Customization](/integration/UI/Widget/Customization/Colors) section.
**Key Features:**
* Primary and secondary color palettes
* Status colors for warnings, errors, and success states
* Button and text colors
* Seamless integration with [Tailwind Shades](https://tailwindshades.app/) for generating color palettes
### Theme Configuration
Configure layout, borders, header visibility, and advanced styling options. Learn more in the [Theme Configuration](/integration/UI/Widget/Customization/ThemeConfiguration) section.
**Key Features:**
* Border radius options
* Header customization (hide menu, tabs, wallets)
* Card background styling with custom CSS
* Remove "Powered by Layerswap" branding
### Complete Examples
Browse ready-to-use theme examples including dark, light, and minimalist themes. See the [Theme Examples](/integration/UI/Widget/Customization/ThemeExamples) section.
## Next Steps
Customize colors and generate color palettes
Configure borders, headers, and other theme elements
Browse complete theme examples for different use cases
## Additional Resources
Test configurations interactively with live preview
Generate color palettes from a single color
# Theme Configuration
Source: https://docs.layerswap.io/integration/UI/Widget/Customization/ThemeConfiguration
Configure borders, headers, layouts, and other elements
## Overview
Beyond colors, the Layerswap Widget offers extensive configuration options for layout, borders, header visibility, and custom styling. These options allow you to control the widget's structure and behavior to match your application's design system.
## Theme Type Definition
The complete theme configuration type:
```typescript theme={null}
export type ThemeData = {
buttonTextColor?: string,
tertiary?: string,
primary?: ThemeColor,
secondary?: ThemeColor,
warning?: StatusColor,
error?: StatusColor,
success?: StatusColor,
borderRadius?: 'none' | 'small' | 'medium' | 'large' | 'extraLarge' | 'default',
header?: {
hideMenu?: boolean,
hideTabs?: boolean,
hideWallets?: boolean,
},
cardBackgroundStyle?: React.CSSProperties,
hidePoweredBy?: boolean
}
```
## Border Radius
Controls the roundness of corners for cards, buttons, and other UI elements throughout the widget.
### Available Options
| Value | Border Radius | Use Case |
| -------------- | -------------- | --------------------------- |
| `'none'` | 0px | Sharp, modern designs |
| `'small'` | 4px | Subtle roundness |
| `'medium'` | 8px | Balanced appearance |
| `'large'` | 12px | Soft, friendly design |
| `'extraLarge'` | 16px | Maximum roundness |
| `'default'` | Widget default | Use widget's built-in style |
**Example:**
```typescript theme={null}
const theme = {
borderRadius: 'medium',
// ... other theme properties
}
```
**Visual Impact:**
* Affects all cards, modals, and containers
* Applies to buttons and interactive elements
* Influences input fields and dropdowns
* Consistent across all widget components
## Header Customization
Configuration object for widget header visibility options. Control which elements appear in the widget header.
### Hide Menu
Hide the menu button in the widget header. Set to `true` to remove the menu icon.
```typescript theme={null}
header: {
hideMenu: true
}
```
### Hide Tabs
Hide the tab switcher (swap / cex / deposit) in the widget header. Set to `true` to lock users into a specific flow.
```typescript theme={null}
header: {
hideTabs: true
}
```
When `hideTabs` is `true`, users can only access the flow specified in `initialValues.defaultTab`. See [Tab Options](/integration/UI/Widget/TabOptions) for more details.
### Hide Wallets
Hide the wallet connection display in the widget header. Set to `true` to remove the wallet indicator.
```typescript theme={null}
header: {
hideWallets: true
}
```
### Complete Header Example
```typescript theme={null}
const theme = {
header: {
hideMenu: true,
hideTabs: true,
hideWallets: true,
},
// ... other theme properties
}
```
This configuration creates a minimal header with no menu, no tab switcher, and no wallet display.
## Card Background Style
Custom CSS styles for card backgrounds. Accepts any valid React CSS properties, enabling transparent backgrounds, blur effects, borders, and more.
### Basic Transparent Background
```typescript theme={null}
cardBackgroundStyle: {
backgroundColor: "transparent"
}
```
### Glassmorphism Effect
```typescript theme={null}
cardBackgroundStyle: {
backgroundColor: "rgba(255, 255, 255, 0.01)",
backdropFilter: "blur(20px)",
border: "1px solid rgba(255, 255, 255, 0.1)"
}
```
### Custom Gradient
```typescript theme={null}
cardBackgroundStyle: {
background: "linear-gradient(135deg, rgba(99, 102, 241, 0.1) 0%, rgba(139, 92, 246, 0.1) 100%)",
border: "1px solid rgba(99, 102, 241, 0.2)"
}
```
### With Shadow
```typescript theme={null}
cardBackgroundStyle: {
backgroundColor: "#1a1a2e",
boxShadow: "0 20px 60px rgba(0, 0, 0, 0.3)",
border: "1px solid rgba(255, 255, 255, 0.05)"
}
```
**Supported Properties:**
* `backgroundColor` / `background`
* `backdropFilter`
* `border` / `borderRadius`
* `boxShadow`
* `padding` / `margin`
* Any valid CSS property that works with React's `style` prop
The `cardBackgroundStyle` applies to the main widget card container. It does not override the `borderRadius` theme property - use the `borderRadius` field for consistent corner styling.
## Hide Powered By
Remove the "Powered by Layerswap" branding from the widget footer. Set to `true` to hide the attribution.
```typescript theme={null}
const theme = {
hidePoweredBy: true,
// ... other theme properties
}
```
## Complete Configuration Example
Here's a complete example combining all configuration options:
```typescript theme={null}
import { LayerswapProvider, Swap } from '@layerswap/widget';
import { createEVMProvider } from '@layerswap/wallet-evm';
export default function App() {
const customTheme = {
// Colors (see Colors documentation)
primary: {
DEFAULT: "99, 102, 241",
500: "99, 102, 241",
text: "255, 255, 255"
},
secondary: {
DEFAULT: "30, 41, 59",
500: "30, 41, 59",
text: "148, 163, 184"
},
tertiary: "148, 163, 184",
buttonTextColor: "255, 255, 255",
// Layout & Structure
borderRadius: "large",
// Header Configuration
header: {
hideMenu: true,
hideTabs: false,
hideWallets: false,
},
// Advanced Styling
cardBackgroundStyle: {
backgroundColor: "rgba(255, 255, 255, 0.05)",
backdropFilter: "blur(10px)",
border: "1px solid rgba(255, 255, 255, 0.1)"
},
// Branding
hidePoweredBy: false
};
return (
);
}
```
## Testing Your Configuration
Experiment with the configuration options in real-time before implementing in your application.
# Theme Examples
Source: https://docs.layerswap.io/integration/UI/Widget/Customization/ThemeExamples
Ready-to-use theme examples for different use cases
## Overview
This page provides complete, production-ready theme examples that you can use as starting points for your own customization. Each example includes full color palettes, configuration options, and usage guidance.
## Dark Theme
Default dark theme example
```typescript theme={null}
const config = {
theme: {
tertiary: "118, 128, 147",
buttonTextColor: "228, 229, 240",
borderRadius: "medium",
warning: {
Foreground: "255, 201, 74",
Background: "47, 43, 29"
},
error: {
Foreground: "255, 97, 97",
Background: "46, 27, 27"
},
success: {
Foreground: "89, 224, 125",
Background: "14, 43, 22"
},
primary: {
100: "255, 148, 176",
200: "245, 103, 141",
300: "235, 84, 129",
400: "229, 64, 114",
500: "204, 45, 93",
600: "178, 29, 74",
700: " 143, 23, 59",
800: "89, 14, 37",
900: "46, 7, 19",
DEFAULT: "204, 45, 93",
text: "225, 227, 230"
},
secondary: {
100: "60, 72, 97",
200: "52, 63, 87",
300: "40, 50, 71",
400: "31, 40, 61",
500: "23, 31, 49",
600: "18, 25, 41",
700: "14, 21, 36",
800: "11, 17, 31",
900: "7, 12, 23",
DEFAULT: "17, 29, 54",
text: "163, 173, 194"
}
}
};
```
## Light Theme
A light theme optimized for bright backgrounds:
Default light theme example
```typescript theme={null}
const config = {
theme: {
tertiary: "86, 97, 123",
buttonTextColor: "17, 17, 17",
borderRadius: "medium",
warning: {
Foreground: "200, 130, 0",
Background: "255, 250, 230"
},
error: {
Foreground: "220, 50, 50",
Background: "255, 240, 240"
},
success: {
Foreground: "40, 180, 80",
Background: "235, 255, 240"
},
primary: {
100: "255, 240, 248",
200: "252, 210, 230",
300: "247, 173, 210",
400: "239, 131, 178",
500: "228, 37, 117",
600: "195, 30, 95",
700: "156, 24, 76",
800: "117, 18, 57",
900: "78, 12, 38",
DEFAULT: "228, 37, 117",
text: "10, 10, 10"
},
secondary: {
100: "255, 255, 255",
200: "245, 247, 252",
300: "235, 238, 245",
400: "223, 227, 238",
500: "210, 215, 230",
600: "190, 196, 214",
700: "168, 176, 199",
800: "140, 150, 175",
900: "110, 121, 150",
DEFAULT: "240, 243, 248",
text: "40, 50, 70"
}
}
};
```
## Brand Theme
## Minimalist Transparent Theme
A seamless integration with transparent background:
Default minimalist transparent theme example
```typescript theme={null}
const config = {
theme: {
tertiary: "182, 182, 182",
buttonTextColor: "19, 19, 19",
cardBackgroundStyle: {
backgroundColor: "transparent"
},
header: {
hideMenu: true,
hideTabs: true,
hideWallets: true
},
primary: {
100: "255, 255, 255",
200: "255, 255, 255",
300: "255, 255, 255",
400: "255, 255, 255",
500: "243, 243, 243",
600: "215, 215, 215",
700: "187, 187, 187",
800: "159, 159, 159",
900: "131, 131, 131",
DEFAULT: "243, 243, 243",
text: "243, 243, 243"
},
secondary: {
100: "119, 119, 119",
200: "98, 98, 98",
300: "78, 78, 78",
400: "57, 57, 57",
500: "37, 37, 37",
600: "13, 13, 13",
700: "13, 13, 13",
800: "13, 13, 13",
900: "0, 0, 0",
DEFAULT: "37, 37, 37",
text: "182, 182, 182"
}
}
};
```
## Complete Integration Example
Here's how to use these themes in your application:
```typescript theme={null}
import { LayerswapProvider, Swap } from '@layerswap/widget';
import { createEVMProvider } from '@layerswap/wallet-evm';
import { darkTheme } from './themes/darkTheme'; // Import your chosen theme
export default function App() {
return (
);
}
```
## Testing Your Theme
Copy any of these themes into the playground to see how they look with real widget components before implementing.
## Next Steps
Customize colors and generate color palettes
Explore all configuration options
Generate custom color palettes from a single color
# Deposit Widget
Source: https://docs.layerswap.io/integration/UI/Widget/DepositWidget
A drop-in component that lets your users fund a fixed address — from a connected wallet or from any wallet or exchange via a deposit address.
The **Deposit Widget** is a standalone, self-contained `` component for letting your users send funds to an address. You hard-code the recipient address, the destination network, and the tokens you accept; the end user only decides **how to fund** the deposit. It can render inline on your page, or as a button that opens the widget in a dialog.
## How it works
When wallet providers are configured, the user lands on a method picker offering a few ways to fund the deposit:
* **Wallet transfer** — the user connects a wallet, picks a source token (only routes the wallet supports and has a balance for are shown), enters an amount, reviews the quote, and executes the transfer.
* **Deposit address** — the widget generates a deposit address with a QR code. The user pays from any wallet app, hardware wallet, or exchange account. No source-side wallet integration is required.
* **Deposit from Hyperliquid** — when an EVM provider is configured and the destination is reachable from Hyperliquid, the user can fund the deposit straight from their Hyperliquid balance, without copying an address or leaving the widget. See [Deposit from Hyperliquid](#deposit-from-hyperliquid).
The destination network is fixed by the `destination` prop; the accepted tokens come from its `tokens` allow-list. When more than one token is accepted, the user gets a "You receive" picker to choose between them; with a single token the picker is hidden and that token is used automatically.
If you pass no `walletProviders`, the **Wallet transfer** method is unavailable, so the method picker is skipped entirely — the widget opens straight on the deposit address flow. This gives you a deposit-address-only integration with zero wallet dependencies.
## Installation
Import the component from the dedicated `@layerswap/widget/deposit` entry point along with the widget styles:
```typescript theme={null}
import { Deposit } from '@layerswap/widget/deposit';
import '@layerswap/widget/index.css';
```
See the [Quickstart](/integration/UI/Widget/Quickstart) for installing `@layerswap/widget` and the wallet provider packages.
## Usage
A complete example rendering the widget as a button that opens a dialog:
```tsx theme={null}
import { Deposit } from '@layerswap/widget/deposit';
import '@layerswap/widget/index.css';
import { createEVMProvider } from '@layerswap/wallet-evm';
export default function Fund() {
return (
);
}
```
To embed the widget directly on the page instead of behind a button, use `mode="inline"` (the default). This example also accepts multiple tokens, so the user gets a token picker:
```tsx theme={null}
```
### Deposit address only (no wallet providers)
Omit `walletProviders` to skip the method picker and open straight on the deposit address flow:
```tsx theme={null}
```
### Choosing which funding methods to show
By default every available method is offered. Pass `methods` as an allow-list to narrow them — only the listed methods can appear, and each still needs its own runtime conditions (e.g. `wallet` needs `walletProviders`, `hyperliquid` needs a reachable destination). For example, to offer wallet and deposit-address funding but hide the Hyperliquid option:
```tsx theme={null}
```
### Inside an existing LayerswapProvider
If your app already renders `LayerswapProvider` (for example alongside ``), use `DepositComponent` instead — it's the same widget without the built-in provider. Pass `DepositLoading` as the provider's `loadingComponent` so the init state matches the deposit layout:
```tsx theme={null}
import { LayerswapProvider } from '@layerswap/widget';
import { DepositComponent, DepositLoading } from '@layerswap/widget/deposit';
}}
walletProviders={[createEVMProvider()]}
>
```
## Deposit from Hyperliquid
Available in `@layerswap/widget` 1.5.0 and later.
If the user has funds on **Hyperliquid**, the method picker can offer a **Deposit from Hyperliquid** option that funds the deposit straight from their Hyperliquid balance — no address to copy and no leaving the widget.
It's enabled by including an EVM provider (`createEVMProvider()`) in `walletProviders`. There's no separate prop to turn it on — the widget shows the option automatically whenever the destination can be reached from Hyperliquid.
```tsx theme={null}
import { Deposit } from '@layerswap/widget/deposit';
import '@layerswap/widget/index.css';
import { createEVMProvider } from '@layerswap/wallet-evm';
```
To hide it, exclude `'hyperliquid'` from the [`methods`](#choosing-which-funding-methods-to-show) allow-list.
## Props
`` accepts the deposit props below plus the `LayerswapProvider` props (`config`, `callbacks`, `walletProviders`), since it renders the provider internally. `DepositComponent` accepts only the deposit props.
The single destination network and its allowed tokens. The network is fixed; the user picks one of the tokens via the token dropdown (hidden when only one token is accepted). See [SupportedDestination](#supporteddestination) for the shape.
The recipient address on the destination network. Fixed by you — the deposit widget never asks the user for it.
`"inline"` renders the widget directly on the page. `"button"` renders a Deposit button that opens the widget inside a dialog.
Title shown in the widget header.
Label for the trigger button. Only applies when `mode="button"`.
Extra `className` applied to the trigger button. Only applies when `mode="button"`.
When `true`, shows the "Send to" destination address row in the quote summary. Hidden by default because the recipient is your own fixed address and the row is usually redundant for the user — opt in only if you want to surface the destination address explicitly.
Custom label for the action (submit) button inside the flow.
Default amount (in USD) seeded into the wallet flow once the user picks a source token. Set to `0` to disable seeding.
Allow-list of funding methods the picker may show — `"wallet"`, `"deposit_address"`, and `"hyperliquid"`. Only listed methods can appear, and each still needs its own runtime conditions to be offered (for example, `"hyperliquid"` only shows when an EVM provider is configured and the destination is reachable). Defaults to every method. The `DepositMethodId` type and the full `DEPOSIT_METHODS` list are exported from `@layerswap/widget`.
Provider configuration — `apiKey`, `version`, `theme`, `settings`, etc. Only on `` (not `DepositComponent`). See the [Quickstart](/integration/UI/Widget/Quickstart) for details.
Wallet providers available for the **Wallet transfer** method. Including an EVM provider (`createEVMProvider()`) also unlocks [Deposit from Hyperliquid](#deposit-from-hyperliquid). Only on `` (not `DepositComponent`). When omitted, the widget runs in deposit-address-only mode. See [Wallet Management](/integration/UI/Widget/WalletManagement/WalletManagement).
Event callbacks. Only on `` (not `DepositComponent`). See [Event Callbacks](/integration/UI/Widget/EventCallbacks/EventsIntroduction).
### SupportedDestination
```typescript theme={null}
type SupportedDestination = {
/** Network name — the canonical identifier, e.g. `BASE_MAINNET`. */
network: string;
/** Token symbols, case-insensitive, e.g. `["USDC", "USDT"]`. The user picks
* one of these via the token dropdown; the network is fixed. */
tokens: string[];
};
```
Tokens that don't match an active token on the network are dropped. If only one valid token remains, the token picker is hidden.
While a transfer is in flight, the dialog's close button is hidden and clicking outside the dialog doesn't dismiss it, so the user can't accidentally abandon an in-progress deposit.
# Events Introduction
Source: https://docs.layerswap.io/integration/UI/Widget/EventCallbacks/EventsIntroduction
Subscribe to widget events and receive swap status updates
Events provide a way to receive updates on the status of swaps and handle errors through custom callbacks.
You do not need to import anything extra to make use of event callbacks, instead simply add them using the events field inside the settings prop on LayerswapProvider.
```jsx theme={null}
...
```
[**onFormChange**](/integration/UI/Widget/EventCallbacks/onFormChange): Called whenever the swap form values change — for example, when the user updates the asset, amount, or destination network.
[**onSwapCreate**](/integration/UI/Widget/EventCallbacks/onSwapCreate): Called when a new swap is successfully created after submitting the form.
[**onSwapComplete**](/integration/UI/Widget/EventCallbacks/onSwapComplete): Called when a swap completes successfully on-chain or via an integrated exchange.
[**onSwapModalStateChange**](/integration/UI/Widget/EventCallbacks/onSwapModalStateChange): Called when the swap modal is opened or closed.
[**onBackClick**](/integration/UI/Widget/EventCallbacks/onBackClick): Called when the user clicks the back button in the swap flow.
# onBackClick
Source: https://docs.layerswap.io/integration/UI/Widget/EventCallbacks/onBackClick
Triggered when the user clicks the back button.
The `onBackClick` event fires when the user clicks the back button inside the widget.
```tsx theme={null}
{
console.log("User navigated back")
},
}}
>
```
### Callback Argument Value
None
# onFormChange
Source: https://docs.layerswap.io/integration/UI/Widget/EventCallbacks/onFormChange
Triggered whenever the swap form values change.
The `onFormChange` event fires whenever the user modifies the swap form — for example, when changing the asset, amount, or destination network.
```tsx theme={null}
import { CallbackProvider } from "@/context/CallbackContext"
{
console.log("Form updated:", formData)
},
}}
>
```
### Callback Argument Value
```TypeScript theme={"system"} theme={null}
formData: {
from: string;
to: string;
fromAsset: string;
toAsset: string;
amount: string;
destination_address: string;
refuel?: boolean;
}
```
# onSwapComplete
Source: https://docs.layerswap.io/integration/UI/Widget/EventCallbacks/onSwapComplete
Triggered when a swap completes successfully.
The `onSwapComplete` event fires when a swap transitions to a **completed** state.
```tsx theme={null}
{
console.log("Swap completed:", swap)
},
}}
>
```
### Callback Argument Value
```TypeScript theme={"system"} theme={null}
type SwapResponse = {
deposit_actions?: DepositAction[];
swap: SwapItem;
quote: SwapQuote
refuel?: Refuel,
}
```
```TypeScript theme={"system"} theme={null}
type DepositAction = {
amount: number,
amount_in_base_units: string,
call_data: string,
fee: number | null,
network: Network,
order: number,
to_address?: string,
token: Token,
fee_token: Token,
type: 'transfer' | 'manual_transfer',
}
```
```TypeScript theme={"system"} theme={null}
type SwapItem = {
id: string,
created_date: string,
source_network: Network,
source_token: Token,
source_exchange?: Exchange,
destination_network: Network,
destination_token: Token,
destination_address: string,
requested_amount: number,
use_deposit_address: boolean
status: SwapStatus,
transactions: Transaction[]
exchange_account_connected: boolean;
exchange_account_name?: string;
fail_reason?: string;
metadata: {
reference_id: string | null;
app: string | null;
sequence_number: number
},
destination_exchange?: Exchange,
}
enum SwapStatus {
Created = 'created',
UserTransferPending= 'user_transfer_pending',
UserTransferDelayed = 'user_transfer_delayed',
LsTransferPending = "ls_transfer_pending",
Completed = 'completed',
Failed = 'failed',
Expired = "expired",
Cancelled = "cancelled",
PendingRefund = "pending_refund",
Refunded = "refunded",
}
type Transaction = {
type: TransactionType,
from: string,
to: string,
created_date: string,
amount: number,
transaction_hash: string,
confirmations: number,
max_confirmations: number,
usd_value: number,
usd_price: number,
status: BackendTransactionStatus,
fee_amount?: number | null,
fee_token?: Token,
timestamp?: string,
}
enum TransactionType {
Input = 'input',
Output = 'output',
Refuel = 'refuel',
Refund = 'refund'
}
enum BackendTransactionStatus {
Completed = 'completed',
Failed = 'failed',
Initiated = 'initiated',
Pending = 'pending'
}
```
```TypeScript theme={"system"} theme={null}
type Network = {
name: string;
display_name: string;
logo: string;
chain_id: string | null;
node_url: string;
type: NetworkType;
transaction_explorer_template: string;
account_explorer_template: string;
metadata?: Metadata;
deposit_methods: string[]
token?: Token
source_rank?: number | null;
destination_rank?: number | null;
}
enum NetworkType {
EVM = "evm",
Starknet = "starknet",
Solana = "solana",
Cosmos = "cosmos",
StarkEx = "starkex",
ZkSyncLite = "zksynclite",
TON = 'ton',
Fuel = 'fuel',
Bitcoin = 'bitcoin'
}
type Metadata = {
evm_oracle_contract?: string | null
evm_multicall_contract?: string | null
listing_date: string
zks_paymaster_contract?: string | null
watchdog_contract?: string | null
}
type Token = {
symbol: string;
display_asset?: string
logo: string;
contract: string | null;
decimals: number;
price_in_usd: number;
precision: number;
listing_date: string;
status?: 'active' | 'inactive' | 'not_found';
source_rank?: number | null;
destination_rank?: number | null;
}
type Exchange = {
display_name: string;
name: string;
logo: string;
}
type ExchangeNetwork = {
token: Token;
network: Network;
}
```
```TypeScript theme={"system"} theme={null}
type SwapQuote = {
source_network?: Network,
source_token?: Token,
destination_network?: Network,
destination_token?: Token,
requested_amount?: number
receive_amount: number,
min_receive_amount: number,
fee_discount?: number
total_fee: number,
total_fee_in_usd: number,
blockchain_fee: number,
service_fee: number,
avg_completion_time: string,
refuel_in_source?: number,
slippage?: number,
}
```
```TypeScript theme={"system"} theme={null}
type Refuel = {
network: Network
token: Token,
amount: number,
amount_in_usd: number
}
```
# onSwapCreate
Source: https://docs.layerswap.io/integration/UI/Widget/EventCallbacks/onSwapCreate
Triggered when a new swap is created.
The `onSwapCreate` event is called when the user submits the form and a swap is successfully created through the API.
```tsx theme={null}
{
console.log("Swap created:", swap)
},
}}
>
```
### Callback Argument Value
```TypeScript theme={"system"} theme={null}
type SwapResponse = {
deposit_actions?: DepositAction[];
swap: SwapItem;
quote: SwapQuote
refuel?: Refuel,
}
```
```TypeScript theme={"system"} theme={null}
type DepositAction = {
amount: number,
amount_in_base_units: string,
call_data: string,
fee: number | null,
network: Network,
order: number,
to_address?: string,
token: Token,
fee_token: Token,
type: 'transfer' | 'manual_transfer',
}
```
```TypeScript theme={"system"} theme={null}
type SwapItem = {
id: string,
created_date: string,
source_network: Network,
source_token: Token,
source_exchange?: Exchange,
destination_network: Network,
destination_token: Token,
destination_address: string,
requested_amount: number,
use_deposit_address: boolean
status: SwapStatus,
transactions: Transaction[]
exchange_account_connected: boolean;
exchange_account_name?: string;
fail_reason?: string;
metadata: {
reference_id: string | null;
app: string | null;
sequence_number: number
},
destination_exchange?: Exchange,
}
enum SwapStatus {
Created = 'created',
UserTransferPending= 'user_transfer_pending',
UserTransferDelayed = 'user_transfer_delayed',
LsTransferPending = "ls_transfer_pending",
Completed = 'completed',
Failed = 'failed',
Expired = "expired",
Cancelled = "cancelled",
PendingRefund = "pending_refund",
Refunded = "refunded",
}
type Transaction = {
type: TransactionType,
from: string,
to: string,
created_date: string,
amount: number,
transaction_hash: string,
confirmations: number,
max_confirmations: number,
usd_value: number,
usd_price: number,
status: BackendTransactionStatus,
fee_amount?: number | null,
fee_token?: Token,
timestamp?: string,
}
enum TransactionType {
Input = 'input',
Output = 'output',
Refuel = 'refuel',
Refund = 'refund'
}
enum BackendTransactionStatus {
Completed = 'completed',
Failed = 'failed',
Initiated = 'initiated',
Pending = 'pending'
}
```
```TypeScript theme={"system"} theme={null}
type Network = {
name: string;
display_name: string;
logo: string;
chain_id: string | null;
node_url: string;
type: NetworkType;
transaction_explorer_template: string;
account_explorer_template: string;
metadata?: Metadata;
deposit_methods: string[]
token?: Token
source_rank?: number | null;
destination_rank?: number | null;
}
enum NetworkType {
EVM = "evm",
Starknet = "starknet",
Solana = "solana",
Cosmos = "cosmos",
StarkEx = "starkex",
ZkSyncLite = "zksynclite",
TON = 'ton',
Fuel = 'fuel',
Bitcoin = 'bitcoin'
}
type Metadata = {
evm_oracle_contract?: string | null
evm_multicall_contract?: string | null
listing_date: string
zks_paymaster_contract?: string | null
watchdog_contract?: string | null
}
type Token = {
symbol: string;
display_asset?: string
logo: string;
contract: string | null;
decimals: number;
price_in_usd: number;
precision: number;
listing_date: string;
status?: 'active' | 'inactive' | 'not_found';
source_rank?: number | null;
destination_rank?: number | null;
}
type Exchange = {
display_name: string;
name: string;
logo: string;
}
type ExchangeNetwork = {
token: Token;
network: Network;
}
```
```TypeScript theme={"system"} theme={null}
type SwapQuote = {
source_network?: Network,
source_token?: Token,
destination_network?: Network,
destination_token?: Token,
requested_amount?: number
receive_amount: number,
min_receive_amount: number,
fee_discount?: number
total_fee: number,
total_fee_in_usd: number,
blockchain_fee: number,
service_fee: number,
avg_completion_time: string,
refuel_in_source?: number,
slippage?: number,
}
```
```TypeScript theme={"system"} theme={null}
type Refuel = {
network: Network
token: Token,
amount: number,
amount_in_usd: number
}
```
# onSwapModalStateChange
Source: https://docs.layerswap.io/integration/UI/Widget/EventCallbacks/onSwapModalStateChange
Triggered when the swap modal opens or closes.
The `onSwapModalStateChange` event fires whenever the swap modal is opened or closed.
```tsx theme={null}
{
console.log("Modal state:", open ? "open" : "closed")
},
}}
>
```
### Callback Argument Value
```TypeScript theme={"system"} theme={null}
open: boolean
```
# Widget Quickstart
Source: https://docs.layerswap.io/integration/UI/Widget/Quickstart
Interactive quick-start for the Layerswap Widget.
### Install Widget
Use the **Widget type** toggle to switch between:
* **Swap** — the full swap widget (``) wrapped in `LayerswapProvider`.
* **Deposit** — the standalone `` widget for funding a fixed address you control. See [Deposit Widget](/integration/UI/Widget/DepositWidget) for the full reference.
[**Zustand**](https://zustand.docs.pmnd.rs/getting-started/introduction) is a lightweight state management library for React applications.
ParadexProvider is dependant on EVMProvider and StarknetProvider
### Configuration
`YOUR_API_KEY` is used for accessing the widget. You can generate and input the [**API key**](/api-keys) from the Partner Dashboard.
**WalletConnect Project ID:** If you selected EVM, Starknet, or Solana providers, see [WalletConnect configuration](/integration/UI/Widget/WalletManagement/EVMProvider#configuration) to get your Project ID.
**TON Configuration:** If you selected the TON provider, see [TON configuration](/integration/UI/Widget/WalletManagement/TonProvider#configuration) to set up your API key and manifest URL.
For further adjustments, refer to the optional [configurations](/integration/UI/Configurations) list.
# Integration guide for Starknet dApps
Source: https://docs.layerswap.io/integration/UI/Widget/Starknet/Starknet
Complete guide for integrating and customizing Layerswap Widget for Starknet dApps
## Quick Start
### Installation
Install the Layerswap Widget along with the Starknet provider.
```typescript npm theme={null}
npm install @layerswap/widget zustand@4.5.7 @layerswap/wallet-starknet
```
```typescript yarn theme={null}
yarn add @layerswap/widget zustand@4.5.7 @layerswap/wallet-starknet
```
```typescript pnpm theme={null}
pnpm add @layerswap/widget zustand@4.5.7 @layerswap/wallet-starknet
```
If you want to customize the wallet provider selection, please refer to the [Widget Quickstart](/integration/UI/Widget/Quickstart).
### Initial Configuration
Set up the providers and widget in your main component:
```jsx theme={null}
import { Swap, LayerswapProvider } from '@layerswap/widget'
import { createStarknetProvider } from "@layerswap/wallet-starknet"
import "@layerswap/widget/index.css"
const PageComponent = () => {
const starknetProvider = createStarknetProvider({ walletConnectConfigs: {
projectId: "YOUR_WALLETCONNECT_PROJECT_ID",
name: "Your App Name",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/logo.png"]
}
})
return (
)
}
```
For a complete list of configuration options, see [Configurations](/integration/UI/Configurations).
You can generate an API key from the [Partner Dashboard](/api-keys) to track swaps done through the widget.
## Wallet Management
The Widget’s wallet management is modular, allowing to extend or override it based on your needs.
If you want to control wallet states internally, refer to the guide on [Dynamic Labs SDK](/integration/UI/Widget/Guides/StarknetWithDynamics) implementation.
## Customization
### Interactive Playground
The Layerswap Widget is fully customizable, letting you adjust its appearance and behavior to match your application. You can play around with colors, layout, border radius, and more in our interactive Playground:
Experiment with all customization options in real-time. Test colors, layouts, styles, and configurations with live preview before implementing in your application.
### Theme Configuration
You can customize the widget's appearance to match your application's branding:
**Custom Theme**
Apply a custom theme configuration:
```tsx App.tsx theme={null}
import '@layerswap/widget/index.css';
import { LayerswapProvider, Swap } from '@layerswap/widget';
import { starknetTheme } from './themeConfigs';
export default function App() {
return (
);
}
```
```typescript themeConfigs.ts theme={null}
export const starknetTheme = {
buttonTextColor: '25, 22, 25',
tertiary: '71, 71, 82',
primary: {
DEFAULT: '55, 207, 211',
100: '189, 239, 240',
200: '155, 231, 233',
300: '122, 223, 226',
400: '88, 215, 218',
500: '55, 207, 211',
600: '38, 169, 172',
700: '28, 124, 126',
800: '18, 78, 80',
900: '8, 33, 34',
text: '248, 250, 252',
},
secondary: {
DEFAULT: '29, 29, 33',
100: '105, 105, 120',
200: '86, 86, 98',
300: '67, 67, 76',
400: '48, 48, 55',
500: '29, 29, 33',
600: '22, 22, 25',
700: '19, 19, 21',
800: '0, 0, 0',
900: '0, 0, 0',
text: '128, 128, 143',
}
};
```
**Theme Properties**
Primary color palette for buttons, links, and key UI elements. Colors are specified as RGB values (e.g., '243, 243, 243').
Secondary color palette for backgrounds, cards, and supporting UI elements.
Tertiary color for subtle UI elements and borders (RGB format).
Text color for buttons (RGB format).
Custom CSS styles for card background.
Header visibility controls:
* `hideMenu`: Hide the menu button
* `hideTabs`: Hide navigation tabs
* `hideWallets`: Hide wallet connection display
All colors use RGB format without the `rgb()` wrapper (e.g., '243, 243, 243' instead of 'rgb(243, 243, 243)'). The widget internally applies the appropriate formatting.
Customize the entire color palette of the widget to match your brand. Learn more in the [Color Customization](/integration/UI/Widget/Customization/Colors) section.
## Testnet Configuration
Here's how to test your integration on Testnet before going live:
```typescript theme={null}
const config = {
apiKey: 'YOUR_TESTNET_API_KEY',
version: 'testnet',
initialValues: {
to: 'STARKNET_SEPOLIA',
toAsset: 'USDC'
},
}
```
# Starknet wallets with Dynamic
Source: https://docs.layerswap.io/integration/UI/Widget/Starknet/StarknetWithDynamics
Complete guide for integrating and customizing Layerswap Widget with Starknet wallets using Dynamic Labs SDK
## Live Demo
Explore a fully working implementation in this live demo:
This demo includes a complete working implementation with Starknet wallets integration using Dynamic Labs SDK. You can explore code at this [repository](https://github.com/layerswap/layerswapapp/tree/dev-monorepo/examples/nextjs-dynamic).
## Quick Start
### Installation
Install the Layerswap Widget along with the necessary wallet providers. This installation includes support for Starknet, Ethereum, Solana, and Bitcoin wallets:
```typescript npm theme={null}
npm install @layerswap/widget zustand@4.5.7 @layerswap/wallet-starknet @layerswap/wallet-evm wagmi viem @tanstack/react-query @layerswap/wallet-svm @layerswap/wallet-bitcoin @bigmi/client @bigmi/core @bigmi/react
```
```typescript yarn theme={null}
yarn add @layerswap/widget zustand@4.5.7 @layerswap/wallet-starknet @layerswap/wallet-evm wagmi viem @tanstack/react-query @layerswap/wallet-svm @layerswap/wallet-bitcoin @bigmi/client @bigmi/core @bigmi/react
```
```typescript pnpm theme={null}
pnpm add @layerswap/widget zustand@4.5.7 @layerswap/wallet-starknet @layerswap/wallet-evm wagmi viem @tanstack/react-query @layerswap/wallet-svm @layerswap/wallet-bitcoin @bigmi/client @bigmi/core @bigmi/react
```
If you want to customize the wallet provider selection, please refer to the [Widget Quickstart](/integration/UI/Widget/Quickstart).
### Initial Configuration
Create a custom Starknet wallet connection hook that bridges Dynamic Labs SDK with Layerswap Widget.
```typescript hooks/useCustomStarknet.ts theme={null}
import { useCallback, useEffect, useMemo } from "react";
import {
useUserWallets,
useDynamicContext,
dynamicEvents,
Wallet as DynamicWallet,
} from "@dynamic-labs/sdk-react-core";
import {
resolveWalletConnectorIcon,
NetworkWithTokens,
} from "@layerswap/widget";
import { WalletConnectionProvider, Wallet, WalletConnectionProviderProps } from "@layerswap/widget/types"
export default function useStarknet({ networks }: WalletConnectionProviderProps): WalletConnectionProvider {
const name = "Starknet";
const id = "starknet";
// Dynamic SDK
const { setShowAuthFlow, handleLogOut } = useDynamicContext();
const userWallets = useUserWallets();
// Starknet network names
const starknetNetworkNames = [
"STARKNET_MAINNET",
"STARKNET_SEPOLIA",
]
// Supported-networks
const supportedNetworks = useMemo(
() => ({
asSource: starknetNetworkNames,
autofill: starknetNetworkNames,
withdrawal: starknetNetworkNames,
}),
[starknetNetworkNames],
);
// Clean up dynamicEvents listeners on unmount
useEffect(() => {
return () => {
dynamicEvents.removeAllListeners("walletAdded");
dynamicEvents.removeAllListeners("authFlowCancelled");
};
}, []);
// connectWallet: log out existing, show authFlow, wait for event, then resolve
const connectWallet = useCallback(async (): Promise => {
if (userWallets.length) {
await handleLogOut();
}
const newDynWallet = await new Promise((resolve, reject) => {
setShowAuthFlow(true);
const onAdded = (w: DynamicWallet) => {
cleanup();
resolve(w);
};
const onCancelled = () => {
cleanup();
reject(new Error("User cancelled the connection"));
};
const cleanup = () => {
dynamicEvents.off("walletAdded", onAdded);
dynamicEvents.off("authFlowCancelled", onCancelled);
};
dynamicEvents.on("walletAdded", onAdded);
dynamicEvents.on("authFlowCancelled", onCancelled);
});
return resolveWallet({
connection: newDynWallet,
networks,
supportedNetworks,
disconnect: handleLogOut,
providerName: name,
});
}, [userWallets, handleLogOut, setShowAuthFlow, networks, supportedNetworks]);
// Logout
const disconnectWallets = useCallback(async () => {
await handleLogOut();
}, [handleLogOut]);
// Map wagmi connectors → Dynamic SDK wallets → our Wallet shape
const connectedWallets: Wallet[] = useMemo(
() =>
userWallets
.map((dyn) => {
if (!dyn) return;
return resolveWallet({
connection: dyn,
networks,
supportedNetworks,
disconnect: disconnectWallets,
providerName: name,
});
})
.filter(Boolean) as Wallet[],
[userWallets, networks, supportedNetworks, disconnectWallets],
);
const logo = networks.find((n) => n.name.toLowerCase().includes("starknet"))?.logo;
return {
connectWallet,
activeWallet: connectedWallets.find((w) => w.isActive),
connectedWallets,
asSourceSupportedNetworks: supportedNetworks.asSource,
autofillSupportedNetworks: supportedNetworks.autofill,
withdrawalSupportedNetworks: supportedNetworks.withdrawal,
name,
id,
providerIcon: logo,
};
}
/** Reusable helper to turn a DynamicWallet + context into our `Wallet` shape */
function resolveWallet(props: {
connection: DynamicWallet;
networks: NetworkWithTokens[];
supportedNetworks: {
asSource: string[];
autofill: string[];
withdrawal: string[];
};
disconnect: () => Promise;
providerName: string;
}): Wallet | undefined {
const { connection, networks, supportedNetworks, disconnect, providerName } = props;
const connectorName = connection.connector.name;
const address = connection.address;
if (!connectorName || !address) return;
const displayName = `${connectorName} – ${providerName}`;
const networkIcon = networks.find((n) => n.name.toLowerCase().includes("starknet"))?.logo;
return {
id: connectorName,
isActive: true,
address,
addresses: [address],
displayName,
providerName,
icon: resolveWalletConnectorIcon({ iconUrl: connection.connector.metadata.icon }),
disconnect: () => disconnect(),
asSourceSupportedNetworks: supportedNetworks.asSource,
autofillSupportedNetworks: supportedNetworks.autofill,
withdrawalSupportedNetworks: supportedNetworks.withdrawal,
networkIcon,
};
}
```
Set up the providers and widget in your main component:
```typescript theme={null}
import { Swap, LayerswapProvider } from '@layerswap/widget'
import { StarknetWalletConnectors } from "@dynamic-labs/starknet";
import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";
import { createEVMProvider, createStarknetProvider, createSVMProvider, createBitcoinProvider } from "@layerswap/wallets"
import useCustomStarknet from "../hooks/useCustomStarknet";
import "@layerswap/widget/index.css"
const PageComponent = () => {
const walletConnectConfigs = {
projectId: "YOUR_WALLETCONNECT_PROJECT_ID",
name: "Your App Name",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/icon.png"]
}
const walletProviders = [
createEVMProvider({ walletConnectConfigs }),
createStarknetProvider({
walletConnectConfigs,
customHook: useCustomStarknet
}),
createSVMProvider({ walletConnectConfigs }),
createBitcoinProvider()
]
return (
)
}
```
You can generate an API key from the [Partner Dashboard](/api-keys) to track swaps done through the widget.
### Environment Variables
Create a `.env.local` file with your API credentials:
```bash .env.local theme={null}
DYNAMIC_ENVIRONMENT_ID=your_dynamic_environment_id
LAYERSWAP_API_KEY=your_layerswap_api_key
```
**Get Your API Keys:**
* Dynamic Labs: [Sign up at Dynamic](https://www.dynamic.xyz/)
* Layerswap: Generate from the [Partner Dashboard](/api-keys)
## Complete Example
Here's a full implementation example combining all concepts:
```typescript App.tsx theme={null}
import { Swap, LayerswapProvider } from '@layerswap/widget'
import { StarknetWalletConnectors } from "@dynamic-labs/starknet";
import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core";
import { createEVMProvider, createStarknetProvider, createSVMProvider, createBitcoinProvider } from "@layerswap/wallets"
import useCustomStarknet from "../hooks/useCustomStarknet";
import "@layerswap/widget/index.css"
const PageComponent = () => {
const walletConnectConfigs = {
projectId: "YOUR_WALLETCONNECT_PROJECT_ID",
name: "Your App Name",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/icon.png"]
}
const walletProviders = [
createEVMProvider({ walletConnectConfigs }),
createStarknetProvider({
walletConnectConfigs,
customHook: useCustomStarknet
}),
createSVMProvider({ walletConnectConfigs }),
createBitcoinProvider()
]
return (
)
}
```
```typescript hooks/useCustomStarknet.ts theme={null}
import { useCallback, useEffect, useMemo } from "react";
import {
useUserWallets,
useDynamicContext,
dynamicEvents,
Wallet as DynamicWallet,
} from "@dynamic-labs/sdk-react-core";
import {
resolveWalletConnectorIcon,
NetworkWithTokens,
} from "@layerswap/widget";
import { WalletConnectionProvider, Wallet, WalletConnectionProviderProps } from "@layerswap/widget/types"
export default function useStarknet({ networks }: WalletConnectionProviderProps): WalletConnectionProvider {
const name = "Starknet";
const id = "starknet";
// Dynamic SDK
const { setShowAuthFlow, handleLogOut } = useDynamicContext();
const userWallets = useUserWallets();
// Starknet network names
const starknetNetworkNames = [
"STARKNET_MAINNET",
"STARKNET_SEPOLIA",
]
// Supported-networks
const supportedNetworks = useMemo(
() => ({
asSource: starknetNetworkNames,
autofill: starknetNetworkNames,
withdrawal: starknetNetworkNames,
}),
[starknetNetworkNames],
);
// Clean up dynamicEvents listeners on unmount
useEffect(() => {
return () => {
dynamicEvents.removeAllListeners("walletAdded");
dynamicEvents.removeAllListeners("authFlowCancelled");
};
}, []);
// connectWallet: log out existing, show authFlow, wait for event, then resolve
const connectWallet = useCallback(async (): Promise => {
if (userWallets.length) {
await handleLogOut();
}
const newDynWallet = await new Promise((resolve, reject) => {
setShowAuthFlow(true);
const onAdded = (w: DynamicWallet) => {
cleanup();
resolve(w);
};
const onCancelled = () => {
cleanup();
reject(new Error("User cancelled the connection"));
};
const cleanup = () => {
dynamicEvents.off("walletAdded", onAdded);
dynamicEvents.off("authFlowCancelled", onCancelled);
};
dynamicEvents.on("walletAdded", onAdded);
dynamicEvents.on("authFlowCancelled", onCancelled);
});
return resolveWallet({
connection: newDynWallet,
networks,
supportedNetworks,
disconnect: handleLogOut,
providerName: name,
});
}, [userWallets, handleLogOut, setShowAuthFlow, networks, supportedNetworks]);
// Logout
const disconnectWallets = useCallback(async () => {
await handleLogOut();
}, [handleLogOut]);
// Map wagmi connectors → Dynamic SDK wallets → our Wallet shape
const connectedWallets: Wallet[] = useMemo(
() =>
userWallets
.map((dyn) => {
if (!dyn) return;
return resolveWallet({
connection: dyn,
networks,
supportedNetworks,
disconnect: disconnectWallets,
providerName: name,
});
})
.filter(Boolean) as Wallet[],
[userWallets, networks, supportedNetworks, disconnectWallets],
);
const logo = networks.find((n) => n.name.toLowerCase().includes("starknet"))?.logo;
return {
connectWallet,
activeWallet: connectedWallets.find((w) => w.isActive),
connectedWallets,
asSourceSupportedNetworks: supportedNetworks.asSource,
autofillSupportedNetworks: supportedNetworks.autofill,
withdrawalSupportedNetworks: supportedNetworks.withdrawal,
name,
id,
providerIcon: logo,
};
}
/** Reusable helper to turn a DynamicWallet + context into our `Wallet` shape */
function resolveWallet(props: {
connection: DynamicWallet;
networks: NetworkWithTokens[];
supportedNetworks: {
asSource: string[];
autofill: string[];
withdrawal: string[];
};
disconnect: () => Promise;
providerName: string;
}): Wallet | undefined {
const { connection, networks, supportedNetworks, disconnect, providerName } = props;
const connectorName = connection.connector.name;
const address = connection.address;
if (!connectorName || !address) return;
const displayName = `${connectorName} – ${providerName}`;
const networkIcon = networks.find((n) => n.name.toLowerCase().includes("starknet"))?.logo;
return {
id: connectorName,
isActive: true,
address,
addresses: [address],
displayName,
providerName,
icon: resolveWalletConnectorIcon({ iconUrl: connection.connector.metadata.icon }),
disconnect: () => disconnect(),
asSourceSupportedNetworks: supportedNetworks.asSource,
autofillSupportedNetworks: supportedNetworks.autofill,
withdrawalSupportedNetworks: supportedNetworks.withdrawal,
networkIcon,
};
}
```
For a complete list of configuration options, see [Configurations](/integration/UI/Configurations).
## Testing
### Testnet Configuration
Test your integration on testnet before going live:
```typescript theme={null}
const config = {
apiKey: 'YOUR_TESTNET_API_KEY',
version: 'testnet',
initialValues: {
to: 'STARKNET_SEPOLIA',
toAsset: 'USDC'
},
}
```
# Next.js Polyfills
Source: https://docs.layerswap.io/integration/UI/Widget/Troubleshooting/NextPolyfills
Configure webpack fallbacks for Next.js projects using the Layerswap Widget.
## Next.js requires webpack fallback configuration
When using the Layerswap Widget with Next.js, you may encounter errors related to Node.js modules that aren't available in the browser environment, such as `fs`, `net`, or `tls`.
### Configure webpack fallbacks in `next.config.ts`
Add the following webpack configuration to your `next.config.ts` (or `next.config.js`) file:
```TypeScript theme={null}
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
webpack: (config) => {
config.resolve.fallback = { fs: false, net: false, tls: false }
config.externals.push('pino-pretty', 'lokijs', 'encoding')
return config
},
};
export default nextConfig;
```
### What this configuration does
* **`resolve.fallback`**: Tells webpack to not attempt to polyfill `fs`, `net`, and `tls` modules, which are Node.js-specific and not needed in the browser.
* **`externals`**: Excludes `pino-pretty`, `lokijs`, and `encoding` from the bundle, as these are optional dependencies that may cause build issues.
If you're also experiencing ESM module resolution errors, see [Next.js Transpile Packages](/integration/UI/Widget/Troubleshooting/NextTranspilePackages) for additional configuration.
# Next.js Transpile Packages
Source: https://docs.layerswap.io/integration/UI/Widget/Troubleshooting/NextTranspilePackages
Configure transpilePackages for ESM module resolution with the Layerswap Widget.
## ESM Module Resolution in Next.js
When using the Layerswap Widget with Next.js, you may encounter errors related to ESM (ECMAScript Modules) that aren't properly resolved. This typically manifests as import errors or module not found errors for `@layerswap/*` packages.
### Configure `transpilePackages` in `next.config.ts`
Add all Layerswap packages to the `transpilePackages` array in your `next.config.ts` (or `next.config.js`) file:
```TypeScript theme={null}
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
transpilePackages: [
'@layerswap/widget',
'@layerswap/wallet-evm',
'@layerswap/wallet-bitcoin',
'@layerswap/wallet-fuel',
'@layerswap/wallet-paradex',
'@layerswap/wallet-starknet',
'@layerswap/wallet-svm',
'@layerswap/wallet-ton',
'@layerswap/wallet-tron',
'@layerswap/wallet-imtbl-x',
'@layerswap/wallet-imtbl-passport',
'@layerswap/wallet-module-zksync',
'@layerswap/wallet-module-loopring',
'@layerswap/wallets'
],
};
export default nextConfig;
```
### What this configuration does
The `transpilePackages` option tells Next.js to transpile the specified packages from `node_modules`. This is necessary because:
* These packages are distributed as ESM modules
* Next.js needs to process them through its build pipeline for proper module resolution
* Without this configuration, you may see errors like `SyntaxError: Cannot use import statement outside a module`
You only need to include the wallet packages that you're actually using in your integration. For example, if you're only using EVM wallets, you can include just `@layerswap/widget`, `@layerswap/wallet-evm`, and `@layerswap/wallets`.
# Vite.js Polyfills
Source: https://docs.layerswap.io/integration/UI/Widget/Troubleshooting/VitePolyfills
Configure polyfills for Vite.js projects using the Layerswap Widget.
## Vite.js requires global and process polyfills
If you're using Vite.js with React and the Layerswap Widget, you may encounter this error in your console:
```
util.js:109 Uncaught ReferenceError: process is not defined
```
This happens because some libraries that the widget depends on use the `process` module, which is not available in the browser environment and is not automatically polyfilled by Vite.
### Polyfill `process` using `vite.config.js`
To resolve this, modify your `vite.config.js` file with the following plugins:
```TypeScript theme={null}
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite'
import { nodePolyfills } from 'vite-plugin-node-polyfills'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [react(), nodePolyfills()],
esbuild: {
target: 'esnext',
},
server: {
port: 3000,
open: true,
},
})
```
### Install the required packages
Make sure to install the polyfill plugins:
```bash npm theme={null}
npm install -D vite-plugin-node-polyfills @vitejs/plugin-react
```
```bash yarn theme={null}
yarn add -D vite-plugin-node-polyfills @vitejs/plugin-react
```
```bash pnpm theme={null}
pnpm add -D vite-plugin-node-polyfills @vitejs/plugin-react
```
### Example Project
For a complete working example of a Vite.js project with the Layerswap Widget, check out our [Vite example in the monorepo](https://github.com/layerswap/layerswapapp/tree/dev-monorepo/examples/vite).
# Bitcoin Wallet
Source: https://docs.layerswap.io/integration/UI/Widget/WalletManagement/BitcoinProvider
## Overview
The Bitcoin wallet provider supports Unisat, Leather, Xverse, and other Bitcoin wallets using the Bigmi library.
## Installation
```typescript yarn theme={"system"} theme={null}
yarn add @layerswap/wallet-bitcoin @bigmi/client @bigmi/core @bigmi/react @tanstack/react-query
```
```typescript pnpm theme={"system"} theme={null}
pnpm add @layerswap/wallet-bitcoin @bigmi/client @bigmi/core @bigmi/react @tanstack/react-query
```
```typescript npm theme={"system"} theme={null}
npm install @layerswap/wallet-bitcoin @bigmi/client @bigmi/core @bigmi/react @tanstack/react-query
```
[Bigmi](https://github.com/lifinance/bigmi) is modular TypeScript library that provides reactive primitives for building Bitcoin applications.
[**TanStack Query**](https://tanstack.com/query/v5) is an async state manager that handles fetching, caching, synchronizing and more.
***
## Basic Usage
The Bitcoin provider requires no configuration:
```typescript theme={null}
import { LayerswapProvider, Swap } from "@layerswap/widget"
import { createBitcoinProvider } from "@layerswap/wallet-bitcoin"
import "@layerswap/widget/index.css"
export const App = () => {
const bitcoinProvider = createBitcoinProvider()
return (
)
}
```
# Custom Wallet Management
Source: https://docs.layerswap.io/integration/UI/Widget/WalletManagement/CustomWalletManagement
Full control over wallet connections using the WalletProvider interface with 3rd party libraries
## Overview
For complete control over wallet connection flows, you can implement a custom `WalletProvider` that bridges any wallet management solution with Layerswap. This deep integration approach enables you to:
* Use your own custom wallet connection UI
* Integrate 3rd party wallet libraries ([Dynamic](https://www.dynamic.xyz/), [Reown](https://reown.com/), [RainbowKit](https://www.rainbowkit.com/), etc.)
* Control the entire wallet lifecycle (connect, disconnect, state management)
* Customize wallet behavior to match your application's UX
***
## Implementation Steps
### 1. Create Custom Hook
Implement `WalletConnectionProvider` that:
* **Connects** to your wallet management solution (Dynamic, Reown, RainbowKit, Privy, etc.)
* **Transforms** external wallet format to Layerswap `Wallet` type
* **Handles** connect/disconnect lifecycle and error states
* **Manages** wallet state and active wallet selection
* **Listens** to wallet events (connection, disconnection, account changes)
### 2. Define Supported Networks
Specify which networks your provider supports:
```typescript theme={null}
const supportedNetworks = {
asSource: ["STARKNET_MAINNET", "STARKNET_SEPOLIA"],
autofill: ["STARKNET_MAINNET", "STARKNET_SEPOLIA"],
withdrawal: ["STARKNET_MAINNET", "STARKNET_SEPOLIA"]
}
```
* `asSource` - Networks that can be used as swap source
* `autofill` - Networks that support address autofill
* `withdrawal` - Networks that support withdrawals
### 3. Implement Connect Flow
The `connectWallet` function is called when users click "Connect Wallet" in the widget:
```typescript theme={null}
const connectWallet = async (): Promise => {
// 1. Show your custom connection UI
showYourWalletModal()
// 2. Wait for wallet connection
const externalWallet = await waitForConnection()
// 3. Transform to Layerswap format
return transformToLayerswapWallet(externalWallet)
}
```
### 4. Handle Wallet State
Keep track of connected wallets and active wallet:
```typescript theme={null}
const connectedWallets: Wallet[] = useMemo(
() => externalWallets.map(transformToLayerswapWallet),
[externalWallets]
)
const activeWallet = connectedWallets.find((w) => w.isActive)
```
### 5. Create Provider with Custom Hook
Use the factory function with your custom hook:
```typescript theme={null}
import { createStarknetProvider } from "@layerswap/wallets"
// ✅ Correct - Use factory function with customHook
const customProvider = createStarknetProvider({
customHook: useYourCustomHook
})
```
**Deprecated Pattern**: Do NOT use the old spreading pattern:
```typescript theme={null}
// ❌ Deprecated - Do not use
const customProvider = {
...StarknetProvider,
walletConnectionProvider: useYourCustomHook
}
```
Always use the factory function (`createStarknetProvider`, `createEVMProvider`, etc.) with the `customHook` parameter.
***
## User Experience Flow
### Connection Flow
1. User opens Layerswap widget
2. User clicks "Connect Wallet" button
3. **Your custom `connectWallet` function is called**
4. Your custom UI/modal appears (Dynamic, Reown, RainbowKit, etc.)
5. User selects wallet and approves connection
6. Your hook transforms the wallet to Layerswap format
7. Widget displays connected wallet and enables swap functionality
### Disconnection Flow
1. User clicks disconnect in widget
2. **Your wallet's `disconnect` callback is called**
3. Your wallet management library handles cleanup
4. Widget updates to show "Connect Wallet" button
***
## Critical Implementation Details
**Important: These details are critical for correct implementation**
1. **Icon Resolution**: The `icon` property in the `Wallet` type must return a React component, NOT a URL string. Always use the utility function:
```typescript theme={null}
import { resolveWalletConnectorIcon } from "@layerswap/widget"
icon: resolveWalletConnectorIcon({ iconUrl: "https://..." })
```
2. **Ready State**: Always return `ready: true` in your `WalletConnectionProvider` when your provider is initialized and ready to handle connections. This is a **required field** - the widget will not function without it.
3. **WalletConnect Configs**: Only needed for native EVM/SVM/Starknet providers when NOT using a custom hook. If you provide `customHook`, the WalletConnect configuration is ignored since you control the connection flow.
4. **Type Imports**: Import types from `@layerswap/widget/types`:
```typescript theme={null}
import {
WalletConnectionProvider,
Wallet,
WalletConnectionProviderProps
} from "@layerswap/widget/types"
```
5. **Disconnect Callback**: The `disconnectWallets` function in `WalletConnectionProvider` is optional but recommended for proper cleanup. Each individual `Wallet` must have its own `disconnect` callback.
***
## Creating a Custom Provider
To create a custom provider, use the provider creator function with the `customHook` parameter:
```typescript theme={null}
import { createStarknetProvider } from "@layerswap/wallets"
import type { WalletConnectionProvider, WalletConnectionProviderProps } from "@layerswap/widget/types"
import useCustomWalletConnection from "./hooks/useCustomWalletConnection"
// Your custom hook implementing WalletConnectionProvider
function useCustomWalletConnection(props: WalletConnectionProviderProps): WalletConnectionProvider {
// Your custom wallet connection logic
return {
connectWallet: async () => { /* ... */ },
disconnectWallets: async () => { /* ... */ },
connectedWallets: [],
activeWallet: undefined,
withdrawalSupportedNetworks: ['STARKNET_MAINNET'],
name: 'Custom Starknet',
id: 'starknet',
ready: true
}
}
// Create provider with custom hook
const customProvider = createStarknetProvider({
customHook: useCustomWalletConnection
})
```
***
## WalletConnectionProvider Interface
Your custom hook must implement this interface:
```typescript theme={null}
type WalletConnectionProvider = (props: WalletConnectionProviderProps) => {
connectWallet: () => Promise
disconnectWallets?: () => Promise
activeWallet: Wallet | undefined
connectedWallets: Wallet[]
asSourceSupportedNetworks: string[]
autofillSupportedNetworks: string[]
withdrawalSupportedNetworks: string[]
name: string
id: string
providerIcon?: string
ready: boolean
multiStepHandlers?: MultiStepHandler[]
unsupportedPlatforms?: string[]
hideFromList?: boolean
}
```
### Props Passed to Your Hook
```typescript theme={null}
type WalletConnectionProviderProps = {
networks: NetworkWithTokens[] // Layerswap's available networks
}
```
### Return Values
| Property | Type | Required | Description |
| ----------------------------- | ------------------------------------ | -------- | ------------------------------------------------------------------ |
| `connectWallet` | `() => Promise` | ✅ | Function called when user clicks "Connect Wallet" in the widget |
| `disconnectWallets` | `() => Promise` | ❌ | Optional function to disconnect all wallets |
| `activeWallet` | `Wallet \| undefined` | ✅ | The currently active/selected wallet |
| `connectedWallets` | `Wallet[]` | ✅ | Array of all connected wallets |
| `asSourceSupportedNetworks` | `string[]` | ✅ | Networks supported as swap source |
| `autofillSupportedNetworks` | `string[]` | ✅ | Networks that support address autofill |
| `withdrawalSupportedNetworks` | `string[]` | ✅ | Networks that support withdrawals |
| `name` | `string` | ✅ | Provider name (e.g., "Starknet", "Ethereum") |
| `id` | `string` | ✅ | Unique provider identifier |
| `providerIcon` | `string` | ❌ | Optional URL to provider icon |
| `ready` | `boolean` | ✅ | **Critical**: Whether the provider is initialized and ready to use |
| `multiStepHandlers` | `MultiStepHandler[]` | ❌ | Optional handlers for multi-step operations |
| `unsupportedPlatforms` | `string[]` | ❌ | Optional list of unsupported platforms |
| `hideFromList` | `boolean` | ❌ | Optional flag to hide provider from wallet list |
### Wallet Type
Your custom provider must transform external wallet objects to Layerswap's `Wallet` format:
```typescript theme={null}
type Wallet = {
id: string // Unique wallet identifier
isActive: boolean // Whether this wallet is actively selected
address: string // Primary wallet address
addresses: string[] // All addresses (for multi-account wallets)
displayName: string // User-facing wallet name
providerName: string // Provider name (e.g., "Starknet")
icon: (props: any) => React.JSX.Element // **Critical**: Wallet icon component (NOT a string URL)
disconnect: () => Promise // Disconnect callback
asSourceSupportedNetworks: string[] // Networks supported as source
autofillSupportedNetworks: string[] // Networks supported for autofill
withdrawalSupportedNetworks: string[] // Networks supported for withdrawal
networkIcon?: string // Optional network icon URL
}
```
***
## Complete Example: Dynamic Labs Integration
This example demonstrates integrating Starknet wallets using Dynamic Labs SDK. The same pattern applies to any 3rd party library.
### Live Demo
Explore the complete source code at [layerswap/layerswapapp - nextjs-dynamic example](https://github.com/layerswap/layerswapapp/tree/dev-monorepo/examples/nextjs-dynamic)
### Installation
```bash npm theme={null}
npm install @layerswap/widget @layerswap/wallets \
@dynamic-labs/sdk-react-core @dynamic-labs/starknet \
@bigmi/client @bigmi/core @bigmi/react
```
```bash yarn theme={null}
yarn add @layerswap/widget @layerswap/wallets \
@dynamic-labs/sdk-react-core @dynamic-labs/starknet \
@bigmi/client @bigmi/core @bigmi/react
```
```bash pnpm theme={null}
pnpm add @layerswap/widget @layerswap/wallets \
@dynamic-labs/sdk-react-core @dynamic-labs/starknet \
@bigmi/client @bigmi/core @bigmi/react
```
### Step 1: Create Custom Connection Hook
Create a hook that implements the `WalletConnectionProvider` interface:
```typescript theme={null}
import { useCallback, useEffect, useMemo } from "react"
import {
useUserWallets,
useDynamicContext,
dynamicEvents,
Wallet as DynamicWallet,
} from "@dynamic-labs/sdk-react-core"
import {
resolveWalletConnectorIcon,
NetworkWithTokens,
} from "@layerswap/widget"
import {
WalletConnectionProvider,
Wallet,
WalletConnectionProviderProps
} from "@layerswap/widget/types"
export default function useCustomStarknet({
networks
}: WalletConnectionProviderProps): WalletConnectionProvider {
const name = "Starknet"
const id = "starknet"
// Dynamic SDK context and hooks
const { setShowAuthFlow, handleLogOut } = useDynamicContext()
const userWallets = useUserWallets()
// Define supported Starknet networks
const starknetNetworkNames = [
"STARKNET_MAINNET",
"STARKNET_SEPOLIA",
]
const supportedNetworks = useMemo(
() => ({
asSource: starknetNetworkNames,
autofill: starknetNetworkNames,
withdrawal: starknetNetworkNames,
}),
[] // Empty deps - starknetNetworkNames is constant
)
// Clean up event listeners on unmount
useEffect(() => {
return () => {
dynamicEvents.removeAllListeners("walletAdded")
dynamicEvents.removeAllListeners("authFlowCancelled")
}
}, [])
// Connect wallet - show Dynamic auth flow and wait for connection
const connectWallet = useCallback(async (): Promise => {
// Log out existing wallets first
if (userWallets.length) {
await handleLogOut()
}
// Show Dynamic auth modal and wait for wallet connection
const newDynWallet = await new Promise((resolve, reject) => {
setShowAuthFlow(true)
const onAdded = (w: DynamicWallet) => {
cleanup()
resolve(w)
}
const onCancelled = () => {
cleanup()
reject(new Error("User cancelled the connection"))
}
const cleanup = () => {
dynamicEvents.off("walletAdded", onAdded)
dynamicEvents.off("authFlowCancelled", onCancelled)
}
dynamicEvents.on("walletAdded", onAdded)
dynamicEvents.on("authFlowCancelled", onCancelled)
})
// Transform Dynamic wallet to Layerswap Wallet format
return resolveWallet({
connection: newDynWallet,
networks,
supportedNetworks,
disconnect: handleLogOut,
providerName: name,
})
}, [userWallets, handleLogOut, setShowAuthFlow, networks, supportedNetworks])
// Disconnect all wallets
const disconnectWallets = useCallback(async () => {
await handleLogOut()
}, [handleLogOut])
// Map Dynamic SDK wallets to Layerswap Wallet shape
const connectedWallets: Wallet[] = useMemo(
() =>
userWallets
.map((dyn) => {
if (!dyn) return
return resolveWallet({
connection: dyn,
networks,
supportedNetworks,
disconnect: disconnectWallets,
providerName: name,
})
})
.filter(Boolean) as Wallet[],
[userWallets, networks, supportedNetworks, disconnectWallets],
)
const logo = networks.find((n) =>
n.name.toLowerCase().includes("starknet")
)?.logo
// Return WalletConnectionProvider interface
return {
connectWallet,
disconnectWallets,
activeWallet: connectedWallets.find((w) => w.isActive),
connectedWallets,
asSourceSupportedNetworks: supportedNetworks.asSource,
autofillSupportedNetworks: supportedNetworks.autofill,
withdrawalSupportedNetworks: supportedNetworks.withdrawal,
name,
id,
providerIcon: logo,
ready: true // CRITICAL: Required field - indicates provider is ready
}
}
/** Helper to transform Dynamic wallet to Layerswap Wallet format */
function resolveWallet(props: {
connection: DynamicWallet
networks: NetworkWithTokens[]
supportedNetworks: {
asSource: string[]
autofill: string[]
withdrawal: string[]
}
disconnect: () => Promise
providerName: string
}): Wallet | undefined {
const { connection, networks, supportedNetworks, disconnect, providerName } = props
const connectorName = connection.connector.name
const address = connection.address
if (!connectorName || !address) return
const displayName = `${connectorName} – ${providerName}`
const networkIcon = networks.find((n) =>
n.name.toLowerCase().includes("starknet")
)?.logo
return {
id: connectorName,
isActive: true,
address,
addresses: [address],
displayName,
providerName,
icon: resolveWalletConnectorIcon({
iconUrl: connection.connector.metadata.icon
}), // Returns React component, NOT a string URL
disconnect: () => disconnect(),
asSourceSupportedNetworks: supportedNetworks.asSource,
autofillSupportedNetworks: supportedNetworks.autofill,
withdrawalSupportedNetworks: supportedNetworks.withdrawal,
networkIcon,
}
}
```
### Step 2: Set Up Providers in Main Component
```typescript theme={null}
import { Swap, LayerswapProvider } from "@layerswap/widget"
import { StarknetWalletConnectors } from "@dynamic-labs/starknet"
import { DynamicContextProvider } from "@dynamic-labs/sdk-react-core"
import {
createStarknetProvider,
createSVMProvider,
createBitcoinProvider
} from "@layerswap/wallets"
import useCustomStarknet from "./hooks/useCustomStarknet"
import "@layerswap/widget/index.css"
const App = () => {
// Create custom Starknet provider that uses Dynamic Labs
const starknetProvider = createStarknetProvider({
customHook: useCustomStarknet
})
// Create other native providers
const walletProviders = [
starknetProvider, // Custom provider
createSVMProvider(),
createBitcoinProvider()
]
return (
)
}
export default App
```
### Step 3: Environment Variables
Create a `.env.local` file:
```bash theme={null}
NEXT_PUBLIC_DYNAMIC_ENVIRONMENT_ID=your_dynamic_environment_id
NEXT_PUBLIC_LAYERSWAP_API_KEY=your_layerswap_api_key
NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID=your_walletconnect_project_id
```
**Get Your API Keys:**
* Dynamic Labs: [Sign up at Dynamic](https://www.dynamic.xyz/)
* Layerswap: Generate from the [Partner Dashboard](/api-keys)
* WalletConnect: Get project ID at [WalletConnect Cloud](https://cloud.walletconnect.com/)
***
## Additional Examples
For more custom integration examples:
* [EVM with Reown](https://github.com/layerswap/layerswapapp/tree/dev-monorepo/examples/nextjs-reown)
* [EVM with RainbowKit](https://github.com/layerswap/layerswapapp/tree/dev-monorepo/examples/nextjs-rainbowkit)
* [Starknet with Dynamic](https://github.com/layerswap/layerswapapp/tree/dev-monorepo/examples/nextjs-dynamic)
# EVM Wallet
Source: https://docs.layerswap.io/integration/UI/Widget/WalletManagement/EVMProvider
## Overview
The EVM wallet provider supports Ethereum and all EVM-compatible chains including Arbitrum, Optimism, Base, Polygon, and more. It uses wagmi v2 for wallet connections and supports MetaMask, WalletConnect, Coinbase Wallet, and other EVM wallets.
## Installation
```typescript yarn theme={"system"} theme={null}
yarn add @layerswap/wallet-evm wagmi viem @tanstack/react-query
```
```typescript pnpm theme={"system"} theme={null}
pnpm add @layerswap/wallet-evm wagmi viem @tanstack/react-query
```
```typescript npm theme={"system"} theme={null}
npm install @layerswap/wallet-evm wagmi viem @tanstack/react-query
```
[**Wagmi**](https://wagmi.sh/) is a React Hooks library for Ethereum.
[**Viem**](https://wagmi.sh/react/guides/viem) is a low-level TypeScript Interface for Ethereum that enables developers to interact with the blockchain.
[**TanStack Query**](https://tanstack.com/query/v5) is an async state manager that handles fetching, caching, synchronizing and more.
## Basic Usage
```typescript theme={null}
import { LayerswapProvider, Swap } from "@layerswap/widget"
import { createEVMProvider } from "@layerswap/wallet-evm"
import "@layerswap/widget/index.css"
export const App = () => {
const evmProvider = createEVMProvider({
walletConnectConfigs: {
projectId: "YOUR_WALLETCONNECT_PROJECT_ID",
name: "Your App Name",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/icon.png"]
}
})
return (
)
}
```
***
## Configuration
### WalletConnect Configuration
The EVM provider requires WalletConnect configuration:
```typescript theme={null}
import { createEVMProvider } from "@layerswap/wallet-evm"
import type { WalletConnectConfig } from "@layerswap/wallets"
const walletConnectConfigs: WalletConnectConfig = {
projectId: string, // Required: Your WalletConnect project ID
name: string, // Required: Your app name
description: string, // Required: Your app description
url: string, // Required: Your app URL
icons: string[] // Required: Array of logo URLs
}
const evmProvider = createEVMProvider({ walletConnectConfigs })
```
Get your WalletConnect project ID at [WalletConnect Cloud](https://cloud.walletconnect.com/).
***
## Advanced: EVM Modules
Extend the EVM provider with network-specific functionality using modules:
```typescript theme={null}
import { createEVMProvider, zkSyncModule, LoopringModule } from "@layerswap/wallets"
const evmProvider = createEVMProvider({
walletConnectConfigs: {
projectId: "YOUR_WALLETCONNECT_PROJECT_ID",
name: "Your App",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/icon.png"]
},
walletProviderModules: [zkSyncModule, LoopringModule]
})
```
### Available Modules
* **zkSyncModule** - Adds zkSync-specific balance providers and multi-step transaction handlers
* **LoopringModule** - Adds Loopring-specific balance providers and multi-step transaction handlers
***
## Integration with Existing Wagmi
If your application already uses wagmi, see the [Partial Integration guide](/integration/UI/Widget/WalletManagement/PartialIntegration) for details on integrating Layerswap with your existing wagmi setup.
# Fuel Wallet
Source: https://docs.layerswap.io/integration/UI/Widget/WalletManagement/FuelProvider
## Overview
The Fuel wallet provider supports Fuel Network wallets and ecosystem.
## Installation
```typescript yarn theme={"system"} theme={null}
yarn add @layerswap/wallet-fuel @tanstack/react-query
```
```typescript pnpm theme={"system"} theme={null}
pnpm add @layerswap/wallet-fuel @tanstack/react-query
```
```typescript npm theme={"system"} theme={null}
npm install @layerswap/wallet-fuel @tanstack/react-query
```
[**TanStack Query**](https://tanstack.com/query/v5) is an async state manager that handles fetching, caching, synchronizing and more.
***
## Basic Usage
The Fuel provider requires no configuration:
```typescript theme={null}
import { LayerswapProvider, Swap } from "@layerswap/widget"
import { createFuelProvider } from "@layerswap/wallet-fuel"
import "@layerswap/widget/index.css"
export const App = () => {
const fuelProvider = createFuelProvider()
return (
)
}
```
# Immutable Passport Wallet
Source: https://docs.layerswap.io/integration/UI/Widget/WalletManagement/ImmutablePassportProvider
## Overview
The Immutable Passport provider offers Immutable's gaming-focused wallet solution with seamless onboarding. It requires EVM provider as a dependency.
## Installation
```typescript yarn theme={"system"} theme={null}
yarn add @layerswap/wallet-imtbl-passport @layerswap/wallet-evm wagmi viem @tanstack/react-query
```
```typescript pnpm theme={"system"} theme={null}
pnpm add @layerswap/wallet-imtbl-passport @layerswap/wallet-evm wagmi viem @tanstack/react-query
```
```typescript npm theme={"system"} theme={null}
npm install @layerswap/wallet-imtbl-passport @layerswap/wallet-evm wagmi viem @tanstack/react-query
```
## Setting up Immutable Passport
Before you can use Immutable Passport in your application, you need to create and configure your Passport credentials:
1. Go to [https://hub.immutable.com/](https://hub.immutable.com/) and log in
2. Create a new Passport configuration for your application
3. Configure the `redirectUri` with the correct path where you'll create your redirect page
* This URI must match the path where you implement the `ImtblRedirectPage` component (shown in the example below)
* For example, if you set `redirectUri: "https://yourapp.com/imtbl-redirect"`, you need to create a page at that path that renders the `ImtblRedirectPage` component
The redirect page is essential for the Immutable Passport authentication flow. Make sure the `redirectUri` in your Immutable Hub configuration exactly matches the path where you deploy the `ImtblRedirectPage` component.
***
## Basic Usage
The Immutable Passport provider must be used alongside the EVM provider:
```typescript WidgetPage.tsx theme={null}
import { LayerswapProvider, Swap } from "@layerswap/widget"
import { createEVMProvider, createImmutablePassportProvider } from "@layerswap/wallet-imtbl-passport"
import "@layerswap/widget/index.css"
import { imtblPassportConfigs, walletConnectConfigs } from "./configs"
export const WidgetPage = () => {
const walletProviders = [
createEVMProvider({ walletConnectConfigs }),
createImmutablePassportProvider({ imtblPassportConfigs })
]
return (
)
}
```
```typescript ImtblRedirectPage.tsx theme={null}
import { LayerswapProvider } from "@layerswap/widget"
import { createEVMProvider, createImmutablePassportProvider, ImtblRedirect } from "@layerswap/wallet-imtbl-passport"
import "@layerswap/widget/index.css"
import { imtblPassportConfigs, walletConnectConfigs } from "./configs"
export const ImtblRedirectPage = () => {
const walletProviders = [
createEVMProvider({ walletConnectConfigs }),
createImmutablePassportProvider({ imtblPassportConfigs })
]
return (
)
}
```
```typescript configs.ts theme={null}
export const walletConnectConfigs = {
projectId: "YOUR_WALLETCONNECT_PROJECT_ID",
name: "Your App Name",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/logo.png"]
}
export const imtblPassportConfigs = {
clientId: "YOUR_CLIENT_ID",
publishableKey: "YOUR_PUBLISHABLE_KEY",
redirectUri: "https://yourapp.com/imtbl-redirect",
logoutRedirectUri: "https://yourapp.com"
}
```
***
## Configuration
### Immutable Passport Configuration
The Immutable Passport provider requires specific configuration:
```typescript theme={null}
import { createImmutablePassportProvider } from "@layerswap/wallet-imtbl-passport"
import type { ImtblPassportConfig } from "@layerswap/wallet-imtbl-passport"
const imtblPassportConfigs: ImtblPassportConfig = {
publishableKey: string, // Required: Your publishable key
clientId: string, // Required: Your client ID
redirectUri: string, // Required: Redirect URI for auth flow
logoutRedirectUri: string // Required: Redirect URI after logout
}
const passportProvider = createImmutablePassportProvider({ imtblPassportConfigs })
```
***
## Dependencies
The Immutable Passport provider depends on the EVM provider. Make sure to include both providers in your configuration.
The Immutable Passport provider requires:
* **EVM Provider** - For Ethereum wallet connections
* **Immutable Passport Provider** - For Passport-specific functionality
# ImmutableX Wallet
Source: https://docs.layerswap.io/integration/UI/Widget/WalletManagement/ImmutableXProvider
## Overview
The ImmutableX provider supports Immutable X Layer 2 scaling solution for NFTs and gaming on Ethereum.
## Installation
```typescript yarn theme={"system"} theme={null}
yarn add @layerswap/wallet-imtbl-x
```
```typescript pnpm theme={"system"} theme={null}
pnpm add @layerswap/wallet-imtbl-x
```
```typescript npm theme={"system"} theme={null}
npm install @layerswap/wallet-imtbl-x
```
## Basic Usage
The ImmutableX provider requires no configuration:
```typescript theme={null}
import { LayerswapProvider, Swap } from "@layerswap/widget"
import { createImmutableXProvider } from "@layerswap/wallet-imtbl-x"
import "@layerswap/widget/index.css"
export const App = () => {
return (
)
}
```
# Native Wallet Packages
Source: https://docs.layerswap.io/integration/UI/Widget/WalletManagement/NativeWalletPackages
Use pre-built wallet providers with automatic balance fetching, gas resolution, and connection management
## Overview
Native wallet packages provide complete, out-of-the-box wallet management for multiple blockchain ecosystems. This is the recommended approach for most integrations, offering the fastest path to production with minimal configuration.
***
## Basic Usage
### Quick Start with getDefaultProviders()
The fastest way to get started with all wallet providers:
```typescript theme={"system"} App.tsx theme={null}
import { LayerswapProvider, Swap } from "@layerswap/widget"
import { getDefaultProviders } from "@layerswap/wallets"
import "@layerswap/widget/index.css"
import { walletConnectConfigs, tonConfigs } from "./configs"
export const App = () => {
const walletProviders = getDefaultProviders({
walletConnect: walletConnectConfigs,
ton: tonConfigs
})
return (
)
}
```
```typescript theme={"system"} configs.ts theme={null}
export const walletConnectConfigs = {
projectId: "YOUR_WALLETCONNECT_PROJECT_ID",
name: "Your App Name",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/logo.png"]
}
export const tonConfigs = {
tonApiKey: "YOUR_TON_API_KEY",
manifestUrl: "https://yourapp.com/tonconnect-manifest.json"
}
```
`getDefaultProviders()` returns all available providers: EVM (with zkSync + Loopring), Starknet, Solana, Bitcoin, TON, Tron, Fuel, Paradex, and Immutable X.
### Single Ecosystem
If you only need to support one blockchain ecosystem:
```typescript theme={null}
import { LayerswapProvider, Swap } from "@layerswap/widget"
import { createEVMProvider } from "@layerswap/wallets"
import "@layerswap/widget/index.css"
export const App = () => {
const evmProvider = createEVMProvider({
walletConnectConfigs: {
projectId: "YOUR_WALLETCONNECT_PROJECT_ID",
name: "Your App Name",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/logo.png"]
}
})
return (
)
}
```
### Multi-Ecosystem Support
To support multiple blockchain ecosystems, create providers individually:
```typescript theme={null}
import { LayerswapProvider, Swap } from "@layerswap/widget"
import {
createEVMProvider,
createStarknetProvider,
createSVMProvider,
createBitcoinProvider,
createTONProvider
} from "@layerswap/wallets"
import "@layerswap/widget/index.css"
export const App = () => {
const walletConnectConfigs = {
projectId: "YOUR_WALLETCONNECT_PROJECT_ID",
name: "Your App Name",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/logo.png"]
}
const walletProviders = [
createEVMProvider({ walletConnectConfigs }),
createStarknetProvider({ walletConnectConfigs }),
createSVMProvider({ walletConnectConfigs }),
createBitcoinProvider(),
createTONProvider({
tonConfigs: {
tonApiKey: "YOUR_TON_API_KEY",
manifestUrl: "https://yourapp.com/tonconnect-manifest.json"
}
})
]
return (
)
}
```
***
## Installation
### Recommended: Install Aggregator Package
The easiest way to get all wallet providers:
```bash npm theme={null}
npm install @layerswap/widget @layerswap/wallets wagmi viem @tanstack/react-query @bigmi/client @bigmi/core @bigmi/react
```
```bash yarn theme={null}
yarn add @layerswap/widget @layerswap/wallets wagmi viem @tanstack/react-query @bigmi/client @bigmi/core @bigmi/react
```
```bash pnpm theme={null}
pnpm add @layerswap/widget @layerswap/wallets wagmi viem @tanstack/react-query @bigmi/client @bigmi/core @bigmi/react
```
The `@layerswap/wallets` package includes:
* All provider creator functions (`createEVMProvider`, `createStarknetProvider`, etc.)
* The `getDefaultProviders()` helper function
* All necessary dependencies
### Alternative: Install Individual Providers
If you only need specific ecosystems, you can install individual packages:
```bash npm theme={null}
npm install @layerswap/wallet-evm wagmi viem @tanstack/react-query
```
```bash yarn theme={null}
yarn add @layerswap/wallet-evm wagmi viem @tanstack/react-query
```
```bash pnpm theme={null}
pnpm add @layerswap/wallet-evm wagmi viem @tanstack/react-query
```
Available individual packages:
* `@layerswap/wallet-evm` - Ethereum and EVM-compatible chains
* `@layerswap/wallet-starknet` - Starknet
* `@layerswap/wallet-svm` - Solana
* `@layerswap/wallet-bitcoin` - Bitcoin
* `@layerswap/wallet-ton` - TON
* `@layerswap/wallet-tron` - Tron
* `@layerswap/wallet-fuel` - Fuel
* `@layerswap/wallet-paradex` - Paradex
* `@layerswap/wallet-imtbl-passport` - Immutable Passport
* `@layerswap/wallet-imtbl-x` - Immutable X
***
## Configuration
Each provider creator function accepts its own configuration options. Some providers require specific setup (like WalletConnect for EVM, API keys for TON), while others work without any configuration.
### Provider-Specific Configuration
Configuration is passed directly to each provider:
```typescript theme={null}
import { createEVMProvider, createTONProvider } from "@layerswap/wallets"
// Provider with configuration
const evmProvider = createEVMProvider({
walletConnectConfigs: {
projectId: "YOUR_PROJECT_ID",
name: "Your App",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/icon.png"]
}
})
// Provider without configuration
const bitcoinProvider = createBitcoinProvider()
```
For detailed configuration options for each provider, see their respective documentation pages:
* [EVM Provider Configuration](/integration/UI/Widget/WalletManagement/EVMProvider)
* [Starknet Provider Configuration](/integration/UI/Widget/WalletManagement/StarknetProvider)
* [Solana Provider Configuration](/integration/UI/Widget/WalletManagement/SolanaProvider)
* [Bitcoin Provider Configuration](/integration/UI/Widget/WalletManagement/BitcoinProvider)
* [TON Provider Configuration](/integration/UI/Widget/WalletManagement/TonProvider)
* [Tron Provider Configuration](/integration/UI/Widget/WalletManagement/TronProvider)
* [Fuel Provider Configuration](/integration/UI/Widget/WalletManagement/FuelProvider)
* [Paradex Provider Configuration](/integration/UI/Widget/WalletManagement/ParadexProvider)
* [Immutable Passport Configuration](/integration/UI/Widget/WalletManagement/ImmutablePassportProvider)
* [Immutable X Configuration](/integration/UI/Widget/WalletManagement/ImmutableXProvider)
***
## When to Use Native Packages
Native wallet packages are ideal when:
* You want the fastest integration path with minimal setup
* Standard wallet support meets your requirements
* You don't have existing wallet management infrastructure
***
## Supported Ecosystems
The following native providers are available and ready to use:
### EVM (Ethereum Virtual Machine)
**Package:** `@layerswap/wallet-evm`
Supports MetaMask, WalletConnect, and all EVM-compatible wallets across Ethereum, Polygon, Arbitrum, Optimism, Base, and more.
[View EVM Provider Documentation →](/integration/UI/Widget/WalletManagement/EVMProvider)
### Starknet
**Package:** `@layerswap/wallet-starknet`
Supports ArgentX, Braavos, and all Starknet-compatible wallets on Starknet Mainnet and Sepolia.
[View Starknet Provider Documentation →](/integration/UI/Widget/WalletManagement/StarknetProvider)
### Solana (SVM)
**Package:** `@layerswap/wallet-svm`
Supports Phantom, Solflare, Backpack, and all Solana wallet adapter compatible wallets.
[View Solana Provider Documentation →](/integration/UI/Widget/WalletManagement/SolanaProvider)
### Bitcoin
**Package:** `@layerswap/wallet-bitcoin`
Supports Unisat, Leather, Xverse, and other Bitcoin wallets using the Bigmi library.
[View Bitcoin Provider Documentation →](/integration/UI/Widget/WalletManagement/BitcoinProvider)
### Fuel
**Package:** `@layerswap/wallet-fuel`
Supports Fuel Network wallets and ecosystem.
[View Fuel Provider Documentation →](/integration/UI/Widget/WalletManagement/FuelProvider)
### TON
**Package:** `@layerswap/wallet-ton`
Supports TON Connect compatible wallets including Tonkeeper, MyTonWallet, and more.
[View TON Provider Documentation →](/integration/UI/Widget/WalletManagement/TonProvider)
### Tron
**Package:** `@layerswap/wallet-tron`
Supports TronLink and other Tron ecosystem wallets.
[View Tron Provider Documentation →](/integration/UI/Widget/WalletManagement/TronProvider)
### Specialized Providers
#### Paradex
**Package:** `@layerswap/wallet-paradex`
Integration for Paradex decentralized exchange. Requires Starknet wallet as dependency.
[View Paradex Provider Documentation →](/integration/UI/Widget/WalletManagement/ParadexProvider)
#### Immutable Passport
**Package:** `@layerswap/wallet-immutable-passport`
Immutable's gaming-focused wallet solution with seamless onboarding.
[View Immutable Passport Documentation →](/integration/UI/Widget/WalletManagement/ImmutablePassportProvider)
#### ImmutableX
**Package:** `@layerswap/wallet-immutablex`
Layer 2 scaling solution for NFTs and gaming on Ethereum.
[View ImmutableX Provider Documentation →](/integration/UI/Widget/WalletManagement/ImmutableXProvider)
# Paradex Wallet
Source: https://docs.layerswap.io/integration/UI/Widget/WalletManagement/ParadexProvider
## Overview
The Paradex provider integrates with Paradex decentralized exchange. It requires both EVM and Starknet wallet providers as dependencies.
## Installation
```typescript yarn theme={"system"} theme={null}
yarn add @layerswap/wallet-paradex @layerswap/wallet-evm @layerswap/wallet-starknet wagmi viem @tanstack/react-query
```
```typescript pnpm theme={"system"} theme={null}
pnpm add @layerswap/wallet-paradex @layerswap/wallet-evm @layerswap/wallet-starknet wagmi viem @tanstack/react-query
```
```typescript npm theme={"system"} theme={null}
npm install @layerswap/wallet-paradex @layerswap/wallet-evm @layerswap/wallet-starknet wagmi viem @tanstack/react-query
```
## Basic Usage
The Paradex provider must be used alongside EVM and Starknet providers:
```typescript theme={null}
import { LayerswapProvider, Swap } from "@layerswap/widget"
import { createEVMProvider, createStarknetProvider, createParadexProvider } from "@layerswap/wallet-paradex"
import "@layerswap/widget/index.css"
export const App = () => {
const walletConnectConfigs = {
projectId: "YOUR_WALLETCONNECT_PROJECT_ID",
name: "Your App Name",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/icon.png"]
}
const walletProviders = [
createEVMProvider({ walletConnectConfigs }),
createStarknetProvider({ walletConnectConfigs }),
createParadexProvider()
]
return (
)
}
```
***
## Dependencies
The Paradex provider depends on both EVM and Starknet providers. Make sure to include all three providers in your configuration.
The Paradex provider requires:
* **EVM Provider** - For Ethereum wallet connections
* **Starknet Provider** - For Starknet wallet connections
* **Paradex Provider** - For Paradex-specific functionality
# Partial Integration (EVM)
Source: https://docs.layerswap.io/integration/UI/Widget/WalletManagement/PartialIntegration
Integrate Layerswap with your existing wagmi setup for seamless external wallet detection
## Overview
For applications that already use wagmi for EVM wallet management, Layerswap offers seamless integration that detects and manages externally connected wallets. This hybrid approach gives you the best of both worlds: your existing wallet infrastructure plus Layerswap's swap functionality.
## How It Works
When you integrate Layerswap with your existing wagmi configuration:
1. **External Connection Detection** - Wallets connected outside the widget (through your app's UI) are automatically detected and displayed in Layerswap
2. **Bidirectional Management** - Users can connect and disconnect wallets from both your app's UI and the Layerswap widget
3. **Unified State** - Single source of truth for wallet connections across your entire application
## Implementation
### Prerequisites
Your application should already have:
* `wagmi` installed and configured
* `@tanstack/react-query` set up
* Wallet connectors configured (WalletConnect, MetaMask, Coinbase, etc.)
### Step 1: Install Layerswap Packages
```bash npm theme={null}
npm install @layerswap/widget @layerswap/wallet-evm
```
```bash yarn theme={null}
yarn add @layerswap/widget @layerswap/wallet-evm
```
```bash pnpm theme={null}
pnpm add @layerswap/widget @layerswap/wallet-evm
```
### Step 2: Fetch Layerswap Settings
Use the `useSettings` hook to fetch Layerswap's network configurations:
```tsx theme={null}
import { useSettings } from "@layerswap/widget"
const App = () => {
const apiKey = "YOUR_API_KEY" // Optional, from Partner Dashboard
const { settings, loading } = useSettings(apiKey)
if (loading) return
// Use settings to configure wagmi...
}
```
**Server-Side Settings:** For server-side rendering (SSR), you can fetch Layerswap settings server-side using the `getSettings()` function:
```typescript theme={null}
import { getSettings } from "@layerswap/widget"
// In your server-side code or getServerSideProps
const settings = await getSettings(apiKey)
```
This allows you to pre-configure Layerswap networks on the server before rendering, eliminating the loading state on the client.
### Step 3: Configure Wagmi with Layerswap Networks
Use `useChainConfigs` to get chain configurations from Layerswap settings:
```jsx theme={null}
import { useSettings, LayerswapProvider, Swap, WidgetLoading } from "@layerswap/widget"
import { useChainConfigs, createEVMProvider } from "@layerswap/wallets"
import { createConfig, WagmiProvider } from "wagmi"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { walletConnect, coinbaseWallet } from "wagmi/connectors"
const queryClient = new QueryClient()
const App = () => {
const apiKey = "YOUR_API_KEY"
const { settings, loading } = useSettings(apiKey)
const { chains, transports } = useChainConfigs(settings.networks)
if (loading) return
const connectors = [
// Configure your connectors
]
// Create wagmi config with Layerswap's chains and transports
const wagmiConfig = createConfig({
chains,
transports,
connectors
})
// Create EVM provider
const evmProvider = createEVMProvider({
walletConnectConfigs: {
projectId: "YOUR_WALLETCONNECT_PROJECT_ID",
name: "Your App Name",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/logo.png"]
}
})
return (
)
}
```
***
## Complete Example
Here's a full example showing the entire integration:
```jsx theme={null}
import { useSettings, LayerswapProvider, Swap, WidgetLoading } from "@layerswap/widget"
import { useChainConfigs, createEVMProvider } from "@layerswap/wallet-evm"
import { createConfig, WagmiProvider, useAccount, useConnect, useDisconnect } from "wagmi"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
import { walletConnect, coinbaseWallet, injected } from "wagmi/connectors"
import "@layerswap/widget/index.css"
const queryClient = new QueryClient()
const App = () => {
const apiKey = process.env.NEXT_PUBLIC_LAYERSWAP_API_KEY
const { settings, loading } = useSettings(apiKey)
const { chains, transports } = useChainConfigs(settings.networks)
if (loading) return
const connectors = [
injected(), // MetaMask, Rabby, etc.
walletConnect({
projectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID,
metadata: {
name: "Your App",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/logo.png"]
}
}),
coinbaseWallet({
appName: "Your App"
})
]
const wagmiConfig = createConfig({
chains,
transports,
connectors
})
const evmProvider = createEVMProvider({
walletConnectConfigs: {
projectId: process.env.NEXT_PUBLIC_WALLETCONNECT_PROJECT_ID,
name: "Your App",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/logo.png"]
}
})
return (
)
}
// Your app's header with wallet connection
const Header = () => {
const { address, isConnected } = useAccount()
const { connect, connectors } = useConnect()
const { disconnect } = useDisconnect()
return (
Your App
{isConnected ? (
{address?.slice(0, 6)}...{address?.slice(-4)}
) : (
{connectors.map((connector) => (
))}
)}
)
}
export default App
```
***
## User Experience Flow
### Scenario 1: User Connects via Your App
1. User clicks "Connect Wallet" in your app's header
2. User selects MetaMask and approves connection
3. Your app displays connected wallet in header
4. **Layerswap widget automatically detects the wallet** and shows it as connected
5. User can now use Layerswap swap features immediately
### Scenario 2: User Connects via Layerswap Widget
1. User opens Layerswap widget (no wallet connected yet)
2. User clicks "Connect Wallet" in the widget
3. User selects WalletConnect and approves connection
4. **Your app automatically detects the wallet** and shows it in header
5. User can now use both your app features and Layerswap swaps
### Scenario 3: User Disconnects from Either Interface
1. User has wallet connected
2. User clicks "Disconnect" in either your app OR the widget
3. **Both interfaces update** to show disconnected state
4. Wallet state remains synchronized
***
## Limitations
* **EVM Only** - Partial integration currently only works for EVM chains
* **wagmi Required** - Your app must already use wagmi
* **Network Compatibility** - Only Layerswap-supported EVM networks will be configured
For non-EVM chains, use:
* [Native Wallet Packages](/integration/UI/Widget/WalletManagement/NativeWalletPackages) for standard integration
* [Custom Wallet Management](/integration/UI/Widget/WalletManagement/CustomWalletManagement) for full control
# SVM (Solana) Wallet
Source: https://docs.layerswap.io/integration/UI/Widget/WalletManagement/SolanaProvider
## Overview
The Solana (SVM) wallet provider supports Phantom, Solflare, Backpack, and all Solana wallet adapter compatible wallets.
## Installation
```typescript yarn theme={"system"} theme={null}
yarn add @layerswap/wallet-svm
```
```typescript pnpm theme={"system"} theme={null}
pnpm add @layerswap/wallet-svm
```
```typescript npm theme={"system"} theme={null}
npm install @layerswap/wallet-svm
```
## Basic Usage
```typescript theme={null}
import { LayerswapProvider, Swap } from "@layerswap/widget"
import { createSVMProvider } from "@layerswap/wallet-svm"
import "@layerswap/widget/index.css"
export const App = () => {
const svmProvider = createSVMProvider({
walletConnectConfigs: {
projectId: "YOUR_WALLETCONNECT_PROJECT_ID",
name: "Your App Name",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/icon.png"]
}
})
return (
)
}
```
***
## Configuration
### WalletConnect Configuration
The Solana provider requires WalletConnect configuration:
```typescript theme={null}
import { createSVMProvider } from "@layerswap/wallet-svm"
import type { WalletConnectConfig } from "@layerswap/wallet-svm"
const walletConnectConfigs: WalletConnectConfig = {
projectId: string, // Required: Your WalletConnect project ID
name: string, // Required: Your app name
description: string, // Required: Your app description
url: string, // Required: Your app URL
icons: string[] // Required: Array of logo URLs
}
const svmProvider = createSVMProvider({ walletConnectConfigs })
```
Get your WalletConnect project ID at [WalletConnect Cloud](https://cloud.walletconnect.com/).
# Starknet Wallet
Source: https://docs.layerswap.io/integration/UI/Widget/WalletManagement/StarknetProvider
## Overview
The Starknet wallet provider supports ArgentX, Braavos, and all Starknet-compatible wallets on Starknet Mainnet and Sepolia.
## Installation
```typescript npm theme={null}
npm install @layerswap/wallet-starknet
```
```typescript yarn theme={null}
yarn add @layerswap/wallet-starknet
```
```typescript pnpm theme={null}
pnpm add @layerswap/wallet-starknet
```
## Basic Usage
```typescript theme={null}
import { LayerswapProvider, Swap } from "@layerswap/widget"
import { createStarknetProvider } from "@layerswap/wallet-starknet"
import "@layerswap/widget/index.css"
export const App = () => {
const starknetProvider = createStarknetProvider({
walletConnectConfigs: {
projectId: "YOUR_WALLETCONNECT_PROJECT_ID",
name: "Your App Name",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/icon.png"]
}
})
return (
)
}
```
***
## Configuration
### WalletConnect Configuration
The Starknet provider requires WalletConnect configuration:
```typescript theme={null}
import { createStarknetProvider } from "@layerswap/wallet-starknet"
import type { WalletConnectConfig } from "@layerswap/wallet-starknet"
const walletConnectConfigs: WalletConnectConfig = {
projectId: string, // Required: Your WalletConnect project ID
name: string, // Required: Your app name
description: string, // Required: Your app description
url: string, // Required: Your app URL
icons: string[] // Required: Array of logo URLs
}
const starknetProvider = createStarknetProvider({ walletConnectConfigs })
```
Get your WalletConnect project ID at [WalletConnect Cloud](https://cloud.walletconnect.com/).
# TON Wallet
Source: https://docs.layerswap.io/integration/UI/Widget/WalletManagement/TonProvider
## Overview
The TON wallet provider supports TON Connect compatible wallets including Tonkeeper, MyTonWallet, and more.
## Installation
```typescript yarn theme={"system"} theme={null}
yarn add @layerswap/wallet-ton
```
```typescript pnpm theme={"system"} theme={null}
pnpm add @layerswap/wallet-ton
```
```typescript npm theme={"system"} theme={null}
npm install @layerswap/wallet-ton
```
## Basic Usage
```typescript theme={null}
import { LayerswapProvider, Swap } from "@layerswap/widget"
import { createTONProvider } from "@layerswap/wallet-ton"
import "@layerswap/widget/index.css"
export const App = () => {
const tonProvider = createTONProvider({
tonConfigs: {
tonApiKey: "YOUR_TON_API_KEY",
manifestUrl: "https://yourapp.com/tonconnect-manifest.json"
}
})
return (
)
}
```
***
## Configuration
### TON Configuration
The TON provider requires specific configuration:
```typescript theme={null}
import { createTONProvider } from "@layerswap/wallet-ton"
import type { TonClientConfig } from "@layerswap/wallet-ton"
const tonConfigs: TonClientConfig = {
tonApiKey: string, // Required: Your TON API key
manifestUrl: string // Required: URL to your TON Connect manifest
}
const tonProvider = createTONProvider({ tonConfigs })
```
### TON API Key
The `tonApiKey` is required to interact with the TON blockchain network. It's used for:
* Fetching wallet balances
* Initiating transactions
* Accessing TON Center's HTTP API
#### Getting Your API Key
To obtain a TON API key:
1. Open Telegram and navigate to the [@tonapibot](https://t.me/tonapibot) bot
2. Follow the bot's instructions to register and generate your API key
3. Copy the API key provided by the bot
Without an API key, usage is limited to 1 request per second. Registering for an API key provides access to higher rate limits necessary for production applications. Visit [TON Center](https://toncenter.com/) for more information.
### Manifest URL Configuration
The `manifestUrl` is required by TON Connect and points to a `tonconnect-manifest.json` file that contains essential metadata about your application.
#### Creating the Manifest File
Create a `tonconnect-manifest.json` file in your project's public directory with the following structure:
```json theme={null}
{
"url": "https://your-app-domain.com",
"name": "Your App Name",
"iconUrl": "https://your-app-domain.com/icon.png",
"termsOfUseUrl": "https://your-app-domain.com/terms",
"privacyPolicyUrl": "https://your-app-domain.com/privacy"
}
```
Your application's URL. This should match the domain where your app is hosted.
The name of your application as it will appear to users in TON wallets.
URL to your application's icon/logo. Should be a publicly accessible image file.
Optional URL to your terms of use page.
Optional URL to your privacy policy page.
The manifest file must be publicly accessible. For local development, you may need to host it on a development server or use a public URL. Learn more in the [TON Connect documentation](https://docs.ton.org/v3/guidelines/ton-connect/frameworks/react#set-up-ton-connect).
# Tron Wallet
Source: https://docs.layerswap.io/integration/UI/Widget/WalletManagement/TronProvider
## Overview
The Tron wallet provider supports TronLink and other Tron ecosystem wallets.
## Installation
```typescript yarn theme={"system"} theme={null}
yarn add @layerswap/wallet-tron
```
```typescript pnpm theme={"system"} theme={null}
pnpm add @layerswap/wallet-tron
```
```typescript npm theme={"system"} theme={null}
npm install @layerswap/wallet-tron
```
## Basic Usage
The Tron provider requires no configuration:
```typescript theme={null}
import { LayerswapProvider, Swap } from "@layerswap/widget"
import { createTronProvider } from "@layerswap/wallet-tron"
import "@layerswap/widget/index.css"
export const App = () => {
const tronProvider = createTronProvider()
return (
)
}
```
# Wallet Management
Source: https://docs.layerswap.io/integration/UI/Widget/WalletManagement/WalletManagement
Configure wallet connection for your integration
The Layerswap Widget provides flexible, modular wallet management that can be configured, extended, or fully customized to match your integration needs. You can choose from three approaches depending if you want pre-built solutions or need more control over wallet connections:
Pre-configured wallet providers with automatic balance fetching, gas resolution, and connection management.
Seamless integration with existing Wagmi apps. External wallet connections are automatically detected and synchronized.
Full control over wallet connections using the WalletProvider interface. Integrate Dynamic, Reown, RainbowKit, Privy, or custom solutions.
***
## Comparison
| Approach | Control Level | Complexity | Best For |
| ----------------------------- | -------------- | ---------- | ------------------------------------------------------------------------------------- |
| **Native Wallet Packages** | Pre-configured | Low | Quick integration, standard wallet support |
| **Partial Integration (EVM)** | Medium | Medium | Apps with existing Wagmi setup, hybrid wallet management |
| **Custom Wallet Management** | Complete | High | Custom wallet UX, using 3rd party wallet libraries (Dynamic, Reown, RainbowKit, etc.) |
## Quick Start Examples
### Approach 1: Native Wallet Packages
```typescript App.tsx theme={null}
import { LayerswapProvider, Swap } from "@layerswap/widget"
import { createEVMProvider, createStarknetProvider } from "@layerswap/wallets"
import "@layerswap/widget/index.css"
import { walletConnectConfigs } from "./configs"
export const App = () => {
return (
)
}
```
```typescript configs.ts theme={null}
export const walletConnectConfigs = {
projectId: "YOUR_WALLETCONNECT_PROJECT_ID",
name: "Your App Name",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/logo.png"]
}
```
[Learn more about Native Wallet Packages →](/integration/UI/Widget/WalletManagement/NativeWalletPackages)
### Approach 2: Partial Integration (EVM)
```typescript theme={null}
import { useSettings, LayerswapProvider, Swap } from "@layerswap/widget"
import { useChainConfigs, createEVMProvider } from "@layerswap/wallets"
import { createConfig, WagmiProvider } from "wagmi"
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
const App = () => {
const { settings, loading } = useSettings("YOUR_API_KEY")
const { chains, transports } = useChainConfigs(settings.networks)
if (loading) return
Loading...
const wagmiConfig = createConfig({
chains,
transports,
connectors // Your connectors
})
const evmProvider = createEVMProvider({
walletConnectConfigs: {
projectId: "YOUR_PROJECT_ID",
name: "Your App",
description: "Your app description",
url: "https://yourapp.com",
icons: ["https://yourapp.com/logo.png"]
}
})
return (
)
}
```
[Learn more about Partial Integration →](/integration/UI/Widget/WalletManagement/PartialIntegration)
### Approach 3: Custom Wallet Management
```typescript theme={null}
import { LayerswapProvider, Swap } from "@layerswap/widget"
import { createStarknetProvider } from "@layerswap/wallets"
import useCustomWalletConnection from "./hooks/useCustomWalletConnection"
const App = () => {
// Override native provider with custom implementation
const customProvider = createStarknetProvider({
customHook: useCustomWalletConnection
})
return (
)
}
```
[Learn more about Custom Wallet Management →](/integration/UI/Widget/WalletManagement/CustomWalletManagement)
***
## Supported Ecosystems
Native wallet packages are available for multiple blockchain ecosystems:
* [**EVM (Ethereum)**](/integration/UI/Widget/WalletManagement/EVMProvider) - MetaMask, WalletConnect, and all EVM-compatible wallets
* [**Starknet**](/integration/UI/Widget/WalletManagement/StarknetProvider) - ArgentX, Braavos, and other Starknet wallets
* [**SVM (Solana)**](/integration/UI/Widget/WalletManagement/SolanaProvider) - Phantom, Solflare, and other Solana wallets
* [**Bitcoin**](/integration/UI/Widget/WalletManagement/BitcoinProvider) - Unisat, Leather, and other Bitcoin wallets
* [**Fuel**](/integration/UI/Widget/WalletManagement/FuelProvider) - Fuel Network wallets
* [**TON**](/integration/UI/Widget/WalletManagement/TonProvider) - TON Connect compatible wallets
* [**Tron**](/integration/UI/Widget/WalletManagement/TronProvider) - TronLink and Tron wallets
* [**Paradex**](/integration/UI/Widget/WalletManagement/ParadexProvider) - Paradex exchange integration
* [**Immutable Passport**](/integration/UI/Widget/WalletManagement/ImmutablePassportProvider) - Immutable's gaming wallet
* [**ImmutableX**](/integration/UI/Widget/WalletManagement/ImmutableXProvider) - ImmutableX Layer 2 wallets
***
## Choosing the Right Approach
### Use Native Packages when:
* You want the fastest integration path
* Standard wallet support meets your needs
* You don't have existing wallet management infrastructure
* You want Layerswap to handle all wallet complexity
### Use Partial Integration when:
* Your app already uses Wagmi for EVM chains
* You want external wallet connections detected automatically
* You need unified wallet state across your app
* You want to minimize code duplication
### Use Custom Wallet Management when:
* You need a specific wallet connection UX
* You're using 3rd party wallet libraries (Dynamic, Reown, RainbowKit, Privy, etc.)
* You have custom wallet management requirements
* You need deep control over the connection lifecycle
# IFrame Integration
Source: https://docs.layerswap.io/integration/UI/iFrame
## Layerswap iFrame
The Layerswap iFrame integration allows you to embed the full Layerswap interface directly within your web application. This provides a seamless in-app experience where users can complete swaps without leaving your platform.
Unlike the Widget integration which requires installation and configuration, the iFrame approach is simpler - you just embed a URL. However, it offers less customization and control compared to the Widget.
## Embedded Form
Embed the Layerswap interface directly in your app and customize it to fit your specific use case.
```javascript theme={null}
```
## Customization
For adjusting the look and UX of the Layerswap interface you can pass any configuration parameter listed in the [Configurations](/integration/UI/Configurations) page through URL query parameters:
```html theme={null}
```
All configuration parameters can be passed as URL query parameters, including:
* **Network Selection**: `from`, `to`, `lockFrom`, `lockTo`, `hideFrom`, `hideTo`
* **Asset Selection**: `fromAsset`, `toAsset`, `lockFromAsset`, `lockToAsset`
* **Pre-filled Values**: `amount`, `destAddress`, `account`
* **UI Customization**: `actionButtonText`, `hideAddress`, `hideRefuel`
* **Tracking**: `clientId`, `externalId`
* **Flow Type**: `defaultTab` (`swap`, `cex`, or `deposit`)
When the `destAddress` parameter is included in the URL, a warning will be displayed in the UI and the user must confirm the destination address before proceeding with the transaction.
**If you want to bypass the destination address confirmation flow**, you must integrate Layerswap via the [Widget](/integration/UI/Widget/Quickstart) instead of the iFrame approach.
## Example Implementation
### React Example
```jsx theme={null}
import React from 'react';
const LayerswapEmbed = () => {
const userAddress = '0x1234...'; // User's destination address
const params = new URLSearchParams({
to: 'STARKNET_MAINNET',
from: 'ETHEREUM_MAINNET',
destAddress: userAddress,
asset: 'ETH',
actionButtonText: 'Deposit'
});
return (
);
};
export default LayerswapEmbed;
```
For maximum customization and control, consider using the [Widget integration](/integration/UI/Widget/Quickstart).
# Introduction
Source: https://docs.layerswap.io/introduction
Learn about Layerswap and find quick answers to your questions
### What is Layerswap?
Layerswap is the most affordable cross-chain asset bridging and swapping solution. The app enables fast and frictionless token swapping across 70+ blockchains as well as direct transfers between chains and 15+ exchanges.
Layerswap is trusted by a number of wallets, DEXes and dApps powering cost-efficent, fast and reliable cross-chain asset transaction experience. Highlighting a few of our partners:
Ready (ex Argent) uses Layerswap API to enable fast and easy deposits from other chains to the Mobile wallet and card on Starknet.
Paradex integrated Layerswap API to provide seamless deposit and withdrawal flows for the appchain DEX users.
Immutable uses Layerswap as a fast bridge in Immutable Toolkit and Play allowing gamers and traders to easily bridge to Immutable zkEVM from chains and CEXes.
Linea uses Layerswap widget on Linea Hub for fast, low-cost user onbaording from Centralized Exchanges.
Clave integrated Layerswap API enabling users to instantly deposit funds to the zkSync wallet from CEXes and other wallets.
Nostra's in-app bridge is powered by Layerswap providing secure and fast bridging experience with minimal fees.
### What makes Layerswap different?
Layerswap is committed to providing exceptional experience for cross-chain transactions. Specifically, Layerswap:
* charges the **lowest** possible **fees**,
* enables swaps to/from **blockchains** **that aren't supported elsewhere**,
* provides **exceptional customer support** to its community,
* focuses on **simplifying** the **UX** of crypto transactions,
* enables direct and instant **deposits from CEXes** to blockchains,
* continuously integrates **new** networks and tokens.
### Get connected with us
# Supported Networks & Tokens
Source: https://docs.layerswap.io/networks-tokens
Browse Layerswap's supported networks and tokens
# Bridge with Privy Server Wallets
Source: https://docs.layerswap.io/recipes/privy-wallets
Bridge ERC-20 tokens through Layerswap from a Privy server wallet using gas sponsorship
This flow bridges ERC-20 tokens through Layerswap using a Privy server wallet. It creates a swap with `use_depository: true`, batches the token approval with the bridge call, and submits everything through Privy's gas sponsorship — so the wallet doesn't need native tokens for gas. See the [full example on GitHub](https://github.com/layerswap/examples/tree/main/privy-wallets).
## Prerequisites
* A [Layerswap API key](https://layerswap.io/dashboard)
* A [Privy app](https://docs.privy.io/guide/quickstart) with [server wallets](https://docs.privy.io/guide/server-wallets) and gas sponsorship enabled
* Source-token funds on the Privy wallet address
Testnets are supported — use a testnet API key from the [dashboard](https://layerswap.io/dashboard) and get testnet funds from the [Circle faucet](https://faucet.circle.com/).
## Integration
Create the swap with `use_depository: true` for the Privy wallet address:
```bash theme={null}
curl --request POST 'https://api.layerswap.io/api/v2/swaps' \
--header 'Content-Type: application/json' \
--header 'X-LS-APIKEY: ' \
--data '{
"source_network": "ETHEREUM_SEPOLIA",
"source_token": "USDC",
"destination_network": "ARC_TESTNET",
"destination_token": "USDC",
"destination_address": "",
"amount": 1,
"use_depository": true
}'
```
`use_depository: true` is recommended when using Layerswap from a contract or a server wallet.
The response includes `deposit_actions` with everything needed for the on-chain calls:
```json theme={null}
{
"data": {
"swap": {
"id": "...",
"status": "user_transfer_pending",
"requested_amount": 1,
"source_token": { "symbol": "USDC", "contract": "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238", "decimals": 6 }
},
"deposit_actions": [
{
"order": 0,
"type": "transfer",
"to_address": "",
"call_data": "",
"amount_in_base_units": "0",
"token": { "symbol": "USDC", "contract": "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238", "decimals": 6 },
"encoded_args": ["", "", "", "0x"]
}
],
"quote": { "..." : "..." }
}
}
```
For ERC-20 transfers, the wallet needs to approve the depository contract before the bridge call:
```ts theme={null}
import { encodeFunctionData, erc20Abi, parseUnits, toHex } from "viem";
const depositAction = preparedSwap.deposit_actions.find(
(a) => a.type.toLowerCase().includes("transfer"),
);
const requiredAmount = parseUnits(
String(preparedSwap.swap.requested_amount),
preparedSwap.swap.source_token.decimals,
);
const allowance = await publicClient.readContract({
address: depositAction.token.contract,
abi: erc20Abi,
functionName: "allowance",
args: [walletAddress, depositAction.to_address],
});
const bridgeCall = {
to: depositAction.to_address,
data: depositAction.call_data,
value: toHex(BigInt(depositAction.amount_in_base_units)),
};
const calls =
allowance < requiredAmount
? [
{
to: depositAction.token.contract,
data: encodeFunctionData({
abi: erc20Abi,
functionName: "approve",
args: [depositAction.to_address, requiredAmount],
}),
value: "0x0",
},
bridgeCall,
]
: [bridgeCall];
```
If allowance is already sufficient the `approve` call is skipped. Otherwise both calls are batched into a single sponsored `wallet_sendCalls` request.
Submit the batched calls with Privy `wallet_sendCalls` and `sponsor: true`:
```ts theme={null}
const sendCallsResponse = await privyClient.wallets().rpc(walletId, {
method: "wallet_sendCalls",
chain_type: "ethereum",
caip2: "eip155:11155111",
sponsor: true,
params: {
calls,
},
});
```
Privy returns a `transaction_id` — poll `privyClient.transactions().get(transactionId)` until `transaction_hash` is available, then use it in the next step.
After Privy returns the final chain transaction hash, look it up through Layerswap:
```bash theme={null}
curl \
--header 'X-LS-APIKEY: ' \
"https://api.layerswap.io/api/v2/swaps/by_transaction_hash/"
```
This maps the final transaction back to the Layerswap swap record. See the [Swap Lifecycle](/api-reference/swap-lifecycle) for all possible statuses.
# Security
Source: https://docs.layerswap.io/security
## Smart Contract Audit
The LayerswapDepository smart contract has been audited by [Hexens](https://hexens.io). You can view the [full audit report here](https://hexens.io/audit-reports/layerswap-depository-mar-2026).
## TRAIN Protocol
Layerswap has already processed billions of dollars in transaction volume, showcasing its robustness and safety. The upcoming [TRAIN protocol](https://docs.train.tech) will set new standards in security and decentralization, introducing trustless cross-chain transfers powered by atomic swaps.