Skip to Content
Constellation as Code5. Roles & permissions

5. Roles & permissions

A role is the unit of delegation: a set of members (who can act) and a set of permissions (what they can call). Each role lives in its own folder under constellation/roles/.

constellation/roles/backend_operator/ ├── index.ts # combines members + permissions ├── members.ts # who can act as this role └── permissions.ts # what the role can call

index.ts just re-exports the two:

// roles/backend_operator/index.ts export { default as members } from './members' export { default as permissions } from './permissions'

Members

Members are anything that resolves to an address — raw addresses, user accessors, or node references:

// roles/backend_operator/members.ts export default [ '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045', eth.user['alice'], eth.safe['Trading Bot'], ] satisfies Members

Permissions

Permissions are expressed with the allow kit (auto-generated from the contracts in your zodiac.config.ts) or defi-kit for common DeFi presets. Each entry scopes the role to a specific contract, function, and — optionally — parameter constraints:

// roles/backend_operator/permissions.ts import { allow as allowAction } from 'defi-kit/eth' export default [ // exact functions on a known contract allow.eth.weth.deposit({ send: true }), allow.eth.weth.withdraw(), // a DeFi preset allowAction.aave_v3.deposit({ market: 'Core', targets: ['WETH'], }), ] satisfies Permissions

Anything you don’t allow reverts at execution time — enforced by the Roles module onchain, not by operational trust.

Spending allowances

For value-bounded permissions, define a reusable allowance under constellation/allowances/ and reference it with c.withinAllowance. An allowance is a refillable budget — here, a rolling daily cap:

// constellation/allowances/index.ts import { encodeKey } from 'zodiac-roles-sdk' const USDC_DECIMALS = 6n const DAILY = 10_000n * 10n ** USDC_DECIMALS export const usdc_payouts = { key: encodeKey('usdc_payouts'), refill: DAILY, maxRefill: DAILY, period: 60n * 60n * 24n, // one day, in seconds balance: DAILY, timestamp: 0n, }
// roles/backend_operator/permissions.ts import { usdc_payouts } from '../../allowances' export default [ allow.eth.usdc.transfer( undefined, // any recipient c.withinAllowance(usdc_payouts.key), ), ] satisfies Permissions

Then register the allowance on the Roles node back in constellation/index.ts:

export const backendRoles = eth.roles['Backend Operator']({ nonce: 0n, target: backendSafe, owner: backendSafe, avatar: backendSafe, roles: { backend_operator }, allowances: { usdc_payouts }, })

The role is fully described. Time to ship it → Review & deploy.

Last updated on