List your NFT collection
Overview
This guide describes the steps for listing your NFT collection on Mavis Market.
Before you start
Before you start listing your NFT collection on Mavis Market, make sure you have the following:
- Your application for listing on Mavis Market is approved.
- Basic knowledge of Solidity.
Create an NFT contract on the testnet
This step walks you through creating a smart contract used for minting your NFTs.
- To deploy an NFT smart contract on the testnet, the contract must extend the Sky Mavis's
ERC721Common
smart contract. This contract, in turn, is derived from the OpenZeppelin ERC721 smart contract and includes some additional required methods that make it easier to operate. The dependencies are located in the contract-template repository.
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;
import "./refs/IERC721State.sol";
import "./refs/ERC721Nonce.sol";
import "./ERC721PresetMinterPauserAutoIdCustomized.sol";
abstract contract ERC721Common is ERC721Nonce, ERC721PresetMinterPauserAutoIdCustomized, IERC721State {
constructor(string memory name, string memory symbol, string memory baseTokenURI)
ERC721PresetMinterPauserAutoIdCustomized(name, symbol, baseTokenURI)
{}
/**
* @inheritdoc IERC721State
*/
function stateOf(uint256 _tokenId) external view virtual override returns (bytes memory) {
require(_exists(_tokenId), "ERC721Common: query for non-existent token");
return abi.encodePacked(ownerOf(_tokenId), nonces[_tokenId], _tokenId);
}
/**
* @dev Override `ERC721-_baseURI`.
*/
function _baseURI()
internal
view
virtual
override(ERC721, ERC721PresetMinterPauserAutoIdCustomized)
returns (string memory)
{
return super._baseURI();
}
/**
* @dev Override `IERC165-supportsInterface`.
*/
function supportsInterface(bytes4 interfaceId)
public
view
virtual
override(ERC721, ERC721PresetMinterPauserAutoIdCustomized)
returns (bool)
{
return super.supportsInterface(interfaceId);
}
/**
* @dev Override `ERC721PresetMinterPauserAutoIdCustomized-_beforeTokenTransfer`.
*/
function _beforeTokenTransfer(address _from, address _to, uint256 _firstTokenId, uint256 _batchSize)
internal
virtual
override(ERC721Nonce, ERC721PresetMinterPauserAutoIdCustomized)
{
super._beforeTokenTransfer(_from, _to, _firstTokenId, _batchSize);
}
/**
* @dev Bulk create new tokens for `_recipients`. Tokens ID will be automatically
* assigned (and available on the emitted {IERC721-Transfer} event), and the token
* URI autogenerated based on the base URI passed at construction.
*
* See {ERC721-_mint}.
*
* Requirements:
*
* - the caller must have the `MINTER_ROLE`.
*/
function bulkMint(address[] calldata _recipients)
external
virtual
onlyRole(MINTER_ROLE)
returns (uint256[] memory _tokenIds)
{
require(_recipients.length > 0, "ERC721Common: invalid array lengths");
_tokenIds = new uint256[](_recipients.length);
for (uint256 _i = 0; _i < _recipients.length; _i++) {
_tokenIds[_i] = _mintFor(_recipients[_i]);
}
}
}
The following is an example ERC721 smart contract that extends ERC721Common
:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "./ERC721Common.sol";
/**
* @title TestNFT
* TestNFT - a contract for a test NFT.
*/
contract TestNFT is ERC721Common {
constructor() ERC721Common("TestNFT", "TestNFT", "https://link.to.base.metadata.uri/") {}
}
- Create your metadata following the Mavis Market convention so that the system can parse and display it. Two types of conventions are supported:
// Type 1
{
"name": "NFT name",
"description": "Description for your NFT",
"image": "Link to your NFT image",
"external_url": "Link to your NFT (optional)",
"attributes": [
{
"trait_type": "key1",
"value": 1
},
{
"trait_type": "key2",
"value": "value2"
},
...
]
}
// Type 2
{
"name": "NFT Name",
"description": "Description for your NFT",
"image": "Link to your NFT image",
"external_url": "Link to your NFT (optional)",
"properties": {
"key1": 1,
"key2": "value2",
...
}
}
Keep in mind that Mavis Market doesn't support nested attributes. The supported value types are number
and string
.
Here's an example of NFT metadata for each convention:
// Type 1
{
"name": "Kwang",
"description": "A sample NFT named Kwang",
"image": "https://link.to.NFT.image/1",
"external_url": "https://link.to.NFT.website/1",
"attributes": [
{
"trait_type": "generation",
"value": 2
},
{
"trait_type": "skill",
"value": "time control"
},
{
"trait_type": "trait",
"value": "fire"
}
]
}
// Type 2
{
"name": "DJ Tilo",
"description": "A sample NFT named Tilo",
"image": "https://link.to.NFT.image/2",
"external_url": "https://link.to.NFT.website/2",
"properties": {
"generation": 2,
"skill": "music make",
"trait": "metal"
}
}
- Mint your new NFT contract and set your metadata on it.
Have a look at an example collection deployed on the Saigon testnet: Sidekicks.
Provide information and list on the testnet
Provide information about your collection by filling out the form. This information is used on the Mavis Market's storefront and on your collection's page (pictured).

After you submit the form, Sky Mavis's team reviews the information and deploys your collection on the testnet.
When the collection is listed on the testnet, test the buy and sell flows to ensure that no issues exist with the implementation.
Launch on the mainnet
After the testnet deployment, your next step is to schedule the mainnet launch with Sky Mavis's team.
On the specified date, Sky Mavis deploys your NFT smart contract on the mainnet and transfers the contract ownership to you.