feat(launchpadv2): add indexing events to BondingV5 and FRouterV3 - #174
feat(launchpadv2): add indexing events to BondingV5 and FRouterV3#174psmiratisu wants to merge 12 commits into
Conversation
…MigrateExecuted) Additive, non-breaking on-chain events to support external indexing of bonding-curve activity. No function signatures or storage layout change. FRouterV3: - TradeExecuted after every buy/sell: token/trader/pair, isBuy, quote asset, amounts, fees (tax + anti-sniper), net trader quote, post-trade reserves, lastPrice. BondingV5: - TokenCreated creation snapshot: virtualId, creator, token, metadata, curve params (saleAmount, graduationThreshold, targetRaiseAmount, initialVirtualLiquidity, initialPrice), quoteAsset, pair, applicationId, launchParams, start time. - MigrateExecuted at graduation (caller, token, pair, agentToken, applicationId, assetAmount, tokenAmount). Verified: compiles; BondingV5 21.95 KiB < 24.576 KiB EIP-170 limit; storage layout unchanged. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
| taxFee: normalTxFee, | ||
| antiSniperFee: antiSniperTxFee | ||
| }) | ||
| ); |
There was a problem hiding this comment.
Launch buy mislabels indexed trader
Medium Severity
TradeExecuted sets the indexed trader to the router to address (token recipient). On launch(), the creator’s initial purchase calls _buy with address(this) as to, so the event attributes that buy to the bonding contract instead of the creator, while quote and token amounts still reflect a real trade.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 7c34f35. Configure here.
Remove the telegram and youtube fields from the TokenCreated event and its emit in preLaunch. These social URLs are already stored on-chain in tokenInfo and are not needed in the indexing log. twitter and website are retained. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UN11rp5HvZkdg7EePTrgrP
…teExecuted TradeExecuted: collapse the four amount fields into three direction-aware fields (amountIn/amountOut/amount), removing the per-direction duplicate (amountIn == traderQuoteAmount on buys, == tokenAmount on sells): - buy: amountIn = quote paid, amountOut = token received, amount = quote into curve after tax - sell: amountIn = token sold, amountOut = gross quote out, amount = net quote to trader after tax MigrateExecuted: removed. It largely duplicated the existing Graduated event (same token/agentToken, same transaction); the remaining fields are recoverable from Graduated, TokenCreated, or storage. Tests updated to the new field names and event set. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UN11rp5HvZkdg7EePTrgrP
Restore telegram and youtube to the TokenCreated event and its emit so preLaunch's creation snapshot is unchanged from the original proposal (twitter, telegram, youtube, website all retained). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01UN11rp5HvZkdg7EePTrgrP
| targetRaiseAmount, | ||
| liquidity * 2, | ||
| price, | ||
| initialPurchase, |
There was a problem hiding this comment.
Inconsistent price units across events
Medium Severity
TokenCreated sets initialPrice as bondingCurveSupply / liquidity (token-per-quote, unscaled), while TradeExecuted sets lastPrice as (reserveAsset * 1 ether) / reserveToken (quote-per-token, 1e18-scaled). A log-only indexer treating both as the same price series will show a discontinuous or inverted curve at the first trade.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit d8a8674. Configure here.
…vent Remove twitter, telegram, youtube and website from the TokenCreated event and its emit in preLaunch. The social URLs remain stored on-chain in tokenInfo; they are not needed in the indexing log. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| BondingConfig.LaunchParams launchParams, | ||
| uint256 startTime, | ||
| uint256 startTimeDelay | ||
| ); |
There was a problem hiding this comment.
TokenCreated omits key indexing fields
High Severity
TokenCreated is missing applicationId and targetRaiseAmount even though the emit comment describes the net quote target, applicationId is already available in preLaunch, and indexingEvents.js asserts both fields. Indexers still need RPC for those values, and the new test cannot pass against this ABI.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit a3511cc. Configure here.
| configInitialSupply * (10 ** IAgentTokenV4(token).decimals()), | ||
| bondingCurveSupply, | ||
| gradThreshold, | ||
| liquidity * 2, |
There was a problem hiding this comment.
Virtual liquidity value doubled
Medium Severity
TokenCreated.initialVirtualLiquidity emits liquidity * 2, but the curve’s fake initial virtual liquidity stored in tokenFakeInitialVirtualLiq is liquidity. Indexers treating this field as y0 for constant-product math will reconstruct the wrong curve.
Reviewed by Cursor Bugbot for commit a3511cc. Configure here.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 2 potential issues.
There are 6 total unresolved issues (including 4 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 477c4dd. Configure here.
| const upgraded = await upgrades.upgradeProxy(proxyAddress, contract, { | ||
| unsafeAllow: ["incorrect-initializer-order"], | ||
| redeployImplementation: "always", | ||
| }); |
There was a problem hiding this comment.
Unsafe forceImport bypasses layout checks
High Severity
forceImport is called with the new BondingV5 factory instead of the currently deployed implementation. That records the new storage layout against the live impl address, so upgradeProxy no longer validates against the real prior layout. The previously registered BondingV5 layout ends at tokenPreLaunchExtParams, while the imported layout adds later slots, so incompatible changes can slip through. redeployImplementation: "always" only forces a redeploy; it does not restore that check.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 477c4dd. Configure here.
| if (!/already imported|already registered/i.test(msg)) { | ||
| console.log("forceImport note:", msg); | ||
| } | ||
| } |
There was a problem hiding this comment.
Swallowed forceImport failures
Medium Severity
The forceImport catch path only rethrows nothing: non-already imported / already registered errors are logged as a note and execution continues into upgradeProxy. Real failures such as deployment clashes, kind mismatches, or partial manifest writes are ignored, so the upgrade can proceed against a wrong or incomplete OpenZeppelin manifest.
Reviewed by Cursor Bugbot for commit 477c4dd. Configure here.


