Skip to main content

Roles & Access Control

SkyBridge V2 uses OpenZeppelin AccessControl on both contracts. The role sets are distinct.


Diamond — 6 roles

The Diamond's role model is defined in AccessControlInternal.sol:

Rolebytes32 hashPurpose
DEFAULT_ADMIN_ROLEbytes32(0)Grant and revoke all other roles
MULTISIG_ROLEkeccak256("MULTISIG_ROLE")Finalize or reject Diamond upgrades; manage CCIP chain/sender allowlists; execute timelocked withdrawals
PROPOSER_ROLEkeccak256("PROPOSER_ROLE")Propose Diamond upgrades (facet cuts)
CCIP_OPERATOR_ROLEkeccak256("CCIP_OPERATOR_ROLE")Mint/burn bridge-deployed tokens; manage CCIP token admin
OPERATOR_ROLEkeccak256("OPERATOR_ROLE")Set neverWrapTokens flag on canonical tokens
PAUSER_ROLEkeccak256("PAUSER_ROLE")Pause/unpause ERC-20 bridging in BridgeFacet

Role topology (post-handoff)

RoleDiamondEntryPoint
DEFAULT_ADMIN_ROLE / MULTISIG_ROLE / CCIP_OPERATOR_ROLE / OPERATOR_ROLE0x1Bd922A1Dc99303CF8a683a5a5C9215045873Ddb0x1Bd922A1Dc99303CF8a683a5a5C9215045873Ddb
PAUSER_ROLE0x1cFd452EB369a7B9475B07D1457dd1d0500fD788 + 0x1Bd922A1Dc99303CF8a683a5a5C9215045873Ddb0x1cFd452EB369a7B9475B07D1457dd1d0500fD788 + 0x1Bd922A1Dc99303CF8a683a5a5C9215045873Ddb
PROPOSER_ROLE0x7966D48F5c0f14A33Bf325ceABdE87893F1677F7 (only role this address holds anywhere)

Diamond upgrade flow

Upgrades require two parties:

PROPOSER_ROLE  ──► diamondCut(...)         ──► proposal queued (7-day TTL)
MULTISIG_ROLE ──► finalizeUpgrade(id) ──► change applied
rejectUpgrade(id) ──► proposal cancelled

MULTISIG can also call rejectAllUpgrades() or rejectRange(start, end) to bulk-cancel proposals in an emergency.


EntryPoint — 3 roles

Defined on SkyBridgeEntryPoint itself (OpenZeppelin AccessControlUpgradeable):

Rolebytes32 hashPurpose
DEFAULT_ADMIN_ROLEbytes32(0)All admin functions: fee, diamond address, token messenger, fee collector, chain registry, upgrade authorization
PAUSER_ROLEkeccak256("aviator.pauser_role")Call setPaused(true/false)
OPERATOR_ROLEkeccak256("OPERATOR_ROLE")Register/remove OP Standard Bridge L1→L2 token mappings

Note: The EntryPoint PAUSER_ROLE uses a different hash ("aviator.pauser_role") from the Diamond's PAUSER_ROLE ("PAUSER_ROLE"). They are separate roles on separate contracts.


Fee custody — not a role

There is no FEE_COLLECTOR_ROLE. It was retired in V5.3.

Fee custody is a stored address, feeCollector, set by DEFAULT_ADMIN_ROLE via changeFeeCollector(address). The current fee collector is the Fee Safe 0xA953B9DF3b081709eA75895cF5a8fAf7DCC29354 (Gnosis Safe, 2-of-3).

Fees are collected permissionlessly — anyone can call:

  • collectFees() → sweeps all accumulated ETH to feeCollector
  • collectTokenFees(address token) → sweeps all accumulated ERC-20 (e.g., USDC from fast CCTP) to feeCollector

Neither caller nor msg.sender retains any fees; the full balance always goes to feeCollector.


Role constants comparison

DiamondEntryPoint
DEFAULT_ADMIN_ROLEbytes32(0)bytes32(0)
PAUSER_ROLEkeccak256("PAUSER_ROLE")keccak256("aviator.pauser_role")
OPERATOR_ROLEkeccak256("OPERATOR_ROLE")keccak256("OPERATOR_ROLE")
MULTISIG_ROLEkeccak256("MULTISIG_ROLE")
PROPOSER_ROLEkeccak256("PROPOSER_ROLE")
CCIP_OPERATOR_ROLEkeccak256("CCIP_OPERATOR_ROLE")

See also