[#242] P5-9a/b: Zap frontend integration#431
Conversation
P5-9a: Create lib/zap.ts with ZapPlotLink contract wrappers: - zapPlotLinkAbi: mint, mintReverse, estimateMintCostInPlot, estimateMintReverseFromPlot - getZapQuote(): quote estimation with 0.5% swap slippage buffer - buildZapMintTx(): wagmi-compatible tx builder for payable mints P5-9b: Add ETH/PL_TEST input selector to TradingWidget buy tab: - Toggle between ETH (via ZapPlotLink) and PLOT (direct MCV2_Bond) - Default: ETH when Zap is available - ETH mode: shows ETH balance, ETH cost estimate, executes payable tx - PLOT mode: keeps existing approve + mint flow unchanged - Sell tab: unchanged (burn → PL_TEST) - Trade indexing (/api/index/trade) fires for both modes - Zap selector hidden when ZAP_PLOTLINK is zero address Also fixes RESERVE_LABEL mainnet value: "PL_TEST" → "PLOT" Fixes #242 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
project7-interns
left a comment
There was a problem hiding this comment.
Verdict: REQUEST CHANGES
Summary
The ETH buy path is wired up in the widget, but the quote math is still not using the Uniswap V4 swap price and the exact-input zap helper sends minTokensOut = 0. That makes the new zap flow unsafe and produces misleading ETH estimates.
Findings
- [high]
getZapQuote()still treats PL_TEST and ETH as if they were interchangeable units instead of actually quoting the WETH -> PL_TEST swap. In exact-output mode it setsethCost = applySwapSlippage(plotRequired), and in exact-input mode it derivesplotEstimate = removeSwapSlippage(amount). Both paths are just 1:1 unit transforms on raw wei/PL_TEST amounts, so the displayed ETH cost will be badly wrong for the real pool price. This also misses the explicit scope requirement to estimate the Uniswap swap leg.- File:
lib/zap.ts:74 - Suggestion: call the V4 quoter (or another real pool-pricing path) to convert between ETH and PL_TEST, then apply the 0.5% buffer to that quote.
- File:
- [medium]
buildZapMintTx()hardcodesminTokensOut = 0formintReverse(), which removes all user-side slippage protection on the exact-input zap path. If this helper gets used, users can receive arbitrarily fewer storyline tokens than quoted.- File:
lib/zap.ts:130 - Suggestion: compute
minTokensOutfrom the quote result with an explicit slippage haircut and pass that intomintReverse().
- File:
Decision
Requesting changes because the new ETH quote / execution helpers are not safe to ship in their current form.
project7-interns
left a comment
There was a problem hiding this comment.
T2b Review — REQUEST CHANGES
BLOCK (2 issues)
1. ETH quote is a no-op — never queries actual swap pricing
lib/zap.ts:80-82, 87-88 — getZapQuote calls estimateMintCostInPlot (returns PLOT amount) then just applies 0.5% slippage to that PLOT value and returns it as ethCost. This assumes 1 PLOT = 1 ETH. The quoterAbi and UNISWAP_V4_QUOTER are defined but never called. Users will see wildly incorrect cost estimates (off by orders of magnitude given real pool ratio of ~1 ETH = 20,000 PL_TEST).
Must actually query the Uniswap V4 Quoter to convert PLOT cost → ETH cost, or document clearly that the frontend shows PLOT-denominated estimates and the wallet handles ETH calculation at tx time.
2. minTokensOut hardcoded to 0 in exact-input mode — zero slippage protection
lib/zap.ts:123 — buildZapMintTx passes BigInt(0) as minTokensOut for mintReverse. The contract does NOT protect against swap-leg slippage, only bonding curve failures. Users are fully exposed to sandwich attacks. Calculate a reasonable minTokensOut from the quote with slippage applied.
WARN
UNISWAP_V4_QUOTER,quoterAbi,PLOT_TOKENimported/defined but unused — dead code (lib/zap.ts:12, 45-48)- Gas limit hardcoded to 3,000,000 (
lib/zap.ts:117, 126) — let wallet estimate or useestimateGas - Trade indexing POST may record wrong cost unit for ETH mode (PLOT vs ETH) — verify the
/api/index/tradepayload shape
NIT
WETHandPOOL_FEEconstants unused — dead code artifactsisZapAvailableuses string comparison instead of viem'szeroAddressconstant — fragile- Removed helpful slippage math comments in TradingWidget
Verified correct
- ZAP_PLOTLINK uses redeployed address
0xC7C4...5682✓ - RESERVE_LABEL mainnet fix
"PL_TEST"→"PLOT"✓ - Sell tab unchanged ✓
- UI matches existing design tokens ✓
- Scope clean — only 3 files changed ✓
Addresses T2a review feedback: - Replace 1:1 ETH/PLOT assumption with real V4 Quoter calls: - quoteExactOutputSingle for exact-output (how much ETH for N PLOT?) - quoteExactInputSingle for exact-input (how much PLOT for N ETH?) - Add minTokensOut parameter to buildZapMintTx with 3% slippage protection for exact-input mode (was hardcoded to 0) - Pool key computed from sorted WETH/PLOT_TOKEN addresses Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
project7-interns
left a comment
There was a problem hiding this comment.
Follow-up review after the latest push:
Findings
- [high] The PR still does not satisfy the requested frontend helper contract from issue #242. The scope called for
executeZapMint(...), butlib/zap.tsonly exportsbuildZapMintTx(...). That leaves the required wrapper API incomplete.- File:
lib/zap.ts:198 - Suggestion: either implement
executeZapMint(...)as requested, or update the issue/PR scope explicitly if the API contract is intentionally changing.
- File:
- [high] CI is still red.
lint-and-typecheckfails onlib/zap.tsbecause the quoter argument construction usesas any, which violates the repo lint rules, and there are additional unused imports/warnings.- File:
lib/zap.ts:92 - Suggestion: remove the
anycasts by typing the quoter params properly and clear the unused imports / unusedZapModewarning so the PR goes green.
- File:
I am not approving while the build is failing.
project7-interns
left a comment
There was a problem hiding this comment.
T2b Re-Review — APPROVED
Both previous BLOCKs verified as resolved:
- ✅
getZapQuotenow calls V4 Quoter (quoteExactInputSingle/quoteExactOutputSingle) for real ETH↔PLOT pricing - ✅
minTokensOutnow accepts real value with 3% slippage protection
Previously unused imports (UNISWAP_V4_QUOTER, PLOT_TOKEN, quoterAbi) are now live.
Remaining non-blocking notes
- Silent quoter failure: Both quote functions catch errors and return
BigInt(0)— could let users see "Est. cost: 0 ETH" and submit a 0-value tx that reverts. Consider disabling the button on zero quote. UNISWAP_V4_POOL_MANAGERstill imported but unused inlib/zap.ts— minor cleanup.- Hardcoded gas limit (3M) remains — acceptable for testnet, wallet refunds unused gas.
- Exact-input mode not wired in widget — widget only uses exact-output, which is fine for current scope.
No blockers. Clean to merge.
- Remove unused imports (encodeAbiParameters, decodeFunctionResult, etc.) - Replace `as any` casts with properly typed ABI object for V4 Quoter - Add executeZapMint() helper as requested in issue spec - Remove unused ZapMode type import in TradingWidget Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
project7-interns
left a comment
There was a problem hiding this comment.
Verdict: APPROVE
Summary
The remaining blockers are resolved. lib/zap.ts now includes the requested executeZapMint() helper, the quoter path is properly typed without the prior lint violations, and lint-and-typecheck is passing.
Findings
- No blocking findings.
Decision
Approving because the requested frontend helper API is now complete and the build is green.
Summary
lib/zap.tswith ZapPlotLink contract wrappers (getZapQuote,buildZapMintTx, ABI subset)TradingWidget.tsxbuy tab. Default: ETH. Sell tab unchanged.RESERVE_LABELmainnet value from"PL_TEST"to"PLOT"Details
mint()— no token approval neededapprove → MCV2_Bond.mintflowZAP_PLOTLINKis zero address (mainnet)/api/index/trade) fires for both ETH and PLOT tradesTest plan
Fixes realproject7/agent-os#242
🤖 Generated with Claude Code