Minter Mods

Minter Mods are a special type of mod that can distribute Totems to users. Unlike regular hooks that observe events, Minter Mods have a callable mint function that controls how Totems are distributed.

How Minting Works

When a user wants to mint Totems:

  1. A user calls totems.mint() with optional payment
  2. The Totems contract calls your mod’s mint() function (must be payable!)
  3. Your mod transfers Totems from its balance to the user (or mints new supply if unlimited)
  4. The onMint hook is called on all licensed mods
%%{init: {'theme': 'dark', 'mirrorActors': false, 'themeVariables': { 'primaryColor': '#1e293b', 'primaryTextColor': '#e5e7eb', 'primaryBorderColor': '#38bdf8', 'lineColor': '#38bdf8', 'actorTextColor': '#e5e7eb', 'actorBkg': '#1e293b', 'actorBorder': '#38bdf8', 'signalColor': '#38bdf8', 'signalTextColor': '#e5e7eb', 'activationBkgColor': '#1e293b', 'activationBorderColor': '#38bdf8', 'sequenceNumberColor': '#e5e7eb', 'noteBkgColor': '#000000', 'noteTextColor': '#9ca3af', 'noteBorderColor': '#4b5563' }}}%%
sequenceDiagram
    participant User
    participant Totems
    participant MinterMod
    participant OtherMods

    User->>Totems: mint(mod, minter, ticker, amount, memo) + payment
    Totems->>MinterMod: mint(...details)
    MinterMod->>Totems: transfer(...details)
    Totems->>OtherMods: onMint(...details)
    Totems-->>User: Success

The IModMinter Interface

interface IModMinter {
    function mint(
        string calldata ticker,
        address minter,
        uint256 amount,
        string calldata memo
    ) external payable;
}
ParameterDescription
tickerThe Totem’s ticker symbol
minterAddress requesting the mint
amountNumber of Totems requested (can be 0!)
memoOptional message (used for proxy mod routing)
msg.valueNative currency payment attached

Types of Minter Mods

Allocated Minters

When a Totem is created, the creator allocates a specific amount to each minter mod. Your mod receives these Totems and distributes them according to your logic.

contract AllocatedMinter is TotemMod, IModMinter, IModCreated, IModTransfer {
    mapping(bytes32 => uint256) public balances;

    constructor(address _totemsContract, address payable _seller)
        TotemMod(_totemsContract, _seller) {}

    function isSetupFor(string calldata ticker) external view override returns (bool) {
        return true;
    }

    // Track initial allocation (will not be a transfer notification!)
    function onCreated(
        string calldata ticker,
        address creator
    ) external onlyTotems onlyLicensed(ticker) {
        bytes32 tickerBytes = TotemsLibrary.tickerToBytes(ticker);
        balances[tickerBytes] = TotemsLibrary.getBalance(totemsContract, ticker, address(this));
    }

    // Track transfers in
    function onTransfer(
        string calldata ticker,
        address from,
        address to,
        uint256 amount,
        string calldata memo
    ) external onlyTotems onlyLicensed(ticker) {
        bytes32 tickerBytes = TotemsLibrary.tickerToBytes(ticker);

        if (to == address(this)) {
            balances[tickerBytes] += amount;
        }
    }

    function mint(
        string calldata ticker,
        address minter,
        uint256 amount,
        string calldata memo
    ) external payable onlyTotems onlyLicensed(ticker) {
        bytes32 tickerBytes = TotemsLibrary.tickerToBytes(ticker);
        require(balances[tickerBytes] >= amount, "Allocation exhausted");

        TotemsLibrary.transfer(totemsContract, ticker, minter, amount, "");
        balances[tickerBytes] -= amount;
    }
}

Unlimited Minters

Unlimited minters can mint whatever they want. They’re marked with needsUnlimited: true when published, and when set as an allocation should always have 0 amount allocated to them.

Warning

Unlimited minters will never be able to receive Totems via transfers.

contract UnlimitedMinter is TotemMod, IModMinter {

    constructor(address _totemsContract, address payable _seller)
        TotemMod(_totemsContract, _seller) {}

    function isSetupFor(string calldata ticker) external view override returns (bool) {
        return true;
    }

    function mint(
        string calldata ticker,
        address minter,
        uint256 amount,
        string calldata memo
    ) external payable onlyTotems onlyLicensed(ticker) {
        // Unlimited minters transfer from themselves
        // The Totems contract mints new supply as needed
        TotemsLibrary.transfer(totemsContract, ticker, minter, amount, "");
    }
}

Caution

Unlimited minters display prominent warnings in the UI. Users are alerted that supply can increase indefinitely, or become malicious. They also cannot be added via the Proxy Mod. This prevents creators from inflating supply unexpectedly.

How Minted Amount is Determined

The Totems contract automatically calculates how many totems were minted based on the minter type:

  • Allocated minters: The minted amount is the minter’s balance decrease (how many totems left the mod)
  • Unlimited minters: The minted amount is the supply increase (new totems created)

When an event is emitted at the end of the mint process, the actual minted amount is reported.

Handling Payments

The msg.value contains any native currency sent with the mint request:

function mint(
    string calldata ticker,
    address minter,
    uint256 amount,
    string calldata memo
) external payable onlyTotems onlyLicensed(ticker) {
    // Example: Require 0.01 ETH per totem
    uint256 price = amount * 0.01 ether;
    require(msg.value >= price, "Insufficient payment");

    TotemsLibrary.transfer(totemsContract, ticker, minter, amount, "");
}

Note

Both amount and msg.value can be zero. This enables use cases like free giveaways, mining mechanics, or signature-gated mints.

Minting Less Than Requested

Since the minted amount is calculated from balance/supply changes, you can mint fewer totems than requested:

function mint(
    string calldata ticker,
    address minter,
    uint256 amount,
    string calldata memo
) external payable onlyTotems onlyLicensed(ticker) {
    uint256 actualAmount = amount / 2;
    TotemsLibrary.transfer(totemsContract, ticker, minter, actualAmount, "");
}

The Totems contract will detect that fewer totems were transferred and report the actual minted amount.

Custom Minting Interfaces

You can provide a custom minting experience by setting website and websiteTickerPath in your mod’s metadata when publishing. When users click “Mint” on a totem using your mod, they can be redirected to your custom interface instead of the generic Totems UI.

Calling Mint from Your Interface

Always call the Totems contract, not your mod directly:

// Correct - goes through Totems contract
await totemsContract.mint(modAddress, minterAddress, ticker, amount, memo, { value: payment });

// Wrong - will fail the onlyTotems check
await yourModContract.mint(ticker, minterAddress, amount, memo);
<-
Hooks
Library
->