How NFT marketplace works-1: Wyvern
It’s a series about NFT trading exploration. If you want to sell your NFT with your peers or have a marketplace on your social media, you can tryBlockBoator join ourdiscordfor more information.

Fig 1. Wyvern protocol and OpenSea.
Background
In 2018, some ERC20 token exchanges matched the buyer and seller to trade tokens. The Wyvern team builds an ERC721 version of such settlement exchange. However, there’re some disadvantages of relying on a central exchange trading assert, such as fallible gatekeepers and segmentation of the market due to interface constraints. Wyvern team builds Wyvern protocol which makes orders matching and assets trading automatically without relying on a particular exchange [1]. Besides, the Wyvern protocol is leveraged by OpenSea and has become one of the most-used contracts.
How Wyvern Protocol works
We speculate the NFT exchange protocol by three aspects: order matching, order validation, and asset transfer in the series. If you have more interest in why such three parts, please refer to Productivity, Digital asset, and Asset exchange.
1. Order match
Wyvern Protocol [2] matches seller and buyer by atomic match. When the orders are matched, two orders listed by the sellers and buyer fulfill the requirement of the counter order, respectively.
As the seller sells NFT, she has to specify the NFT to be sold, the price expected to receive, and the fee. The order can be split into info, ** asset sold**, ** expected payment**, and ** fee**.
Order {
address exchange;
address maker;
address taker;
uint makerRelayerFee;
uint takerRelayerFee;
uint makerProtocolFee;
uint takerProtocolFee;
address feeRecipient;
FeeMethod feeMethod;
SaleKindInterface.Side side;
SaleKindInterface.SaleKind saleKind;
address target;
AuthenticatedProxy.HowToCall howToCall;
bytes calldata;
bytes replacementPattern;
address staticTarget;
bytes staticExtradata;
address paymentToken;
uint basePrice;
uint extra;
uint listingTime;
uint expirationTime;
uint salt;
}
Info
The user has to specify the exchange contract to interact with, who is the order maker, and the side of the asset trading, which can be buy or sell. The taker's addressis the restricted one who can take the order. If there is no constraint on the taker, it would be ZERO ADDRESS. The salt is specified with different random numbers to prevent duplicate orders matched.
Asset sold
Instead of listing transferred assets in the order, the asset transition is abstracted as calldata. (If you don’t know what calldata is, please refer to this excellent article.) The order lists what it will do in the asset transferring stage, including the target address to call with calldata and howToCall(call/delegateCall).
For example, if a seller lists an order to sell BAYC #0 to the_buyer._You can create an order with
- target: address of BAYC
- calldata: calldata of_transferFrom(seller, buyer, 0)_
- howToCall: 0 (call)
If the order maker doesn’t restrict who can take the order, the_to_address of the calldata is unknown while making the order. Thus, replacement of the same bit-length as calldata, assigns the bits to be replaced by the counter order. In the following example, the seller specifies the unknown address to be replaced by the_to_address of the calldata in the buyer order.