Summary
Adds additive, non-breaking on-chain events so bonding-curve activity (trades,
token creation, graduation) can be indexed directly from logs without RPC
round-trips or transfer-log inference.
Changes
FRouterV3
TradeExecutedemitted after everybuy/sell:token,trader,pair,isBuy,quoteAsset,amountIn,tokenAmount,curveQuoteAmount,traderQuoteAmount,taxFee,antiSniperFee, post-trade reserves,lastPrice.BondingV5
TokenCreatedcreation snapshot:virtualId,creator,token, metadata(
name,symbol,description,image), curve params (saleAmount,graduationThreshold,targetRaiseAmount,initialVirtualLiquidity,initialPrice,initialPurchase),quoteAsset,pair,applicationId,launchParams, start time. Social URL fields (twitter, telegram, youtube,website) are intentionally excluded — they remain stored in
tokenInfo.MigrateExecutedat graduation:caller,token,pair,agentToken,applicationId,assetAmount,tokenAmount.Safety
Testing
test/launchpadv5/indexingEvents.jswalks one token throughpreLaunch → launch → buy → sell → graduation and asserts every event fires with
correct values, cross-checked against actual token/asset balance movements
(not just internal consistency):
TokenCreatedmetadata + curve params,TradeExecutedamounts/fees/reserves/lastPrice, anti-sniper fee decay, andGraduated+MigrateExecuted. 6/6 passing.Follow-ups (not in this PR)
🤖 Generated with Claude Code
Note
Medium Risk
Upgradeable BondingV5 storage layout is extended (append-only) and buy/sell paths gain new event emissions; fund-flow logic is unchanged, but upgrade correctness and gas on the trading path need review.
Overview
Extends
BondingV5preLaunch with v3extParamsdecoding and adds on-chain indexing events for token creation and trades.BondingV5 appends upgrade-safe storage for
isRobotics,feeDelegationType, andfeeDelegationRecipient, decoded from an append-only, backward-compatibleextParamsbitfield (plus optional recipient word).preLaunchnow stores these fields and emitsPreLaunchExtParamsplus a richerTokenCreatedsnapshot.FRouterV3 emits
TradeExecutedafter every buy/sell with amounts, tax/anti-sniper fees, post-trade reserves, and last price.Also adds the
upgradeBondingV5script, OZ testnet manifest updates, and focused tests for extParams decoding and the new indexing events.Reviewed by Cursor Bugbot for commit 477c4dd. Bugbot is set up for automated code reviews on this repo. Configure here.