Skip to content

feat(launchpadv2): add indexing events to BondingV5 and FRouterV3 - #174

Open
psmiratisu wants to merge 12 commits into
mainfrom
feat/add-indexing-events
Open

feat(launchpadv2): add indexing events to BondingV5 and FRouterV3#174
psmiratisu wants to merge 12 commits into
mainfrom
feat/add-indexing-events

Conversation

@psmiratisu

@psmiratisu psmiratisu commented Jul 3, 2026

Copy link
Copy Markdown

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

  • TradeExecuted emitted after every buy/sell: token, trader, pair,
    isBuy, quoteAsset, amountIn, tokenAmount, curveQuoteAmount,
    traderQuoteAmount, taxFee, antiSniperFee, post-trade reserves, lastPrice.

BondingV5

  • TokenCreated creation 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.
  • MigrateExecuted at graduation: caller, token, pair, agentToken,
    applicationId, assetAmount, tokenAmount.

Safety

  • Purely additive — only new events and their emits; no existing lines removed or modified.
  • No function signature changes → non-breaking; no caller updates required.
  • No new storage variables → storage layout unchanged (upgrade-safe).
  • Events are inert: no effect on control flow or fund movement.
  • BondingV5 21.95 KiB < 24.576 KiB (EIP-170) deployed-size limit.

Testing

test/launchpadv5/indexingEvents.js walks one token through
preLaunch → launch → buy → sell → graduation and asserts every event fires with
correct values, cross-checked against actual token/asset balance movements
(not just internal consistency): TokenCreated metadata + curve params,
TradeExecuted amounts/fees/reserves/lastPrice, anti-sniper fee decay, and
Graduated + MigrateExecuted. 6/6 passing.

Follow-ups (not in this PR)

  • OpenZeppelin upgrade storage-layout validation against the deployed implementation
  • Audit review
  • Testnet deployment

🤖 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 BondingV5 preLaunch with v3 extParams decoding and adds on-chain indexing events for token creation and trades.

BondingV5 appends upgrade-safe storage for isRobotics, feeDelegationType, and feeDelegationRecipient, decoded from an append-only, backward-compatible extParams bitfield (plus optional recipient word). preLaunch now stores these fields and emits PreLaunchExtParams plus a richer TokenCreated snapshot.

FRouterV3 emits TradeExecuted after every buy/sell with amounts, tax/anti-sniper fees, post-trade reserves, and last price.

Also adds the upgradeBondingV5 script, 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.

…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
})
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 7c34f35. Configure here.

claude added 3 commits July 6, 2026 03:01
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,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit d8a8674. Configure here.

psmiratisu and others added 7 commits July 6, 2026 21:01
…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
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a3511cc. Configure here.

configInitialSupply * (10 ** IAgentTokenV4(token).decimals()),
bondingCurveSupply,
gradThreshold,
liquidity * 2,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit a3511cc. Configure here.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Fix All in Cursor

❌ 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",
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 477c4dd. Configure here.

if (!/already imported|already registered/i.test(msg)) {
console.log("forceImport note:", msg);
}
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 477c4dd. Configure here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants