Permissioned Vs Permissionless Deposits¶
Phase 1 is permissioned. The current DepositContractCTN source always enforces intent-based admission; it does not contain a permissionless switch.
Removed switches
The latest refactor removed pubkeyAllowlistEnabled, pubkeyAllowlistDisabledForever, ownerOnlyDepositorEnabled, and the permanent allowlist-disable flow. If a future permissionless mode is desired, it is a governed implementation change, not an existing runtime toggle.
Who May Deposit¶
A caller may deposit only if allowlistAdmin has created an active intent for:
- the exact validator pubkey;
- the exact withdrawal credentials;
- the exact amount in gwei;
- the exact authorized depositor address;
- the current
allowlistEpoch.
The deposit caller is the authorized depositor. The allowlist admin is not automatically the depositor unless the admin used the default addAllowedDeposit path, which binds the default depositor to allowlistAdmin.
Intent Hashing¶
The current source computes:
text keccak256(abi.encode(pubkey, withdrawal_credentials, amountGwei, authorizedDepositor, allowlistEpoch))
This prevents stale cross-admin reuse and binds admission to the intended sender. When acceptAllowlistAdmin succeeds, allowlistEpoch += 1; old intent preimages stop matching the active epoch.
Replay Protection¶
Successful deposit consumption does two things:
- clears
allowedDeposits[intentHash]; - sets
consumedIntents[intentHash] = true.
_allowIntent rejects any intent hash already marked consumed. Removing an active intent does not mark it consumed, but successful use is permanent for that hash and epoch.
Admission Functions¶
| Function | Authority | Use |
|---|---|---|
addAllowedDeposit | allowlistAdmin | Allow a default 32 CTN intent for allowlistAdmin as depositor. |
addAllowedDeposits | allowlistAdmin | Batch default intents. |
addAllowedDepositFor | allowlistAdmin | Allow an explicit amount and authorized depositor. |
addAllowedDepositsFor | allowlistAdmin | Batch explicit amount/depositor intents. |
removeAllowedDeposit | allowlistAdmin | Remove a default active intent. |
removeAllowedDepositFor | allowlistAdmin | Remove an explicit active intent. |
transferAllowlistAdmin | allowlistAdmin | Stage admin transfer. |
acceptAllowlistAdmin | pendingAllowlistAdmin | Accept transfer and increment allowlistEpoch. |
cancelAllowlistAdminTransfer | allowlistAdmin | Cancel staged transfer. |
Permissioning Is Not Custody Safety¶
Permissioning answers “may this caller try this deposit?” Custody checks answer “is this route safe?” The custody layer still verifies factory registration, vault metadata, withdrawal credentials, upgrade governor policy assertions, beacon authority, controller readiness, reserve coverage, and bootstrap closure.
Do not bypass custody with admission
An allowlist event is not proof that funds can later leave safely. Operators must verify both the active intent and the full readiness/custody path before opening a deposit window.
Four-Layer Separation¶
| Layer | Deposit-specific meaning |
|---|---|
| Upgrade governance | The current implementation, factory, controller, claim gatekeeper, and beacon must satisfy governor policy assertions. |
| Deposit permissioning | The exact caller must have an active epoch-scoped intent. |
| Custody/readiness | The vault route, metadata, beacon authority, router bootstrap state, and controller readiness must be safe. |
| Economic/claim safety | Reserve, risk, trigger, beneficiary, schema, and claim assumptions must support later safe accounting. |
Transition To Future Permissionless Mode¶
Because there is no runtime permissionless switch in the current source, any transition must be treated as a code upgrade. Minimum requirements before proposing such an upgrade:
- written governance decision explaining the new abuse model;
- reviewed implementation diff that removes or changes intent checks intentionally;
- updated runbooks, monitoring, and incident response paths;
- test evidence covering replay, sender binding, root handling, and custody checks;
- staged deployment and rollback/cancel plan through the upgrade governor.
Source-Grounded Examples¶
Phase 1 Permissioned Normal Mode¶
allowlistAdmincallsaddAllowedDepositFor(pubkey, withdrawal_credentials, 32_000_000_000, authorizedDepositor).- The active hash is
keccak256(abi.encode(pubkey, withdrawal_credentials, amountGwei, authorizedDepositor, allowlistEpoch)). authorizedDepositorsubmitsdeposit(...)with exactly 32 CTN.- The source clears
allowedDeposits[intentHash], setsconsumedIntents[intentHash] = true, emitsDepositIntentConsumedFor, then emitsDepositEventafter root validation.
Result: Phase-1 admission is permissioned by an exact intent and exact caller. Custody/readiness checks still have to pass.
Invalid Depositor¶
- The intent authorizes
0xAAA...asauthorizedDepositor. 0xBBB...submits the deposit with otherwise identical pubkey, credentials, and amount.- The computed hash includes
msg.sender, so it differs from the allowlisted hash.
Result: the deposit fails with DepositIntentNotAllowlisted.
Replayed Intent¶
- A valid intent is consumed by a successful deposit.
- The same tuple is submitted again in the same
allowlistEpoch. - The active map is already false and
consumedIntents[intentHash]is true.
Result: the second deposit cannot reuse the intent, and _allowIntent will not re-authorize that consumed hash.
Future Permissionless Transition¶
The current source has no pubkeyAllowlistEnabled, no irreversible allowlist-disable flag, and no owner-only depositor toggle. A permissionless transition therefore requires a governed implementation change that deliberately alters the admission path. That change must preserve custody/readiness checks unless governance explicitly accepts a different safety model.
Unsafe Confusion¶
Admission permissioning is not custody validation. Removing an allowlist requirement in a future implementation would not prove the vault route, controller readiness, reserve coverage, beacon authority, or upgrade policy. Custody/readiness and Economic/claim safety must remain independently checked.