Skip to content

On-chain Contracts

graph LR
  Owner[FoundationOwner]
  Deposit[DepositContractCTN]
  Factory[CenturionVaultFactory]
  Vault[CenturionWithdrawalVault]
  Controller[CenturionEconomicController]
  Gatekeeper[CenturionClaimGatekeeper]
  ExitReq[ExitRequestContract]
  Owner --> Deposit
  Owner --> Factory
  Factory --> Vault
  Factory --> Controller
  Deposit --> Factory
  Deposit --> Controller
  Controller --> Gatekeeper
  Controller --> Vault
  Vault --> ExitReq

DepositContractCTN

Purpose

DepositContractCTN is the execution-layer deposit entrypoint with allowlist enforcement and phase-1 custody gates. The constructor enables pubkey allowlist and phase-1 custody checks in contracts/src/DepositContractCTN.sol:336, and the backend operation executor uses it for the final deposit transaction after vault deployment and allowlist setup in seat-manager/src/contracts/operationExecutor.ts:982.

Key Invariants

  • Phase 1 custody checks are enabled by default and can be locked once the baseline factory is frozen or deposits exist in contracts/src/DepositContractCTN.sol:337 and contracts/src/DepositContractCTN.sol:718.
  • A deposit must use a 48-byte pubkey, 32-byte withdrawal credentials, 96-byte signature, and a value that is an exact gwei multiple in contracts/src/DepositContractCTN.sol:367.
  • Phase 1 deposits enforce the frozen baseline factory, exact principal amount, execution-address withdrawal credentials, registered vault, vault runtime code hash, pubkey binding, and readiness checks in contracts/src/DepositContractCTN.sol:377 and contracts/src/DepositContractCTN.sol:801.
  • The allowlist intent hash is keccak256(abi.encode(pubkey, withdrawal_credentials, amountGwei, authorizedDepositor, ownershipEpoch)); it is computed in contracts/src/DepositContractCTN.sol:746 and consumed during deposit in contracts/src/DepositContractCTN.sol:389.
  • A validator pubkey hash can be activated only once under phase-1 custody checks in contracts/src/DepositContractCTN.sol:382.

Public Functions an Integrator May Call

function signature access control reverts events emitted line
deposit deposit(bytes,bytes,bytes,bytes32) public, optionally owner-only depositor invalid lengths, amount, root, allowlist, custody baseline DepositEvent, intent consumed events contracts/src/DepositContractCTN.sol:357
addAllowedDepositFor addAllowedDepositFor(bytes,bytes,uint64,address) owner invalid pubkey, wc, amount, depositor, consumed intent DepositIntentAllowedFor contracts/src/DepositContractCTN.sol:487
isAllowedDepositIntentFor isAllowedDepositIntentFor(bytes,bytes,uint64,address) view invalid input length none contracts/src/DepositContractCTN.sol:533
freezeBaselineVaultFactory freezeBaselineVaultFactory() owner missing factory, unfrozen custody, codehash mismatch, metadata mismatch baseline pinned/frozen events contracts/src/DepositContractCTN.sol:630
get_deposit_root get_deposit_root() view none none contracts/src/DepositContractCTN.sol:424

Events to Subscribe To

event topics emitted when consumer in seat-manager line
DepositEvent none indexed a deposit is accepted into the deposit tree EL deposit watcher parses it contracts/src/DepositContractCTN.sol:202
DepositIntentAllowedFor intentHash, authorizedDepositor owner allows one depositor-bound intent no direct watcher; operation executor expects success contracts/src/DepositContractCTN.sol:216
DepositIntentConsumedFor intentHash, authorizedDepositor deposit consumes an allowlist intent no direct watcher; transaction receipt is operation evidence contracts/src/DepositContractCTN.sol:226
BaselineVaultFactoryFrozen baselineVaultFactory custody baseline is frozen no direct watcher; deposit path depends on it contracts/src/DepositContractCTN.sol:234

Upgrade / Freeze Posture

