04 · docs/PONS-RESEARCH.mdOpenPons manual

Pons V2 research — Robinhood Chain

This is the research OpenPons is built on. Every write flow OpenPons exposes (launch, buy, sell, claim, sweep) was reproduced with read-only eth_call simulations against live state before implementation. No transaction was sent during research. Implementation: src/lib/pons/ (addresses, ABIs, curve math, reads, prepare).

Re-verified during implementation (2026-09-27): buy quote from the curve formula equals eth_call buy() output; sell with exact-approval state override simulates OK; launchToken and launchAndBuy simulate OK and return the predicted token address; escrow.claim() simulated from a real creditee returned exactly balanceOf; graduated curves are refused. Measured RPC behaviour: ~0.1 s blocks, ~590 launches per 100k blocks, ≤10,000 logs per eth_getLogs, HTTP 429 under bursts, occasional "log query timed out" for unfiltered Transfer scans (OpenPons halves the range adaptively).

  • Date of live checks: 2026-09-27, around head block 73,873,763
  • Network: Robinhood Chain mainnet. eth_chainId returned 0x1237 (4663), checked live.
  • RPC: https://rpc.mainnet.chain.robinhood.com
  • Explorer: https://robinhoodchain.blockscout.com

Evidence labels

Label Meaning
VERIFIED-LIVE Checked today (2026-09-27) with JSON-RPC (eth_getCode, eth_call including read-only simulations with state overrides, eth_getLogs) or with the Sourcify verification API for chain 4663
FROM-LOCAL-RESEARCH Taken from ~/pons-launcher, ~/ponscexx or ~/pons-burn-tool, or from an earlier local clone of github.com/ponsdotdev/pons-labs@162310fb. Not re-checked today unless it says so.
UNKNOWN Not established

No transaction was signed or broadcast. The only calls made were eth_call, eth_getLogs, eth_getCode, eth_getStorageAt and eth_getTransaction.

Source-of-truth note: Blockscout was blocked, so Sourcify was used

  • Blockscout API: UNKNOWN (blocked). Every call to https://robinhoodchain.blockscout.com/api/v2/... and /api?module=contract... returned HTTP 403 with a Cloudflare managed challenge (cf-mitigated: challenge) today. That includes a real browser tab, which stayed on the challenge page. I did not try to get around the bot check. The ponscexx research saw the same thing (PONS_V2_ANALYSIS.md section 10). So Blockscout verification status could not be checked.
  • Sourcify: VERIFIED-LIVE. https://sourcify.dev/server/v2/contract/4663/<addr> returns verified source and ABI for most Pons V2 contracts. The match status per contract is in the table below. I compiled the curve, token and guard ABIs from the Sourcify-verified std-json sources, because the deployer and router verifications embed those sources.
  • Selector cross-check: VERIFIED-LIVE. For every ABI saved to ~/openpons-research-abis/, every function selector was found as a PUSH4 in the live runtime bytecode of the matching deployed contract. Every event topic0 was also found in that bytecode.
  • Published GitHub source vs deployment:
    • The pons-labs contractsV2 factory source is identical to the Sourcify-verified factory, apart from a trailing newline (VERIFIED-LIVE diff).
    • The published PonsV2BondingCurve.sol and PonsV2LaunchDeployer.sol are stale. The deployed versions add snipe-tax logic, a salt field and CREATE2. That matches ponscexx PONS_V2_ANALYSIS.md section 0, and the diff against the verified source was confirmed today.

1. Contract map

Code sizes and codehashes were read live today. The factory and router codehashes equal the values pinned in pons-launcher/src/pons.js and VERIFICATION.md, so the contracts have not changed since 2026-09-11.

