Smart Contract Roles Overview
This document provides an overview of all roles used across the smart contract system.
Detailed role behavior is documented in the relevant component docs. Timelock execution for owner-gated role changes is documented in TimelockController.
Production assignment. This document lists what each role can do. It is not a recommendation to assign roles the way
BootstrapProtocol.s.soldoes by default — that flow grants broad operational role bundles to a single admin actor, with the most sensitive asset-moving roles held by the timelock. For the recommended production role matrix (separation of duties, governance-only roles, hardened signers/services, secured onboarding signing service, fast freeze/disable containment, and the mandatoryFREEZE_ROLE/SEIZURE_ROLEsplit), see the operational model. A role grant is usable immediately once it exists — production must place owners behind a multisig + non-zero-delayTimelockControllerbefore launch.
Token Contracts
BaseToken
BURNER_ROLE(_ROLE_0): Reserved in the shared bitmap;DEUSSTokenburn and burnBatch are currently restricted toBondRegistryrather than this roleTOKEN_FREEZER_ROLE(_ROLE_1): Can freeze/unfreeze tokensFORCE_TRANSFER_ROLE(_ROLE_2): Can force transfer tokens; caller must also be an enabled account inEntityRegistry. Bootstrap grants this role only toTimelockControllerand registers the timelock as an enabled protocol entity account.
pause() and unpause() are owner-only. Token-id pause controls are reserved for BondRegistry through pauseTokenId(...) / unpauseTokenId(...).
DEUSSToken
Inherits all roles from BaseToken. TOKEN_FREEZER_ROLE controls partial balance freezes, FORCE_TRANSFER_ROLE controls forced transfers, and the owner can mark protected custody addresses through protectAddress(address). Forced transfers cannot move tokens from or into protected custody. See DEUSSToken.
Freezes restrict normal holder movement, but they are not an absolute block against privileged paths. BondRegistry burns and FORCE_TRANSFER_ROLE transfers may automatically unfreeze the needed amount before burning or transferring.
Registry Contracts
BondRegistry
PUBLISHER(_ROLE_0): Can publish bonds and update published bonds; also can callissueBondCANCEL(_ROLE_1): Can cancel published bondsCLOSE(_ROLE_2): Can close issued bonds after redemptionCURRENCY(_ROLE_3): Can manage allowed bond currenciesSUSPEND(_ROLE_4): Can suspend issued bondsUNSUSPEND(_ROLE_5): Can unsuspend suspended bondsBURNER(_ROLE_6): Can execute third-partyFINAL_SETTLEMENTburns throughburnBond/burnBondBatch; caller must also be enabled inEntityRegistry. Bond issuers canFINAL_SETTLEMENTburn only their own balance without this roleSCORING(_ROLE_7): Can append issuer scoring recordsISSUER_RECOVERY(_ROLE_8): Critical governance-only role that can rotate a bond issuer forPublished,Issued,Suspended, orReplacedversions during exceptional recovery. This role should be granted only toTimelockController, not deployers or operational actors
issueBond authorization path is: caller has PUBLISHER role or caller is the bond issuer.
closeIssuance authorization path is: caller has PUBLISHER role or caller is the bond issuer.
Issuer identity is checked first in both functions. If the caller equals bond.issuer, the issuer-enabled check applies even when that address also has PUBLISHER.
rotateIssuer authorization path is: caller has ISSUER_RECOVERY; in production this means execution through timelock governance. The replacement issuer must be enabled in EntityRegistry.
Bootstrap intentionally leaves BURNER unset and grants ISSUER_RECOVERY to TimelockController.
See BondRegistry.
EntityRegistry
ADMIN_ROLE(_ROLE_0): Protocol-level admin override for entity governance (setEntityAuthority,setEntityManager) and immediate account registration (registerAccount); can also request pending account registrationGUARD(_ROLE_1): Can manage lifecycle status controls (setEntityStatus,setAccountStatus)ONBOARDING(_ROLE_2): Can run onboarding flows (registerEntity,registerEntityBatch) and onboarding metadata/flags updates (setEntityMetadata,setAccountRoleFlags)WALLET_TRANSFER(_ROLE_3): Can run wallet/account relinking administration (removeAccount,transferAccountToEntity), but cannot move or remove an account while it remains the linked entity's authority or has its manager flag enabled. Production should hold this behind a multisig and timelock where operational latency allowsENTITY_TYPE_MANAGER(_ROLE_4): Can define and freeze entity type metadata (defineEntityType,freezeEntityType)
Entity authorities and managers may be unregistered EOAs for bootstrap flows. Once such an address is registered as an account, the scoped assignment is valid only while the account is enabled and linked to the same entity; disabling, removing, or transferring the account invalidates stale scoped assignments until authority or manager access is explicitly reassigned.
See EntityRegistry.
Marketplace Contracts
Marketplace
ADMIN(_ROLE_0): Administrative functions (dependency wiring, configuration parameters, currency allowlist, entity-type allowlist)PAYMENT_HANDLER(_ROLE_1): Can handle paymentsARBITRATOR(_ROLE_2): Can arbitrate disputesSEIZURE_ROLE(_ROLE_3): Can seize escrowed assets from frozen offers (seizeOfferEscrow) and frozen deals (seizeDeal); bootstrap grants this role toTimelockController, not the operational admin actor.FREEZE_ROLE(_ROLE_4): Can freeze/unfreeze offers and deals (setOfferFrozen,setDealFrozen)INTEREST_DISCOVERY_OPERATOR(_ROLE_5): Operational maintenance role; can callcloseExpiredInterestswhen the offer owner is unavailable or disabled, and can cancel expired failedINTEREST_DISCOVERYoffers when the offer owner is unavailable and the owner account is enabled to receive escrow withdrawals. Distinct fromADMIN— does not grant configuration authority.
See Marketplace.
OrderbookMarketplace
ADMIN(_ROLE_0): Administrative functions (setAssetManager,setEntityRegistry,setEscrowManager,setMarketFilter,setPaymentExpiryThreshold,setMinExpiryThreshold,setDisputeBufferPeriod)PAYMENT_HANDLER(_ROLE_1): Can confirm off-chain payment outcomes for pending trades viamarkTradePaidARBITRATOR(_ROLE_2): Can resolve trade disputes (IN_DISPUTE -> PAID | UNPAID)SEIZURE_ROLE(_ROLE_3): Can seize escrowed assets from frozen orders (seizeOrder) and frozen trades (seizeTrade); bootstrap grants this role toTimelockController, not the operational admin actor.FREEZE_ROLE(_ROLE_4): Can freeze/unfreeze orders and trades (setOrderFrozen,setTradeFrozen)
See OrderbookMarketplace.
AssetManager
ADMIN(_ROLE_0): Can configure token-level asset support throughsetAssetand optional token-id allowlists throughsetAssetTokenId
See AssetManager.
BondMarketFilter
ADMIN(_ROLE_0): Can set per-token metadata adapters throughsetAdapter
See BondMarketFilter.
EscrowManager
ADMIN(_ROLE_0): Administrative functions (module management,setAssetManager, surplus asset sweep)
See EscrowManager.
Wallet Contracts
CompanyWallet
- Owner: Can execute arbitrary calls, set the
PolicyRegistry, manually advance the policy epoch, and transfer ownership. - Non-owner execution is delegated to
PolicyRegistry.canExecute(wallet, caller, target, value, data). - The wallet itself does not define role constants; per-wallet operation permissions live in
PolicyRegistry. - Owner bypass also applies when the owner is a smart account or another
CompanyWallet. Broker/custodian wallet fleets should follow thesecurity operational model, especially the controls for Root-Controlled Subwallet Mode.
See CompanyWallet.
PolicyRegistry
- Owner: Can manage global policy module allowlist and role labels.
- Wallet owner: Can grant/revoke wallet policy admins and configure wallet policy state.
- Wallet policy admin: Can manage the wallet-specific roles/modules permitted by the wallet owner.
See PolicyRegistry.
Role granting and revoking
Each contract that extends OwnableRolesExtension (wrapper of Solady OwnableRoles) enables the authorized owner to assign user roles using the following methods.
Contracts may also wrap these methods to enforce only specific roles. In case the owner assigns an invalid role, the call reverts with InvalidRoles()
grantRoles(address user, uint256 roles)
Grants roles to a user.
Prerequisites:
- Caller must be owner
- User address must not be zero
Parameters:
user: Address of the user to grant roles toroles: Bitmap of roles to grant
Events:
RolesUpdated(address, uint256): Emitted when roles are updated
Errors:
ZeroAddress(): When user address is zero
Sequence Diagram:
grantRoles(address[] calldata users, uint256 roles)
Grants roles to multiple users at once.
Prerequisites:
- Caller must be owner
- User addresses must not be zero
Parameters:
users: Array of user addresses to grant roles toroles: Bitmap of roles to grant
Errors:
ZeroAddress(): When user address is zero
Events:
RolesUpdated(address, uint256): Emitted for each user when roles are updated
Sequence Diagram:
revokeRoles(address user, uint256 roles)
Revokes roles from a user.
Prerequisites:
- Caller must be owner
- User address must not be zero
Parameters:
user: Address of the user to revoke roles fromroles: Bitmap of roles to revoke
Events:
RolesUpdated(address, uint256): Emitted when roles are updated
Sequence Diagram:
revokeRoles(address[] calldata users, uint256 roles)
Revokes roles from multiple users at once
Prerequisites:
- Caller must be owner
- User addresses must not be zero
- Roles must be valid
Parameters:
users: Addresses of the users to revoke roles fromroles: Bitmap of roles to revoke
Events:
RolesUpdated(address, uint256): Emitted for each user when roles are updated
Sequence Diagram:
renounceRoles(uint256 roles)
Allows the caller to renounce their own roles.
Prerequisites:
- Caller must have the roles they want to renounce
Parameters:
roles: Bitmap of roles to renounce
Events:
RolesUpdated(address, uint256): Emitted when roles are updated
Sequence Diagram: