[#138] Finding 5: Frontend — reduce maxUint256 approvals (H-01 blast radius) #143

Closed
opened 2026-05-04 08:52:21 +00:00 by PlasticDigits · 15 comments
PlasticDigits commented 2026-05-04 08:52:21 +00:00 (Migrated from gitlab.com)

Parent: Split from #138 — Pre-deploy review pass 2 (Finding 5).

Severity: Low–Medium (risk coupling to audit H-01 — compromised upgrade reusing allowances)

Problem

The frontend issues maxUint256 infinite ERC20 approvals in nine places across buy / Kumbaya / referral flows. Combined with audit H-01 (malicious upgraded TimeCurve could drain existing CL8Y allowances), infinite approval amplifies blast radius from “amount in flight” to full wallet balance for repeat visitors. No revokeApproval UX, no exact-amount default.

References

Kind Path / link
Arena frontend/src/pages/timeCurveArena/useTimeCurveArenaModel.tsx — WETH/USDM → SwapRouter; CL8Y → TimeCurve
Simple buy frontend/src/pages/timecurve/useTimeCurveSaleSession.ts
Referrals frontend/src/pages/referrals/ReferralRegisterSection.tsx
Kumbaya frontend/src/lib/timeCurveKumbayaSingleTx.ts
Audit audits/ — H-01 (proxy upgrade + allowance reuse); cross-link in issue
Kumbaya doc docs/integrations/kumbaya.md
Invariants docs/testing/invariants-and-business-logic.md — buy router / Kumbaya rows
Skill .cursor/skills/yieldomega-guardrails/SKILL.md (TimeCurve / Kumbaya)
  • Default to approve(spender, exactAmount) (may require one approval per buy; wallet UX copy should say so).
  • For hot paths (CL8Y → TimeCurve), offer opt-in infinite allowance via checkbox + concise disclosure: upgraded proxy could reuse unlimited approval.
  • Consider revoke or approve(0) helper in settings (stretch).

Acceptance criteria

  • No silent maxUint256 as sole path for first-time users; exact-amount default or explicit opt-in documented in UI.
  • Vitest/component or E2E assertion that approval amount matches swap/buy sizing where feasible.
  • Product/docs note on residual risk vs H-01 linked from settings or footnote.

Verification checklist

  • Manual: complete CL8Y, ETH, USDM buy paths on Anvil — fewer infinite grants unless opted in.
  • Gas regression acceptable (document extra txs if any).
  • Screen reader / copy review for disclosure checkbox.
  • Cross-browser wallet (Rabby/MetaMask) smoke.
**Parent:** Split from [#138 — Pre-deploy review pass 2](https://gitlab.com/PlasticDigits/yieldomega/-/issues/138) (Finding 5). **Severity:** Low–Medium (risk coupling to audit **H-01** — compromised upgrade reusing allowances) ## Problem The frontend issues **`maxUint256` infinite ERC20 approvals** in nine places across buy / Kumbaya / referral flows. Combined with audit **H-01** (malicious upgraded TimeCurve could drain existing CL8Y allowances), infinite approval **amplifies blast radius** from “amount in flight” to **full wallet balance** for repeat visitors. No `revokeApproval` UX, no exact-amount default. ## References | Kind | Path / link | |------|-------------| | Arena | [`frontend/src/pages/timeCurveArena/useTimeCurveArenaModel.tsx`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/src/pages/timeCurveArena/useTimeCurveArenaModel.tsx) — WETH/USDM → SwapRouter; CL8Y → TimeCurve | | Simple buy | [`frontend/src/pages/timecurve/useTimeCurveSaleSession.ts`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/src/pages/timecurve/useTimeCurveSaleSession.ts) | | Referrals | [`frontend/src/pages/referrals/ReferralRegisterSection.tsx`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/src/pages/referrals/ReferralRegisterSection.tsx) | | Kumbaya | [`frontend/src/lib/timeCurveKumbayaSingleTx.ts`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/frontend/src/lib/timeCurveKumbayaSingleTx.ts) | | Audit | `audits/` — H-01 (proxy upgrade + allowance reuse); cross-link in issue | | Kumbaya doc | [`docs/integrations/kumbaya.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/docs/integrations/kumbaya.md) | | Invariants | [`docs/testing/invariants-and-business-logic.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/docs/testing/invariants-and-business-logic.md) — buy router / Kumbaya rows | | Skill | [`.cursor/skills/yieldomega-guardrails/SKILL.md`](https://gitlab.com/PlasticDigits/yieldomega/-/blob/main/.cursor/skills/yieldomega-guardrails/SKILL.md) (TimeCurve / Kumbaya) | ## Recommended fix - Default to **`approve(spender, exactAmount)`** (may require one approval per buy; wallet UX copy should say so). - For hot paths (CL8Y → TimeCurve), offer **opt-in infinite allowance** via checkbox + concise disclosure: *upgraded proxy could reuse unlimited approval*. - Consider **revoke** or **approve(0)** helper in settings (stretch). ## Acceptance criteria - [ ] No silent `maxUint256` as sole path for first-time users; exact-amount default or explicit opt-in documented in UI. - [ ] Vitest/component or E2E assertion that approval amount matches swap/buy sizing where feasible. - [ ] Product/docs note on residual risk vs H-01 linked from settings or footnote. ## Verification checklist - [ ] Manual: complete CL8Y, ETH, USDM buy paths on Anvil — fewer infinite grants unless opted in. - [ ] Gas regression acceptable (document extra txs if any). - [ ] Screen reader / copy review for disclosure checkbox. - [ ] Cross-browser wallet (Rabby/MetaMask) smoke.
PlasticDigits commented 2026-05-04 08:53:13 +00:00 (Migrated from gitlab.com)

mentioned in issue #138

mentioned in issue #138
PlasticDigits commented 2026-05-04 14:58:30 +00:00 (Migrated from gitlab.com)

mentioned in commit 49c758bc81

mentioned in commit 49c758bc8191501ff213110534de861277580040
PlasticDigits commented 2026-05-04 14:58:31 +00:00 (Migrated from gitlab.com)

mentioned in commit 179c621ed6

mentioned in commit 179c621ed687afee516fd1a0b989a9a051d9a703
PlasticDigits commented 2026-05-04 14:58:31 +00:00 (Migrated from gitlab.com)

mentioned in commit e7afcf4d77

mentioned in commit e7afcf4d77dda76dd36ecbc226b09a66a544b1a5
PlasticDigits commented 2026-05-04 15:03:47 +00:00 (Migrated from gitlab.com)

Status: merged to main (commit range ending at current origin/main)

What shipped

  • Default exact approve amounts for Kumbaya router legs (slippage-bounded maxIn), ReferralRegistry (registrationBurnAmount), and CL8Y → TimeCurve for buy / WarBow flows.
  • Opt-in unlimited CL8Y → TimeCurve via Cl8yTimeCurveUnlimitedApprovalFieldset + localStorage key yieldomega.erc20.cl8yTimeCurveUnlimited.v1, with H-01 disclosure in UI/docs.
  • Session guard preserved: after allowance read, guardBuySession() still runs; approveAmt comes from cl8yTimeCurveApprovalPreference (cl8yTimeCurveApprovalAmountWei + readCl8yTimeCurveUnlimitedApproval).
  • Docs / cross-links: docs/frontend/wallet-connection.md, docs/testing/invariants-and-business-logic.md (INV-ERC20-APPROVAL-143), docs/integrations/kumbaya.md, docs/frontend/timecurve-views.md, .cursor/skills/yieldomega-guardrails/SKILL.md, skills/README.md, skills/play-timecurve-doubloon/SKILL.md.

How to verify

  • Unit tests: cd frontend && npm ci && npm test -- --run — includes cl8yTimeCurveApprovalPreference.test.ts.
  • CL8Y buy (default): Connect wallet → TimeCurve Simple or Arena → pay with CL8Y → inspect wallet approve tx: spender = TimeCurve proxy, amount = exact gross for that buy (not maxUint256).
  • Unlimited toggle: Enable opt-in in buy panel → next approve uses type(uint256).max for CL8Y→TimeCurve; disable toggle does not revoke onchain (documented).
  • Kumbaya paths: ETH/USDM → swap → buy: router approvals remain maxIn-bounded per quote (not silent unlimited on router legs).
  • Referrals: /referrals register burn: approve matches registrationBurnAmount exactly.
  • Mid-flow wallet: Switch account/chain mid-purchase → should still abort with wallet-session message (GitLab #144), independent of approval sizing.

cc @brouie for sign-off when convenient.

Issue left open per process — close when you’re satisfied.

## Status: merged to `main` (commit range ending at current `origin/main`) ### What shipped - **Default exact `approve` amounts** for Kumbaya router legs (slippage-bounded `maxIn`), **ReferralRegistry** (`registrationBurnAmount`), and **CL8Y → TimeCurve** for `buy` / WarBow flows. - **Opt-in unlimited** CL8Y → TimeCurve via `Cl8yTimeCurveUnlimitedApprovalFieldset` + `localStorage` key `yieldomega.erc20.cl8yTimeCurveUnlimited.v1`, with **H-01** disclosure in UI/docs. - **Session guard preserved:** after allowance read, `guardBuySession()` still runs; `approveAmt` comes from `cl8yTimeCurveApprovalPreference` (`cl8yTimeCurveApprovalAmountWei` + `readCl8yTimeCurveUnlimitedApproval`). - **Docs / cross-links:** `docs/frontend/wallet-connection.md`, `docs/testing/invariants-and-business-logic.md` (`INV-ERC20-APPROVAL-143`), `docs/integrations/kumbaya.md`, `docs/frontend/timecurve-views.md`, `.cursor/skills/yieldomega-guardrails/SKILL.md`, `skills/README.md`, `skills/play-timecurve-doubloon/SKILL.md`. ### How to verify - [ ] **Unit tests:** `cd frontend && npm ci && npm test -- --run` — includes `cl8yTimeCurveApprovalPreference.test.ts`. - [ ] **CL8Y buy (default):** Connect wallet → TimeCurve Simple or Arena → pay with CL8Y → inspect wallet `approve` tx: spender = TimeCurve proxy, amount = **exact** gross for that buy (not `maxUint256`). - [ ] **Unlimited toggle:** Enable opt-in in buy panel → next `approve` uses **`type(uint256).max`** for CL8Y→TimeCurve; disable toggle does **not** revoke onchain (documented). - [ ] **Kumbaya paths:** ETH/USDM → swap → buy: router approvals remain **`maxIn`**-bounded per quote (not silent unlimited on router legs). - [ ] **Referrals:** `/referrals` register burn: `approve` matches **`registrationBurnAmount`** exactly. - [ ] **Mid-flow wallet:** Switch account/chain mid-purchase → should still abort with wallet-session message (GitLab #144), independent of approval sizing. cc @brouie for sign-off when convenient. _Issue left open per process — close when you’re satisfied._
PlasticDigits commented 2026-05-04 15:08:08 +00:00 (Migrated from gitlab.com)

mentioned in commit 838a2c1e9b

mentioned in commit 838a2c1e9ba935bb4a5f1a1c2f51c440004d3226
Brouie commented 2026-05-05 02:13:37 +00:00 (Migrated from gitlab.com)

@PlasticDigits — verification PASS on the approval-sizing rework. walked your checklist:

  1. npx vitest run cl8yTimeCurveApprovalPreference green: 3/3 PASS — covers default-needWei branch, opt-in unlimited branch, and storage round-trip.
    2-6. runtime smoke (live wallet walk + cross-browser + screen reader copy) skipped this round — no localnet stack up. source verified end-to-end:
  • cl8yTimeCurveApprovalPreference.ts L41-45: returns needWei by default, maxUint256 only when localStorage flag (yieldomega.erc20.cl8yTimeCurveUnlimited.v1) is opted in.
  • useTimeCurveSaleSession.ts L1066-1075: CL8Y→TimeCurve approval sized via cl8yTimeCurveApprovalAmountWei(amount, readCl8yTimeCurveUnlimitedApproval()). default flow approves exactly the buy spend.
  • timeCurveKumbayaSingleTx.ts L90, L102-107: maxIn = swapMaxInputFromQuoted(qIn, KUMBAYA_SWAP_SLIPPAGE_BPS), conditional approve only if existing allowance below maxIn, approval is exactly maxIn (slippage-bounded, not unlimited).
  • ReferralRegisterSection.tsx L106: reads registrationBurnAmount from registry, sized exactly per spec.
  • Cl8yTimeCurveUnlimitedApprovalFieldset.tsx exposes the opt-in checkbox with H-01 disclosure copy.
    maxUint256 no longer appears in any of the buy/Kumbaya/referral hot paths outside the explicit opt-in helper. that's the key invariant. H-01 blast radius reduced from 'full balance forever' to 'amount in flight per buy' for default users.
    INV-ERC20-APPROVAL-143 + cross-links in wallet-connection.md / kumbaya.md / timecurve-views.md / guardrails read cleanly.
    ready for close on your side.
@PlasticDigits — verification PASS on the approval-sizing rework. walked your checklist: 1. `npx vitest run cl8yTimeCurveApprovalPreference` green: 3/3 PASS — covers default-needWei branch, opt-in unlimited branch, and storage round-trip. 2-6. runtime smoke (live wallet walk + cross-browser + screen reader copy) skipped this round — no localnet stack up. source verified end-to-end: - `cl8yTimeCurveApprovalPreference.ts` L41-45: returns `needWei` by default, `maxUint256` only when localStorage flag (`yieldomega.erc20.cl8yTimeCurveUnlimited.v1`) is opted in. - `useTimeCurveSaleSession.ts` L1066-1075: CL8Y→TimeCurve approval sized via `cl8yTimeCurveApprovalAmountWei(amount, readCl8yTimeCurveUnlimitedApproval())`. default flow approves exactly the buy spend. - `timeCurveKumbayaSingleTx.ts` L90, L102-107: `maxIn = swapMaxInputFromQuoted(qIn, KUMBAYA_SWAP_SLIPPAGE_BPS)`, conditional approve only if existing allowance below `maxIn`, approval is exactly `maxIn` (slippage-bounded, not unlimited). - `ReferralRegisterSection.tsx` L106: reads `registrationBurnAmount` from registry, sized exactly per spec. - `Cl8yTimeCurveUnlimitedApprovalFieldset.tsx` exposes the opt-in checkbox with H-01 disclosure copy. `maxUint256` no longer appears in any of the buy/Kumbaya/referral hot paths outside the explicit opt-in helper. that's the key invariant. H-01 blast radius reduced from 'full balance forever' to 'amount in flight per buy' for default users. INV-ERC20-APPROVAL-143 + cross-links in wallet-connection.md / kumbaya.md / timecurve-views.md / guardrails read cleanly. ready for close on your side.
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-05-05 02:36:18 +00:00
PlasticDigits commented 2026-05-13 15:13:33 +00:00 (Migrated from gitlab.com)

mentioned in commit 32cf66eebb

mentioned in commit 32cf66eebb677c536d222d5db045419016a26d35
PlasticDigits commented 2026-05-13 15:13:34 +00:00 (Migrated from gitlab.com)

mentioned in commit 8731db1f1c

mentioned in commit 8731db1f1c172dae821e09e82e538e12666fdefd
PlasticDigits commented 2026-05-20 04:30:23 +00:00 (Migrated from gitlab.com)

mentioned in issue #224

mentioned in issue #224
PlasticDigits commented 2026-05-27 14:33:22 +00:00 (Migrated from gitlab.com)

mentioned in issue #262

mentioned in issue #262
PlasticDigits commented 2026-05-29 03:24:02 +00:00 (Migrated from gitlab.com)

mentioned in issue #263

mentioned in issue #263
PlasticDigits commented 2026-06-01 13:14:14 +00:00 (Migrated from gitlab.com)

mentioned in issue #277

mentioned in issue #277
PlasticDigits commented 2026-06-14 22:08:38 +00:00 (Migrated from gitlab.com)

mentioned in issue #325

mentioned in issue #325
PlasticDigits commented 2026-06-21 03:43:53 +00:00 (Migrated from gitlab.com)

mentioned in issue #341

mentioned in issue #341
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code/yieldomega#143
No description provided.