The contract is not a proxy. Mutable controls are owner-only flags and baseline configuration; the important freeze is the irreversible baseline factory freeze enforced in contracts/src/DepositContractCTN.sol:630, plus the permanent allowlist-disable schedule enforced in contracts/src/DepositContractCTN.sol:557. Phase-1 custody checks cannot be toggled after baseline freeze or after deposits start in contracts/src/DepositContractCTN.sol:718.

CenturionVaultFactory

Purpose

CenturionVaultFactory deploys one deterministic withdrawal vault per validator pubkey and initializes that vault in the economic controller. It stores the controller, exit request contract, config hashes, runtime code hash, and pubkey-to-vault registry in contracts/src/CenturionVaultFactory.sol:24.

Key Invariants

  • Deployment is owner-only through the inherited owner check at contracts/src/CenturionVaultFactory.sol:103.
  • The CREATE2 salt is derived from sha256(validatorPubkey), and the preview path uses the same pubkey hash in contracts/src/CenturionVaultFactory.sol:132 and contracts/src/CenturionVaultFactory.sol:168.
  • One vault per pubkey is enforced by vaultByValidatorPubkeyHash; a second deployment reverts VaultAlreadyExists in contracts/src/CenturionVaultFactory.sol:133.
  • Each deployment initializes the controller seat config after writing the vault registry in contracts/src/CenturionVaultFactory.sol:148 and contracts/src/CenturionVaultFactory.sol:150.

Public Functions an Integrator May Call

function signature access control reverts events emitted line
deployVault deployVault(bytes,address,address,uint64,uint128,uint128,uint8,uint64) owner invalid pubkey, duplicate vault, invalid credential type, deployment failure VaultDeployed contracts/src/CenturionVaultFactory.sol:103
previewVaultAddress previewVaultAddress(bytes) view invalid pubkey length none contracts/src/CenturionVaultFactory.sol:168
previewExecutionWithdrawalCredentials previewExecutionWithdrawalCredentials(bytes,bytes32) view invalid pubkey length none contracts/src/CenturionVaultFactory.sol:173
isVault isVault(address) view none none contracts/src/CenturionVaultFactory.sol:9
vaultByValidatorPubkeyHash public mapping getter view none none contracts/src/CenturionVaultFactory.sol:36

Events to Subscribe To

event topics emitted when consumer in seat-manager line
VaultDeployed vault, validatorPubkeyHash a vault is created and registered operation executor receipt/intents, no log watcher contracts/src/CenturionVaultFactory.sol:47
PolicyBootstrapOpenSet none policy bootstrap flag changes no direct watcher contracts/src/CenturionVaultFactory.sol:54

Upgrade / Freeze Posture

The factory is not upgradeable. Its controller and exit request contract are immutable constructor arguments in contracts/src/CenturionVaultFactory.sol:63, and the deployed vault runtime code hash is fixed from the factory runtime in contracts/src/CenturionVaultFactory.sol:81. The deposit contract separately freezes the factory metadata before accepting phase-1 deposits in contracts/src/DepositContractCTN.sol:630.

CenturionWithdrawalVault

Purpose

CenturionWithdrawalVault is the per-validator execution withdrawal address. It receives ETH, exposes the execution withdrawal credentials, lets the controller transfer ETH, and requests validator exit through the EIP-7002 exit request contract. Its controller, factory, exit request contract, pubkey hash, and vault config hash are set in the constructor at contracts/src/CenturionWithdrawalVault.sol:55.

Key Invariants

  • Only the controller can transfer ETH, set exit fallback, or request exit; the modifier enforces that in contracts/src/CenturionWithdrawalVault.sol:41.
  • The vault is bound to one validator pubkey hash and verifies isBoundToPubkey in contracts/src/CenturionWithdrawalVault.sol:81.
  • Exit request submission is one-way; a second exit request reverts ExitAlreadySubmitted in contracts/src/CenturionWithdrawalVault.sol:122.
  • Transfer and exit paths are non-reentrant through the local guard in contracts/src/CenturionWithdrawalVault.sol:46.

Public Functions an Integrator May Call

