Hooks

Hooks are the core mechanism that allows your Mod to respond to Totem events. When a Totem action occurs (transfer, mint, burn, creation, or ownership transfer), the Totems contract calls the corresponding hook on all licensed mods that are set up to handle that event.

How Hooks Work

%%{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 Mod

    User->>Totems: transfer(ticker, to, amount, memo)
    Totems-->>Totems: Core logic
    Totems->>Mod: onTransfer(ticker, from, to, amount, memo)
    Mod-->>Mod: Custom logic
    Mod->>Totems: Success/Revert
    Totems->>User: Transaction complete

When a user performs an action on a Totem, the Totems contract:

  1. Executes the core operation (transfer, mint, etc.)
  2. Calls the corresponding hook on each licensed mod
  3. If any mod reverts, the entire transaction reverts

Important

State changes happen before hooks are called. When your hook executes, balances, supply, and other state have already been applied. For example, in onTransfer, the recipient’s balance already includes the transferred amount.

This gives your mod the power to enforce rules, track state, or trigger side effects for any Totem event.

Hook Interfaces

IModTransfer

Called after every Totem transfer.

interface IModTransfer {
    function onTransfer(
        string calldata ticker,
        address from,
        address to,
        uint256 amount,
        string calldata memo
    ) external;
}
ParameterDescription
tickerThe Totem’s ticker symbol
fromAddress sending the Totems
toAddress receiving the Totems
amountNumber of Totems transferred
memoOptional message attached to the transfer

IModMint

Called after Totems are minted.

interface IModMint {
    function onMint(
        string calldata ticker,
        address minter,
        uint256 amount,
        uint256 payment,
        string calldata memo
    ) external;
}
ParameterDescription
tickerThe Totem’s ticker symbol
minterAddress that minted the Totems
amountNumber of Totems minted
paymentAmount of native currency paid
memoOptional message attached to the mint

IModBurn

Called after Totems are burned.

interface IModBurn {
    function onBurn(
        string calldata ticker,
        address owner,
        uint256 amount,
        string calldata memo
    ) external;
}
ParameterDescription
tickerThe Totem’s ticker symbol
ownerAddress that burned the Totems
amountNumber of Totems burned
memoOptional message attached to the burn

IModCreated

Called once when a Totem is first created.

interface IModCreated {
    function onCreated(
        string calldata ticker,
        address creator
    ) external;
}
ParameterDescription
tickerThe Totem’s ticker symbol
creatorAddress that created the Totem

Warning

During Totem creation, all operations on that ticker are frozen. This means no transfers, mints, or burns can occur of that specific ticker until the creation process is fully complete. This ensures a consistent state during setup.

IModTransferOwnership

Called when a Totem’s creator transfers ownership to a new address. This is useful for mods that need to track or react to changes in Totem ownership, such as updating permissions, notifying external systems, or enforcing ownership transfer rules.

interface IModTransferOwnership {
    function onTransferOwnership(
        string calldata ticker,
        address previousOwner,
        address newOwner
    ) external;
}
ParameterDescription
tickerThe Totem’s ticker symbol
previousOwnerAddress of the previous creator/owner
newOwnerAddress of the new creator/owner

Note

Only the current creator can transfer ownership. This hook is called after the ownership has been updated, so totems.getTotem(ticker).creator will already return the new owner when your hook executes.

Reverting in Hooks

If your hook reverts, the entire transaction reverts. This is powerful but should be used carefully:

function onTransfer(
    string calldata ticker,
    address from,
    address to,
    uint256 amount,
    string calldata memo
) external onlyTotems onlyLicensed(ticker) {
    // Block transfers to specific addresses
    require(!blocklisted[to], "Recipient is blocked");
}

Caution

Be careful with revert conditions. If your mod reverts unexpectedly, users won’t be able to transfer, mint, or burn their Totems. Always test thoroughly and consider edge cases. Though it’s frowned upon that mods are proxy-upgradable, it’s better to have a fixable mod than bricked Totems.

Implementing Multiple Hooks

A single mod can implement any combination of hooks:

contract MultiHookMod is TotemMod, IModTransfer, IModMint, IModBurn, IModTransferOwnership {

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

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

    function onTransfer(
        string calldata ticker,
        address from,
        address to,
        uint256 amount,
        string calldata memo
    ) external onlyTotems onlyLicensed(ticker) {
        // Transfer logic
    }

    function onMint(
        string calldata ticker,
        address minter,
        uint256 amount,
        uint256 payment,
        string calldata memo
    ) external onlyTotems onlyLicensed(ticker) {
        // Mint logic
    }

    function onBurn(
        string calldata ticker,
        address owner,
        uint256 amount,
        string calldata memo
    ) external onlyTotems onlyLicensed(ticker) {
        // Burn logic
    }

    function onTransferOwnership(
        string calldata ticker,
        address previousOwner,
        address newOwner
    ) external onlyTotems onlyLicensed(ticker) {
        // Ownership transfer logic
    }
}
<-
Quickstart
Minter Mods
->