diff --git a/design/0006_nft_bridge.md b/design/0006_nft_bridge.md new file mode 100644 index 000000000..51e9b743a --- /dev/null +++ b/design/0006_nft_bridge.md @@ -0,0 +1,144 @@ +# NFT bridge App + +[TOC] + +## Objective + +To use the Wormhole message passing protocol to transfer NFTs between different connected chains. + +## Background + +NFTs are a new asset class that has grown in popularity recently. It especially attracts new users and companies to +crypto. NFTs, just like traditional tokens, are minted on a single blockchain and cannot be transferred to other chains. +Howevre as more chains introduce NFT standards and marketplaces there is demand for ways to transfer NFTS across chains +to access these markets and collect them in a single wallet. + +## Goals + +We want to implement a generalized NFT bridge using the Wormhole message passing protocol that is able to bridge any +standards-compliant NFT between chains, creating unique wrapped representations on each connected chain on demand. + +* Allow transfer of standards-compliant NFTs between chains. +* Use a universal NFT representation that is compatible with most VM data types. + +## Non-Goals + +* Support EIP1155 +* Manage / Transfer chain-specific NFT metadata that isn't broadly applicable to all chains. +* Automatically relay NFT transfer messages to the target chain. + +## Overview + +On each chain of the NFT bridge network there will be a NFT bridge endpoint program. + +These programs will manage authorization of payloads (emitter filtering), wrapped representations of foreign chain +NFTs ("Wrapped NFTs") and custody locked NFTs. + +We aim to support: + +- EIP721: Ethereum, BSC +- Metaplex SPL Meta: Solana + +## Detailed Design + +For outbound transfers, the contracts will have a lock method that either locks up a native NFT and produces a +respective Transfer message that is posted to Wormhole, or burns a wrapped NFT and produces/posts said message. + +For inbound transfers they can consume, verify and process Wormhole messages containing a NFT bridge payload. + +There will be three different payloads: + +* Transfer - Will trigger the release of locked NFTs or minting of wrapped NFTs. + +Identical to the NFT bridge: + +* RegisterChain - Register the NFT bridge contract (emitter address) for a foreign chain. +* UpgradeContract - Upgrade the contract. + +In order to transfer an NFT to another chain, a user needs to call the transfer method of the bridge contract with the +recipient details. The contract will either hold the NFTs in a custody account (in case it is a native NFT) or burn +wrapped NFTs. Wrapped NFTs can be burned because they can be freely minted once they are transferred back. After the +lockup the contract will post a Transfer payload message to Wormhole. Once the message has been signed by the guardians, +it can be posted to the target chain of the transfer. The target chain will then either release the native NFT from +custody or mint a wrapped NFT depending on whether it's a native NFT there. The program will keep track of consumed +message digests for replay prevention. + +Since the method for posting a VAA to the NFT bridge is authorized by the message signature itself, anyone can post any +message. + +Since every NFT has unique metadata the Transfer messages contain all metadata, a transfer (even the first on per NFT) +only requires a single Wormhole message to be passed compared to the Token Bridge. On the first transfer action of an +NFT (address / symbol / name) a wrapped asset (i.e. master edition or new contract) is created. When the wrapped asset ( +contract) is already initialized or was just initialized, the (new) token_id and metadata URL are registered. + +### API / database schema + +Proposed bridge interface: + +transfer(address token, uint256 token_id, uint16 recipient_chain, bytes32 recipient) - Initiate a Transfer + +completeTransfer(Message transfer) - Execute a Transfer message + +registerChain(Message registerChain) - Execute a RegisterChain governance message + +upgrade(Message upgrade) - Execute a UpgradeContract governance message + +--- +Payloads: + +Transfer: + +``` +PayloadID uint8 = 1 +// Address of the NFT. Left-zero-padded if shorter than 32 bytes +NFTAddress [32]uint8 +// Chain ID of the NFT +NFTChain uint16 +// Name of the NFT +Name [32]uint8 +// Symbol of the NFT +Symbol [10]uint8 +// ID of the token (big-endian uint256) +TokenID [32]uint8 +// URL of the NFT +URLLength u8 +URL [n]uint8 +// Address of the recipient. Left-zero-padded if shorter than 32 bytes +To [32]uint8 +// Chain ID of the recipient +ToChain uint16 +``` + +RegisterChain: + +``` +PayloadID uint8 = 2 +// Chain ID +ChainID uint16 +// Emitter address. Left-zero-padded if shorter than 32 bytes +EmitterAddress [32]uint8 +``` + +UpgradeContract: + +``` +PayloadID uint8 = 3 +// Address of the new contract +NewContract [32]uint8 +``` + +## Caveats + +There is no guarantee for completion of transfers. If a user initiates a transfer and doesn't call completeTransfer on +the target chain, a transfer might not be completed. In case a guardian set change happens in-between and the original +signer guardian set expires, the transfer will be stuck indefinitely. + +Since there is no way for a NFT bridge endpoint to know which other chain already has wrapped assets set up for the +native asset on its chain, there may be transfers initiated for assets that don't have wrapped assets set up yet on the +target chain. However, the transfer will become executable once the wrapped asset is set up (which can be done any time) +. + +Currently Solana only supports u64 token ids which is incompatible with Ethereum which specifically mentions the use of +UUIDs as token ids (utilizing all bytes of the uint256). There will either need to be a mechanism to translate ids i.e. +a map of `[32]u8 -> incrementing_u64` (in the expectation there will never be more than MaxU64 editions) or Solana needs +to change their NFT contract. \ No newline at end of file