The purpose of this is to validate that makeAsset
of the left order matches takeAsset
from the right order and vice versa. New types of assets can be added without a smart contract upgrade. This is done using a custom IAssetMatcher.
There are possible improvements to the protocol using these custom matcher contracts such as:
- Support for parametric assets. For example, the user can put an order to exchange 10ETH for any NFT from a popular collection.
- Support for NFT bundles.
The general process for completing an order with the Rarible Exchange is as follows:
- Seller approves the Rarible exchange contract to transfer NFT on their behalf
- Seller creates and signs an order. They specify the types and amounts of assets they would like in return.
- Seller submits the order to the indexer
- Potential buyers query the indexer to get sell orders for a specific item or collection
- Buyers can create a matching buy order for a sell order from the indexer. The buyer can then submit a matching buy order to the smart contract and execute the transfer (no sig required).
- Or the buyer can create a new bid and submit it to the indexer (requires signature)
- If the buyer submits a bid, the seller can choose to accept it by creating the matching sell order and submitting it to the contract, or they can send a signed order back to the buyer which the buyer would submit to the contract.
Each order (both buy & sell) consist of a makeAsset
and a takeAsset
Make Asset is what you are sending.
- In a buy order this is what you are paying for the seller's NFT. This can be ETH, ERC20, ERC721, ERC1155, or any custom asset using their Asset Matcher interface
- In a sell order this is the NFT you are selling
Take Asset is what you are accepting in return
- In a buy order this is the NFT you are buying
- In a sell order this is what you are willing to accept.
Field | Description | Required |
---|---|---|
Maker | Address of entity giving up makeAsset |
Yes |
makeAsset | Asset the entity is giving up | Yes |
taker | Address of counterparty | no, if 0 then anyone can fill the order |
takeAsset | Asset the entity is receving | Yes |
salt | nonce for signatures submitted with the order | Generally signatures are only needed if msg.sender != maker |
start | uint - order can't be filled before this time | no |
end | uint - order can't be filled after this time | no |
dataType | bytes4, usually hash of a string like v1 or v2 | Yes |
data | generic bytes . Can be used for protocol extensions |
no |
Field | Description | Required |
---|---|---|
assetType | Specifies ETH, specific ERC20, ERC721, ERC1155 | Yes |
value | uint | Yes |
Field | Description | Required |
---|---|---|
assetClass | bytes4 specifies ETH, ERC20, ERC721 | Yes |
data | bytes - generic data depending on tp. Ex. address for ERC20, token + tokenID for ERC721 | yes |
Asset Class data field is calculated as follows
bytes4 constant public ETH_ASSET_CLASS = bytes4(keccak256("ETH"));
bytes4 constant public ERC20_ASSET_CLASS = bytes4(keccak256("ERC20"));
bytes4 constant public ERC721_ASSET_CLASS = bytes4(keccak256("ERC721"));
bytes4 constant public ERC1155_ASSET_CLASS = bytes4(keccak256("ERC1155"));
All asset types get encoded using these helper functions https://github.com/rariblecom/protocol-contracts/blob/master/exchange-v2/test/assets.js
assetClass
: Truncated hash of string "ERC721" data
: ABI encoded parameters of address
and tokenId
value
: 1
ERC721 Input (Pre encoding)
{
assetType: {
assetClass: "ERC721",
token: "0x25646b08d9796ceda5fb8ce0105a51820740c049",
tokenId: "0xc66d094ed928f7840a6b0d373c1cd825c97e3c7c00000000000000000000000a"
},
value: "1",
}
assetClass
: Truncated hash of string "ERC1155" data
: ABI encoded parameters of address
and tokenId
value
: 1->totalSupply
ERC1155 Input (Pre encoding)
{
assetType: {
assetClass: "ERC1155",
token: "0x25646b08d9796ceda5fb8ce0105a51820740c049",
tokenId: "0xc66d094ed928f7840a6b0d373c1cd825c97e3c7c00000000000000000000000a"
},
value: "100",
}
assetClass
: Truncated hash of string "ERC20" data
: ABI encoded parameters of address
value
: 1->totalSupply
ERC20 Input (Pre encoding)
{
assetType: {
assetClass: "ERC20",
token: "0x25646b08d9796ceda5fb8ce0105a51820740c049",
},
value: "10000000000000000",
}
assetClass
: Truncated hash of string "ETH" data
: 0x value
: 1->1e18
Input (pre encoding)
{
assetType: {
assetClass: "ETH"
},
value: "10000000000000000",
},
Any asset can be added
assetClass
: Truncated hash of any string data
: Whatever is relevant value
: some uint
- Check the start/end date of the orders.
- Check if the taker of the order is blank or taker =
order.taker
- Check if the order is signed by its maker or maker of the order is executing the transaction.
- If the maker of the order is a contract, then an ERC-1271 check is performed.
{% hint style="info" %} Currently, only off-chain orders are supported, this part of the smart contract can be easily updated to support on-chain order books. {% endhint %}
Order execution is done by TransferManager. There are 2 variants:
- SimpleTransferManager (it simply transfers assets from maker to taker and vice versa).
- RaribleTransferManager (sophisticated version, it takes into account protocol commissions, royalties, etc).
{% hint style="success" %} There are plans to extend RaribleTransferManager to support more royalty schemes and add new features like custom fees, multiple order beneficiaries. {% endhint %}
This part of the algorithm can be extended with a custom ITransferExecutor. In the future, new executors will be added to support new asset types, for example, executor for handling bundles can be added.
{% hint style="info" %} Possible improvements:
- Support bundles.
- Support random boxes. {% endhint %}
RaribleTransferManager supports these types of fees:
- Protocol fees (These fees are taken from both sides of the deal).
- Origin fees (Origin and origin fee is set for every order. it can be different for two orders involved).
- Royalties (Authors of the work will receive part of each sale).
Fees calculation, fee side
To take a fee we need to calculate, what side of the deal can be used as money. There is a simple algorithm to do it:
- If ETH is from any side of the deal, it's used.
- If not, then if ERC-20 is in the deal, it's used.
- If not, then if ERC-1155 is in the deal, it's used.
- Otherwise, the fee is not taken (for example, two ERC-721 are involved in the deal).
When we established, what part of the deal can be treated as money, then we can establish, that
- The Buyer is the side of the deal who owns the money.
- The Seller is the other side of the deal.
Then the total amount of the asset (money side) should be calculated
- Protocol fee is added on top of the filled amount.
- The origin fee of the buyer's order is added on top too.
If the buyer is using an ERC-20 token for payment, then he must approve at least this calculated amount of tokens.
If the buyer is using ETH, then he must send this calculated amount of ETH with the transaction.