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:
| Type | Can be removed? | User expectation |
|---|---|---|
| Core Mods | No | Permanent rules that won’t change |
| Proxy Mods | Yes | Creator 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:
- Pay the mod’s license fee plus referrer fee (if mod not already licensed)
- Specify which hooks to enable for this mod
- 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:
| Fee | Recipient | Description |
|---|---|---|
| Mod fee | Mod seller | The price set by the mod publisher |
| Referrer fee | Referrer address | Platform 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:
- Core mods:
totem.mods.transfer,totem.mods.mint, etc. - 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.