2. Custom sale contract — Introduction

Introduction to the factory contract

The downside of the simple item sale is that you'll need pre-mint the items that you want to sell (as opposed to minting them on the fly when they're purchased). This can make it difficult to sell many items to a broad user base. Additionally, with the simple item sale method, it's difficult to create custom logic for the sale of your items (like packs of random items, randomized stats for the items that are minted, etc.). If you want any of these features, the Factory contract is for you!

In order to mint items only when they're purchased, OpenSea provides a Factory interface that you can use to define how your items will be minted. You then use OpenSea to create custom "orders" that call your minting function. Here's a look at the main mint method of the Factory contract.

interface Factory {

  ...

  /**
    * @dev Mints asset(s) in accordance to a specific address with a particular "option".
    * Options should also be delineated 0 - (numOptions() - 1) for convenient indexing.
    * @param _optionId the option id
    * @param _toAddress address of the future owner of the asset(s)
    */
  function mint(uint256 _optionId, address _toAddress) external;
}

The mint method takes in an _optionId and a _toAddress. The _toAddress is the address to which the items will be minted. The _optionId is a unique identifier used by the mint method to distinguish what logic to call to mint the items. For example, _optionId 1 might mint a single item, while _optionId 2 might mint several random items. It's really up to you what you want your options to do! Since you can include any logic in the mint method, the possibilities are limitless!

OpenSea Creature Sale Example

To make this more concrete, let's look at an example. In the OpenSea creature factory, we have three different options:

  • Option 0: Mint a single creature
  • Option 1: Mint four creatures all at once
  • Option 2: Mint a lootbox of creatures

Here's what the code looks like for that logic:

function mint(uint256 _optionId, address _toAddress) public {
    Creature openSeaCreature = Creature(nftAddress);
    if (_optionId == SINGLE_CREATURE_OPTION) {
      openSeaCreature.mintTo(_toAddress);
    } else if (_optionId == MULTIPLE_CREATURE_OPTION) {
      for (uint256 i = 0; i < NUM_CREATURES_IN_MULTIPLE_CREATURE_OPTION; i++) {
        openSeaCreature.mintTo(_toAddress);
      }
    } else if (_optionId == LOOTBOX_OPTION) {
      CreatureLootBox openSeaCreatureLootBox = CreatureLootBox(lootBoxNftAddress);
      openSeaCreatureLootBox.mintTo(_toAddress);
    }
  }

This means that when you call mint(0, <destination_address>), the destination address will receive one new OpenSea creature, when you call mint(1, <destination_address>), the destination address will receive four new OpenSea creatures, etc.

If you'd like to deploy the OpenSea Creature example contract yourself, simply check out the OpenSea Creatures Github repository and uncomment the lines in migrations/deploy_contracts.js (you'll want to comment out the line above it as well, as your newly uncommented code will also deploy the Creature NFT).

As we'll learn in the next section, each option will have a dedicated page on OpenSea and (excitingly) can be sold in any of the many ways that regular items can be sold on OpenSea!