Skip to main content

Ownership structure and access control

A chain owner of an Arbitrum chain is an entity that can carry out critical upgrades to the chain's core protocol; this includes upgrading protocol contracts, setting core system parameters, and adding and removing other chain owners.

An Arbitrum chain's initial chain owner is set by the chain's creator when the chain is deployed.

The chain-ownership architecture is designed to give Arbitrum chain creators flexibility in deciding how upgrades to their chain occur.

Architecture

Chain ownership affordance is handled via Upgrade Executor contracts.

Each Arbitrum chain is deployed with two Upgrade Executors — one on the Arbitrum chain itself, and one on its parent chain. At deployment, the chain's critical affordances are given to the Upgrade Executor contracts.

Some examples:

  • The parent chain's core protocol contracts are upgradeable proxies that are controlled by a proxy admin; the proxy admin is owned by the Upgrade Executor on the parent chain.
  • The core Rollup contract's admin role is given to the Upgrade Executor on the parent chain.
  • The affordance to call setters on the ArbOwner precompile—which allows for setting system gas parameters and scheduling ArbOS upgrades (among other things)—is given to the Upgrade Executor on the Arbitrum chain.

Calls to an Upgrade Executor can only be made by chain owners; e.g., entities granted the EXECUTOR_ROLE affordance on the Upgrade Executor. Upgrade Executors also have the ADMIN_ROLE affordance granted to themselves, which lets chain owners add or remove chain owners.

With this architecture, the Upgrade Executor represents a single source of truth for affordances over critical upgradability of the chain.

Precompile reference

Upgrades

Upgrades occur via a chain owner initiating a call to an Upgrade Executor, which in turn calls some chain-owned contract.

Chain owners can either call UpgradeExecutor.executeCall, which will in turn call the target contract directly, or UpgradeExecutor.execute, which will delegate-call to an "action contract" and use its code to call the target contract.

Per-function permissions

The tables below list the privileged functions on the SequencerInbox, Rollup/RollupAdminLogic, and Bridge contracts, who is allowed to call each one, and the Arbitrum Chain SDK (formerly the Orbit SDK) helper that performs the call (where one exists). This answers the recurring question of which calls the Rollup owner makes and which must be routed through the Upgrade Executor.

How to read the Required caller column. The source of truth is the access-control modifier on each function in the nitro-contracts source:

  • A function gated to the Rollup owner or admin — an onlyRollupOwner-style modifier, or any RollupAdminLogic admin function — is, in a standard deployment, triggered by the owner through the Upgrade Executor, because the Upgrade Executor contract is the owner/admin of these contracts. See Upgrades above for the conceptual explanation of that flow.
  • A function callable directly by a dedicated role (for example the batch poster manager) does not need to go through the Upgrade Executor; that role's address calls it directly.

The Chain SDK's sequencerInboxPrepareTransactionRequest and rollupAdminLogicPrepareTransactionRequest helpers accept an upgradeExecutor argument and wrap the call in UpgradeExecutor.executeCall automatically, so the owner path is handled for you.

SequencerInbox

Access is enforced by the onlyRollupOwner and onlyRollupOwnerOrBatchPosterManager modifiers.

FunctionRequired callerChain SDK helper
setValidKeyset(bytes)Rollup owner — via Upgrade ExecutorsetValidKeyset / buildSetValidKeyset
invalidateKeysetHash(bytes32)Rollup owner — via Upgrade ExecutorbuildInvalidateKeysetHash
setIsBatchPoster(address, bool)Rollup owner (via Upgrade Executor) or batch poster manager (directly)buildSetIsBatchPoster (also buildEnableBatchPoster / buildDisableBatchPoster)
setIsSequencer(address, bool)Rollup owner (via Upgrade Executor) or batch poster manager (directly)sequencerInboxPrepareTransactionRequest
setMaxTimeVariation(MaxTimeVariation)Rollup owner — via Upgrade ExecutorbuildSetMaxTimeVariation
setBatchPosterManager(address)Rollup owner — via Upgrade ExecutorsequencerInboxPrepareTransactionRequest
setFeeTokenPricer(IFeeTokenPricer)Rollup owner — via Upgrade ExecutorsequencerInboxPrepareTransactionRequest
setBufferConfig(BufferConfig)Rollup owner — via Upgrade ExecutorsequencerInboxPrepareTransactionRequest
updateRollupAddress()Rollup owner (inline check) — via Upgrade ExecutorsequencerInboxPrepareTransactionRequest
Info

