Bridge Tokens
Most users should bridge through the official web UI. The programmatic flow below is for scripts and AI agents.
Bridge UI
| Network | URL |
|---|---|
| Mainnet (Taiko Alethia) | bridge.taiko.xyz |
| Testnet (Taiko Hoodi) | bridge.hoodi.taiko.xyz |
The UI handles approvals, fee estimation, and relay monitoring. On testnet, the sidebar exposes a faucet for test tokens like HORSE — fund your wallet with Hoodi L1 ETH from a public Hoodi faucet first.
Programmatic bridging
Use the flow below only when scripting a bridge transfer or driving it from an agent.
Agent prompt
Paste this into your coding agent:
Reference https://docs.taiko.xyz/SKILL.md
Bridge 0.1 WETH from Ethereum L1 to Taiko L2.
Approve the ERC20Vault, then call sendToken with the BridgeTransferOp struct.Manual mode
Contract addresses
Mainnet (Ethereum L1 ↔ Taiko Alethia L2)| Contract | Address |
|---|---|
| L1 Bridge | 0xd60247c6848B7Ca29eDdF63AA924E53dB6Ddd8EC |
| L1 ERC20Vault | 0x996282cA11E5DEb6B5D122CC3B9A1FcAAD4415Ab |
| L1 SignalService | 0x9e0a24964e5397B566c1ed39258e21aB5E35C77C |
| L2 Bridge | 0x1670000000000000000000000000000000000001 |
| L2 ERC20Vault | 0x1670000000000000000000000000000000000002 |
| L2 SignalService | 0x1670000000000000000000000000000000000005 |
| Contract | Address |
|---|---|
| L1 Bridge | 0x6a4cf607DaC2C4784B7D934Bcb3AD7F2ED18Ed80 |
| L1 ERC20Vault | 0x0857cd029937E7a119e492434c71CB9a9Bb59aB0 |
| L1 SignalService | 0x4c70b7F5E153D497faFa0476575903F9299ed811 |
| L2 Bridge | 0x1670130000000000000000000000000000000001 |
| L2 ERC20Vault | 0x1670130000000000000000000000000000000002 |
| L2 SignalService | 0x1670130000000000000000000000000000000005 |
Bridge ERC-20 tokens (L1 → L2)
The mainnet examples below use a generic 18-decimal ERC-20. Adjust parseUnits / --to-wei for tokens with different decimals (for example, USDC has 6 decimals). For the Hoodi testnet version, see Testnet example.
Approve the ERC20Vault
The ERC20Vault pulls tokens from your wallet. Approve it for the amount you want to bridge.
import { ethers } from "ethers";
const provider = new ethers.JsonRpcProvider("https://eth.llamarpc.com");
const signer = new ethers.Wallet(process.env.PRIVATE_KEY, provider);
const ERC20_ABI = ["function approve(address spender, uint256 amount) returns (bool)"];
const token = new ethers.Contract(TOKEN_ADDRESS, ERC20_ABI, signer);
const L1_ERC20_VAULT = "0x996282cA11E5DEb6B5D122CC3B9A1FcAAD4415Ab";
const amount = ethers.parseUnits("100", 18);
const tx = await token.approve(L1_ERC20_VAULT, amount);
await tx.wait();Call sendToken on the ERC20Vault
Send a BridgeTransferOp struct to initiate the bridge.
BridgeTransferOp fields:| Field | Type | Description |
|---|---|---|
destChainId | uint64 | Destination chain ID (167000 for Alethia, 167013 for Hoodi) |
destOwner | address | Message owner on the destination chain (usually your own address) |
to | address | Token recipient on L2 |
fee | uint64 | Processing fee in wei paid to the relayer (set 0 to self-relay) |
token | address | L1 token contract address |
gasLimit | uint32 | Gas limit for L2 execution (140000 is a safe default) |
amount | uint256 | Token amount in smallest unit |
const VAULT_ABI = [
"function sendToken((uint64 destChainId, address destOwner, address to, uint64 fee, address token, uint32 gasLimit, uint256 amount) op) payable returns (tuple)"
];
const vault = new ethers.Contract(L1_ERC20_VAULT, VAULT_ABI, signer);
const op = {
destChainId: 167000,
destOwner: signer.address,
to: signer.address,
fee: 0,
token: TOKEN_ADDRESS,
gasLimit: 140000,
amount: ethers.parseUnits("100", 18),
};
// fee = 0 means self-relay; otherwise pass msg.value ≥ fee to cover the relayer
const tx = await vault.sendToken(op, { value: 0 });
const receipt = await tx.wait();
console.log("Bridge tx:", receipt.hash);Wait for relay
After the L1 transaction confirms, the bridge message is relayed to L2 — typically within a few minutes. Track progress in the Bridge UI or by watching the L2 SignalService.
Testnet example
Swap in the Hoodi L1 ERC20Vault, a Hoodi L1 RPC, and chain ID 167013:
cast send 0x0857cd029937E7a119e492434c71CB9a9Bb59aB0 \
"sendToken((uint64,address,address,uint64,address,uint32,uint256))" \
"(167013,$YOUR_ADDRESS,$YOUR_ADDRESS,0,$TOKEN_ADDRESS,140000,$(cast --to-wei 100))" \
--rpc-url https://hoodi.ethpandaops.io \
--private-key $PRIVATE_KEYNotes
- First bridge deploys a BridgedERC20. The first time a given L1 token is bridged to L2, Taiko deploys a
BridgedERC20contract for it. Subsequent bridges reuse the same contract. - Bridged USDC ≠ native USDC. Bridging L1 USDC through the ERC20Vault produces a BridgedERC20, not Taiko's native USDC (
0x07d83526730c7438048D55A4fc0b850e2aaB6f0b). For native USDC use Circle CCTP. - No rebase tokens. Rebasing tokens (e.g., stETH) aren't supported. Use the wrapped version (e.g., wstETH).
- Fee-on-transfer tokens. Supported, but the amount credited on L2 reflects the post-fee balance.
- Self-relay. Setting
feeto0skips the relayer — you finalize the message on L2 yourself via the Bridge UI or by callingprocessMessageon the L2 Bridge. - Common L2 tokens:
| Token | L2 Address |
|---|---|
| WETH | 0xA51894664A773981C6C112C43ce576f315d5b1B6 |
| USDC (native) | 0x07d83526730c7438048D55A4fc0b850e2aaB6f0b |