Skip to content

Instantly share code, notes, and snippets.

@ryanio
Last active August 19, 2025 17:40
Show Gist options
  • Select an option

  • Save ryanio/72e48f176ab395a13c417da495510324 to your computer and use it in GitHub Desktop.

Select an option

Save ryanio/72e48f176ab395a13c417da495510324 to your computer and use it in GitHub Desktop.

How to Create an ERC-721 Public Mint Order

Overview

To mint ERC-721 tokens from a public drop, you need to create a transaction that calls the SeaDrop contract's mintPublic function.

Required Information

  • dropContractAddress: The ERC-721 drop contract
  • quantity: Number of tokens to mint (for ERC-721)
  • minterAddress: Wallet address performing the mint
  • Drop Stage Info: Current price, feeBps, startTime/endTime
  • Fee Recipient: Must be OpenSea's fee recipient: 0x0000a26b00c1F0DF003000390027140000fAa719
  • Creator Payout Address: Where remaining payment goes after fees

Pre-Mint Validation

Drop Stage Validation

// Check if drop stage is active
const currentTime = Math.floor(Date.now() / 1000);
if (currentTime < dropStage.startTime || currentTime > dropStage.endTime) {
  throw new Error("Drop stage is not currently active");
}

Supply Validation

// Check remaining supply
const remainingSupply = dropStage.maxSupply - dropStage.totalMinted;
if (quantity > remainingSupply) {
  throw new Error(`Insufficient supply remaining: ${remainingSupply} available`);
}

Wallet Limits Validation

// Check per-wallet total limit
const walletMinted = getCurrentWalletMintCount(minter, dropContract);
const walletLimit = dropStage.maxTotalMintableByWallet;
if (walletMinted + quantity > walletLimit) {
  const remaining = walletLimit - walletMinted;
  throw new Error(`Wallet limit exceeded: ${remaining} remaining`);
}

Balance Validation

const totalCost = quantity * dropStage.price;
const walletBalance = await getWalletBalance(minter);

// Add minimum gas buffer for free mints
const minBalance = totalCost === 0 ? parseEther("0.000006") : totalCost;

if (walletBalance < minBalance) {
  throw new Error("Insufficient balance for mint transaction");
}

Fee Recipient Validation

// Fee recipient passed to mintPublic MUST be OpenSea's designated address
const OPENSEA_FEE_RECIPIENT = "0x0000a26b00c1F0DF003000390027140000fAa719";

Transaction Structure

Direct Contract Call

Unlike ERC-1155 which uses Seaport orders, ERC-721 public mints call the SeaDrop contract directly:

// Contract call to SeaDrop
const transaction = {
  to: seaDropAddress, // SeaDrop contract address
  from: minterAddress,
  data: mintPublicCalldata,
  value: totalValue.toString()
};

Mint Public Calldata

The mintPublic function calldata includes:

// Function signature: mintPublic(address nftContract, address feeRecipient, address minterIfNotPayer, uint256 quantity)
const feeRecipientAddress = "0x0000a26b00c1F0DF003000390027140000fAa719"; // MUST be OpenSea's fee recipient

const calldata = encodeFunctionCall("mintPublic", [
  dropContractAddress,  // NFT contract to mint from
  feeRecipientAddress,  // OpenSea fee recipient (required)
  minterAddress,        // Minter address
  quantity              // Number to mint (typically 1 for ERC-721)
]);

// Append domain hash for attribution (SIP-6)
const finalCalldata = calldata + domainHash.slice(2); // Remove 0x prefix from domain hash

Domain Hash Attribution

OpenSea appends a domain hash for attribution following SIP-6:

const OS2_WEB_DOMAIN_HASH = "0x3d958fe2" // first 4 bytes of the keccak256 hash of the domain "v2.opensea.io"
const calldataWithAttribution = mintPublicCalldata + OS2_WEB_DOMAIN_HASH.slice(2);

Use a domain hash for your own domain by using the first 4 bytes of the keccak256 hash of your domain. Please share this with the OpenSea team.

Transaction Execution

  1. Validate all requirements above (including fee recipient validation)
  2. Set fee recipient to OpenSea's required address: 0x0000a26b00c1F0DF003000390027140000fAa719
  3. Calculate total payment value: price * quantity
  4. Encode the mintPublic function call with parameters
  5. Append domain hash for attribution
  6. Submit transaction to SeaDrop contract with ETH value

Payment Calculation

const stagePrice = dropStage.price; // Price per token in wei
const totalValue = stagePrice * quantity;

// Payment is automatically split by the contract:
// - Fee goes to OpenSea fee recipient
// - Remainder goes to creator payout address

Error Handling

Common validation errors to handle:

  • INSUFFICIENT_SUPPLY: Not enough tokens remaining
  • WALLET_LIMIT_EXCEEDED: Minter has reached their limit
  • INSUFFICIENT_BALANCE: Not enough ETH for payment
  • STAGE_NOT_ACTIVE: Drop stage is not currently running
  • INVALID_QUANTITY: ERC-721 must mint at least 1 token
  • INVALID_FEE_RECIPIENT: Fee recipient must be OpenSea's designated address

Example Drop URI File (OpenSea will create on your behalf)

{
"stages": [
  {
    "uuid": "01926f83-4b8e-7000-8000-000000000001",
    "name": "Public Sale",
    "description": "Open minting for everyone",
    "isPublic": true,
    "maxTotalMintableByWallet": 3,
    "maxTokenSupplyForStage": 10000,
    "startTime": 1704067200,
    "endTime": 1735689600,
    "restrictFeeRecipients": true,
    "feeBps": 750,
    "mintPrice": "100000000000000000"
  }
]
}

Key Fields Explained

Stage Fields

  • uuid: Unique identifier for the stage (ULID format)
  • isPublic: true for public stages (no allowlist required)
  • maxTotalMintableByWallet: Maximum tokens per wallet
  • maxTokenSupplyForStage: Total supply limit for this stage
  • mintPrice: Fixed price in wei (100000000000000000 = 0.1 ETH)
  • feeBps: Fee basis points (750 = 7.5%)
  • restrictFeeRecipients: true to only allow configured fee recipients

Pricing

  • Example: 0.1 ETH (100000000000000000 wei)

Timeline

Stage runs from Jan 1, 2024 to Dec 31, 2024 (timestamps in seconds).

The transaction directly calls the SeaDrop contract, which handles payment splitting and token minting automatically.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment