Elysium

Token bridging

Bridging a HyperEVM-native ERC-20 to Elysium and back through the canonical bridge, permissionlessly and with zero issuer action.

Last updated

How to bridge a HyperEVM-native ERC-20 to Elysium and back through the canonical bridge: lock on HyperEVM, mint a 1:1 representation on Elysium. This is the stock token bridge of the Arbitrum Nitro stack that Elysium runs on, unchanged.

Bridging is permissionless with zero issuer action. Any standard ERC-20 works, and the first-ever deposit auto-deploys the representation on Elysium at a deterministic address. See Token bridging on Elysium for how this leg fits the other routes.

Testnet is live — its addresses are in the table below. Mainnet is pre-launch, so the address constants in the samples (ROUTER, TOKEN, …) are deliberately left undefined: substitute the testnet values to try this today, or the mainnet values published at launch.

Overview

  • HyperEVM → Elysium (deposit). Approve the standard gateway, then call outboundTransfer on the router with a small amount of native HYPE to fund delivery. The token locks in the gateway on HyperEVM, and the representation is minted to the recipient on Elysium, typically within a minute.
  • Elysium → HyperEVM (withdraw). Call outboundTransfer on the Elysium router. The representation burns immediately, and the original is claimable on HyperEVM after the challenge period.

The gateway holds the escrow itself; there is no separate escrow wallet in this leg.

Testnet

Mainnet

Router (HyperEVM)

0x1aAE2caD8B0249905492087EF230FcCEa3707C45

Coming soon

Standard gateway (HyperEVM), holds the escrow

0x792487eF5E423104bec94e8c58E814e5E0450cEA

Coming soon

Router (Elysium)

0x89659883a9d980925733B0A698F117AAb65ac718

Coming soon

Elysium chain ID

99801

Coming soon

Elysium RPC

https://testnet-rpc.elysium.kinetiq.xyz

Coming soon

HyperEVM chain ID

998

999

Fee asset (both directions)

native HYPE via msg.value

native HYPE via msg.value

Read the gateway per token with getGateway rather than hardcoding it — a token with a custom route resolves elsewhere, and the standard gateway is only the default.

The representation is a standard bridge token: it has the same decimals as the original, only the bridge can mint and burn it, and its supply always equals the amount locked in the gateway on HyperEVM.

Finding the representation

The router maps every HyperEVM token to its gateway and to its deterministic Elysium address, before or after the first deposit:

solidity
// The same router surface exists on both chains: deposits enter on HyperEVM,
// withdrawals enter on Elysium.
interface IGatewayRouter {
    function outboundTransfer(address token, address to, uint256 amount,
        uint256 maxGas, uint256 gasPriceBid, bytes calldata data)
        external payable returns (bytes memory);
    function getGateway(address token) external view returns (address);
    function calculateL2TokenAddress(address token) external view returns (address);
}
solidity
// ROUTER: published at launch. TOKEN: the HyperEVM-native ERC-20 you're bridging.
address rep = IGatewayRouter(ROUTER).calculateL2TokenAddress(TOKEN);
// -> the representation's Elysium address (same before and after first deposit)
address gateway = IGatewayRouter(ROUTER).getGateway(TOKEN);
// -> the gateway that holds the escrow; the contract your approval must target

For a token with no custom registration, getGateway returns the standard gateway, the default every plain ERC-20 uses.

HyperEVM → Elysium (deposit)

Two transactions on HyperEVM: approve the gateway, then deposit through the router. The first-ever deposit of a token also deploys its representation on Elysium. That needs no extra step, though the transaction costs slightly more gas.

Step 1: Approve the gateway

Approve the gateway returned by getGateway, not the router:

solidity
// GATEWAY = IGatewayRouter(ROUTER).getGateway(TOKEN)
IERC20(TOKEN).approve(GATEWAY, 100e18); // 100 tokens at 18 dp

Step 2: Deposit through the router

msg.value funds delivery on Elysium and must cover maxSubmissionCost + maxGas × gasPriceBid, in native HYPE. Over-provisioning is safe: the excess is refunded to the recipient on Elysium.

solidity
uint256 maxGas = 300_000;
uint256 gasPriceBid = 0.1 gwei;   // Elysium gas price bid
uint256 maxSubmissionCost = 4e14; // headroom; excess refunds on Elysium
uint256 fee = maxSubmissionCost + maxGas * gasPriceBid;

// data = abi.encode(maxSubmissionCost, ""), the standard 2-tuple
IGatewayRouter(ROUTER).outboundTransfer{value: fee}(
    TOKEN, RECIPIENT, 100e18, maxGas, gasPriceBid, abi.encode(maxSubmissionCost, bytes(""))
);

What happens: the gateway pulls amount into escrow on HyperEVM and sends a delivery message to Elysium. The representation is minted to to when that message executes, normally automatically within about a minute.

Common reverts: ERC-20 allowance or balance errors (approve the gateway first), NO_VALUE-class errors when msg.value doesn't cover the delivery cost, and non-standard tokens. Fee-on-transfer tokens are not supported, because the bridge mints against the requested amount rather than the amount received.

Elysium → HyperEVM (withdraw)

One transaction on Elysium, with no approval. The bridge burns the representation directly:

solidity
// ELYSIUM_ROUTER: published at launch.
// TOKEN = the ORIGINAL HyperEVM address. The router keys on it, not the representation.
// maxGas/gasPriceBid/data are unused on withdrawals: pass 0, 0, "".
IGatewayRouter(ELYSIUM_ROUTER).outboundTransfer(TOKEN, RECIPIENT, 100e18, 0, 0, "");

Then:

  1. The representation burns immediately on Elysium.
  2. The withdrawal message finalizes after the challenge period.
  3. Claim the original on HyperEVM. The bridge UI lists claimable withdrawals, or you can execute the message against the bridge's outbox programmatically.

The token argument is always the HyperEVM address of the original, in both directions. The bridge keys every route on the parent-chain address.

Common reverts: representation balance too low, or EXTRA_DATA_DISABLED when data is non-empty on a withdrawal.

If a deposit doesn't arrive

Delivery on Elysium is a retryable message. If it fails to execute automatically (for example, because gasPriceBid was below the live Elysium gas price), the funds are not lost. The message stays claimable, anyone can re-execute it, and re-executing mints the representation as if delivery had succeeded the first time. The bridge UI surfaces stuck deposits. Messages left unredeemed for several days can expire, so re-execute promptly.

Reference

solidity
// router (deposit entrypoint on HyperEVM, withdrawal entrypoint on Elysium)
function outboundTransfer(address token, address to, uint256 amount,
    uint256 maxGas, uint256 gasPriceBid, bytes calldata data) external payable returns (bytes memory);
// views (both routers)
function getGateway(address token) external view returns (address);
function calculateL2TokenAddress(address token) external view returns (address);
  • Deposits: approve the gateway, call the router, fund with msg.value HYPE.
  • Withdrawals: call the router on Elysium with 0, 0, "". No approval, no value.
  • The representation address is deterministic and identical before and after deployment.

For sequence diagrams and the trust model, see Token bridging architecture.