Documentation menu

Originals & representations

Every asset bridged through Omnisea has exactly one home chain - the chain where the token contract was originally deployed. On its home chain the asset is called the original. On every other chain it exists as a representation: a minimal, bridge-controlled token deployed lazily by the local bridge.

The lock / mint / burn / unlock cycle

Home chain (original)                    Remote chain (representation)
┌─────────────────────┐   LayerZero    ┌──────────────────────────┐
│ sendOriginal()      │ ─────────────▶ │ deploy (first time only) │
│  -> lock in bridge   │                │  -> mint to recipient     │
└─────────────────────┘                └──────────────────────────┘
┌─────────────────────┐                ┌──────────────────────────┐
│  -> unlock from      │ ◀───────────── │ sendOFT() / sendONFT()   │
│    bridge           │                │  -> burn from sender      │
└─────────────────────┘                └──────────────────────────┘
  • sendOriginal is used on the asset's home chain. The bridge pulls the token with transferFrom and locks it. For ERC-20s the locked amount is measured by balance delta, so fee-on-transfer tokens bridge the amount that actually arrived (OriginalLocked reports both requestedAmount and actualAmount). Rebasing, reflection, blacklist, pausable, or admin-blocked transfer tokens are unsupported because balances or transferability can change outside the bridge's accounting.
  • sendOFT / sendONFT are used anywhere the asset is a representation. The representation is burned; the message carries the original chain ID and original token address, so the asset's identity is preserved end to end.
  • A representation-to-representation transfer (e.g. Optimism -> Arbitrum for a Base-native token) burns on the source and mints on the destination. The home chain is not involved.
  • When a message arrives on the asset's home chain, the bridge unlocks from its locked balance instead of minting.

The bridge enforces direction-correct usage: calling sendOriginal with a representation reverts with RepresentationMustUseSendOFT, and sendOFT with a non-representation reverts with NotRepresentation.

Identity: tokenKey

A bridged asset is globally identified by the pair (originalChainId, originalToken), hashed into a tokenKey:

function tokenKey(uint32 originalChainId, address originalToken) public pure returns (bytes32);      // OmniseaOFTs
function collectionKey(uint32 originalChainId, address originalToken) public pure returns (bytes32); // OmniseaONFTs

Both bridges maintain two mappings around this key:

mapping(bytes32 originalTokenKey => address representation) public originalToOFT;   // originalToONFT on the NFT bridge
mapping(address representation   => OriginalToken original) public oftToOriginal;   // onftToOriginal on the NFT bridge

oftToOriginal(address) / onftToOriginal(address) is the canonical way to check whether a local token is an Omnisea representation and, if so, where its original lives. The web app and indexer both use it.

First transfers and lazy deployment

Representations are deployed on demand, as OpenZeppelin deterministic clones of an immutable implementation, the first time an asset arrives on a chain. The first transfer to a new chain therefore costs more destination gas than subsequent ones.

Senders signal this with the isFirstTransfer flag on quoteSend* / send*, and must buy enough executor gas for the deployment (minCreationGas, default 500,000) instead of the plain transfer floor (minTransferGas, default 400,000). If the provided lzReceive gas in the options is below the applicable floor, the send reverts with InsufficientLayerZeroGas.

You can check whether a representation already exists - and where it will be deployed - before sending:

function representationFor(uint32 originalChainId, address originalToken) external view returns (address);
function predictRepresentation(uint32 originalChainId, address originalToken) external view returns (address);

predictRepresentation is truthful globally and ahead of time thanks to deterministic addresses.

Metadata propagation

The transfer payload carries tokenName, tokenSymbol, tokenDecimals (ERC-20), contractURI, and contractOwner read from the original at send time. Representations are initialized with this metadata, so a bridged USDC keeps its name, symbol, and 6 decimals on every chain. ERC-20 originals that do not implement decimals() default to 18.

Message flow and trust

Transfers are LayerZero V2 messages between bridge peers. Each endpoint ID's peer is immutable once set: repeating the same peer is a no-op, but clearing it or replacing it with a different address reverts. First-time same-address CREATE3 peers activate immediately, while first-time different-address peers have a 7-day activation delay before inbound messages are trusted. Messages are verified by configured DVNs and executed by the LayerZero executor. See Security for the verification model and Failed messages for what happens when destination execution fails.

Feedback