This file summarizes security-relevant behavior in the current contracts. It is not a replacement for a full audit.
Key trust assumptions in the current design:
UmiaHub.owner()is trusted to set critical registry pointers and parameters:- market core and market stake
- default and per-venture governance executor
- conditional market oracle
- LBP strategy factory, CCA factory, venture beacon
- market creation signer
- spot migrator and active liquidity keeper
- fee recipient and fee rates, execution delay, veto guardian, vesting admin
marketCreationSigneris trusted to approve market definitions via EIP-712 signatures.- Winning proposal execution can perform powerful treasury actions (including arbitrary external calls).
UmiaMarketCore enforces an internal invariant after split/merge:
totalRealVenture >= maxUserVirtualVentureSupplytotalRealMoney >= maxUserVirtualMoneySupply
where totalReal* includes user-deposited backing plus initial LP liquidity removed during market creation.
This invariant is central to settlement safety.
hasClaimed[marketId][user] prevents duplicate settlement claims.
marketExecuted[marketId] gates repeat execution of winning proposal payloads.
In Venture, liquidationActive blocks treasury mutation functions via whenNotLiquidating. Claims are handled by an external ILiquidator contract set via venture.setLiquidator().
Owner-only setters control protocol pointers, fee parameters and token approvals; the owner is also the upgrade key. Compromise impact: broad protocol control.
- Swaps are self-custodial; the permit variants recover an EIP-712 signer and consume a per-signer
swapNoncesentry for replay protection. - Market creation requires valid signer signature and nonce.
- Winning proposal execution uses executor resolved from hub.
- Only the current market core may call
executeProposal. - Executor must match
HUB.governanceExecutor(venture). - Plan version and action versions are strictly checked.
Privilege tiers:
onlyHub: initialization and hub-level operations.onlyExecutor: mint and burn (both blocked while liquidating), withdraw (theauthorizedLiquidatorinstead once liquidation is active, which also alone mayburnFrom), arbitrary call, team member updates, allowance updates, document uploads, liquidation setup (setLiquidator).
-
Arbitrary call action (
CALL)- Governance payload can call arbitrary target/value/data through
Venture.executeCall. - Powerful by design; increases blast radius of malicious governance outcomes.
- Governance payload can call arbitrary target/value/data through
-
Registry pointer updates in hub
- Changing executor/oracle/market core/spot migrator can materially alter system behavior.
-
Market creation signer compromise
- Could approve malicious market definitions and payloads.
-
LP liquidity operations
createMarketpulls 50% of the venture's spot vault liquidity (pullForDecisionMarket); settlement returns the excess (returnFromDecisionMarket). Both run under the vault's TWAP guard.- Arithmetic and slippage protections are security-critical.
-
Liquidation setup
- Liquidation is terminal and snapshots balances/supply.
- Ensure governance process around liquidation is strict.
UmiaMarketCoreusesReentrancyGuardon every state-changing entrypoint (market creation, split/merge, swaps and baskets, settlement, claims, execution).GovernanceExecutorusesReentrancyGuard.VentureusesReentrancyGuardon transfer/call sensitive methods.- External call paths include:
- token transfers,
- Uniswap helper interactions,
Venture.executeCallarbitrary targets.
- Elapsed-time truncated TWAP: The accepted tick slews toward the pool tick at no more than 4,558 ticks/second (9,116 ticks over a normal two-second Base block), with the ramp and recovery path integrated exactly. Extra writes cannot ratchet the filter faster, and a restored price recovers during quiet time instead of leaking a stale clamped tick across the gap.
- Operator-only, full-range liquidity enforcement:
beforeAddLiquidityandbeforeRemoveLiquidityreject any caller other than the pool's registered operator (the venture'sSpotLiquidityVault), and any position that isn't full-range (minUsableTicktomaxUsableTick). Concentrated liquidity would create zero-liquidity tick gaps exploitable for cheap tick manipulation.UmiaHookV2adds a second operator per pool: theactiveOperator(the venture'sActiveLiquidityManager) may add any range on top of the anchor vault's full-range position, and removals are not gated. No tick has zero liquidity while the anchor is funded. - Spot-vs-TWAP sandwich guard:
SpotLiquidityVault._requireSpotWithinTwapDeviationcompares the current spot tick against the 30-minute TWAP tick (TWAP_WINDOW) and reverts withSpotPriceDeviationTooHighbeyondMAX_TICK_DEVIATION(1000 ticks, ~10.5%). It runs on deposits, withdrawals, decision-market creation, and settlement. V2 also rejects spot more thanMAX_LONG_TICK_DEVIATION(2000 ticks, ~22%) from the 6-hour TWAP of the coarse ring (SpotTwapGuard.withinBands, shared with the migrator seam, which uses 300 ticks over 6 hours, and the ALM, which uses 500 ticks over 12 hours), since a price held for 30 minutes passes the short window on its own. It fails closed until the coarse ring covers six hours. Fail-closed: if the oracle holds fewer than 2 observations or cannot serve the full window (e.g. buffer exhausted by dust swaps), it reverts withInsufficientOracleHistoryrather than silently allowing the operation. The only skip is an empty position (currentLiquidity() == 0), which has no reserves to sandwich and must stay open so a drained vault can be re-bootstrapped. - Oracle buffer sizing:
afterInitializeseeds cardinality 1 andbootstrapFromLBPgrows it to 100 slots. At one observation per block that covers only ~200s on Base, short of the 30-minute window, so busy pools should be grown to at least 1,000 slots via the permissionlessincreaseCardinalityNext(). Combined with the fail-closed guard, buffer exhaustion blocks operations rather than bypassing them. See SPOT_ORACLE.md.
- Elapsed-time tick slew: Same mechanism as the spot oracle (
TickSlew) at a far lower rate: the accepted tick moves toward the pool's tick at no more than 7 ticks/second (420 ticks, about 4.3%, per minute; faster only on markets shorter than a few hours), and the ramp and hold are integrated exactly. A crash or pump is credited only as fast as the slew allows, so moving a two-day settlement average by the 2% threshold takes a hold of about 52 minutes in the open rather than a few minutes at the end of the window, and once trading stops the accepted tick settles on the pool's real tick. The TWAP is the mean tick (a geometric mean), so the winning threshold is a fixed tick offset applied identically to every proposal. - Overflow-free reads: The pool tick is computed from the reserve ratio with
FullMath.mulDiv, saturating before any product can overflow and clamping to the V4 tick range, so no reserve pair can make an update or settlement read revert. - Trading-start anchor: The TWAP is anchored at
tradingStart(with a zero cumulative baseline) by a one-shotinitializeat market creation, immutable thereafter. No price before trading start affects the average. - Swappable implementation: The oracle is resolved through the Hub registry on every call and its
initialize/update/calculateTWAPinterface carries the market's winning threshold, so a hardened implementation can replace this one viahub.setConditionalMarketOracle()without touchingUmiaMarketCore. - Trading-end freeze: Oracle timestamps are capped at
tradingEndvia_effectiveTimestamp(). After trading ends,update()stops accumulating andcalculateTWAP()returns a fixed value. This prevents settlement delay from diluting the TWAP or extending the influence of last-block manipulation. - Settlement oracle update: Oracle is updated at settlement time to include the final price observation before TWAP computation.
- No protocol-wide pause mechanism is present in current code. The Hub's per-market circuit breaker (
tripDecisionMarketCircuitBreaker, owner orvetoGuardian) blocks onlyexecuteWinningProposal; the ALM has its ownsetPaused. UmiaMarketCore.executeWinningProposalruns only afterexecutionDelayfrom settlement (UmiaHub.decisionMarketExecutionDelay, default 4 hours, max 12 hours), the window in which the circuit breaker can veto it.- Oracle correctness depends on market-core-triggered updates around swaps/settlement.
- Permit/nonces are per-contract and need replay protection monitoring.
- Market accounting and solvency proofs (
split,merge,settleMarket,claimSettlement). - Governance payload decoding/dispatch correctness (
GovernanceExecutor,GovernancePayloadValidator,GovernanceActions). - Access control and pointer trust model (
UmiaHub,Venturemodifiers). - Uniswap v4 integration assumptions in LP removal/re-add flow.
- Liquidation state transitions and proportional payouts.
DECISION_MARKET_FLOW.mdGOVERNANCE_TREASURY_LAYER.mdUPGRADES.mddocs/contracts/references