### Read and release buyback vest with release Source: https://www.ponsfamily.com/docs/v2 Reads the buyback vest state (locked, released, vested, releasable, start, and VESTING_DURATION) and releases tokens when releasable is greater than zero. The release function is callable only by the vest's beneficiaries (creator fee recipient or protocol recipient); any other caller reverts with NotVestBeneficiary. Progress is computed from the live vestingStart, which moves forward as new buybacks land, not from the launch date. ```javascript const vaultAbi = parseAbi([ "function totalLocked(address token) view returns (uint256)", "function totalReleased(address token) view returns (uint256)", "function vestedAmount(address token) view returns (uint256)", "function releasable(address token) view returns (uint256)", "function vestingStart(address token) view returns (uint256)", "function VESTING_DURATION() view returns (uint256)", "function release(address token) returns (uint256 released)", ]); const [locked, released, vested, releasable, start, duration] = await Promise.all([ "totalLocked", "totalReleased", "vestedAmount", "releasable", "vestingStart", ].map((fn) => client.readContract({ address: buybackVault, abi: vaultAbi, functionName: fn, args: [token] }), ).concat( client.readContract({ address: buybackVault, abi: vaultAbi, functionName: "VESTING_DURATION" }), )); // vestingStart moves forward as new buybacks land, so progress is derived // from the live start rather than from the launch date. const progress = Number(vested) / Number(locked); // Callable only by the vest's own beneficiaries, meaning the creator fee // recipient or the protocol recipient. Any other caller reverts with // NotVestBeneficiary. Either side releasing pays out both, splitting the // released amount and crediting each in the escrow. if (releasable > 0n) { await wallet.writeContract({ address: buybackVault, abi: vaultAbi, functionName: "release", args: [token], }); } ``` -------------------------------- ### Launch a token with launchToken and pinned economics Source: https://www.ponsfamily.com/docs/v2 Launches a token by calling launchToken with a pinned economics pin. Fetch expectedEconomics via previewLaunchEconomics immediately before launching and pass it as expectedEconomics; if the owner changes the config in between, the launch reverts. The launch fee is sent as value on the call, and the salt is used for CREATE2 address derivation. ```javascript import { parseAbi, toHex } from "viem"; const factoryAbi = parseAbi([ "struct Socials { string twitter; string telegram; string discord; string website; string farcaster; }", "struct TokenParams { string name; string symbol; string logo; string description; Socials socials; address creatorFeeRecipient; uint16 creatorTaxBps; bool buybackEnabled; bytes32 expectedEconomics; bytes32 salt; }", "function launchToken(TokenParams params, uint256 launchConfigId, address pairToken) payable returns (address token, address curve)", "function previewLaunchEconomics(uint256 launchConfigId, address pairToken) view returns (bytes32)", "function launchFee() view returns (uint256)", ]); const launchConfigId = 0n; const pairToken = "0x0000000000000000000000000000000000000000"; // native ETH // The curve and token are deployed with CREATE2, so their addresses follow // from this salt and the launch's own terms rather than from the order // launches land in. Any value you have not used before will do. Salts are // namespaced per authenticated initiating account, so no one else's choice // collides with yours, and mining one is how you claim a vanity address. const salt = toHex(crypto.getRandomValues(new Uint8Array(32))); // Pin the terms you were quoted. If the owner changes launch economics // between your read and your transaction, the launch reverts instead of // settling on terms you never agreed to. const [expectedEconomics, launchFee] = await Promise.all([ client.readContract({ address: factory, abi: factoryAbi, functionName: "previewLaunchEconomics", args: [launchConfigId, pairToken], }), client.readContract({ address: factory, abi: factoryAbi, functionName: "launchFee", }), ]); const hash = await wallet.writeContract({ address: factory, abi: factoryAbi, functionName: "launchToken", args: [ { name: "Example", symbol: "EXMPL", logo: "ipfs://...", description: "An example launch.", socials: { twitter: "", telegram: "", discord: "", website: "", farcaster: "" }, creatorFeeRecipient: creator, // zero address defaults to the caller creatorTaxBps: 0, // optional creator tax, capped by the protocol buybackEnabled: true, expectedEconomics, salt, }, launchConfigId, pairToken, ], value: launchFee, }); ``` -------------------------------- ### Index launches and trades with viem getLogs Source: https://www.ponsfamily.com/docs/v2 Indexes every launch and trade using viem's getLogs. The first block fetches TokenLaunched events from the factory; the second fetches CurveBuy and CurveSell events from a single curve. Note that a buy finishing a launch may be partially filled, so read tokensOut and quoteIn from the event rather than assuming the requested amount. ```javascript import { parseAbiItem } from "viem"; // Every launch in the protocol, with the curve that prices it. const launches = await client.getLogs({ address: factory, event: parseAbiItem( "event TokenLaunched(address indexed token, address indexed curve, address indexed deployer, address pairToken, uint256 launchConfigId, uint256 graduationThreshold)", ), fromBlock: deploymentBlock, toBlock: "latest", }); // Trades on a single curve. A buy that finishes a launch may be partially // filled, so read tokensOut and quoteIn from the event rather than assuming // the amount the caller requested. // // fee is the base trade fee and, on a buy inside the launch window, the // snipe tax folded in with it. tax is the creator tax, reported separately. const trades = await client.getLogs({ address: curve, events: [ parseAbiItem( "event CurveBuy(address indexed buyer, address indexed recipient, uint256 quoteIn, uint256 tokensOut, uint256 fee, uint256 tax)", ), parseAbiItem( "event CurveSell(address indexed seller, address indexed recipient, uint256 tokensIn, uint256 quoteOut, uint256 fee, uint256 tax)", ), ], fromBlock: launchBlock, toBlock: "latest", }); ``` -------------------------------- ### Launch and buy atomically using launchAndBuy router Source: https://www.ponsfamily.com/docs/v2 Use this router call to atomically create a launch and buy into it in one transaction, preventing front-running of the opening buy. The router is optional and enforces the factory's launch gate. Note that creatorFeeRecipient must be explicit, the launch is recorded to the initiating wallet, and the buy's recipient is automatically exempted from the opening tax. ```javascript // One transaction: create the launch, then buy into it. // Nothing can trade between the two, so the opening buy cannot be front-run. const routerAbi = parseAbi([ "struct Socials { string twitter; string telegram; string discord; string website; string farcaster; }", "struct TokenParams { string name; string symbol; string logo; string description; Socials socials; address creatorFeeRecipient; uint16 creatorTaxBps; bool buybackEnabled; bytes32 expectedEconomics; bytes32 salt; }", "function launchAndBuy(TokenParams params, uint256 launchConfigId, address pairToken, uint256 quoteIn, uint256 minTokensOut, address recipient, address[] snipeTaxExemptions) payable returns (address token, address curve, uint256 tokensOut)", ]); const quoteIn = parseEther("0.5"); // Native launch: the fee and the buy travel together as value. // ERC-20 pair: send only the fee, and approve the router for quoteIn first. const hash = await wallet.writeContract({ address: launchAndBuy, abi: routerAbi, functionName: "launchAndBuy", args: [ params, // creatorFeeRecipient must be set, zero is rejected here launchConfigId, pairToken, quoteIn, minTokensOut, // still enforced when the curve clamps the fill recipient, [], // extra wallets to exempt; recipient is added for you ], value: pairToken === zeroAddress ? launchFee + quoteIn : launchFee, }); ``` -------------------------------- ### Buy launch tokens with curveAbi (native and custom-pair) Source: https://www.ponsfamily.com/docs/v2 Shows how to buy launch tokens directly against the curve before graduation. For a native-quote launch, quoteIn must equal the value sent; for a custom-pair launch, approve the curve first and send no value. The sell side closes slightly earlier than the buy side once readyToGraduate() is true, and the curve reverts with CurveGraduated after graduation. ```javascript const curveAbi = parseAbi([ "function buy(uint256 quoteIn, uint256 minTokensOut, address recipient) payable returns (uint256 tokensOut)", "function sell(uint256 tokensIn, uint256 minQuoteOut, address recipient) returns (uint256 quoteOut)", "function isNativeQuote() view returns (bool)", "function pairToken() view returns (address)", ]); // Native-quote launch: quoteIn must equal the value sent. await wallet.writeContract({ address: curve, abi: curveAbi, functionName: "buy", args: [quoteIn, minTokensOut, recipient], value: quoteIn, }); // Custom-pair launch: approve the curve first and send no value. await wallet.writeContract({ address: pairToken, abi: parseAbi(["function approve(address spender, uint256 amount) returns (bool)"]), functionName: "approve", args: [curve, quoteIn], }); await wallet.writeContract({ address: curve, abi: curveAbi, functionName: "buy", args: [quoteIn, minTokensOut, recipient], }); ``` -------------------------------- ### Build Uniswap v4 pool key and read pending fees with viem Source: https://www.ponsfamily.com/docs/v2 Builds the Uniswap v4 pool key from a launch record, computes the pool ID, and defines the hook ABI for reading pending fees and creator tax. Requires the launch record fields (pairToken, token, poolFee, tickSpacing) and the shared pons hook address. The pool fee is zero because the hook charges the fee. ```typescript import { encodeAbiParameters, keccak256 } from "viem"; // Uniswap v4 sorts the two currencies by address. Native ETH is the zero // address, so it always takes the currency0 slot. const [currency0, currency1] = launch.pairToken.toLowerCase() < launch.token.toLowerCase() ? [launch.pairToken, launch.token] : [launch.token, launch.pairToken]; const poolKey = { currency0, currency1, fee: launch.poolFee, // zero: the hook charges the fee, not the pool tickSpacing: launch.tickSpacing, hooks: memeHook, }; const poolId = keccak256( encodeAbiParameters( [ { type: "address" }, { type: "address" }, { type: "uint24" }, { type: "int24" }, { type: "address" }, ], [poolKey.currency0, poolKey.currency1, poolKey.fee, poolKey.tickSpacing, poolKey.hooks], ), ); // Fees the hook is holding for this pool, per currency, before a sweep. const hookAbi = parseAbi([ "function pendingFees(bytes32 poolId, address currency) view returns (uint256)", "function pendingCreatorTax(bytes32 poolId, address currency) view returns (uint256)", ]); ``` -------------------------------- ### Read launch record with getLaunchedToken and phase checks Source: https://www.ponsfamily.com/docs/v2 Reads the launch record from the factory contract using viem's `readContract` with a parsed ABI. The `phase` field is the authoritative signal for routing: 0 means trading on the curve, 2 means trading on Uniswap v4. Use this to determine whether to route swaps to the curve or the pool. ```javascript const factoryAbi = parseAbi([ "struct LaunchedToken { address token; address curve; address deployer; address creatorFeeRecipient; address pairToken; uint256 graduationThreshold; uint24 poolFee; int24 tickSpacing; uint16 creatorTaxBps; bool buybackEnabled; uint8 phase; uint256 sweptQuote; uint256 sweptTokens; uint256 sweptAt; bool exists; }", "function getLaunchedToken(address token) view returns (LaunchedToken)", ]); const launch = await client.readContract({ address: factory, abi: factoryAbi, functionName: "getLaunchedToken", args: [token], }); // phase: 0 NotGraduated, 1 Swept, 2 PoolCreated, 3 Rescued const isLive = launch.phase === 0; const isTradingOnV4 = launch.phase === 2; ``` -------------------------------- ### Launch with snipe tax exemptions using launchToken overload Source: https://www.ponsfamily.com/docs/v2 Use this overload of launchToken to pass an array of extra wallets exempt from the opening snipe tax. The launcher and creator fee recipient are always exempted automatically. The list is fixed at creation and reverts with ExemptionListTooLong if it exceeds 32 entries. ```javascript // Same launch, plus extra wallets exempt from the opening snipe tax. // The launcher and the creator fee recipient are always exempted anyway. const factoryAbi = parseAbi([ "struct Socials { string twitter; string telegram; string discord; string website; string farcaster; }", "struct TokenParams { string name; string symbol; string logo; string description; Socials socials; address creatorFeeRecipient; uint16 creatorTaxBps; bool buybackEnabled; bytes32 expectedEconomics; bytes32 salt; }", "function launchToken(TokenParams params, uint256 launchConfigId, address pairToken, address[] snipeTaxExemptions) payable returns (address token, address curve)", ]); const hash = await wallet.writeContract({ address: factory, abi: factoryAbi, functionName: "launchToken", args: [params, launchConfigId, pairToken, [teamWalletA, teamWalletB]], value: launchFee, }); ``` -------------------------------- ### Enumerate open launch configs with launchConfigCount and getLaunchConfig Source: https://www.ponsfamily.com/docs/v2 Reads the factory's launch configs using launchConfigCount and getLaunchConfig, filtering out disabled ones. Configs are append-only, so ids are stable, but disabled configs revert with LaunchConfigDisabled, so read the list at create time rather than caching ids indefinitely. ```javascript const factoryAbi = parseAbi([ "struct LaunchConfig { uint256 supply; uint256 curveFeeBps; uint256 phantomQuote; uint256 graduationThreshold; uint24 poolFee; int24 tickSpacing; bool enabled; }", "function launchConfigCount() view returns (uint256)", "function getLaunchConfig(uint256 id) view returns (LaunchConfig)", ]); /** * Returns the launch configs currently open for new launches. Disabled * configs stay readable so historic launches remain explainable, but a * create flow should not offer them. */ async function openLaunchConfigs() { const count = await client.readContract({ address: factory, abi: factoryAbi, functionName: "launchConfigCount", }); const configs = await Promise.all( Array.from({ length: Number(count) }, (_, id) => client.readContract({ address: factory, abi: factoryAbi, functionName: "getLaunchConfig", args: [BigInt(id)], }), ), ); return configs .map((config, id) => ({ id: BigInt(id), ...config })) .filter((config) => config.enabled); } ``` -------------------------------- ### Read fee rates and fee policy from curve and factory Source: https://www.ponsfamily.com/docs/v2 Reads the base trade fee and creator tax from the launch curve, then fetches the fee policy from the factory. Use this to surface the total cost to the trader and how the fee is divided. The base fee and creator tax are immutable, and the policy reveals the protocol share, buyback slice, and post-graduation rates. ```javascript // Pre-graduation, both rates live on the launch's own curve. const curveAbi = parseAbi([ "function feeBps() view returns (uint256)", "function creatorTaxBps() view returns (uint256)", "function buybackEnabled() view returns (bool)", ]); const [feeBps, creatorTaxBps] = await Promise.all([ client.readContract({ address: curve, abi: curveAbi, functionName: "feeBps" }), client.readContract({ address: curve, abi: curveAbi, functionName: "creatorTaxBps" }), ]); // Total cost to the trader, in basis points of the quote leg. const totalTradeCostBps = feeBps + creatorTaxBps; // How that fee is divided, and the rate the pool charges after graduation. const policyAbi = parseAbi([ "struct FeePolicy { address protocolFeeRecipient; uint16 protocolFeeShareBps; uint16 buybackBurnBps; uint16 hookFeeBps; uint16 maxInternalPriceImpactBps; }", "function getLaunchFeePolicy(address token) view returns (FeePolicy)", ]); const policy = await client.readContract({ address: factory, abi: policyAbi, functionName: "getLaunchFeePolicy", args: [token], }); ``` -------------------------------- ### Read escrow balances with balanceOf and balanceOfToken Source: https://www.ponsfamily.com/docs/v2 Use to check a creator's claimable fees from the escrow. The escrow keeps separate ledgers: native-quote launches credit the ETH ledger (balanceOf), while custom-pair launches credit the quote asset's ledger and released buyback vests credit the launch token's ledger (balanceOfToken). A creator with multiple launches may hold several balances and must claim each independently. ```javascript const escrowAbi = parseAbi([ "function balanceOf(address recipient) view returns (uint256)", "function balanceOfToken(address recipient, address token) view returns (uint256)", "function claim()", "function claimToken(address token)", ]); // A native-quote launch credits the ETH ledger. const ethOwed = await client.readContract({ address: feeEscrow, abi: escrowAbi, functionName: "balanceOf", args: [creator], }); // A custom-pair launch credits the quote asset's ledger instead, and a // released buyback vest credits the launch token's ledger. A creator with // several launches may therefore be owed several different assets. const assetOwed = await client.readContract({ address: feeEscrow, abi: escrowAbi, functionName: "balanceOfToken", args: [creator, quoteAsset], }); ``` -------------------------------- ### Read creator metadata with getTokenInfo Source: https://www.ponsfamily.com/docs/v2 Reads the creator-supplied token metadata (deployer, logo, description, and socials) in a single call using the getTokenInfo function. The ABI includes a Socials struct with twitter, telegram, discord, website, and farcaster fields. Use this when you need the full metadata beyond the standard ERC-20 name, symbol, and decimals. ```javascript const tokenAbi = parseAbi([ "struct Socials { string twitter; string telegram; string discord; string website; string farcaster; }", "function getTokenInfo() view returns (address tokenDeployer, string tokenLogo, string tokenDescription, Socials tokenSocials)", ]); const [deployer, logo, description, socials] = await client.readContract({ address: token, abi: tokenAbi, functionName: "getTokenInfo", }); ``` -------------------------------- ### Quote curve pricing in both directions with fee and snipe tax handling Source: https://www.ponsfamily.com/docs/v2 Quotes the curve's pricing in both directions using the same integer arithmetic as the contract. Reads reserves, sellable tokens, and fee rates, then computes the output for a buy or sell while applying fees off the input and capping the snipe tax so the buyer nets at least 1% of spend. The buy path also handles crossing the reserved allocation by repricing from the token side and refunding the excess input. ```javascript const curveAbi = parseAbi([ "function getReserves() view returns (uint256 quoteReserve, uint256 tokenReserve)", "function sellableTokens() view returns (uint256)", "function feeBps() view returns (uint256)", "function creatorTaxBps() view returns (uint256)", "function currentSnipeTaxBps(address recipient) view returns (uint256)", ]); const BPS = 10_000n; const ceilDiv = (a, b) => (a + b - 1n) / b; /** * Constant product in the curve's own integer order. No fee is applied * here, because both directions charge their fees outside this step. */ function amountOut(inAmount, reserveIn, reserveOut) { return (inAmount * reserveOut) / (reserveIn + inAmount); } function amountIn(outAmount, reserveIn, reserveOut) { return (outAmount * reserveIn) / (reserveOut - outAmount) + 1n; } /** Quote asset in, launch token out. */ async function quoteBuy(curve, quoteIn, recipient) { const read = (functionName) => client.readContract({ address: curve, abi: curveAbi, functionName }); const [reserves, sellable, feeBps, creatorTaxBps, rawSnipeBps] = await Promise.all([ read("getReserves"), read("sellableTokens"), read("feeBps"), read("creatorTaxBps"), read("currentSnipeTaxBps", [recipient]), ]); const [quoteReserve, tokenReserve] = reserves; // The snipe tax is capped so the buyer always nets at least 1% of spend. let snipeBps = rawSnipeBps; if (snipeBps > 0n) { const maxSnipeBps = BPS - feeBps - creatorTaxBps - 100n; if (snipeBps > maxSnipeBps) snipeBps = maxSnipeBps; } // Every fee comes off the input before the curve prices the trade. let spent = quoteIn; const fee = (spent * feeBps) / BPS; const tax = (spent * creatorTaxBps) / BPS; const snipeTax = (spent * snipeBps) / BPS; let tokensOut = amountOut(spent - fee - tax - snipeTax, quoteReserve, tokenReserve); // A buy that would cross the reserved allocation fills to the edge, and // the input is repriced from the token side so the rest is refunded. if (tokensOut > sellable) { tokensOut = sellable; const net = amountIn(sellable, quoteReserve, tokenReserve); const grossed = ceilDiv(net * BPS, BPS - feeBps - creatorTaxBps - snipeBps); spent = grossed < quoteIn ? grossed : quoteIn; } return { tokensOut, spent, refund: quoteIn - spent }; } /** Launch token in, quote asset out. */ async function quoteSell(curve, tokensIn) { const read = (functionName) => client.readContract({ address: curve, abi: curveAbi, functionName }); const [reserves, feeBps, creatorTaxBps] = await Promise.all([ read("getReserves"), read("feeBps"), read("creatorTaxBps"), ]); const [quoteReserve, tokenReserve] = reserves; ``` -------------------------------- ### Check quote asset approval and economics with factoryAbi Source: https://www.ponsfamily.com/docs/v2 Use to filter candidate quote assets before offering them in a create flow. Calls approvedPairTokens and pairTokenEconomics on the factory; an asset that fails either read will revert at launch, so it should not be listed. Skips assets that are unapproved or have zero phantomQuote or graduationThreshold, and formats the threshold using the asset's own decimals rather than assuming 18. ```javascript const factoryAbi = parseAbi([ "function approvedPairTokens(address pairToken) view returns (bool)", "function pairTokenEconomics(address pairToken) view returns (uint256 phantomQuote, uint256 graduationThreshold, uint8 decimals)", ]); /** * Returns the quote assets a create flow may safely offer. An asset that * fails either read will revert at launch, so it should not be listed. */ async function usableQuoteAssets(candidates) { const usable = []; for (const asset of candidates) { const [approved, economics] = await Promise.all([ client.readContract({ address: factory, abi: factoryAbi, functionName: "approvedPairTokens", args: [asset], }), client.readContract({ address: factory, abi: factoryAbi, functionName: "pairTokenEconomics", args: [asset], }), ]); const [phantomQuote, graduationThreshold, decimals] = economics; if (!approved || phantomQuote === 0n || graduationThreshold === 0n) continue; // Threshold is denominated in the asset's own decimals, so format it // with these decimals rather than assuming 18. usable.push({ asset, graduationThreshold, decimals }); } return usable; } ``` -------------------------------- ### Sell quote calculation with fee and tax deduction Source: https://www.ponsfamily.com/docs/v2 Calculates the quote output for a sell on the bonding curve. Fees (trade fee and creator tax) are deducted from the gross output; no snipe tax applies on sells. Use this when pricing a sell order, as quoting a sell as a mirrored buy overstates proceeds. ```javascript // A sell is priced first and the fees come off the output. There is no // snipe tax on this side. const gross = amountOut(tokensIn, tokenReserve, quoteReserve); const fee = (gross * feeBps) / BPS; const tax = (gross * creatorTaxBps) / BPS; return gross - fee - tax; } ``` -------------------------------- ### Claim native and token payouts from escrow Source: https://www.ponsfamily.com/docs/v2 Claims native and ERC-20 payouts from the escrow. The first block claims native fees when ethOwed is positive; the second claims the quote asset when assetOwed is positive. Both use the same escrow contract and ABI. ```javascript if (ethOwed > 0n) { await wallet.writeContract({ address: feeEscrow, abi: escrowAbi, functionName: "claim" }); } if (assetOwed > 0n) { await wallet.writeContract({ address: feeEscrow, abi: escrowAbi, functionName: "claimToken", args: [quoteAsset], }); } ``` -------------------------------- ### Read price and graduation progress from curve contract Source: https://www.ponsfamily.com/docs/v2 Reads the current price and graduation progress from a curve contract. Use this to display the marginal token price and the fraction of the graduation threshold raised. The price uses the quote reserve including the phantom reserve, and the progress uses the real quote reserve against the graduation threshold. ```javascript const curveAbi = parseAbi([ "function getReserves() view returns (uint256 quoteReserve, uint256 tokenReserve)", "function realQuoteReserve() view returns (uint256)", "function graduationThreshold() view returns (uint256)", "function sellableTokens() view returns (uint256)", "function readyToGraduate() view returns (bool)", "function graduated() view returns (bool)", ]); const [quoteReserve, tokenReserve] = await client.readContract({ address: curve, abi: curveAbi, functionName: "getReserves", }); // Marginal price of one token in the quote asset. quoteReserve already // includes the phantom reserve, so this is the price a very small buy pays. const price = Number(quoteReserve) / Number(tokenReserve); // Graduation progress. Both forms agree by construction, so use whichever // reads better in your interface. const [raised, threshold] = await Promise.all([ client.readContract({ address: curve, abi: curveAbi, functionName: "realQuoteReserve" }), client.readContract({ address: curve, abi: curveAbi, functionName: "graduationThreshold" }), ]); const progress = Number(raised) / Number(threshold); ``` -------------------------------- ### Read pending fees with pendingFees Source: https://www.ponsfamily.com/docs/v2 Use to read the accrued fees for a pool and currency. The hook holds only accrued fees awaiting a sweep, and this call returns the pending amount for the specified pool and token. ```javascript const pendingQuote = await client.readContract({ address: memeHook, abi: hookAbi, functionName: "pendingFees", args: [poolId, launch.pairToken], }); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.