setAllowListEnabled is not a SequencerInbox function, despite being commonly asked about in that context. It lives on the Inbox (the delayed inbox/AbsInbox)—see the Inbox row below.

Rollup/RollupAdminLogic

Every function on RollupAdminLogic is reachable only through the Rollup proxy's admin, which in a standard deployment is the Upgrade Executor. There is no per-function modifier—the proxy routes admin calls to this logic contract only when the caller is the admin—so all of these require the Upgrade Executor and none have a dedicated Chain SDK helper. They are all issued through the generic rollupAdminLogicPrepareTransactionRequest({ functionName, args, upgradeExecutor, rollup, account }) dispatcher.

FunctionRequired callerChain SDK helper
setValidator(address[], bool[])Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
setOwner(address)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
setConfirmPeriodBlocks(uint64)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
setMinimumAssertionPeriod(uint256)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
setValidatorAfkBlocks(uint64)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
setValidatorWhitelistDisabled(bool)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
increaseBaseStake(uint256)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
decreaseBaseStake(uint256, uint64)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
setWasmModuleRoot(bytes32)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
setLoserStakeEscrow(address)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
setInbox(IInboxBase)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
setSequencerInbox(address)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
setDelayedInbox(address, bool)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
setOutbox(IOutbox)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
removeOldOutbox(address)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
setAnyTrustFastConfirmer(address)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
setChallengeManager(address)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
pause()Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
resume()Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
forceRefundStaker(address[]) (only whenPaused)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
forceCreateAssertion(...) (only whenPaused)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
forceConfirmAssertion(...) (only whenPaused)Rollup admin — via Upgrade ExecutorrollupAdminLogicPrepareTransactionRequest
Note

The function set above reflects the BoLD-era RollupAdminLogic. On pre-BoLD chains some names differ (for example, setBaseStake in place of increaseBaseStake/decreaseBaseStake, and forceCreateNode/forceConfirmNode in place of the *Assertion variants). Always confirm against the nitro-contracts version your chain runs.

Bridge

Access is enforced by the onlyRollupOrOwner modifier—the caller must be the Rollup contract itself or the Rollup owner. The owner path runs through the Upgrade Executor. The Chain SDK has no dedicated Bridge helpers; these are configured during deployment or via the Rollup admin forwarders above.

FunctionRequired callerChain SDK helper
setSequencerInbox(address)Rollup contract, or Rollup owner — owner path via Upgrade Executor
setDelayedInbox(address, bool)Rollup contract, or Rollup owner — owner path via Upgrade Executor
setOutbox(address, bool)Rollup contract, or Rollup owner — owner path via Upgrade Executor
setSequencerReportedSubMessageCount(uint256)Rollup contract, or Rollup owner — owner path via Upgrade Executor
updateRollupAddress(IOwnable)Rollup contract, or Rollup owner — owner path via Upgrade Executor

Inbox (delayed inbox)

setAllowListEnabled and the allowlist setter live on the Inbox (AbsInbox), not on SequencerInbox. Access is enforced by onlyRollupOrOwner.

FunctionRequired callerChain SDK helper
setAllowListEnabled(bool)Rollup contract, or Rollup owner — owner path via Upgrade ExecutorbuildSetAllowListEnabled
setAllowList(address[], bool[])Rollup contract, or Rollup owner — owner path via Upgrade ExecutorbuildSetAllowList

Ownership flexibility

A chain owner is simply an address; it is set by the Arbitrum chain's deployer and can represent any sort of governance scheme (i.e., it could be an EOA (as is set via the Chain SDK Rollup creation example), a Multisig, a governance token system, etc.)

The Arbitrum DAO governed chains, while not Arbitrum chains themselves, use a similar architecture and upgrade pattern as Arbitrum chains, with both a governance token and a Multisig (aka, the "Security Council") as chain owners. For more info and best practices on action contracts, see "DAO Governance Action Contracts".

Note

The DAO-governed chains' Upgrade Executor contracts don't have the .executeCall method; only the .execute method.