Access Control¶
Function Permissions¶
| Function(s) | Caller | Notes |
|---|---|---|
deposit |
Any address | Open to all; no pause check |
requestWithdraw |
Any address | Shares pulled from msg.sender; no pause check |
cancelWithdraw |
Original requester only | queue[requestId].owner must equal msg.sender; no pause check |
processWithdrawals |
Keeper only | require(msg.sender == keeper) — explicit on-chain check |
setKeeper |
Governance only | require(newKeeper != address(0)); emits KeeperUpdated |
setOperator |
Governance only | require(newOperator != address(0)); emits OperatorUpdated |
proposeGovernance |
Governance only | Sets pendingGovernance; emits GovernanceProposed |
acceptGovernance |
pendingGovernance address only |
Completes transfer; emits GovernanceTransferred |
openPosition |
Operator only | Hot-wallet role — multisig not required |
markSettling |
Operator only | Hot-wallet role |
closePosition |
Operator only | Hot-wallet role |
writeOff |
Operator only | Hot-wallet role |
rebasePosition |
Operator only | Hot-wallet role |
reclaimSlot |
Operator only | Hot-wallet role |
emergencyLiquidate |
Governance only | System must be paused — enforced on-chain |
Role Architecture¶
The implementation uses three roles instead of the original two:
| Role | Address | Scope |
|---|---|---|
governance |
Multisig | Role rotation (setKeeper, setOperator), governance transfer, emergency liquidation |
operator |
Hot wallet | All position lifecycle management (openPosition through reclaimSlot) |
keeper |
Bot | Queue processing (processWithdrawals) only |
The operator role allows routine position management from a hot wallet without exposing the governance multisig key for daily operations. Using the same address for both governance and operator collapses to a two-role model equivalent to the original spec.
Governance can rotate the operator at any time via setOperator(address).
Governance Transfer¶
Governance is transferred via a two-step pending/accept pattern:
- Current governance calls
proposeGovernance(newAddress)— setspendingGovernance - The proposed address calls
acceptGovernance()— completes the transfer
This prevents fat-finger transfers to wrong addresses. There is no enforced time delay between proposal and acceptance. The multisig process provides the equivalent governance control.
No mandatory time delay
A compromised governance key can transfer in a single block if the attacker also controls the target address. Multisig governance with multiple required signers is essential.
Governance Centralization¶
MVP Governance Risk
The operator address has broad power over position lifecycle but cannot change system parameters or rotate roles. Governance retains exclusive control over role rotation and emergency liquidation. For later versions, consider:
- Timelocks on openPosition and rebasePosition
- Immediate execution only for writeOff (which can only decrease NAV, never increase it — safe to allow without delay)
- Multi-party signing thresholds for the operator role
Keeper Role¶
The processWithdrawals keeper is distinct from both governance and the operator. It is a hot-wallet or automated bot address that processes the redemption queue. Separation ensures that:
- The keeper cannot alter positions or NAV
- A compromised keeper can only process legitimate queued withdrawals (net neutral to the vault)
- Governance retains exclusive control over capital deployment and role rotation
Security Implications¶
- Write-off can only decrease NAV — conservative by design and safe to execute immediately
- emergencyLiquidate is the only function that sells assets at market price; it is guarded by the on-chain pause check and governance authorization
- rebasePosition can only move entry price downward (enforced) — it cannot inflate NAV
- Governance transfer requires the pending address to accept — prevents one-step handoff to a wrong address