Documentation menu

OmniseaONFTs

The ERC-721 bridge and representation factory. Immutable, permissionless, deployed at 0x5c0cFc7B0758e5bedE5E9E3A8BA226591f09dC5b on every chain.

contract OmniseaONFTs is Ownable, ReentrancyGuard

The contract mirrors OmniseaOFTs - fees, options validation, immutable peer setup and first-set activation timing, hooks, owner functions, and failed-message recovery are identical. This page documents what differs for NFTs.

Send functions

The value argument is a tokenId, not an amount. Each function keeps the same three overloads (plain / isFirstTransfer / isFirstTransfer + Hook).

sendOriginal

Locks an original NFT (requires approve or setApprovalForAll to the bridge) and sends it to dstEid:

function sendOriginal(
    uint32 dstEid,
    address originalCollection,
    uint256 tokenId,
    address recipient,
    bool isFirstTransfer,
    Hook calldata hook,        // optional overload
    bytes calldata options
) external payable returns (MessagingReceipt memory receipt);

sendONFT

Burns a representation NFT and sends it back home or onward. Reverts with RepresentationMustUseSendONFT if you call sendOriginal with a representation, and NotRepresentation if you call sendONFT with anything else:

function sendONFT(
    uint32 dstEid,
    address representation,
    uint256 tokenId,
    address recipient,
    bool isFirstTransfer,
    Hook calldata hook,        // optional overload
    bytes calldata options
) external payable returns (MessagingReceipt memory receipt);

quoteSendOriginal / quoteSendONFT

View counterparts returning the exact total fee, like the OFT quotes.

What the message carries

The NFT payload propagates collection metadata, the token's URI, and ERC-2981 royalty configuration with every transfer:

struct TokenPayload {
    MessageKind messageKind;   // Transfer | Restore
    uint32  originalChainId;
    address originalToken;
    uint256 tokenId;
    address sender;
    address recipient;
    string  tokenName;
    string  tokenSymbol;
    string  tokenURI;          // per-token, stored on the representation at mint
    string  contractURI;
    address royaltyReceiver;   // ERC-2981 default royalty, propagated from the original
    uint96  royaltyBps;
    address contractOwner;     // original collection's owner() - becomes the representation's owner
    Hook    hook;
}

See Representations for what the issuer-owned representation can do with this metadata.

Identity & discovery

function collectionKey(uint32 originalChainId, address originalToken) public pure returns (bytes32);
function representationFor(uint32 originalChainId, address originalToken) external view returns (address);
function predictRepresentation(uint32 originalChainId, address originalToken) external view returns (address);

/// Reverse lookup for local representations.
mapping(address representation => OriginalCollection original) public onftToOriginal;

/// Forward lookup by collection key.
mapping(bytes32 originalCollectionKey => address representation) public originalToONFT;

/// Per-token lock registry for originals held by the bridge.
mapping(address originalToken => mapping(uint256 tokenId => bool locked)) public locked;

Safe-transfer guard

The bridge implements onERC721Received defensively: it only accepts the exact token it is actively locking inside a sendOriginal call. Any other NFT pushed at the bridge via safeTransferFrom reverts with UnexpectedERC721Received - tokens cannot be stranded in the bridge by accident.

NFT-specific events & errors

event OriginalLocked(address indexed originalToken, uint256 indexed tokenId, address indexed owner);
event OriginalUnlocked(address indexed originalToken, uint256 indexed tokenId, address indexed recipient);
event RepresentationBurned(address indexed representation, uint32 indexed originalChainId, address indexed originalToken, address owner, uint256 tokenId);

error NotLocked();                      // unlock for a token the bridge does not hold
error TokenAlreadyLocked();             // double-lock attempt
error UnexpectedERC721Received();       // unsolicited safeTransferFrom into the bridge
error RepresentationMustUseSendONFT();  // sendOriginal called with a representation

RepresentationDeployed, RepresentationMinted, BridgeMessageSent/Received, hook events, recovery events, and configuration events match the OFT bridge with tokenId taking the place of amount.

Metadata

First transfers deploy an ONFT representation initialized with the original collection's name, symbol, contractURI, default royalty, and owner. Each minted token stores the tokenURI carried in its transfer, so bridged NFTs render correctly in wallets and marketplaces - including ERC-2981 royalty queries.

Feedback