Wormhole's Wrapped Token Transfers (WTT), formerly known as the Token Bridge, moves ERC-20 tokens between the XRPL EVM and the 30+ chains where WTT is deployed, using a lock-and-mint model:
- Token registration (one-time): The token's metadata (symbol, name, decimals) is attested and registered on the destination chain.
- Lock: Tokens are locked in custody by the WTT contract on the source chain.
- Observe and sign: The Guardian network emits a signed VAA for the transfer.
- Mint / Release: The VAA is verified on the destination chain, minting wrapped tokens (or releasing native ones on the way back). Wrapped tokens are backed 1:1.
WTT is available on XRPL EVM Mainnet and Testnet. Contract addresses are listed in Deployed Contracts.
For manual transfers, use Portal Bridge, the official user-facing app built on Wormhole. Connect your wallet, pick the source and destination chains, and follow the prompts.
npm install @wormhole-foundation/sdk
npm install -D tsx typescript// helper.ts
import {
ChainAddress,
ChainContext,
Network,
Signer,
Wormhole,
Chain,
isTokenId,
TokenId,
} from "@wormhole-foundation/sdk";
import evm from "@wormhole-foundation/sdk/evm";
export async function getSigner<N extends Network, C extends Chain>(
chain: ChainContext<N, C>,
): Promise<{
chain: ChainContext<N, C>;
signer: Signer<N, C>;
address: ChainAddress<C>;
}> {
const signer = await (
await evm()
).getSigner(await chain.getRpc(), process.env.EVM_PRIVATE_KEY!);
return {
chain,
signer: signer as Signer<N, C>,
address: Wormhole.chainAddress(chain.chain, signer.address()),
};
}
export async function getTokenDecimals<N extends Network>(
wh: Wormhole<N>,
token: TokenId,
chain: ChainContext<N, any>,
): Promise<number> {
return isTokenId(token)
? Number(await wh.getDecimals(token.chain, token.address))
: chain.config.nativeTokenDecimals;
}The XRPL EVM's chain name in the Wormhole SDK is XRPLEVM:
// transfer.ts
import { wormhole, amount, Wormhole } from "@wormhole-foundation/sdk";
import evm from "@wormhole-foundation/sdk/evm";
import { getSigner, getTokenDecimals } from "./helper";
(async function () {
const wh = await wormhole("Mainnet", [evm]);
const sendChain = wh.getChain("XRPLEVM");
const rcvChain = wh.getChain("Ethereum");
const source = await getSigner(sendChain);
const destination = await getSigner(rcvChain);
// Pass the address of the ERC20 token you want to bridge.
// To bridge XRP itself, use its ERC20 representation at the
// sentinel address 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE.
const tokenId = Wormhole.tokenId(
"XRPLEVM",
"0xINSERT_YOUR_ERC20_TOKEN_ADDRESS",
);
const amt = "1";
const decimals = await getTokenDecimals(wh, tokenId, sendChain);
const transferAmount = amount.units(amount.parse(amt, decimals));
const xfer = await wh.tokenTransfer(
tokenId,
transferAmount,
source.address,
destination.address,
"TokenBridge", // WTT route (the SDK still uses the legacy route name)
undefined,
);
console.log("Starting Transfer");
const srcTxids = await xfer.initiateTransfer(source.signer);
console.log("Started Transfer:", srcTxids);
console.log("Fetching Attestation");
const timeout = 5 * 60 * 1000;
await xfer.fetchAttestation(timeout);
console.log("Completing Transfer");
const destTxids = await xfer.completeTransfer(destination.signer);
console.log("Completed Transfer:", destTxids);
process.exit(0);
})();Run it with:
npx tsx transfer.tsThis is a manual transfer: your script initiates the transfer on XRPL EVM, waits for the Guardian attestation, and completes it on the destination chain with the destination signer. For development, switch "Mainnet" to "Testnet" and fund your account from the faucet.
The WTT contract on XRPL EVM has no native token wrapping configured (its WETH() slot is unset), so Wormhole.tokenId("XRPLEVM", "native") transfers revert on-chain. Always pass an ERC20 token address. XRP itself is bridgeable this way: it is natively exposed as an ERC20 at the sentinel address 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE, which is also how the Connect widget and Portal Bridge route XRP. Mind the sentinel address usage limits documented in that guide when batching transfers.
A token must be attested on the destination chain once before it can be transferred there. If your token has never been bridged to the target chain, follow the attestation guide first.
- Supported Tokens on XRPL EVM: the tokens currently live on each network.
- WTT overview
- WTT get-started guide
- Native Token Transfers: issue a multichain-native token instead of a wrapped one.
- Track transfers on WormholeScan