Role Address Code (bytes) keccak256(runtime) Sourcify (4663) Name in verified source
V2 Launch Factory (entry point, registry, graduation) 0x7eD598BcEf8bd9Edd8C97A195C6d13f40801EC7e 24,177 0x89a27da6…a67d84 exact_match (2026-08-04) contracts/src/v2/PonsV2LaunchFactory.sol:PonsV2LaunchFactory, solc 0.8.35
Launch-and-buy router (the factory's launchForwarder) 0xe33E9E479dF8802cb0866d5d05258bEc4cF62948 4,416 0xed906518…0e52e7 exact_match (2026-08-04) PonsV2LaunchAndBuy
Launch deployer (CREATE2 for curve and token) 0x3711ceA4feaDE896C913C68F01Eda97Cb06D1A42 20,906 0xeade2256…8e624c exact_match PonsV2LaunchDeployer
Fee escrow (creator and protocol claims) 0xd3AFEB2a57f70eF218Aa82451c51B2fb0416Ac9e 1,932 0xf25f75cf…fed2f1 match PonsV2FeeEscrow
Meme hook (Uniswap V4 hook and fee policy) 0xE5e702641Ea86F4ae6cC3cDaeD2B886f976Be044 15,167 0xc21b1e6c…6a10db match PonsV2MemeHook
Buyback vault (5-year vest) 0x42df2a798f82289E177311362e8f5ccC45c1219c 4,602 0x5de84808…aece03 match PonsV2BuybackVault
LP / position locker 0x267444D099b10fB5Ed7c3Cc7B7c767AdcA574952 1,969 0x58455f80…742025 match Verified under the name RobinFunFiV2LaunchLocker (src/v2/RobinFunFiV2LaunchLocker.sol, solc 0.8.26)
Graduation executor 0xC7819B64A1dAECD7eC19856d026cb14EfBd89046 4,402 0xf59f4307…046e2a match PonsV2GraduationExecutor
Graduation guard 0xf5695117b99B6f6401e67d4195BD653628176C6C 2,896 0x6847e5d5…16ecea not verified (404) ABI compiled from the verified router bundle; both selectors present live
Per-launch BondingCurve (one per token) e.g. 0xE0b476A92adc907c6E160AD52AE14d0080A3D467 10,229 n/a not individually verified Source is PonsV2BondingCurve.sol inside the verified deployer. The deployer is exact_match and CREATE2-deploys this creation code. 46/46 selectors present.
Per-launch LauncherToken e.g. 0x5746c7FF9250E785233f3ce0EFeD5B17776ec1c4 3,248 n/a not individually verified PonsV2LauncherToken.sol inside the verified deployer. 18/18 selectors present.
V1 factory (legacy) 0xA5aAb3F0c6EeadF30Ef1D3Eb997108E976351feB 24,353 0x0a62b8ed…6391d4 exact_match PonsLaunchFactory. 0 logs in the last 50,000 blocks (inactive).
Uniswap V4 PoolManager 0x8366a39CC670B4001A1121B8F6A443A643e40951 24,009 from factory.poolManager()
Uniswap V4 PositionManager 0x58daec3116aae6D93017bAAea7749052E8a04fA7 23,877 from factory.positionManager()
Unidentified "trading router" 0x65af70b69a36e6e6ab6263ac1fe6d378c9d0740d 5,429 0x8ebc161b…a4d3aa not verified UNKNOWN / not Pons. Its selectors resolve to unrelated names (TOR(), eigentuemer(), stand(address), hook()). Treat it as a third-party bot router.

Factory wiring (VERIFIED-LIVE eth_call on the factory)

Read Value
launchForwarder() 0xe33E…2948
router.factory() 0x7eD5…EC7e (round-trip matches)
launchDeployer() 0x3711…1A42
feeEscrow() 0xd3AF…Ac9e
memeHook() 0xE5e7…e044
locker() 0x2674…4952
buybackVault() 0x42df…219c
graduationGuard() 0xf569…6C6C
graduationExecutor() 0xC781…9046
owner() 0x263ed295dAFaE1d9AAdD6E56c4B6F9f38eE019Dd. ponscexx records this as a Gnosis Safe 2/3 (FROM-LOCAL-RESEARCH).
hook.feeSweepOperator() 0xa1018c1D9655292A2dE0F7dEa9a0F848EaA8cA83 (changed). The ponscexx research from 2026-09-22 recorded 0x49Bb…73d2.
hook.protocolFeeRecipient() 0x263e…19Dd
escrow ETH balance 1,467.10 ETH today

2. Live factory parameters (VERIFIED-LIVE)

launchEnabled()                   true
launchFee()                       500000000000000  (0.0005 ETH, always native, even for ERC-20-quoted launches)
maxCreatorTaxBps()                1000   (10%)
launchConfigCount()               1
canLaunch(0x…dEaD)                true   (= launchEnabled || whitelistedLaunchers[x]; open to everyone)
snipeTaxStartBps()                9900
snipeTaxSeconds()                 3
CREATOR_FEE_RECIPIENT_TIMELOCK()  259200 (3 days)
GRADUATION_RESCUE_DELAY()         604800 (7 days)
previewLaunchEconomics(0, 0x0)    0xa9fc75d4203a33fe660e8fa32c74c3aa41c1fda4bf23d3a39b6bc22a1f8b1ca7
getLaunchConfig(0):
  supply              1e27 (1,000,000,000 * 1e18)
  curveFeeBps         100  (1%)
  phantomQuote        1.68e18 (1.68 ETH, virtual)
  graduationThreshold 4.2e18  (4.2 ETH)
  poolFee             0
  tickSpacing         200
  enabled             true
Fee policy (hook, and getLaunchFeePolicy(token)):
  protocolFeeShareBps 3000, buybackBurnBps 5000, hookFeeBps 100, maxInternalPriceImpactBps 300
Approved ERC-20 quote examples:
  approvedPairTokens(USDG 0x5fc5…d168) = true, pairTokenEconomics = (3236e6, 8090e6, 6)
  approvedPairTokens(NVDA 0xd060…9EEC) = true, pairTokenEconomics = (16.64e18, 41.6e18, 18)

3. Launch flow

3.1 Structs (VERIFIED-LIVE: Sourcify ABI and selector present in bytecode)

struct Socials { string twitter; string telegram; string discord; string website; string farcaster; }
struct TokenParams {
  string  name;               // 1..64 bytes (deployer MAX_NAME_LENGTH)
  string  symbol;             // 1..16 bytes
  string  logo;               // <=512 bytes; an ipfs:// URI in practice
  string  description;        // <=2048 bytes
  Socials socials;            // each <=256 bytes
  address creatorFeeRecipient;// 0x0 => defaults to originalDeployer (factory); router REQUIRES non-zero
  uint16  creatorTaxBps;      // <= maxCreatorTaxBps (1000)
  bool    buybackEnabled;
  bytes32 expectedEconomics;  // 0 waives the check; else must equal previewLaunchEconomics(cfg, pairToken)
  bytes32 salt;               // CREATE2 salt, namespaced: keccak256(abi.encode(originalDeployer, salt))
}

The metadata length limits come from the verified PonsV2LaunchDeployer.sol (VERIFIED-LIVE source). The factory also reverts InvalidTokenParams() when name or symbol is empty.

3.2 Launch without a dev buy: call the factory directly (VERIFIED-LIVE simulation)

// selector 0xa72101af  (used by the current official app, per pons-launcher VERIFICATION.md)
function launchToken(TokenParams params, uint256 launchConfigId, address pairToken, address[] snipeTaxExemptions)
    payable returns (address token, address curve)
// selector 0xf35abbcf  (3-arg overload, also live)
function launchToken(TokenParams params, uint256 launchConfigId, address pairToken)
    payable returns (address token, address curve)
  • Value: msg.value == launchFee() exactly. Otherwise it reverts LaunchFeeNotPaid() (verified source; simulating launchFee-1 reverted).
  • Arguments:
    • launchConfigId = 0.
    • pairToken = 0x0 for native ETH, or an approved ERC-20.
    • snipeTaxExemptions: at most 32 entries (MAX_SNIPE_TAX_EXEMPTIONS).
  • Automatic snipe-tax exemptions: the factory also exempts originalDeployer and creatorFeeRecipient.
  • Simulation today: an eth_call from a synthetic account (balance set by state override) with value = 0.0005 ETH returned (token 0xB5d5…2991, curve 0xF791…2F91). So a third party can simulate a launch and pre-compute its addresses.
  • Stale expectedEconomics: simulating with a wrong value reverted (LaunchEconomicsMismatch, per source).
  • Launch fee: paid to hook.protocolFeeRecipient at the end of _launchToken (verified source).

3.3 Launch with a dev buy: the router (VERIFIED-LIVE simulation and verified source)

// PonsV2LaunchAndBuy @ 0xe33E…2948, selector 0xf85f8e41
function launchAndBuy(TokenParams params, uint256 launchConfigId, address pairToken,
                      uint256 quoteIn, uint256 minTokensOut, address recipient, address[] snipeTaxExemptions)
    payable returns (address token, address curve, uint256 tokensOut)
  • Value:
    • Native quote: msg.value == launchFee + quoteIn.
    • ERC-20 quote: msg.value == launchFee, and the router pulls quoteIn with transferFrom, so approve pairToken to the router first.
    • A wrong value reverts NativeValueMismatch.
  • Required checks:
    • params.creatorFeeRecipient != 0 and recipient != 0, otherwise ZeroAddress.
    • quoteIn != 0.
    • factory.canLaunch(msg.sender) must be true.
  • What it does:
    • Calls factory.launchTokenFor(..., originalDeployer = msg.sender, exemptions + [recipient]).
    • Then calls curve.buy(quoteIn, minTokensOut, recipient).
    • Refunds any unspent quote to msg.sender.
    • Emits Launched.
  • Simulation today: eth_call with value = launchFee + 0.01 ETH returned the same token and curve addresses as the direct launchToken simulation (same sender and salt), plus 5,799,502.8997 tokens. That confirms the CREATE2 namespace is originalDeployer, not msg.sender.
  • Real mainnet examples (FROM-LOCAL-RESEARCH, pons-launcher VERIFICATION.md):
    • Router launch: REEL tx 0x15355d67…1828887, selector 0xf85f8e41, value = 0.0005 + devBuy.
    • Direct factory launch: tx 0x5ba5b13a…7fe0f, selector 0xa72101af, value = 0.0005.
  • Slippage: the official UI uses 200 bps slippage on the dev buy (FROM-LOCAL-RESEARCH). pons-launcher computes minTokensOut = simulatedTokensOut * (10000 - slippageBps) / 10000.

3.4 Address prediction (VERIFIED-LIVE ABI)

  • PonsV2LaunchDeployer.predictLaunchAddresses(LaunchDeployment) (selector 0x2bc63e61) is a view.
  • The simpler path is an eth_call of launchToken or launchAndBuy, which returns (token, curve).
  • Reusing a salt on otherwise identical terms reverts.

3.5 What the frontend has to produce

  • Metadata goes directly into calldata and is stored on the token: logo(), description(), socials(), getTokenInfo(). No off-chain metadata JSON is needed (VERIFIED-LIVE: token ABI; live tokens return an ipfs://… logo and socials).
  • Logo:
    • Live tokens use ipfs://Qm… or ipfs://bafkrei… URIs (VERIFIED-LIVE reads).
    • The only on-chain limit is 512 bytes.
    • pons-launcher accepts PNG, JPEG and WebP up to 5 MB and says this is "same ceiling pons uses" (FROM-LOCAL-RESEARCH, pons-launcher/src/ipfs.mjs). The file-type and size limits of the official UI are not verified on-chain.
  • Image upload:
    • pons-launcher states that pons' own image endpoint is "origin-locked to the pons web app and rejects other callers" (FROM-LOCAL-RESEARCH, src/ipfs.mjs and README.md).
    • The endpoint URL is not recorded anywhere locally (UNKNOWN).
    • A third-party frontend must pin to IPFS itself. pons-launcher uses Pinata https://uploads.pinata.cloud/v3/files or a local IPFS daemon.
  • Website field: it is written on-chain. The official pons token page currently does not render socials.website, while it does render twitter, telegram and discord (FROM-LOCAL-RESEARCH, VERIFICATION.md section 8).

4. Bonding curve mechanics

Formulas come from the verified curve source (VERIFIED-LIVE via Sourcify). They were checked numerically today with eth_call: the formula output matched the contract to the wei for both buy and sell. The test curve was 0xE0b4…D467 (token CENTER 0x5746…c1c4, creatorTaxBps 200).

4.1 Reserves

Q (quote reserve) = phantomQuote + trackedQuote - quoteFeeBalance - creatorTaxBalance     // getReserves().quoteReserve_ / quoteReserve()
X (token reserve) = trackedTokens                                                          // getReserves().tokenReserve_ / tokenReserve()
realQuoteReserve  = trackedQuote - quoteFeeBalance - creatorTaxBalance
reservedTokens    = floor(supply * phantomQuote / (phantomQuote + graduationThreshold))   // 285,714,285.714... for config 0
sellableTokens()  = X - reservedTokens

Reserves are tracked variables, not live balances, so a donation cannot move the price.

4.2 Buy with exact input s (quote)

snipeBps = currentSnipeTaxBps(recipient)          // capped at 10000 - feeBps - creatorTaxBps - 100
fee      = floor(s*feeBps/1e4) ; tax = floor(s*creatorTaxBps/1e4) ; snipe = floor(s*snipeBps/1e4)
tokensOut= floor((s-fee-tax-snipe) * X / (Q + (s-fee-tax-snipe)))
  • Clamp: if tokensOut > sellable, it is clamped to sellable. The spend is grossed up from the token side and the excess is refunded (event CurveBuyRefunded).
  • Slippage: it reverts SlippageExceeded when spent*minTokensOut > received*tokensOut. Without a clamp, that is the same as tokensOut >= minTokensOut.
  • Snipe tax: it goes into the base-fee bucket and is emitted as SnipeTaxCharged.
  • Auto-graduation: after a buy, readyToGraduate() triggers factory.graduate(token) inside a try/catch.
  • Live check: 0.01 ETH gave formula 4,464,018.292214393290418670, which is exactly the result of eth_call buy().

4.3 Sell with exact input tokensIn

gross = floor(tokensIn * Q / (X + tokensIn)); fee = floor(gross*feeBps/1e4); tax = floor(gross*creatorTaxBps/1e4)
quoteOut = gross - fee - tax     // revert SlippageExceeded if < minQuoteOut
  • Selling is closed once graduated || readyToGraduate() (it reverts CurveGraduated).
  • Live check: selling 1,000,000 tokens gave formula 0.002094692482295018 ETH, which is exactly the result of eth_call sell(). This used a state override: token _balances at slot 0 (confirmed), _allowances at slot 1.

4.4 Snipe tax (verified source; live values)

currentSnipeTaxBps(r) = snipeTaxExempt[r] ? 0 :
                        (elapsed = now - launchedAt) >= snipeTaxSeconds ? 0 :
                        snipeTaxStartBps >> (elapsed*14 / snipeTaxSeconds)
  • The curve snapshots snipeTaxStartBps (9900) and snipeTaxSeconds (3) at initialize.
  • The test curve returned launchedAt = 1790505793, currentSnipeTaxBps(random) = 0, snipeTaxExempt(deployer) = true and snipeTaxExempt(random) = false (VERIFIED-LIVE).

4.5 Curve shape for config 0 (FROM-LOCAL-RESEARCH, ponscexx; consistent with the live parameters)

  • Opening spot price is 1.68e-9 ETH/token and terminal spot price is 2.058e-8 ETH/token, a 12.25x rise.
  • 5/7 of supply is sellable on the curve.
  • Every approved quote asset keeps phantomQuote/graduationThreshold = 0.4.

5. Buy and sell: exact calls

Buy directly on the curve (VERIFIED-LIVE simulation; real traffic uses it)

// PonsV2BondingCurve (per-launch address), selector 0x59a87bc1
function buy(uint256 quoteIn, uint256 minTokensOut, address recipient) payable returns (uint256 tokensOut)
  • Native quote: msg.value == quoteIn exactly. A mismatch reverts NativeValueMismatch(9999999999999999,10000000000000000), reproduced live.
  • ERC-20 quote: msg.value == 0 (otherwise UnexpectedNativeValue), and you need approve(curve, quoteIn) on the pair token.
  • Approvals for native buys: none.
  • Live traffic: a sample of recent CurveBuy txs includes calls straight to the curve with selector 0x59a87bc1. Others arrive through various third-party routers and aggregators, for example 0x65050a9b… with swap(...) selector 0x4d819a2a, and the Pons router launchAndBuy.
  • Official UI buy path: UNKNOWN. Which contract the official Pons UI calls for ordinary buys was not established.

Sell directly on the curve (VERIFIED-LIVE simulation)

// selector 0xd04c6983
function sell(uint256 tokensIn, uint256 minQuoteOut, address recipient) returns (uint256 quoteOut)
  • Approval required: token.approve(curve, tokensIn), because the curve calls safeTransferFrom(msg.sender, curve, tokensIn).
  • Without an allowance, the live eth_call reverted ERC20InsufficientAllowance(curve, 0, 1000e18).
  • Quote paid out: native ETH is sent to recipient, or the ERC-20 pair token is transferred.
  • Live traffic includes direct curve sells with selector 0xd04c6983.

Quote functions

  • There is no getAmountOut/quoteBuy/quoteSell view on the curve, factory or router (VERIFIED-LIVE: none in the verified ABIs, and the dispatcher selectors match the ABI).
  • Option 1: compute off-chain with the formulas in section 4 from these views:
    • getReserves(), feeBps(), creatorTaxBps(), currentSnipeTaxBps(recipient), sellableTokens(), reservedTokens()
    • graduated(), readyToGraduate(), pairToken()
    • Verified wei-exact against the contract today.
  • Option 2: eth_call buy or sell directly.
    • For buys, pass value = quoteIn from any account with enough balance, or use a balance state override.
    • For sells, the caller needs balance and allowance, or use a storage override (_balances slot 0, _allowances slot 1).
    • Both return the exact output.

6. Creator relationship, creator fees and claims

Who is the creator (VERIFIED-LIVE ABI and source)

Where Meaning
token.deployer() Immutable originalDeployer: msg.sender of launchToken, or the router caller. Metadata only; no privileges on the token (burn-tool research).
factory.getLaunchedToken(token) → (token, curve, deployer, creatorFeeRecipient, pairToken, graduationThreshold, poolFee, tickSpacing, creatorTaxBps, buybackEnabled, phase, sweptQuote, sweptTokens, sweptAt, exists) Selector 0x3cf28b5a. phase: 0 NotGraduated, 1 Swept, 2 PoolCreated, 3 Rescued.
curve.deployer() The current creator fee recipient. The deployer passes creatorFeeRecipient into the curve constructor, and it is updatable through the factory.
TokenLaunched.deployer (topic 3) originalDeployer

Fee flow

  • Every curve trade:
    • 1% base fee (curveFeeBps), plus any snipe tax.
    • creatorTaxBps, chosen by the creator (up to 10%), paid 100% to the creator.
    • Both come out of the quote leg.
  • Pending fees accrue on the curve as quoteFeeBalance and creatorTaxBalance.
  • Sweep:
    • curve.sweepFees(uint256 minBuybackTokensOut), selector 0x3729bb9a.
    • Callable by hook.feeSweepOperator() or by the curve's deployer() (the creator fee recipient).
    • The creator is blocked when a buyback would execute (InternalSwapRequiresOperator).
    • Live: eth_call of sweepFees(0) from the test curve's deployer() succeeded. From a random account it reverted NotFeeSweepOperator().
  • Split on sweep:
    • Protocol gets 30% of the base fee.
    • The creator bucket gets 70%, minus a buyback slice (50% of the bucket) only if buybackEnabled.
    • The creator tax goes 100% to the creator.
    • Amounts are credited to the fee escrow; there is no push payment.
    • Event: FeesSwept(protocolAmount, buybackAmount, creatorAmount).
  • After graduation: the hook takes hookFeeBps (1%) plus creatorTaxBps on the unspecified leg of each V4 swap. hook.sweepPoolFees(bytes32 poolId, uint256 minConversionQuoteOut, uint256 minBuybackTokensOut) (operator or launches[poolId].creator) credits the escrow (VERIFIED-LIVE ABI and source).

Claiming (VERIFIED-LIVE simulation)

// PonsV2FeeEscrow @ 0xd3AF…Ac9e (address also readable via factory.feeEscrow() / curve.feeEscrow())
function balanceOf(address recipient) view returns (uint256)                  // 0x70a08231  native ETH owed
function balanceOfToken(address recipient, address token) view returns (uint256) // 0xf59e38b7 ERC-20 owed
function claim() returns (uint256 amount)                                     // 0x4e71d92d  claim all ETH to msg.sender
function claim(uint256 amount) returns (uint256)                              // 0x379607f5
function claimToken(address token) returns (uint256 amount)                   // 0x32f289cf  claim all of an ERC-20
function claimToken(address token, uint256 amount) returns (uint256)          // 0x1698755f
  • A claim pays msg.sender only. There are no parameters naming someone else, so it is safe to expose.
  • claim() with a zero balance reverts NoBalance() (0xc2caa2a6), reproduced live.
  • The balance is aggregated per wallet across all of that wallet's launches.
  • Only swept fees are claimable. Unswept fees sit on the curve until the operator or the creator calls sweepFees.
  • Live check:
    • escrow.balanceOf(0x76DD…29bA) (trace-A creator) = 2.315230562009536237 ETH.
    • eth_call claim() from that address returned the same amount.
    • balanceOf(0x9498…DCe6) (CENTER creator) = 0.009315497682114054 ETH.

Changing the fee recipient (VERIFIED-LIVE ABI and source)

  • factory.transferCreatorFeeRecipient(address token, address newRecipient) (selector 0x2931861b): only the current creatorFeeRecipient can call it, and it takes effect immediately.
  • The owner can override through setCreatorFeeRecipient. That is a proposal with a 3-day timelock, executed by anyone via executeCreatorFeeRecipientChange and cancellable via cancelCreatorFeeRecipientChange.
  • factory.setBuybackEnabled(address token, bool): the creator recipient can enable or disable it; the owner can only disable.

7. Graduation and migration

  • DEX: Uniswap V4. PoolManager 0x8366…0951, PositionManager 0x58da…04fA7. The hook is PonsV2MemeHook 0xE5e7…e044, fee = 0, tickSpacing = 200, full range. Native ETH is currency0 = 0x0 (VERIFIED-LIVE factory reads; FROM-LOCAL-RESEARCH trace A pool 0xc308e15a…b3b2).
  • Threshold:
    • The curve is ready when sellableTokens() == 0, which is equivalent to realQuoteReserve == graduationThreshold.
    • That is 4.2 ETH for native config 0. ERC-20 quotes use their own pairTokenEconomics thresholds, for example NVDA 41.6 and USDG 8,090 (VERIFIED-LIVE).
  • Phase 1:
    • factory.graduate(address token) (selector 0xff6d8d05) is permissionless. It is also called automatically by the crossing buy.
    • Reverts NotReadyToGraduate() early (live eth_call).
    • Emits LaunchSwept(token, quoteOut, tokenOut).
  • Phase 2:
    • factory.createGraduatedPool(address token) returns (uint256 positionId) (selector 0x2f53ef2f) is permissionless and retryable.
    • Emits GraduationTokensPermanentlyLocked(token, amount) and PoolGraduated(token, positionId, tokenAmount, pairTokenAmount).
    • The locker emits PositionLocked(token, tokenId).
    • The position NFT is locked permanently in 0x2674…4952.
  • Pool seeding (FROM-LOCAL-RESEARCH, reproduced to the wei in ponscexx):
    • poolTokens = swept * sweptQuote / (sweptQuote + phantomQuote), which is 204,081,632.65 for native config 0.
    • The remaining 81,632,653.06 is locked.
    • The pool opens at the curve's terminal price.
  • Live activity: in the last 20,000 blocks the factory emitted 110 TokenLaunched, 1 LaunchSwept, 1 GraduationTokensPermanentlyLocked, 1 PoolGraduated and 14 CreatorFeeRecipientUpdated.
  • After graduation: curve buy/sell revert CurveGraduated. Trading happens on the V4 pool, through any V4 router.

8. Events and topic0 (VERIFIED-LIVE: computed from verified ABIs and found in bytecode)

Factory 0x7eD5…EC7e

topic0 Event
0x8d4aad4953d0ca700d468f3753aa14432d1b35b43ec6409f051fb6aa43a89607 TokenLaunched(address indexed token, address indexed curve, address indexed deployer, address pairToken, uint256 launchConfigId, uint256 graduationThreshold)
0xcdb72f157fd3666758a6ce201387ffb52038c7562e4fff352828da1096c4b6b4 LaunchSwept(address indexed token, uint256 quoteOut, uint256 tokenOut)
0x0a44ef75df69c534f43cd6c1aa3ef8983065fe5fe79ef9e79f6494e6f258c259 PoolGraduated(address indexed token, uint256 positionId, uint256 tokenAmount, uint256 pairTokenAmount)
0xa0a18f5bf205becee8b268d7cf69addab8548ae8ef361791464cf0e0e17c1361 GraduationTokensPermanentlyLocked(address indexed token, uint256 amount)
0x308c390ed1ab5873392818e036cabdf408bc8ad042fbaead3108954ff75ba980 CreatorFeeRecipientUpdated(address indexed token, address indexed previousRecipient, address indexed newRecipient)
0x7f119e44c84a715429bee60d30ad2e14afdef6c60bb1a7eaa01290ecf6d1b2e5 CreatorFeeRecipientChangeProposed(address indexed token, address indexed currentRecipient, address indexed proposedRecipient, uint256 effectiveAt, uint256 expiresAt)
0xbd886f85b7731f66269f57707414d435bf8df930d3357a10becc48a69377f6d5 BuybackEnabledUpdated(address indexed token, bool enabled, address indexed controller)
0x52c1a28345695afc7f6b7629133124dec5d61ee745affd65e4fd2a776bc05840 LaunchForceSwept(address indexed token)
0x7017304fdd491394686dce984eac721f0be1a22228346210f16694772bde44ca LaunchGraduationRescued(address indexed token, address indexed recipient, uint256 quoteAmount, uint256 tokenAmount)

The admin events are in the ABI file: LaunchFeeUpdated, LaunchEnabledUpdated, SnipeTax*Updated, PairToken*, and others.

Curve (per launch)

topic0 Event
0xec36bf571f136799e8dc0b0b8bea4b04d8bd3d43de838aab0d5fc21d4cbfc455 CurveBuy(address indexed buyer, address indexed recipient, uint256 quoteIn, uint256 tokensOut, uint256 fee, uint256 tax). fee includes the snipe tax.
0x8113d738abdcb6b38357e9d53a54a7157861a09031b453651f0fe7fe151f59df CurveSell(address indexed seller, address indexed recipient, uint256 tokensIn, uint256 quoteOut, uint256 fee, uint256 tax)
0xa69e8258ccc7b9bbb70ab953fc2d1062b4ee28b8ca827534097e1732e87b0262 CurveBuyRefunded(address indexed buyer, uint256 refund)
0x9f4cd7c4ed99d08a797804560c9c5d71d2cf7e101f2e3b5e7d1ca8a24c370e4f FeesSwept(uint256 protocolAmount, uint256 buybackAmount, uint256 creatorAmount)
0xf8d37a90738ae063b8b8058b66f5880cf3cf7ab0c5d4fa78219696591dfbfb67 CurveCompleted(address recipient, uint256 quoteOut, uint256 tokenOut)
0xe2cd2f31ebc05ec28640102987f4c8fc5f20e269e1b3aa82577f3f2f0e35c7c6 AutoGraduationFailed(address indexed token, uint256 gasRemaining)
0x3bc39a5562b28f5fe8f36cecabfbaa12bb969acf05717994709225fc412a9934 SnipeTaxCharged(address indexed recipient, uint256 amount)
0xe4b7e48fbd47c2f602bacadee76ad33b16542ddb4997cfc0de04c311adcfa8c7 SnipeTaxExempted(address indexed account)
0x5feba9b0d52c92ada4b9c571c2bee52390c54f2947208ab250221e6ee32f12ff BuybackLocked(uint256 quoteSpent, uint256 tokensLocked)
0x908408e307fc569b417f6cbec5d5a06f44a0a505ac0479b47d421a4b2fd6a1e6 Initialized(address token)
0x2cc664e1ac1e2d05c0d4637bb63ec8189113b6ac39276be8977e26216a8cdd19 CreatorFeeRecipientUpdated(address indexed previousRecipient, address indexed newRecipient)

Live check: 469 CurveBuy and 322 CurveSell logs appeared in the last 3,000 blocks, found with a topic-only eth_getLogs across all addresses.

Router 0xe33E…2948

topic0 Event
0xdcacba5e347ae7abd91cb519eb877af8fa7774e347b85dd3ddcd24a2ba8cdf37 Launched(address indexed token, address indexed curve, address indexed recipient, address launcher, uint256 quoteSpent, uint256 tokensReceived)
0x3af790fafda720819b2fc6e15090606e81154e0ac9a92d38ecad006d99d20ecc Rescued(address indexed asset, address indexed recipient, uint256 amount)

Fee escrow 0xd3AF…Ac9e

topic0 Event
0x4e45da441832cf53bdaa69235704fc0575e68210f459ee1562911024b12967d5 Credited(address indexed recipient, address indexed depositor, uint256 amount)
0x5d104c62f50449fadfe6f4013c8f36588d32737f94b5ac9b83ddad33b3e1ffdf CreditedToken(address indexed recipient, address indexed token, address indexed depositor, uint256 amount)
0xd8138f8a3f377c5259ca548e70e4c2de94f129f5a11036a15b69513cba2b426a Claimed(address indexed recipient, uint256 amount)
0xdbc1ea3a8459e4c7e11fb385b52bbb5cc8c8ab85eec5d883ac9aa78c171f5141 ClaimedToken(address indexed recipient, address indexed token, uint256 amount)

Hook, locker and token

topic0 Event
0x01bf263a1db1652580721573296e1a1fa70b3d4c87f61d02a69c4e1109d2d573 PoolRegistered(bytes32 indexed poolId, address memecoin, address quoteToken, address creator)
0xc532c43b3423e14ef72748f1c8291238829ca0af8ba9b67975ad1483485a4b4d HookFeeCollected(bytes32 indexed poolId, address currency, uint256 feeAmount, uint256 taxAmount)
0x2f3c43579b9064b6f28edcf41608f3815792d274a56afe024359703cb4ea9b30 PoolFeesSwept(bytes32 indexed poolId, uint256 protocolAmount, uint256 buybackAmount, uint256 creatorAmount, uint256 tokensLocked)
0x2cabb2a2973327d5863ceb4707e9441851243897e86d587ee35943599752eb54 Locker: PositionLocked(address,uint256)
0xaf33c4aba92959b3e7ddc83ab728938262da159a6c05ca836f6c46f9bcb2c740 Locker: TokenSupplyLocked(address,uint256)
0xddf252ad… / 0x8c5be1e5… Token: standard ERC-20 Transfer / Approval

V1 factory (legacy, inactive)

topic0 Event
0x1461370115e1c2be79cb529f8cfcbd11316e789d9c6099fc83417b0b4c48c62a TokenDeployed(address indexed token, address indexed deployer, address indexed dexFactory, address pairToken, uint256 dexId, uint256 launchConfigId)
0xdb51ea9ad51ab453a65a4cb7e60c3cb378c9501bb002609f8f97778fb6c4235a TokenLaunched(address indexed token, address indexed deployer, address indexed dexFactory, address pairToken, address pool, uint256 dexId, uint256 launchConfigId, uint256 positionId, uint256 restrictionsEndBlock, uint256 initialBuyAmount)

9. Token discovery

  • There is no on-chain enumerable registry.
    • The factory has getLaunchedToken(token) but no array or count of tokens (VERIFIED-LIVE ABI).
    • Enumerate with eth_getLogs({address: factory, topics: [0x8d4aad49…]}). topics[1] is the token, topics[2] the curve and topics[3] the deployer. The data holds pairToken, launchConfigId and graduationThreshold.
  • V2 start block: 26,841,846 (FROM-LOCAL-RESEARCH, pons-launcher/src/pons-history.mjs).
  • Public RPC behaviour (FROM-LOCAL-RESEARCH, burn-tool README):
    • It serves logs for all blocks, but old state is pruned (not archival).
    • Windows of about 100k blocks work for getLogs.
    • Rate limits return HTTP 429. Batch responses were unreliable, so pons-burn-tool disables JSON-RPC batching.
  • Metadata: read name, symbol, logo, description, socials or getTokenInfo() from the token. Multicall3 is at 0xcA11bde05977b3631167028862bE2a173976CA11 (FROM-LOCAL-RESEARCH; seen live as a caller in the buy-tx sample).
  • Status per token:
    • factory.getLaunchedToken(token).phase
    • curve.graduated(), curve.readyToGraduate(), curve.getReserves()
    • LaunchSwept and PoolGraduated logs
  • Provenance check (pons-burn-tool method, FROM-LOCAL-RESEARCH; the ABI functions are verified):
    • getLaunchedToken(t).token == t
    • token.launchFactory() == factory
    • token.curve() and token.deployer() match the record
    • The masked runtime hash is 0xfaa55f9f9b0db66181468400d0609ed9b9499520e5c0dbb04561042781ef391f, with the immutables curve, deployer and launchFactory zeroed.
  • Live rate: 110 launches in the last 20,000 blocks. About 1,470 per 100k blocks per ponscexx.

10. Pons HTTP / frontend API endpoints

Endpoint Status Evidence
https://www.ponsfamily.com/launchpad/<tokenAddress> (token page, HTML) VERIFIED-LIVE: HTTP 200 for 0x5746…c1c4 pattern from pons-launcher/src/main.js:441,465
https://docs.ponsfamily.com/docs/v2 (docs) responds (308 redirect) cited in VERIFICATION.md
Official app bundle /_next/static/immutable/chunks/0c-0tu-ylsqzm.js (contains the getPonsV2FactoryAddress default) FROM-LOCAL-RESEARCH VERIFICATION.md section 1
Pons "image endpoint" for logo upload UNKNOWN URL. Described as origin-locked, rejecting non-pons origins. pons-launcher/src/ipfs.mjs:8, README.md
Any JSON API for token lists, prices or trades UNKNOWN. None found in any local project. none

Not Pons APIs. The /api/... routes in pons-launcher/server.mjs (/api/ipfs/image, /api/fast/*, /api/launches, /api/import/*, /api/history/lookup) belong to that local launcher's own server on 127.0.0.1:8733.


11. Third-party frontend feasibility

Action Feasible? Basis
Launch without a dev buy Yes. factory.launchToken(params, 0, pairToken, exemptions) with value = launchFee(). Open to anyone (canLaunch = true). eth_call simulation succeeded today and returns the addresses. Read previewLaunchEconomics(0, pairToken) fresh, or pass 0 to waive the check. Metadata goes on-chain; the logo must be pinned by the frontend itself.
Launch with a dev buy Yes. router.launchAndBuy(...) with value = launchFee + quoteIn for native. eth_call simulation succeeded today and returned tokensOut. Use that result minus slippage for minTokensOut. creatorFeeRecipient must be non-zero. For ERC-20 quotes, approve the router.
Buy Yes. curve.buy(quoteIn, minOut, recipient) with value = quoteIn (native), or approve(curve) first (ERC-20). Wei-exact formula and eth_call both verified. Watch for the snipe tax during the first 3 s (currentSnipeTaxBps(recipient)) and for clamp or refund near graduation.
Sell Yes, after token.approve(curve, tokensIn). eth_call confirmed that a sell without allowance reverts ERC20InsufficientAllowance and that it succeeds with an allowance. Closed once readyToGraduate().
Quote Yes, off-chain formula or eth_call of buy/sell. There is no view quote function.
Claim creator fees Yes. escrow.claim() / claimToken(token) from the recipient's wallet. eth_call returned the exact balance. Pays msg.sender only. The creator can also call curve.sweepFees(0) first to move pending fees into the escrow when buyback is disabled (eth_call succeeded).
After graduation Trade on the Uniswap V4 pool, keyed as (currency0, currency1, fee 0, tickSpacing 200, hooks 0xE5e7…e044). The V4 router path was not verified here: UNKNOWN.

Caveats:

  • The factory is owner-configurable (a Safe) for launchFee, launchEnabled, snipe parameters and configs. Read these live instead of hardcoding them.
  • New Pons versions ship at new addresses, so pin the codehashes, as pons-launcher does.
  • Error selectors for UX mapping are in PonsV2LaunchFactory.json, PonsV2BondingCurve.json and PonsV2LaunchAndBuy.json. Common ones:
    • NativeValueMismatch 0xbc760cfe
    • SlippageExceeded 0x71c4efed
    • CurveGraduated 0x025ac17e
    • LaunchFeeNotPaid 0x7e6d78a5
    • LaunchEconomicsMismatch 0xecb27319
    • CreatorTaxTooHigh 0x9ad465dc
    • InvalidTokenParams 0x374852ca
    • NoBalance 0xc2caa2a6
    • NotFeeSweepOperator 0x8d42130c
    • ERC20InsufficientAllowance 0xfb8f41b2

12. ABI files (/Users/denizyazar/openpons-research-abis/)

File Source
PonsV2LaunchFactory.json (139 entries) Sourcify exact_match ABI for 0x7eD5…EC7e. Also identical to the ABI compiled from its std-json.
PonsV2LaunchAndBuy.json (23) Sourcify exact_match 0xe33E…2948
PonsV2LaunchDeployer.json (9) Sourcify exact_match 0x3711…1A42
PonsV2FeeEscrow.json (18) Sourcify match 0xd3AF…Ac9e
PonsV2MemeHook.json (87) Sourcify match 0xE5e7…e044
PonsV2BuybackVault.json (39) Sourcify match 0x42df…219c
PonsV2LaunchLocker.json (30) Sourcify match 0x2674…4952, verified as RobinFunFiV2LaunchLocker
PonsV2GraduationExecutor.json (15) Sourcify match 0xC781…9046
PonsV2BondingCurve.json (82) Compiled from the verified deployer and router std-json sources. The ABI is identical in both bundles. 46/46 selectors are in a live curve.
PonsV2LauncherToken.json (28) Compiled from the verified deployer std-json. 18/18 selectors are in a live token.
PonsV2GraduationGuard.json (6) Compiled from the verified router std-json. The address itself is not verified. 2/2 selectors present.
PonsLaunchFactoryV1.json (58) Sourcify exact_match V1 0xA5aA…1feB (legacy)

13. Local sources consulted

  • ~/pons-launcher:
    • README.md, VERIFICATION.md
    • src/pons.js, src/fees.js, src/launch-core.js, src/pons-history.mjs, src/ipfs.mjs, src/main.js
    • research/dry-run.mjs
  • ~/ponscexx:
    • research/PONS_V2_ANALYSIS.md, research/TRANSACTION_TRACES.md, research/SECURITY_NOTES.md
    • HANDOFF.md, research/STATUS.md
  • ~/pons-burn-tool:
    • README.md
    • lib/pons.ts, lib/chain.ts, lib/config.ts, lib/burn.ts
  • Earlier local clone pons-labs@162310fb in a previous session's scratchpad: used only to show that the published curve and deployer are stale.
Independent · not affiliated with Pons or Robinhood@openpons