function signature access control reverts events emitted line
executionWithdrawalCredentials executionWithdrawalCredentials() view none none contracts/src/CenturionWithdrawalVault.sol:77
isBoundToPubkey isBoundToPubkey(bytes) view none none contracts/src/CenturionWithdrawalVault.sol:81
currentExitRequestFeeWei currentExitRequestFeeWei() view exit fee unavailable none contracts/src/CenturionWithdrawalVault.sol:85
depositProtectionReadiness depositProtectionReadiness() view controller readiness failure none contracts/src/CenturionWithdrawalVault.sol:100
transferETH transferETH(address payable,uint256) controller insufficient balance, transfer failed ETHTransferred contracts/src/CenturionWithdrawalVault.sol:114
requestExit requestExit(bytes,uint256) controller invalid pubkey, already submitted, fee failure ExitRequested contracts/src/CenturionWithdrawalVault.sol:122

Events to Subscribe To

event topics emitted when consumer in seat-manager line
ETHReceived from vault receives ETH no direct watcher; status reads balances contracts/src/CenturionWithdrawalVault.sol:36
ETHTransferred to controller transfers ETH operation receipt/intents contracts/src/CenturionWithdrawalVault.sol:37
ExitRequested validatorPubkeyHash, target EIP-7002 request succeeds operation worker result/status contracts/src/CenturionWithdrawalVault.sol:38
ExitRequestFallbackUpdated fallbackContract controller updates fallback no direct watcher contracts/src/CenturionWithdrawalVault.sol:39

Upgrade / Freeze Posture

The vault is not upgradeable. It has a controller-set exit fallback, but only the controller can change it in contracts/src/CenturionWithdrawalVault.sol:104. The EIP-7002 path is CenturionEconomicController.requestValidatorExitDynamic or requestValidatorExitWithManualFee, which calls vault requestExit; those controller functions are at contracts/src/CenturionEconomicController.sol:781, and the vault call is at contracts/src/CenturionEconomicController.sol:1455.

CenturionEconomicController

Purpose

CenturionEconomicController owns seat policy, risk observations, reserve coverage, claim lifecycle, exit triggers, settlement, and read-only accounting for every vault. The backend operation executor calls it for claim executor grants, claim execution, exit requests, and status economics in seat-manager/src/contracts/operationExecutor.ts:693 and seat-manager/src/api/server.ts:822.

Key Invariants

  • Only the factory initializes seats in contracts/src/CenturionEconomicController.sol:250.
  • Only the owner sets policy controls, risk observations, reserve coverage, and executor grants through the owner modifier at contracts/src/CenturionEconomicController.sol:245.
  • Claims require running claim state and available rewards in contracts/src/CenturionEconomicController.sol:742.
  • Validator exit requires trigger arming and only the owner or seat beneficiary can request it in contracts/src/CenturionEconomicController.sol:1437 and contracts/src/CenturionEconomicController.sol:1468.
  • Deposit readiness is exposed to the deposit contract and checks policy, credential, reserve, and safety data through contracts/src/CenturionEconomicController.sol:1177.

Public Functions an Integrator May Call

function signature access control reverts events emitted line
initializeSeat initializeSeat(address,address,address,bytes32,uint64,uint128,uint128,uint8,uint64) factory already initialized, invalid beneficiary, invalid limits SeatInitialized contracts/src/CenturionEconomicController.sol:373
setClaimExecutorGrant setClaimExecutorGrant(address,address,uint8,uint64) owner invalid grant ClaimExecutorGrantSet contracts/src/CenturionEconomicController.sol:331
setTriggerArmed setTriggerArmed(address,bool) owner unregistered vault TriggerArmedSet contracts/src/CenturionEconomicController.sol:434
initiateClaim initiateClaim(address,uint256) beneficiary, owner, authorized executor through gatekeeper not running, insufficient rewards, pending claim ClaimInitiated contracts/src/CenturionEconomicController.sol:742
finalizeClaim finalizeClaim(address) pending claim caller path not ready, insufficient rewards ClaimFinalized contracts/src/CenturionEconomicController.sol:762
requestValidatorExitDynamic requestValidatorExitDynamic(address,bytes) owner or beneficiary fee, trigger, duplicate exit ExitRequested contracts/src/CenturionEconomicController.sol:781
seatRiskMetadata seatRiskMetadata(address) view uninitialized seat none contracts/src/CenturionEconomicController.sol:889
depositReadiness depositReadiness(address) view uninitialized seat none contracts/src/CenturionEconomicController.sol:1177

