feat(launchpadv2): add indexing events to BondingV5 and FRouterV3 - #174
feat(launchpadv2): add indexing events to BondingV5 and FRouterV3#174psmiratisu wants to merge 10 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>
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.
| 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.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using default effort and found 1 potential issue.
There are 6 total unresolved issues (including 5 from previous reviews).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want higher recall? High effort reviews run extra passes and find more bugs. A team admin can switch effort levels in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit ebb05a1. Configure here.
| (uint256 remainingForSale, uint256 totalRaised) = pair.getReserves(); | ||
| uint256 lastPrice = remainingForSale == 0 | ||
| ? 0 | ||
| : ((totalRaised * 1 ether) / remainingForSale); // last price multiplied by 10 * 10^18 |
There was a problem hiding this comment.
Inconsistent trade price units
Medium Severity
TokenCreated.initialPrice uses tokens-per-quote (bondingCurveSupply / liquidity) while TradeExecuted.lastPrice uses quote-per-token scaled by 1 ether. Indexers comparing creation price to later trade prices will compute inverted, differently scaled values and produce incorrect charts or alerts.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit ebb05a1. 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
Changes sit on the bonding router buy/sell path and require a BondingV5 proxy upgrade; behavior is additive (events only) but FRouterV3 must be upgraded separately for trade logs.
Overview
Adds on-chain indexing events so launchpad activity can be reconstructed from logs without inferring trades from ERC-20 transfers.
BondingV5 defines
TokenCreatedand emits it at the end ofpreLaunchwith curve metadata (supply, graduation threshold, virtual liquidity, price, quote asset, pair, launch params, schedule).FRouterV3 defines
TradeExecutedand emits it after everybuyandsellvia_emitTradeExecuted, including tax/anti-sniper fees, post-trade reserves, and a computedlastPrice.Also adds a Hardhat upgrade script for the BondingV5 transparent proxy, updates the BSC testnet OpenZeppelin manifest for the new implementation layout, and
test/launchpadv5/indexingEvents.jsto exercise preLaunch → launch → buy/sell → graduation against the new logs.Reviewed by Cursor Bugbot for commit ebb05a1. Bugbot is set up for automated code reviews on this repo. Configure here.