Proxy Mod

The Proxy Mod is a special system mod that allows Totem creators to add and remove mods after the Totem has been created.

Note

Despite the name, the Proxy Mod is not a proxy pattern for upgradeability. It’s called “Proxy” because it acts as an intermediary that manages and dispatches to other mods on behalf of Totem creators. The Totems contract itself is immutable.

Immutable vs Mutable Mods

Totems have two categories of mods:

Core Mods (Immutable)

Mods assigned directly to a Totem at creation time are immutable. They cannot be added or removed after the Totem is created.

struct TotemMods {
    address[] transfer;
    address[] mint;
    address[] burn;
    address[] created;
}

These are stored in totem.mods and represent a permanent commitment. Users can trust that these mods will always be active (at least at the Totems protocol level, mods can always choose to disable themselves).

Proxy Mods (Mutable)

Mods added through the Proxy Mod are mutable. The totem creator can add or remove them at any time.

%%{init: {'theme': 'dark', 'flowchart': { 'subGraphTitleMargin': { 'top': 10 } }, 'themeVariables': { 'primaryColor': '#1e293b', 'primaryTextColor': '#e5e7eb', 'primaryBorderColor': '#38bdf8', 'lineColor': '#38bdf8', 'clusterBkg': 'transparent', 'clusterBorder': '#38bdf8' }}}%%
flowchart LR
    Action["Transfer/Mint/Burn"] --> Totems["Totems Contract"]
    Totems --> CoreMods["Core Mods"]
    Totems --> ProxyMod["Proxy Mod"]
    ProxyMod --> DynamicMods["Dynamic Mods"]

    classDef default fill:#1e293b,stroke:#38bdf8,stroke-width:2px,color:#e5e7eb;
    classDef mutable fill:#1e293b,stroke:#fbbf24,stroke-width:2px,color:#e5e7eb;
    class ProxyMod,DynamicMods mutable;

Why This Matters

This separation sets clear expectations:

TypeCan be removed?User expectation
Core ModsNoPermanent rules that won’t change
Proxy ModsYesCreator can modify at any time

Adding Mods via Proxy

Only the totem creator can add mods through the Proxy Mod:

function addMod(
    string calldata ticker,
    ITotemTypes.Hook[] calldata hooks,
    address mod,
    address payable referrer
) external payable;

The creator must:

  1. Pay the mod’s license fee plus referrer fee (if mod not already licensed)
  2. Specify which hooks to enable for this mod
  3. Optionally provide a referrer address to receive the referrer fee

Removing Mods via Proxy

The creator can remove any proxy mod at any time:

function removeMod(
    string calldata ticker,
    address mod
) external;

This removes the mod from all hooks. No refund is given.

Fees

When adding a mod that isn’t already licensed for the totem:

FeeRecipientDescription
Mod feeMod sellerThe price set by the mod publisher
Referrer feeReferrer addressPlatform fee, burned if no referrer provided

If the mod is already licensed for this totem, no fee is required. Sending any value will revert with NoFeeRequired().

uint256 modFee = market.getModFee(mod);
uint256 referrerFee = totems.getFee(referrer);
uint256 totalFee = modFee + referrerFee;

Hook Limitations

The Created hook cannot be added via the Proxy Mod. Since the totem already exists, there’s no creation event to hook into.

if (hook == ITotemTypes.Hook.Created) {
    revert CantUseCreatedHook();
}

Only Transfer, Mint, and Burn hooks can be managed through the proxy.

Minting with Proxy Mods

When a totem uses Minter Mods through the proxy, you pass the ProxyMod as the mod parameter and specify which actual minter mod to use in the memo field:

// Pass proxyMod as the mod, and the actual minter mod address in memo
totems.mint(proxyMod, minter, ticker, amount, "0x1234...abcd");

The ProxyMod extracts the minter address from the memo and forwards the call. This prevents ambiguity when multiple minter mods are available.

Warning

Unlimited minting mods cannot be added via the Proxy Mod. This is to prevent abuse where a creator could add an unlimited mint mod after creation, violating user expectations.

Checking All Active Mods

To get a complete picture of a totem’s active mods, check both:

  1. Core mods: totem.mods.transfer, totem.mods.mint, etc.
  2. Proxy mods: Query the Proxy Mod contract for the totem’s ticker
// Core mods (immutable)
ITotemTypes.Totem memory totem = TotemsLibrary.getTotem(totemsContract, ticker);
address[] memory coreTransferMods = totem.mods.transfer;

// Proxy mods (mutable) - requires querying ProxyMod contract
// The UI displays both together with appropriate labels

For Mod Developers

Your mod works the same whether it’s added as a core mod or via the proxy. The only difference is:

  • Core mod: Added at totem creation, permanent
  • Proxy mod: Added later, can be removed

Your hook functions receive calls from either the Totems contract directly (core) or from the Proxy Mod contract. The built-in onlyTotems modifier from TotemMod handles both cases automatically.

<-
Publishing
Relay Adapters
->