Roles and admin controls
Every on-chain role in Tuo, what each can and cannot do, and the boundaries that keep customer funds inside the position.
Tuo uses OpenZeppelin AccessControl with three roles. The vault wires their administration in its constructor, and the super-admin role is never granted, so there is no single key that can reconfigure the system.
Actors
| Actor | On-chain identity | Can do | Can never do |
|---|---|---|---|
| Customer | Owner of the position NFT | deposit, requestWithdraw, withdraw, emergencyWithdraw, transfer the NFT | Anything else. Ownership of the NFT is the whole authority model |
| Treasury | TREASURY_ROLE, a 2-of-3 Safe with hardware-wallet signers | Grant and revoke KEEPER_ROLE and PAUSER_ROLE, rotate itself, set the fee recipient, sweep accrued fees, set the per-position cap, maintain the Hyperliquid operator allowlist, register and retire products, add and retire aggregators, set the NFT base URI | Change the fee percentage, upgrade anything, touch a position, reach a deployed liquidity position, move idle balances, stop a withdrawal |
| Keeper | KEEPER_ROLE, an EOA whose key is held in a cloud HSM | Reshape a position's holdings inside the vault (swapIdle, mintLp, burnLp), send hedge margin to the position's bound operator (bridgeToHl), pull it back or attest a final return (markBridgeInboundComplete), settle an emergency claim (settleHlClaim) | Cause funds to rest anywhere but the vault, the Uniswap position manager, the bound operator, or the NFT owner. Exceed 10 metered actions or 150 bps of basis in measured loss per position per 24 hours |
| Pauser | PAUSER_ROLE, a dedicated hardware wallet | pauseDeposits, unpauseDeposits | Affect withdrawals or emergency exits in any way |
| Hyperliquid operator | One EOA per position, on the treasury allowlist, key in a cloud HSM | Receive bridged margin, act as the position's Hyperliquid account, approve the vault to pull margin back | Call the vault. It is a destination and a pull source, never a caller |
| Aggregator | Address on the router allowlist | Be the target of one call per swap, with an exact-amount approval cleared immediately after | Hold funds across calls or receive an open-ended approval |
Trust boundary
Every arrow into the vault is bounded by a role check. Every arrow out of the vault goes to one of four destinations: the owner, the Uniswap position manager, an allowlisted aggregator inside one atomic swap, or the position's bound Hyperliquid operator.
The treasury surface
| Function | Notes |
|---|---|
setFeeRecipient | Zero address rejected |
claimFees, claimTokenFees | Pull payment. Reverts on a zero balance. A broken recipient blocks Tuo's revenue, never a customer's exit |
setPerNftCap | Zero rejected. Bounds one position's basis and nothing else. No aggregate cap exists on-chain |
setHlOperator | Adds or removes an operator from the allowlist. Never unbinds a position that already bridged to that operator |
registerProduct, setProductOpen | Add-only registry. Retiring a product blocks new positions only |
addAggregator, removeAggregator | Forwarded to the router. Emptying the list stops non-USDC deposits and the exit swap, never the USDC exit or the emergency exit |
setBaseURI | Cosmetic. No custody or accounting reads it |
Role administration
DEFAULT_ADMIN_ROLEis never granted. The deployment script asserts that nobody holds it.TREASURY_ROLEadministers itself,KEEPER_ROLEandPAUSER_ROLE.TREASURY_ROLEcannot be renounced or self-revoked. If the last holder could give the role up, nobody could ever revoke a compromised keeper again. Rotation is grant, then the new holder revokes the old one.KEEPER_ROLEandPAUSER_ROLEare rotatable by the treasury at any time.
Pausing
pauseDeposits stops new deposits and top-ups. It does not affect requestWithdraw, withdraw
or emergencyWithdraw, and it does not stop the keeper from unwinding a position. It is a circuit
breaker on inflows only, held by a single hardware wallet so it never waits on two signatures.
Residual risks in this model
These are disclosed design decisions, listed in full on Audits.
- The Hyperliquid operator set is treasury-mutable, and the vault does not enforce one operator per position. That rule is off-chain discipline. Each position binds to the operator it first bridged to and never rebinds, and both inbound legs pull only from that operator.
- A compromised treasury could retire every aggregator but one it controls. That is a smaller power than the one it already holds over the keeper role, and it cannot touch the guaranteed exits.
- The 150 bps per day loss budget compounds to roughly 36% of a position in a month. Detection and the Safe revoking the keeper are the real bound. See Operational risk.