### Read and release buyback vest Source: https://docs.ponsfamily.com/v2 Calculates vesting progress based on the live start date and releases tokens if the releasable amount is greater than zero. The release function is callable only by the vest's beneficiaries. ```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 token with pinned economics using launchToken and previewLaunchEconomics Source: https://docs.ponsfamily.com/v2 Launches a token by calling launchToken with a launch config ID and pair token, sending the launch fee as value. The expectedEconomics pin is fetched with previewLaunchEconomics immediately before launching; if the owner changes the config in between, the launch reverts. The curve and token are deployed with CREATE2, so addresses follow from the salt and launch terms. ```TypeScript 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, }); ``` -------------------------------- ### Launch and buy atomically Source: https://docs.ponsfamily.com/v2 Perform a launch and an initial buy in a single transaction to prevent front-running. The recipient of the buy 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, }); ``` -------------------------------- ### Execute trades against the launch curve Source: https://docs.ponsfamily.com/v2 Demonstrates how to perform buy operations for both native ETH and custom ERC-20 quote assets, including the necessary approval step for custom tokens. ```typescript 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], }); ``` -------------------------------- ### Enumerate open launch configs with launchConfigCount and getLaunchConfig Source: https://docs.ponsfamily.com/v2 Reads the append-only list of launch configs and returns only those currently enabled. Disabled configs remain readable but should not be offered in a create flow. Use this at create time rather than caching ids indefinitely, since configs can be edited or disabled by the owner. ```TypeScript 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); } ``` -------------------------------- ### Build the pool key and read pending fees Source: https://docs.ponsfamily.com/v2 Constructs a Uniswap v4 pool key using launch record data and defines the ABI for reading pending fees and creator taxes from the hook. ```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)", ]); ``` -------------------------------- ### Launch with snipe tax exemptions Source: https://docs.ponsfamily.com/v2 Use this to launch a token while exempting specific team wallets from the opening snipe tax. The launcher and creator fee recipient are exempted automatically. ```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, }); ``` -------------------------------- ### Index protocol events Source: https://docs.ponsfamily.com/v2 Retrieves logs for token launches from the factory and trade history from a specific curve using viem. ```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", }); ``` -------------------------------- ### Claiming fees across native and token assets Source: https://docs.ponsfamily.com/v2 Uses the escrow ABI to check balances and claim fees. Native-quote launches credit the ETH ledger, while custom-pair launches and buyback vests credit specific token ledgers. ```typescript 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 Fee Rates and Policy Source: https://docs.ponsfamily.com/v2 Retrieves base trade fees, creator taxes, and the launch fee policy from the curve and factory contracts. ```typescript // 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 launch record from factory Source: https://docs.ponsfamily.com/v2 Retrieves the LaunchedToken struct from the factory contract to determine the current phase and routing logic. ```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; ``` -------------------------------- ### Read token metadata Source: https://docs.ponsfamily.com/v2 Retrieves token deployer, logo, description, and social links using the getTokenInfo function. Name, symbol, and decimals are handled as standard ERC-20 reads. ```javascript // Name, symbol, and decimals are standard ERC-20 reads. Everything the // creator set at launch beyond that lives in one call. 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", }); ``` -------------------------------- ### Calculate trade quotes for buy and sell operations Source: https://docs.ponsfamily.com/v2 This implementation provides deterministic pricing for buy and sell trades by replicating the curve's internal arithmetic. It accounts for fees, taxes, and slippage limits off-chain. ```typescript 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, args) => client.readContract({ address: curve, abi: curveAbi, functionName, args }); 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; ``` -------------------------------- ### Claim fees from escrow Source: https://docs.ponsfamily.com/v2 Checks if native or asset balances are owed to the creator and executes the claim function on the fee escrow contract. ```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], }); } ``` -------------------------------- ### Calculate sell output Source: https://docs.ponsfamily.com/v2 Calculates the net output of a sell transaction by subtracting fees and creator taxes from the gross amount. ```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; } ``` -------------------------------- ### Read pending fees from the hook Source: https://docs.ponsfamily.com/v2 Use this snippet to query the pending fees for a specific pool and pair token from the hook contract. ```javascript const pendingQuote = await client.readContract({ address: memeHook, abi: hookAbi, functionName: "pendingFees", args: [poolId, launch.pairToken], }); ``` -------------------------------- ### Check quote assets for launch eligibility Source: https://docs.ponsfamily.com/v2 Validates potential quote assets against the factory contract to ensure they are approved and have valid economic parameters before offering them in a create flow. ```typescript 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; } ``` -------------------------------- ### Calculate Price and Graduation Progress Source: https://docs.ponsfamily.com/v2 Reads reserves and thresholds from the curve contract to calculate the marginal token price and graduation progress percentage. ```typescript 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); ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.