feat(security): H-1 — deposit min_ust1_out + fee_bps cap #26
Labels
No labels
enhancement
governance
QA
testing
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
code/ust1-window#26
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Close H-1 from
audits/INTERNAL_KIMIK3_1786162831.md: give deposits the same slippage seatbelt withdraw already has, and stop allowing governance to set a 100% fee that can zero out in-flight depositors.Audit ref: H-1 (F-05 in GROKCODE)
Deferred from: #25 / MR !41 (intentionally out of scope; zero-output dust guards M-2/M-3 landed, but not min-out / fee cap)
Related: #22 (collapse path can compound fee/slippage grief), withdraw
min_vfdusd_out(existing)Current codebase
contracts/ust1-window/src/msg.rsCw20HookMsg::Deposit {}min_ust1_outWithdraw { min_vfdusd_out }contracts/ust1-window/src/contract.rsust1_outviadeposit_vfdusd_to_ust1; dustZeroOutputguard exists (M-3 / #25); no min-out checkExecuteMsg::SetFeeBps/exec_set_fee_bpsfee_bps > 10_000(BPS_DENOM) — 100% is legalInstantiateMsg.fee_bps<= 10_000validationust1-commonDEFAULT_FEE_BPS = 100Problem: A deposit racing a
SetFeeBpsto 10000 (or a compromised/malicious governance) can yieldust1_out = 0or near-zero while the full vFDUSD principal is still forwarded to treasury. Withdraw already hasmin_vfdusd_out; deposit does not. Dust zero-output revert (M-3) only covers the exact-zero case — not “user expected N UST1 and got dust because fee jumped.”Why this is needed
fee_bps = 10000is a governance footgun and a mempool grief vector against honest depositors.Constraints / guardrails
Deposit { min_ust1_out: Uint128 }(mirror withdraw). Document migration for any off-chain callers/frontends; empty{}must stop decoding once the field is required (or use#[serde(default)]only if product explicitly wants backward-compat withmin=0— prefer required field so callers cannot silently omit protection).fee_bpsat a sane maximum (recommend ≤ 500 bps / 5%, or document chosen constant inust1-common) in both instantiate andSetFeeBps.ZeroOutputguards; min-out is additional (ust1_out < min_ust1_out→ dedicated error, e.g.Slippage/MinOutNotMet).min_vfdusd_out.chain_tax_and_cmm_protocol) beyond the new ceiling.Relevant files
contracts/ust1-window/src/msg.rs—Cw20HookMsg::Depositcontracts/ust1-window/src/contract.rs— deposit path,exec_set_fee_bps, instantiate validationcontracts/ust1-window/src/error.rs— min-out / fee-cap errorscontracts/ust1-window/src/multitest.rs— deposit slip / fee-cap cases (manyDeposit {}call sites)smartcontracts-terraclassic/packages/ust1-common/src/lib.rs— optionalMAX_FEE_BPSconstantsmartcontracts-terraclassic/tests/src/*.rs— update deposit hooks in integration fixturesdocs/DEPLOYMENT.md/README.md— fee cap + deposit min-out docsskills/audit-hardening-bundle/SKILL.md(or new skill) — cross-link H-1Recommended direction
Cw20HookMsg::Depositto{ min_ust1_out: Uint128 }.if ust1_out < min_ust1_out { return Err(...) }before mint / treasury forward.MAX_FEE_BPS(e.g. 500) inust1-common; enforce in instantiate +SetFeeBps.Deposit {}call sites with explicitmin_ust1_out(0 only in tests that intentionally probe dust/min-out).Acceptance criteria
Depositrequiresmin_ust1_out; deposit reverts whenust1_out < min_ust1_outwith no mint / no treasury credit.SetFeeBpsand instantiate rejectfee_bps > MAX_FEE_BPS(chosen cap documented; 100% no longer legal).fee_bpswithin cap andust1_out >= minstill succeed as today (modulo intentional test updates).Test plan (all paths)
min_ust1_out≤ computed out → mint + treasury forward.min_ust1_outabove computed out → err; balances unchanged.ZeroOutput(M-3) before/without needing min-out.fee_bps = MAX+1→ reject;fee_bps = MAX→ ok.MAX+1→ reject; setsMAX→ ok; unauthorized → Unauthorized.min_vfdusd_outstill enforced; fee at cap still withdraws correctly.Deposit {}JSON without field fails decode (if field required) — assert intentionally.Test plan (attack / hack / abuse vectors)
SetFeeBps(10000)against pending deposits — with cap, cannot reach 100%; with min-out, depositor still reverts rather than lose principal.OracleStale/OraclePaused.Withdrawon vFDUSD orDepositon UST1 still rejected by token/routing checks (existing).Verification criteria
MAX_FEE_BPSand that depositors must passmin_ust1_out.marked as related to #25
mentioned in issue #28
Rejected - such an incident can be mitigated by administrative intervention to mint tokens to the affected accounts.
mentioned in merge request !45