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, 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).
How it works
- Create the swap with
use_depository: true.
- Fetch the deposit actions (returned inline on swap creation, or via
GET /swaps/{swapId}/deposit_actions). The action’s
to_address is the Depository contract and call_data is the encoded deposit call.
- For ERC20 only: approve the Depository contract (
to_address) to spend the token amount.
- Submit the transaction to
to_address with the returned call_data (and value for native
deposits — see below).
- 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:
// 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:
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
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 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 |
ERC20 deposit
Native deposit
{
"order": 0,
"type": "transfer",
"to_address": "0x<DepositoryContract>",
"call_data": "0x<encoded depositERC20(id, token, receiver, amount)>",
"amount": 0,
"amount_in_base_units": "0",
"token": { "symbol": "USDC", "contract": "0x<tokenContract>", "decimals": 6 },
"fee_token": { "symbol": "ETH", "contract": null, "decimals": 18 },
"encoded_args": [
"0x0000…<id>",
"0x<tokenContract>",
"0x<receiver>",
"0x<amountHex>"
],
"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.{
"order": 0,
"type": "transfer",
"to_address": "0x<DepositoryContract>",
"call_data": "0x<encoded depositNative(id, receiver)>",
"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…<id>",
"0x<receiver>"
],
"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
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.
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 |