Skip to content
Runner · Reference

Permissions

The strategy_permissions bitfield — which vault operations a strategy may call.

Derived from packages/sdk/src/permissions.ts

Permissions are a Layer 1 vault protection: a fixed grant the vault enforces on-chain, deciding which operations a strategy may ever call. This page is the working reference for the bits and for using them from code. For what they are and why they sit in the contract, start with the concept.

The grant is strategy_permissions, a u64 bitfield stored on the vault. Each bit authorizes one operation, and the vault checks the matching bit before it lets a call through. A strategy holds no standing authority of its own: what it may do is exactly what its vault's bitfield says.

The strategy proposes; the bitfield decides which proposals are even eligible.

The eight permissions

Each permission is one bit, and each gates exactly one action. The values mirror the PERMISSION_* constants in the vault contract.

PermissionBitGates the actionCheck at runtime
MINT_BINARY1 << 0 (1)vault.mintBinaryctx.vault.canMintBinary
REDEEM_BINARY1 << 1 (2)vault.redeemBinaryctx.vault.canRedeemBinary
MINT_RANGE1 << 2 (4)vault.mintRangectx.vault.canMintRange
REDEEM_RANGE1 << 3 (8)vault.redeemRangectx.vault.canRedeemRange
SUPPLY_PLP1 << 4 (16)vault.supplyPlpctx.vault.canSupplyPlp
WITHDRAW_PLP1 << 5 (32)vault.withdrawPlpctx.vault.canWithdrawPlp
PM_DEPOSIT1 << 6 (64)vault.pmDepositctx.vault.canPmDeposit
PM_WITHDRAW1 << 7 (128)vault.pmWithdrawctx.vault.canPmWithdraw

PERMISSIONS_WHITELIST (255) is all eight bits set. Only bits 0–7 are valid; a vault rejects any grant with a bit outside that range. Actions with no permission bit (noop, refreshNavSnapshot, crystallizeFees) are always callable.

Checking from a plugin

ctx.vault decodes the bitfield for you. Check the relevant can* getter before returning an action, so you noop cleanly instead of burning a tick on a call the vault will refuse:

async decide(ctx) {
  if (!ctx.vault.canMintBinary) {
    return [{ kind: "noop", reason: "vault doesn't grant MINT_BINARY" }];
  }
  // ...
}
  • **ctx.vault.can*** — a boolean per permission (the table above).
  • ctx.vault.grantedPermissionsPermissionName[], every permission currently granted. For logging and error messages.
  • ctx.vault.strategyPermissions — the raw bigint bitfield, if you want to test bits yourself.

What happens without the bit

A missing permission costs at most one wasted tick, never gas:

1. The runtime's executor runs a local preflight before building the tx. An action whose bit isn't granted is rejected there — nothing is signed, nothing reaches the chain. 2. The vault enforces the same rule on-chain as a backstop. Anything that bypassed the runtime and reached the chain aborts with E_INSUFFICIENT_PERMISSIONS (code 23). With the runtime, the local check means you never get there.

Encoding and changes

Permissions are composed with the bitwise OR of the bits you want:

import { PERMISSIONS, combinePermissions } from "@automark/sdk/permissions";

const grant = PERMISSIONS.MINT_BINARY | PERMISSIONS.REDEEM_BINARY | PERMISSIONS.PM_DEPOSIT;
// or: combinePermissions(PERMISSIONS.MINT_BINARY, PERMISSIONS.REDEEM_BINARY, PERMISSIONS.PM_DEPOSIT)

A vault's grant is set once at creation (initial_permissions, chosen in Studio). Changing it later is a governance action, not a config edit: it goes through a timelocked permissions upgrade, so depositors see the change coming. This is why the plug is rarely swapped and why permissions sit in Layer 1 of the protection model — the vault, not the strategy, owns what the strategy is allowed to do.