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.