Fig 1. calldata and replacement in order.
Payment expected
The payment can be 1 ETH, or 2 USDC according to the paymentToken and basePrice. The ** saleKindof order can be FixedPrice or DutchAuction. In DutchAuction, the final price varies in terms of the timestamp of transaction executed, ** listingTime, ** expirationTime**, and ** extra,**which is the minimum bid increment in the auction.
Fee
The fee can be divided based on fee payer and fee type. Fee payer can be maker or taker as fee type can be relayer fee or protocol fee. All four fee categories are makerRelayerFee, ** takerRelayerFee**, ** makerProtocolFee**, and ** takerProtocolFee**. After confirming the fee amount, the order maker specifies the feeRecipient address as the fee recipient. The fee payer can pay the fee by two ** feeMethods**: ProtocolFee or SplitFee. When choosing SplitFee, The payer pays the fee as a fraction of the payment paid by the buyer. On the other hand, while the feeMethod is ProtocolFee, the fee payer pays with Wyvern token.
2. Order validation
After the order matcher submits two orders from both sides and executes atomicMatch, the Wyvern contract will authenticate and validate the orders. There are two validations when the order atomicMatch in Wyvern protocol. One is validation before the asset is transferred, and the other is staticCall check after the asset is transferred.
Validation before the asset is transferred
- The order maker signs the signature of the order.
- All the parameter is valid and consistent in both orders.
- Both orders have not been completed or canceled.
- The calldata of both orders after replacement are identical.
Static call after the asset is transferred
The remaining elements of order not mentioned are staticTarget and staticExtradata. After asset transition, the contract will call staticCall to the staticTarget with calldata combined from calldata of seller and staticExtradata. Order makers can use staticCall to ensure the new state after asset transfer, and the seller's calldata are consistent with expectation.
Since the calldata, payment token, and payment amount of both seller and buyer are confirmed to be identical before the asset is transferred. The staticCall and staticExtradata are empty in most cases.
3. Asset transfer
After the orders are validated, the calldata of the seller will be called to transfer the NFT, and payment and fee will be transferred to the maker of sell order and fee recipient, respectively. Since the owner does not transfer the asset, there must be an approved delegate to transfer the listed NFT and payment token if the token is not a native token. The user has to approve the delegate to transfer the users’ token (ERC20, ERC721) before their first atomic match. If an approved delegate is an exchange contract itself, the user has to approve the delegate each time after the exchange contract upgrade [3]. Therefore, a proxy contract is essential to make the user only approve once, no matter how many times the contract is updated in the future. There are two kinds of proxies,_tokenTransferProxy_to transfer ERC20and_OwnableDelegateProxy_to transfer NFT.
TokenTransferProxy and OwnableDelegateProxy
The tokenTransferProxy is the proxy to transfer all ERC20 from anyone as the payment token. Since the payment token, amount and sender are explicitly recorded in the order, tokenTransferProxy can only transfer ERC20 after the owner creates and atomicMatch a valid order. However, if all users use same proxy to transfer NFT, a malicious user can transfer NFT from anyone approving proxy due to NFT transferred through the execution of calldata. To avoid such risk, users must register OwnableDelegateProxy from proxyRegistry before their first atomic math.

Fig 2. OwnableDelegateProxy.
The Wyvern exchange contract would call_proxy_method, which is an implementation of contract AuthenticatedProxy to OwnableDelegateProxy with calldata and target, and howToCall from sender order to execute NFT transfer. (If you would like to know more about the proxy pattern, please read this article in detail)

Fig 3. proxy method in AuthenticatedProxy.
From Wyvern 2.2 to Wyvern 2.3
In January 2022, OpenSea was exploited, causing NFT to be sold at a lower price [4]. The technical view of the exploit is that the order signed by the owner of NFT is not canceled so that anyone who gets the order and signature can atomic match the order with a lower price listed before. OpenSea upgraded Wyvern 2.2 to Wyvern 2.3 to allow users to cancel all the past signed orders simultaneously by increasing the nonce of the user in the Wyvern contract. Only the order signed with the nonce greater than the nonce of the user in contract is valid in atomic match.
Wyvern 2.3 introduce three upgrades to fix the limitation in Wyvern 2.2 [5]. You can check the guide and code for more information.
- Bulk cancellation: add nonce of each user to enable the user to cancel all orders in one transaction
- EIP-712 [6]: user sign the order of typed structured data
- EIP-1271 [7]: order can be signed by contract

Fig 4. Wyvern 2.2 and 2.3.
Conclusion
It’s incredible to trade NFT without intermediate with the Wyvern protocol. However, there are some limitations despite the Wyvern protocol has upgraded from 2.2 to 2.3. First, there’s only one fee receiver in the order. Fee paid to the NFT creator and protocol cannot be split and transferred by contract simultaneously. Another limitation is that it’s hard to include multiple assets in one trade. Atomicizer contract is needed to enable multiple assets trading which makes the whole protocol more complex. To resolve the limitation, OpenSea introduces the Seaport protocol, and we are going to discuss that in the next post.
BlockBoat
If you would like to sell your NFT with your peers or have a marketplace on your social media, you can try BlockBoat, which enables you to trade NFT anytime anywhere with anyone.
If you are interested in BlockBoat, please join our discordfor more information. We are eager to hear any feedback from you!
Reference
[1]Announcing the Wyvern Exchange: Any Ethereum asset, any ERC20 token, zero trust required
[2]Project Wyvern Ethereum Smart Contracts
[3]Special case ETH as a currency of exchange
[4]OpenSea Exploit Sees Bored Ape Yacht Club NFT Sell For $1,700 in Ethereum
[5]Wyvern 2.3: Developer Upgrade Guide
[6]EIP-712: Typed structured data hashing and signing
[7]EIP-1271: Standard Signature Validation Method for Contracts
Thanks to Elton Liao