How to create your own NFT using the OpenSea Creatures template

OpenSea provides example NFT contracts for ERC721 and ERC1155 tokens, which implements many of the best practices and is a great starting point for your project. Under the hood it uses the OpenZeppelin building blocks, which have been extensively tested in production. That said, you should always test your contract on Rinkeby or another testnet before deploying to mainnet.

Before you get started make sure you've got node and yarn installed and available:

$ which node
/Users/snakespear/.nvm/versions/node/v14.17.6/bin/node
$ which yarn
/usr/local/bin/yarn

The first step is to clone the OpenSea creatures repo:

$ git clone [email protected]:ProjectOpenSea/opensea-creatures.git
$ cd opensea-creatures

Next, we'll install the dependancies:

$ yarn

This will take a minute. Go make a coffee.

If you get an error during this process, copy the exact error message into Google and you'll find plenty of resources (or join our discord and ask for help). If you're on a Mac, it's worth making sure all the build tools are installed and running a fresh install:

$ xcode-select --install
$ sudo xcode-select --switch /Library/Developer/CommandLineTools
$ sudo npm explore npm -g -- npm install node-gyp@latest
$ rm -r node_modules
$ yarn

The next step is to make some changes to your contract. This will set basic metadata like the symbol and token name.

contracts/Creature.sol

Here is how the file should look before we get started. It may change after this post is written, so don't take the following line numbers as gospel.

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./ERC721Tradable.sol";

/**
 * @title Creature
 * Creature - a contract for my non-fungible creatures.
 */
contract Creature is ERC721Tradable {
    constructor(address _proxyRegistryAddress)
        ERC721Tradable("Creature", "OSC", _proxyRegistryAddress)
    {}

    function baseTokenURI() override public pure returns (string memory) {
        return "https://creatures-api.opensea.io/api/creature/";
    }

    function contractURI() public pure returns (string memory) {
        return "https://creatures-api.opensea.io/contract/opensea-creatures";
    }
}

Line 13: ERC721Tradable("Creature", "OSC", _proxyRegistryAddress)

  • Change "Creature" to the name of your token
  • Change "OSC" to your desired symbol - make sure you check Etherscan to ensure it's not in use already

Line 17: return"https://creatures-api.opensea.io/api/creature/";

This is the base URL for your token metadata. Each token will be given an auto incrementing ID, starting from 1, which will be appended to this URL (e.g .../creature/1, .../creature/2).

The OpenSea repo contains an example python metadata server in /metadata-api which can easily be deployed to Google App Engine, but doing so is out of the scope of this post. I'll be sharing another post soon with a tutorial just for this, so make sure you subscribe to the newsletter ;)

Update this URL before deploying as you won't be able to change it later.

Line 21: return"https://creatures-api.opensea.io/contract/opensea-creatures";

This is the URL for your contract-level metadata, which serves the collection name, description and any other metadata.

If you'd like to implement your own API make sure to review the metadata specification for tokens and contracts.

To Factory or not to Factory, that is the question

This repo also includes a factory which is just another smart contract - the only purpose of which is to mint tokens. Deploying using a factory contract generally has higher upfront fees but costs less per-token to mint.

It also allows you to lazy mint, which means gas is due when each token is minted rather than upfront. The downside of lazy minting is users can't pick their token, they just get given the next available one. Using a factory contract doesn't mean you have to lazy mint - you could mint 50% and lazy mint the remainder, for example.  

Personally I'd recommend using one, so that's what we're going to do in this tutorial, but I'll include a note on the alternative commands as we go.

contracts/CreatureFactory.sol

You'll need to update this file to set the total supply available.

contract CreatureFactory is FactoryERC721, Ownable {
    ...

    /*
     * Enforce the existence of only 100 OpenSea creatures.
     */
    uint256 CREATURE_SUPPLY = 100;
    
    ...
}

Line 28: uint256 CREATURE_SUPPLY = 100;

This maximum is the total of any minted immediately along with any lazy minted. You won't be able to change this later.

Deploying

Personally, I like to use dotenv to manage environment variables. We're going to make some small changes to enable this.

$ npm i dotenv --save
$ echo ".env" >> .gitignore

Add this line to the top of every file in /migrations:

require('dotenv').config()

Then create a file called .env in the root folder:

ALCHEMY_KEY="YOUR KEY"
MNEMONIC="YOUR MNEMONIC"

Update this with an API key for Alchemy, and the mnemonic for your Ethereum wallet. Do not commit this file to version control. Our command earlier added this file to .gitignore, but it deserves restating. Do not commit this file to version control, your wallet could be emptied by anyone with access.

Now run:

$ DEPLOY_CREATURES_SALE=1 yarn truffle deploy --network rinkeby

This will deploy your creature contract and your factory contract. If you don't want to use a factory, run this instead:

$ DEPLOY_CREATURES=1 yarn truffle deploy --network rinkeby

Once your contract has been deployed you'll see it's address printed in your console. If you got an error about gas you don't have enough Ethereum in your wallet. Buy some on an exchange or get some from the Rinkeby faucet.

Update your .env file as below:

OWNER_ADDRESS="<my_address>"
NFT_CONTRACT_ADDRESS="<deployed_contract_address>"
FACTORY_CONTRACT_ADDRESS="<deployed_factory_contract_address>"
NETWORK="rinkeby"

If you didn't use a factory you can leave that item it blank.

Minting immediately

Run the below command to start minting your tokens immediately.

$ node scripts/mint.js

You'll see messages printed to your console as their minted, and you can view them on OpenSea by constructing the URL with your contract address (opensea.io/asset/<address>).

Lazy minting

If you deployed the factory contract, you can lazy mint by constructing the same URL but for your factory address.

By default, there are three options:

  • Mint one
  • Mint 4
  • Lootbox which can be opened to unlock 3

By purchasing an option, the corresponding tokens will be immediately minted and sent to the purchaser. You can tweak all these options in the lootbox contract.

In order to make them available for sale, you need to create sell orders on OpenSea - for each individual token. You could do this manually, but it would take a long time for thousands of tokens.

There is a script provided for automating, but you'll need to tweak it to your usecase. Make the required changes in scripts/initial_sale.js, then run:

$ node scripts/initial_sale.js

Save all this effort

Sounds like a bit of a process, right? That's where we come in. Diamond Hands Hotel helps creators, influencers and celebrities launch their own line of NFTs. If you've got a social media following or existing audience, contact us and we'll do it for you!