Permissions Manager

No roles
NMT uses a flat permission model, not roles. Each permission is a (delegate, module, action) tuple. There are no "DEPOSITOR" or "MANAGER" roles.

PermissionsManager.sol is the central access control contract for NMT. Every action that an operator wants to perform on a user's Safe must pass through this contract first.

What It Does

The Permissions Manager answers one question: "Is this address allowed to call this module on this Safe?"

It maintains a whitelist of approved modules and maps each delegate (operator) to the specific modules they're allowed to use.

Permissions detail view The permissions UI showing the delegate vs owner permission matrix. Delegates can create swaps and enter/exit DeFi positions but cannot withdraw funds.

Permissions list Managing permissions across multiple Safes on different chains (Arbitrum, Base).

Key Concepts

Permission Model

There are no high-level "roles." The model is flat — each permission is a tuple of (delegate, module, action). The four action strings are defined in Permissions.sol:

Permission String What It Allows
APPROVE "APPROVE" Token spending approvals (required before deposits)
DEPOSIT "DEPOSIT" Supply, mint, add liquidity
WITHDRAW "WITHDRAW" Withdraw, burn, remove liquidity
SWAP "SWAP" Token swaps
* (wildcard) "*" All actions on a module

How Granting Works

The Safe owner calls grantPermissions(delegate, entries[]) where each entry specifies a module address and a permission string. For example, to let a delegate deposit into Aave:

grantPermissions(delegateAddr, [
    { moduleAddr: aaveModule, permission: "APPROVE" },
    { moduleAddr: aaveModule, permission: "DEPOSIT" }
]);

The delegate is automatically registered on the first grantPermissions call — no separate "add delegate" step. If all permissions are later revoked, the delegate is automatically removed.

Module Validation

When granting permissions on a new module for the first time, the contract verifies the module address implements IBaseModule (via ERC-165). This prevents granting permissions on arbitrary contracts.

How Permission Checks Work

1. Delegate calls DelegateBundler.execute(...)
        |
2. Module calls _checkPermission(safe, delegate, "DEPOSIT")
        |
3. PermissionsManager.hasPermission() checks:
   - Does this (safe, delegate, module, "DEPOSIT") entry exist?
   - OR does a wildcard (safe, delegate, module, "*") entry exist?
        |
4. If check passes → module executes the action
   If check fails  → entire transaction reverts

Important Security Properties

  • Owner-only configuration — only the Safe owner (msg.sender) can grant or revoke permissions
  • Per-Safe isolation — permissions are scoped to individual Safes. Being a delegate on Safe A gives you zero access to Safe B
  • No escalation — a delegate cannot grant themselves additional permissions
  • Auto-cleanup — revoking all permissions for a delegate automatically deregisters them
  • Revocable — the Safe owner can revoke any delegate's access at any time

Contract Location

smart-wallets/contracts/PermissionsManager.sol

results matching ""

    No results matching ""