Events to Subscribe To

event topics emitted when consumer in seat-manager line
SeatInitialized vault factory initializes a seat operation receipt/intents contracts/src/CenturionEconomicController.sol:149
TriggerArmedSet vault owner arms or disarms exit trigger status economics contracts/src/CenturionEconomicController.sol:156
RiskObservationAccepted vault, epoch owner records risk observation status economics contracts/src/CenturionEconomicController.sol:165
ClaimInitiated vault, beneficiary pending claim opens operation worker result contracts/src/CenturionEconomicController.sol:189
ClaimFinalized vault, beneficiary claim transfers rewards operation worker result contracts/src/CenturionEconomicController.sol:192
ExitRequested vault exit request is sent operation worker result contracts/src/CenturionEconomicController.sol:196

Upgrade / Freeze Posture

The controller is not a proxy. Runtime mutability is owner-governed policy and phase hardening. Network phase can only move forward through hardenNetworkPhase in contracts/src/CenturionEconomicController.sol:322. The factory address can be set once and then rejects a second set in contracts/src/CenturionEconomicController.sol:292.

CenturionClaimGatekeeper

Purpose

CenturionClaimGatekeeper holds pending-claim state, executor grants, period caps, daily executor caps, and pure classification helpers used by the controller. The controller constructs and calls it internally, and the backend checks executor authorization through the controller/gatekeeper path before queuing sponsored claims in seat-manager/src/api/server.ts:2151.

Key Invariants

  • Only the controller can mutate grant, pending-claim, and claim lifecycle state in contracts/src/CenturionClaimGatekeeper.sol:33.
  • Executor grants require non-zero vault, non-zero executor, non-zero scope mask, and a future expiry in contracts/src/CenturionClaimGatekeeper.sol:42.
  • Only one pending claim can exist per vault in contracts/src/CenturionClaimGatekeeper.sol:454.
  • Finalization rejects missing claims and claims before readyAt in contracts/src/CenturionClaimGatekeeper.sol:478.
  • Claim amounts are bounded by beneficiary period cap and executor daily cap in contracts/src/CenturionClaimGatekeeper.sol:576.

Public Functions an Integrator May Call

function signature access control reverts events emitted line
setClaimExecutorGrant setClaimExecutorGrant(address,address,uint8,uint64) controller invalid grant none contracts/src/CenturionClaimGatekeeper.sol:42
claimExecutorAuthorized claimExecutorAuthorized(address,address,uint8) view none none contracts/src/CenturionClaimGatekeeper.sol:64
remainingClaimInPeriod remainingClaimInPeriod(address,uint128) view none none contracts/src/CenturionClaimGatekeeper.sol:73
remainingExecutorClaimInDay remainingExecutorClaimInDay(address,address,uint128) view none none contracts/src/CenturionClaimGatekeeper.sol:78
deriveClaimState deriveClaimState(...) pure none none contracts/src/CenturionClaimGatekeeper.sol:243
initiatePendingClaim initiatePendingClaim(address,address,address,uint256,uint64,uint128,uint128,uint8) controller pending exists, cap exceeded none contracts/src/CenturionClaimGatekeeper.sol:454
finalizePendingClaim finalizePendingClaim(address,address) controller no pending claim, not ready none contracts/src/CenturionClaimGatekeeper.sol:478

Events to Subscribe To

event topics emitted when consumer in seat-manager line
none none this contract emits no standalone events subscribe to controller claim and grant events contracts/src/CenturionClaimGatekeeper.sol:6

Upgrade / Freeze Posture

The gatekeeper is not upgradeable and mutates only when called by the controller. Operational freeze is controlled through controller policy, including setClaimExecutorsPaused at contracts/src/CenturionEconomicController.sol:339 and grant revocation at contracts/src/CenturionEconomicController.sol:343.