For the complete documentation index, see llms.txt. This page is also available as Markdown.

Token Interop

How ERC-20 and SPL tokens are the same underlying account on Rome.

Rome represents an ERC-20 token and its underlying SPL token as one shared account. This page explains how tokens work across EVM and Solana.

The shared-state model

Rome doesn't lock tokens on one side and mint wrapped copies on another. An ERC-20 token on Rome is a transparent wrapper over an SPL token account on Solana — the ERC-20 balance is the SPL balance.

An ERC-20 wrapper over the same SPL token account on Solana
  • No bridging delay — the ERC-20 balance is the SPL balance

  • No liquidity fragmentation — DeFi on both sides sees the same tokens

  • No bridge risk — there is no separate escrow to exploit

Import paths below use @rome-protocol/rome-solidity. The npm publish is pending; today you consume these from the public rome-solidity repo (git dependency or copied interfaces). Precompile interfaces live in contracts/interface.sol.

The wrapper contract

SPL_ERC20 (and its cached-track variant SPL_ERC20_cached, which the factory deploys today) provide a full ERC-20 interface over an SPL mint:

  • balanceOf() — reads the user's ATA balance from Solana

  • transfer() — moves tokens on Solana

  • approve() / allowance() — use EVM storage (SPL has no EVM-style allowances)

  • totalSupply() — reads the SPL mint supply

The factory

ERC20SPLFactory deploys a wrapper for any SPL mint:

Live factory addresses: Hadrian 0x86149124d74ebb3aa41a19641b700e88202b6285, Martius 0xd7aeeedca26cdd4d34eb7c21110af2e590a8c58a. Always verify against the registry — it is the source of truth for deployed addresses.

Canonical mints

There is no on-chain token-registry contract. Canonical wrappers and gas/bridge tokens are curated in the off-chain rome-protocol/registry; permissionless wrappers created via add_spl_token_no_metadata are discovered from the on-chain TokenCreated event. This keeps each asset mapped to a single canonical SPL mint without fragmenting liquidity.

SPL operations from Solidity

For user-PDA-signed SPL primitives, use the HelperProgram precompile (0xFF…09) — ATA creation, SPL transfers, and gas↔lamports conversion:

transfer_spl has several overloads (including a delegate variant for transferFrom flows); see interface.sol for exact signatures. On the cached track, the equivalent operations live on ISplCached (0xFF…05) and IAssociatedSplCached (0xFF…06). A contract uses one track consistently.

Deposit and withdraw

  • Into EVM — the SPL side credits the user's PDA-owned ATA; the ERC-20 wrapper immediately reflects the balance. Cross-chain inbound transfers settle trustlessly via a user-signed authorization on the bridge.

  • Out to Solana — call the Withdraw precompile (0x42…16): withdraw_to_pda / withdraw_to_ata move tokens from the user's PDA back to Solana. The wrap-gas-to-SPL path is withdraw_to_ata.

Gas token

Each chain has its own gas token — any SPL token, priced via a Meteora DAMM pool (v1 or v2, configurable). The public chains (Martius, Hadrian) use USDC. There is no universal default gas token.

Constraints

  • SPL token amounts are uint64 (max 18,446,744,073,709,551,615)

  • Allowances use EVM storage, not Solana delegates

  • ERC-20 wrapper symbols must be unique per factory

What's Next

Last updated

Was this helpful?