fix(frontend): writeContractWithGasBuffer helper with +30% buffer + soft caps (GitLab #176) #378

Merged
Brouie merged 1 commit from fix/glab-176-estimate-gas-buffer-helper into main 2026-05-07 12:00:02 +00:00
Brouie commented 2026-05-07 04:13:08 +00:00 (Migrated from gitlab.com)

closes #176.

what changed

new helper frontend/src/lib/writeContractWithGasBuffer.ts (148 lines) that pre-flights estimateContractGas, multiplies by configurable bufferNumerator/bufferDenominator (default 130/100 = +30%), optionally enforces a per-site softCapGas (throws GasSoftCapExceededError), then submits via writeContractAsync with the buffered gas units. integer bigint arithmetic only — no Number coercion. onEstimateRevert: "submit-without-override" | "rethrow" controls behavior when estimation itself reverts.

adopted at all 22 wagmi writeContractAsync sites in the frontend across 6 files. HIGH-risk sites use onEstimateRevert: "rethrow" + per-site softCapGas to fail fast on pathological calldata; MEDIUM/LOW sites use defaults.

sites adopted

  • frontend/src/pages/timecurve/TimeCurveProtocolWarbowRefreshSection.tsx — 1 site (C4 refreshWarbowPodium, HIGH, rethrow + 12M cap)
  • frontend/src/pages/timeCurveArena/useTimeCurveArenaModel.tsx — 13 sites (A1-A13: weth deposit, weth approve, usdm approve, kumbaya exactOutput HIGH+6M, CL8Y approve x2, buy, claimWarBowFlag, warbowSteal, warbowActivateGuard, warbowRevenge, refreshWarbowPodium HIGH+12M, runVoid w/ distributePrizes HIGH+18M)
  • frontend/src/lib/timeCurveKumbayaSingleTx.ts — 2 sites (C12 usdm approve, C13 buyViaKumbaya HIGH+8M)
  • frontend/src/pages/timecurve/useTimeCurveSaleSession.ts — 7 sites (C5-C11: weth deposit, weth approve, usdm approve, exactOutput HIGH+6M, acceptedAsset approve, buy, redeemCharms)
  • frontend/src/pages/referrals/ReferralRegisterSection.tsx — 2 sites (C2 CL8Y approve, C3 registerCode)
  • frontend/src/pages/PresaleVestingPage.tsx — 1 site (C1 claim, sync→async conversion)

structural changes

  • WalletWriteAsync type at timeCurveKumbayaSingleTx.ts:34 extended with gas?: bigint field. all callers compatible (verified at HEAD).
  • submitKumbayaSingleTxBuy gains a chainId: number parameter (threaded through to the helper). both callers (useTimeCurveSaleSession.ts:962, useTimeCurveArenaModel.tsx:2319) updated.
  • friendlyRevertFromUnknown at revertMessage.ts:115 gets a new instanceof GasSoftCapExceededError branch that surfaces a clear safety-cap message before falling through to viem BaseError handling.
  • PresaleVestingPage.tsx C1 converted from sync writeContract to async writeContractAsync so it can thread through the helper. claimError rendering still wired through the wagmi error path; helper-thrown errors surface via claimGateError.

risk-class tradeoffs (per #176 audit narrative)

  • HIGH-risk sites use onEstimateRevert: "rethrow" — surfaces estimation reverts immediately rather than falling through to wallet-side estimation. tradeoff: cleaner error UX vs slightly less retry-tolerant under transient state drift (e.g. competing wallet plant-flag interrupting our refresh by one block). recommendation: ship as-is, downgrade specific sites to default if telemetry shows excessive false-positive blocks.
  • soft caps are conservative ceilings, not targets. realistic estimates run 10-25% of the cap on each site.
  • MEDIUM/LOW sites use defaults (onEstimateRevert: "submit-without-override", no softCapGas) — strict superset of pre-patch behavior, no regression vs status quo.

verification

  • npm run typecheck clean.
  • npm test -- --run — 51 files / 307 passed (up from 50/295 — added 11 helper tests + 1 revertMessage case).
  • npm run lint — 0 errors. one pre-existing fast-refresh warning in IndexerConnectivityContext.tsx untouched.

known limitations

  • buy-flow callbacks in useTimeCurveSaleSession.ts and useTimeCurveArenaModel.tsx still have no dedicated unit tests — out of scope for this MR (separate @testing-library/react provider-mock harness needed; see #175 for the broader frontend coverage gap).
  • some wallets (notably MetaMask) may re-estimate gas internally despite the calldata gas field. the helper sets the dapp-preferred ceiling, not a hard wallet-side override.

cc @PlasticDigits

closes #176. ## what changed new helper `frontend/src/lib/writeContractWithGasBuffer.ts` (148 lines) that pre-flights `estimateContractGas`, multiplies by configurable `bufferNumerator/bufferDenominator` (default `130/100` = +30%), optionally enforces a per-site `softCapGas` (throws `GasSoftCapExceededError`), then submits via `writeContractAsync` with the buffered gas units. integer bigint arithmetic only — no Number coercion. `onEstimateRevert: "submit-without-override" | "rethrow"` controls behavior when estimation itself reverts. adopted at all 22 wagmi `writeContractAsync` sites in the frontend across 6 files. HIGH-risk sites use `onEstimateRevert: "rethrow"` + per-site `softCapGas` to fail fast on pathological calldata; MEDIUM/LOW sites use defaults. ## sites adopted - `frontend/src/pages/timecurve/TimeCurveProtocolWarbowRefreshSection.tsx` — 1 site (C4 refreshWarbowPodium, HIGH, rethrow + 12M cap) - `frontend/src/pages/timeCurveArena/useTimeCurveArenaModel.tsx` — 13 sites (A1-A13: weth deposit, weth approve, usdm approve, kumbaya exactOutput HIGH+6M, CL8Y approve x2, buy, claimWarBowFlag, warbowSteal, warbowActivateGuard, warbowRevenge, refreshWarbowPodium HIGH+12M, runVoid w/ distributePrizes HIGH+18M) - `frontend/src/lib/timeCurveKumbayaSingleTx.ts` — 2 sites (C12 usdm approve, C13 buyViaKumbaya HIGH+8M) - `frontend/src/pages/timecurve/useTimeCurveSaleSession.ts` — 7 sites (C5-C11: weth deposit, weth approve, usdm approve, exactOutput HIGH+6M, acceptedAsset approve, buy, redeemCharms) - `frontend/src/pages/referrals/ReferralRegisterSection.tsx` — 2 sites (C2 CL8Y approve, C3 registerCode) - `frontend/src/pages/PresaleVestingPage.tsx` — 1 site (C1 claim, sync→async conversion) ## structural changes - `WalletWriteAsync` type at `timeCurveKumbayaSingleTx.ts:34` extended with `gas?: bigint` field. all callers compatible (verified at HEAD). - `submitKumbayaSingleTxBuy` gains a `chainId: number` parameter (threaded through to the helper). both callers (`useTimeCurveSaleSession.ts:962`, `useTimeCurveArenaModel.tsx:2319`) updated. - `friendlyRevertFromUnknown` at `revertMessage.ts:115` gets a new `instanceof GasSoftCapExceededError` branch that surfaces a clear safety-cap message before falling through to viem `BaseError` handling. - `PresaleVestingPage.tsx` C1 converted from sync `writeContract` to async `writeContractAsync` so it can thread through the helper. `claimError` rendering still wired through the wagmi error path; helper-thrown errors surface via `claimGateError`. ## risk-class tradeoffs (per #176 audit narrative) - HIGH-risk sites use `onEstimateRevert: "rethrow"` — surfaces estimation reverts immediately rather than falling through to wallet-side estimation. tradeoff: cleaner error UX vs slightly less retry-tolerant under transient state drift (e.g. competing wallet plant-flag interrupting our refresh by one block). recommendation: ship as-is, downgrade specific sites to default if telemetry shows excessive false-positive blocks. - soft caps are conservative ceilings, not targets. realistic estimates run 10-25% of the cap on each site. - MEDIUM/LOW sites use defaults (`onEstimateRevert: "submit-without-override"`, no softCapGas) — strict superset of pre-patch behavior, no regression vs status quo. ## verification - `npm run typecheck` clean. - `npm test -- --run` — 51 files / 307 passed (up from 50/295 — added 11 helper tests + 1 revertMessage case). - `npm run lint` — 0 errors. one pre-existing fast-refresh warning in `IndexerConnectivityContext.tsx` untouched. ## known limitations - buy-flow callbacks in `useTimeCurveSaleSession.ts` and `useTimeCurveArenaModel.tsx` still have no dedicated unit tests — out of scope for this MR (separate `@testing-library/react` provider-mock harness needed; see #175 for the broader frontend coverage gap). - some wallets (notably MetaMask) may re-estimate gas internally despite the calldata `gas` field. the helper sets the dapp-preferred ceiling, not a hard wallet-side override. cc @PlasticDigits
PlasticDigits commented 2026-05-07 12:00:03 +00:00 (Migrated from gitlab.com)

mentioned in commit 8eb5f34cfb

mentioned in commit 8eb5f34cfb7a9be50909af79a753a64b39b5d03f
PlasticDigits (Migrated from gitlab.com) merged commit 8eb5f34cfb into main 2026-05-07 12:00:03 +00:00
Brouie commented 2026-05-07 23:33:32 +00:00 (Migrated from gitlab.com)

mentioned in issue #175

mentioned in issue #175
Sign in to join this conversation.
No reviewers
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!378
No description provided.