Skip to main content

Launch a token from code

A launch creates a strategy token, its bonding curve and its treasury in one transaction on the factory. The token sells on the curve against a listed stock; when the curve sells out it graduates to a Uniswap v4 pool, and the treasury runs the strategy the creator chose at launch. Before the launch the creator registers its choices on the two deployers (the opening window, the wallets exempt from the opening surcharge, the strategy kind and its settings), then predict returns the launch terms and launchWithMetadata launches in those terms. launchAndBuy does the same and adds the creator's first buy, paid in ETH, in the same transaction. The script below does all of it from one wallet, which becomes the creator.

You need​

  • A wallet with ETH for the launch fee (getDefaults().launchFeeAmount, 0.0005 ETH), gas for up to four transactions, and the first buy if you make one. On a fork of mainnet the launch transaction used about 10 million gas; each registration uses far less.
  • Node.js with viem. The script runs with npx tsx.
  • factory.publicLaunch() returning true.
address
HedgeFunV2Factory0xC78b6A83d75C5612a1A7323702B56038254a6459
CurveDeployer0x501e94d1b06f955C268AE920c42B6289dA5127fd
V2TreasuryDeployer0xD86745C6e81D4095A47979267267c2bf41fBFC46
HedgeFunV2LaunchNativeRouter0xCcFbB947f841170fA7D220f01B6CbbA0487a5cD8
USDG (6 decimals)0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168
WETH0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73
Uniswap V3 factory0x1f7d7550B1b028f7571E69A784071F0205FD2EfA
Stock, for example TSLA0x322F0929c4625eD5bAd873c95208D54E1c003b2d

The eighteen listed stocks are on the addresses page. Use the stock token address, and check factory.listings(stock): enabled must be true.

Steps​

1. Upload an icon (optional)​

The token page shows a logo only when it is an icon stored by the site. Upload it by hand or from a script as described in Metadata and the icon, and put the returned URL in METADATA.logo. An empty logo is accepted.

2. Register the creator's choices​

Each registration is sent by the creator and keyed by keccak256(abi.encode(symbol, creator, nonce)), so only that launch sees it. Every call can be repeated with new values until the launch.

curveDeployer.setCurveConfig(symbol, nonce, DEFAULT_SALE_BPS(), snipeSeconds) // 7931; window 0..180 s
curveDeployer.setOpeningTaxExemptions(symbol, nonce, wallets) // up to MAX_OPENING_TAX_EXEMPTIONS() = 40
treasuryDeployer.setStrategyKind(symbol, nonce, kind) // kinds 1, 4, 5; kind 0 needs no call
treasuryDeployer.setEngineConfig(symbol, nonce, kind, config) // kinds 2, 3, 6; records the kind too
  • Opening window. For snipeSeconds after the launch a buyer pays a rate that starts at snipeBps (99%) and falls in a straight line to the base tax at the end of the window. Without a registration the window is getDefaults().snipeSeconds, 3 seconds on mainnet. saleBps must be DEFAULT_SALE_BPS().
  • Exemptions. Exempt wallets pay only the base tax during the window. Read the cap from MAX_OPENING_TAX_EXEMPTIONS() (40); a list of 41 reverts BadOpeningTaxExemptions, and so does a list holding the zero address, the creator or a duplicate. The creator is exempt without being listed. A new call replaces the list.
  • Strategy kind. kindManifest(kind) returns (engineVersion, configSchema, ...). A kind with schema 0 takes setStrategyKind; an engine kind (schema 1, 3 or 4) takes setEngineConfig with {schema, engineVersion, policyKey, words}, and setStrategyKind on it reverts BadKind. The kinds and their numbers are explained in Concepts.
  • Cycle (5) and Lots with reserve (6) need tp1Bps and dipBps of at least 2 × (listingGates(stock).maxSlippageBps + pool fee): 210 on TSLA (75 + 30). predict does not check it; the launch reverts TreasuryDeployFailed. The script checks it before sending anything.

3. Predict and check​

(token, treasury, terms) = factory.predict(request)

request.maxFee is launchFeeAmount and request.expectedOpenPriceE18 is listings(stock).openPriceE18; any other value makes the launch revert Restated. predict refuses a bad curve or engine setting and a stop inside the execution cost, but the tax bounds, bandBpsPerHour, the rung floor above and kind 6's reserveBps over 5000 are refused only by the launch. The script therefore simulates every transaction before sending it. Changing a registration after predict makes the launch revert Restated; register the quoted values again, or predict again.

4. Launch​

id = factory.launchWithMetadata{value: launchFeeAmount}(request, terms, metadata)
(id, tokensOut, stockRefund) = launchNativeRouter.launchAndBuy{value: launchFeeAmount + amountIn}(request, terms, metadata, buy, path)

msg.value must equal the fee exactly for launchWithMetadata; more or less reverts BadRequest. launchAndBuy goes through HedgeFunV2LaunchNativeRouter, which the factory authorises (factory.launchers(0xCcFbB947f841170fA7D220f01B6CbbA0487a5cD8) is true). It pays the fee, swaps the rest of the ETH to the stock along path (WETH → USDG → stock) and buys on the new curve for request.creator, who pays only the base tax. buy is (amountIn, minStockReceived, minFinalOut, deadline, allowPartialFill); the script sets minFinalOut from a simulation less BUY_SLIPPAGE_BPS. Details: Quote and launch and One ETH payment.

5. Read the result​

The Launched(id, symbol, token, treasury, hook, stock, creator) event gives the id and addresses. factory.strategies(id) and factory.curves(id) return them later, token.getTokenInfo() the metadata, treasuryDeployer.strategyKindOf(salt) and engineConfigOf(salt) the frozen choice, and on the curve snipeSeconds() and isOpeningTaxExempt(wallet) the window and the exemptions. Reading state lists the rest.

The script​

Save it as launch.mts (the .mts extension lets it use top-level await in any package), set the constants at the top, then run it:

npm i viem
RPC_URL=https://rpc.mainnet.chain.robinhood.com CREATOR_KEY=0x... npx tsx launch.mts
// Launch a Hedgefun v2 token on Robinhood Chain mainnet.
// Run: npm i viem && RPC_URL=https://rpc.mainnet.chain.robinhood.com CREATOR_KEY=0x... npx tsx launch.mts
import {
createPublicClient, createWalletClient, encodeAbiParameters, http, keccak256, pad, parseAbi,
parseEther, parseEventLogs, toHex, zeroAddress, type Address, type Hex, type TransactionReceipt,
} from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { robinhood } from 'viem/chains';

// ---------- your choices ----------
const RPC_URL = process.env.RPC_URL ?? 'https://rpc.mainnet.chain.robinhood.com';
const creator = privateKeyToAccount(process.env.CREATOR_KEY as Hex); // the launching wallet is the creator

const STOCK: Address = '0x322F0929c4625eD5bAd873c95208D54E1c003b2d'; // TSLA; any listed stock
const NAME = 'My Strategy';
const SYMBOL = 'MYS';
const NONCE = 0n; // change it to launch the same symbol again from the same wallet
const TAX_BPS = 300; // total trading tax, 100..1500
const CREATOR_BPS = 1000; // creator's share of the collected tax, 0..5000
const RULE = { tp1Bps: 300, tp2Bps: 600, dipBps: 300, stopBps: 0, lotBps: 5000 };
const SNIPE_SECONDS = 60; // opening window with the surcharge, 0..180
const EXEMPT: Address[] = [ // your buyers' wallets; up to MAX_OPENING_TAX_EXEMPTIONS (40); never the creator
'0x70997970C51812dc3A010C7d01b50e0d17dc79C8',
'0x3C44CdDdB6a900fa2b585dd299e03d12FA4293BC',
'0x90F79bf6EB2c4f870365E785982E1f101E93b906',
];
// Strategy kind: 0 Lots, 1 Buy-back, 2 Spot, 3 Rebalance, 4 Percentage buy-back, 5 Cycle, 6 Lots with reserve.
const KIND: number = 6;
// Engine settings, read only for kinds 2, 3 and 6 (bps: 10000 = 100%; cooldown in seconds; USDG in whole units)
const SPOT = { targetBps: 7000, bandBps: 500, cooldown: 600, payoutBps: 5000, maxTradeUsdg: 500n, maxDailyTurnoverUsdg: 5000n };
const REBALANCE = { targetBps: 7000, bandBps: 300, cooldown: 600, payoutBps: 5000, buyBps: 1000, sellBps: 1000, dailyBuyBps: 3000, dailySellBps: 3000 };
const RESERVE = { reserveBps: 2000, cashFloorBps: 0, dipBudgetBps: 0, maxDipBuys: 0 };
const POLICY_KEY: Record<number, Hex> = { // the policy registered for each engine kind on mainnet
2: '0x0bbb627010933ceb50697c684afa18c7a8e16bad2db2fa0f0ab599f8c594876b', // V2RebalancePolicy, schema 1
3: '0xc03e442b589ccf8ee11bdac143e02af08155bfcf764d7a59b55ec1ae1eac6c44', // V2TradablePercentRebalancePolicy, schema 3
6: '0xbd4bcc5bf76c731539ed5855053e90ea8822b8886afd75bd751d0f72f792a468', // V2LotReservePolicy, schema 4
};
const METADATA = {
logo: '', // https://hedgefun.trade/media/icons/<sha256>.png from the icon upload, or empty
description: 'A strategy token on TSLA.',
socials: { twitter: '', telegram: '', discord: '', website: '', farcaster: '' },
extraURI: '',
};
const FIRST_BUY_ETH: string = '0'; // ETH the creator spends on the new curve in the launch transaction; '0' = no buy
const BUY_SLIPPAGE_BPS = 300;

// ---------- mainnet addresses ----------
const FACTORY: Address = '0xC78b6A83d75C5612a1A7323702B56038254a6459';
const CURVE_DEPLOYER: Address = '0x501e94d1b06f955C268AE920c42B6289dA5127fd';
const TREASURY_DEPLOYER: Address = '0xD86745C6e81D4095A47979267267c2bf41fBFC46';
const LAUNCH_ROUTER: Address = '0xCcFbB947f841170fA7D220f01B6CbbA0487a5cD8';
const WETH: Address = '0x0Bd7D308f8E1639FAb988df18A8011f41EAcAD73';
const USDG: Address = '0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168';
const V3_FACTORY: Address = '0x1f7d7550B1b028f7571E69A784071F0205FD2EfA';

const structs = [
'struct Request { string name; string symbol; address stock; address creator; uint16 taxBps; uint16 creatorBps; uint32 tp1Bps; uint32 tp2Bps; uint16 dipBps; uint16 stopBps; uint16 lotBps; uint16 bandBpsPerHour; uint96 nonce; uint256 maxFee; uint256 expectedOpenPriceE18; }',
'struct Socials { string twitter; string telegram; string discord; string website; string farcaster; }',
'struct Info { string logo; string description; Socials socials; string extraURI; }',
'struct Defaults { uint256 supply; uint24 lpFee; int24 tickSpacing; uint16 minTaxBps; uint16 maxTaxBps; uint16 protocolBps; uint16 maxCreatorBps; uint16 spikeBps; uint32 spikeSeconds; uint16 sweepTipBps; uint16 snipeBps; uint8 snipeSeconds; uint16 bountyBps; uint16 maxSlippageBps; uint16 maxDeviationBps; uint16 maxBuybackImpactBps; uint32 buybackCooldown; uint256 minLotUsdg; uint256 buybackChunkUsdg; uint256 sellChunkUsdg; uint8 launchFeeCurrency; uint256 launchFeeAmount; }',
'struct EngineConfig { uint32 schema; uint32 engineVersion; bytes32 policyKey; bytes32[3] words; }',
'struct Buy { uint256 amountIn; uint256 minStockReceived; uint256 minFinalOut; uint256 deadline; bool allowPartialFill; }',
'struct Hop { address pool; address tokenOut; }',
];
const abi = parseAbi([
...structs,
'function getDefaults() view returns (Defaults)',
'function publicLaunch() view returns (bool)',
'function listings(address) view returns (address oracle, address v3Pool, uint256 openPriceE18, bool enabled)',
'function listingGates(address) view returns (uint16 maxDeviationBps, uint16 maxSlippageBps, uint64 sellChunkUsdg)',
'function predict(Request) view returns (address token, address treasury, bytes32 terms)',
'function launchWithMetadata(Request, bytes32 terms, Info) payable returns (uint256)',
'function curves(uint256) view returns (address)',
'function MAX_OPENING_TAX_EXEMPTIONS() view returns (uint8)',
'function DEFAULT_SALE_BPS() view returns (uint16)',
'function setCurveConfig(string symbol, uint96 nonce, uint16 saleBps, uint8 snipeSeconds)',
'function setOpeningTaxExemptions(string symbol, uint96 nonce, address[] recipients)',
'function kindManifest(uint8) view returns (uint32 engineVersion, uint32 configSchema, bytes32 creationCodeHash, uint256 capabilities)',
'function setStrategyKind(string symbol, uint96 nonce, uint8 kind)',
'function setEngineConfig(string symbol, uint96 nonce, uint8 kind, EngineConfig config)',
'function strategyKindOf(bytes32) view returns (uint8)',
'function launchAndBuy(Request, bytes32 terms, Info, Buy, Hop[]) payable returns (uint256 id, uint256 tokensOut, uint256 stockRefund)',
'function getPool(address, address, uint24) view returns (address)',
'function liquidity() view returns (uint128)',
'function fee() view returns (uint24)',
'function getTokenInfo() view returns (address deployer, string logo, string description, Socials socials)',
'function balanceOf(address) view returns (uint256)',
// errors, so that a revert is reported by name
'error NotOpen()', 'error NotListed()', 'error BadRequest()', 'error Restated()', 'error Unseedable()',
'error BadCurveConfig()', 'error BadOpeningTaxExemptions()', 'error BadKind()', 'error BadPolicy()',
'error BadEngineConfig()', 'error BadConfig()', 'error TreasuryDeployFailed()',
'error StopInsideExecutionFriction(uint256 stopBps, uint256 minimumExclusiveBps)', 'error BadPayment()',
]);
const events = parseAbi([
'event Launched(uint256 indexed id, string symbol, address token, address treasury, address hook, address stock, address creator)',
]);

const chain = { ...robinhood, rpcUrls: { default: { http: [RPC_URL] } } };
const client = createPublicClient({ chain, transport: http(RPC_URL) });
const wallet = createWalletClient({ chain, transport: http(RPC_URL), account: creator });

async function send(address: Address, functionName: string, args: unknown[], value = 0n) {
const { request } = await client.simulateContract({ address, abi, functionName, args, value, account: creator } as never);
const hash = await wallet.writeContract(request as never);
const receipt = await client.waitForTransactionReceipt({ hash });
if (receipt.status !== 'success') throw new Error(`${functionName} reverted: ${hash}`);
console.log(`${functionName}: ${hash}`);
return receipt;
}
const read = (address: Address, functionName: string, args: unknown[] = []) =>
client.readContract({ address, abi, functionName, args } as never) as Promise<any>;

// ---------- 1. checks ----------
const defaults = await read(FACTORY, 'getDefaults');
if (!(await read(FACTORY, 'publicLaunch'))) throw new Error('Public launching is not open yet');
if (defaults.launchFeeCurrency !== 1) throw new Error('This script pays the launch fee in ETH');
if (TAX_BPS < defaults.minTaxBps || TAX_BPS > defaults.maxTaxBps || CREATOR_BPS > defaults.maxCreatorBps) {
throw new Error(`taxBps ${defaults.minTaxBps}..${defaults.maxTaxBps}, creatorBps at most ${defaults.maxCreatorBps}`);
}
const [, stockPool, openPriceE18, listed] = await read(FACTORY, 'listings', [STOCK]);
if (!listed) throw new Error('Stock is not listed');
const cap = Number(await read(CURVE_DEPLOYER, 'MAX_OPENING_TAX_EXEMPTIONS'));
if (EXEMPT.length > cap) throw new Error(`At most ${cap} exemptions`);
const [engineVersion, schema] = await read(TREASURY_DEPLOYER, 'kindManifest', [KIND]);
if (KIND === 5 || KIND === 6) {
// Cycle and Lots with reserve: the treasury refuses rungs under 2 x (slippage + pool fee) only at launch
const [, maxSlippageBps] = await read(FACTORY, 'listingGates', [STOCK]);
const floor = 2 * (maxSlippageBps + Number(await read(stockPool, 'fee')) / 100);
if (RULE.tp1Bps < floor || RULE.dipBps < floor) throw new Error(`tp1Bps and dipBps must be at least ${floor}`);
}

// ---------- 2. register the creator's choices ----------
await send(CURVE_DEPLOYER, 'setCurveConfig', [SYMBOL, NONCE, await read(CURVE_DEPLOYER, 'DEFAULT_SALE_BPS'), SNIPE_SECONDS]);
if (EXEMPT.length > 0) await send(CURVE_DEPLOYER, 'setOpeningTaxExemptions', [SYMBOL, NONCE, EXEMPT]);
if (schema === 0) {
if (KIND !== 0) await send(TREASURY_DEPLOYER, 'setStrategyKind', [SYMBOL, NONCE, KIND]);
} else {
// an engine kind: setEngineConfig records the kind and its three config words together
const word = (v: bigint) => pad(toHex(v), { size: 32 });
const head = (c: { targetBps: number; bandBps: number; cooldown: number; payoutBps: number }) =>
word(BigInt(c.targetBps) | (BigInt(c.bandBps) << 16n) | (BigInt(c.cooldown) << 32n) | (BigInt(c.payoutBps) << 64n));
const usdg = (v: bigint) => word(v * 10n ** 6n); // USDG has 6 decimals
const words =
schema === 1 ? [head(SPOT), usdg(SPOT.maxTradeUsdg), usdg(SPOT.maxDailyTurnoverUsdg)]
: schema === 3 ? [head(REBALANCE), word(BigInt(REBALANCE.buyBps) | (BigInt(REBALANCE.sellBps) << 16n)),
word(BigInt(REBALANCE.dailyBuyBps) | (BigInt(REBALANCE.dailySellBps) << 16n))]
: schema === 4 ? [word(BigInt(RESERVE.reserveBps)),
word(BigInt(RESERVE.cashFloorBps) | (BigInt(RESERVE.dipBudgetBps) << 16n) | (BigInt(RESERVE.maxDipBuys) << 32n)), word(0n)]
: undefined;
if (!words) throw new Error(`Unknown config schema ${schema}`);
await send(TREASURY_DEPLOYER, 'setEngineConfig', [SYMBOL, NONCE, KIND, { schema, engineVersion, policyKey: POLICY_KEY[KIND], words }]);
}

// ---------- 3. predict ----------
const request = {
name: NAME, symbol: SYMBOL, stock: STOCK, creator: creator.address, taxBps: TAX_BPS, creatorBps: CREATOR_BPS,
...RULE, bandBpsPerHour: 0, nonce: NONCE, maxFee: defaults.launchFeeAmount, expectedOpenPriceE18: openPriceE18,
};
const [token, treasury, terms] = await read(FACTORY, 'predict', [request]);
console.log('predicted token', token, 'treasury', treasury);

// ---------- 4. launch ----------
let receipt: TransactionReceipt;
if (FIRST_BUY_ETH === '0') {
receipt = await send(FACTORY, 'launchWithMetadata', [request, terms, METADATA], defaults.launchFeeAmount);
} else {
// ETH reaches the stock as WETH -> USDG -> stock; use the WETH/USDG pool with the most liquidity
let wethPool: Address = zeroAddress, best = -1n;
for (const fee of [100, 500, 3000, 10000]) {
const pool: Address = await read(V3_FACTORY, 'getPool', [WETH, USDG, fee]);
if (pool === zeroAddress) continue;
const liquidity: bigint = await read(pool, 'liquidity');
if (liquidity > best) [wethPool, best] = [pool, liquidity];
}
const path = [{ pool: wethPool, tokenOut: USDG }, { pool: stockPool, tokenOut: STOCK }];
const amountIn = parseEther(FIRST_BUY_ETH);
const deadline = (await client.getBlock()).timestamp + 600n;
const buy = { amountIn, minStockReceived: 1n, minFinalOut: 1n, deadline, allowPartialFill: false };
const value = defaults.launchFeeAmount + amountIn;
const { result } = await client.simulateContract({
address: LAUNCH_ROUTER, abi, functionName: 'launchAndBuy', args: [request, terms, METADATA, buy, path], value, account: creator,
});
buy.minFinalOut = (result[1] * BigInt(10_000 - BUY_SLIPPAGE_BPS)) / 10_000n;
receipt = await send(LAUNCH_ROUTER, 'launchAndBuy', [request, terms, METADATA, buy, path], value);
}

// ---------- 5. read the result ----------
const [launched] = parseEventLogs({ abi: events, eventName: 'Launched', logs: receipt.logs });
const id = launched.args.id;
const salt = keccak256(encodeAbiParameters([{ type: 'string' }, { type: 'address' }, { type: 'uint96' }], [SYMBOL, creator.address, NONCE]));
console.log({
id,
token: launched.args.token,
treasury: launched.args.treasury,
curve: await read(FACTORY, 'curves', [id]),
kind: await read(TREASURY_DEPLOYER, 'strategyKindOf', [salt]),
metadata: await read(launched.args.token, 'getTokenInfo'),
creatorTokens: await read(launched.args.token, 'balanceOf', [creator.address]),
});

Each run of the script sends the registrations again, which is harmless before the launch. After a launch, the same SYMBOL and NONCE from the same wallet cannot launch again (TokenDeployFailed); change NONCE.

Dry-run on a fork​

Run the script unchanged against a local fork of mainnet with anvil:

anvil --fork-url https://rpc.mainnet.chain.robinhood.com --port 8545
# anvil's default accounts carry an EIP-7702 delegation on mainnet; clear it on the fork for account 0
cast rpc --rpc-url http://127.0.0.1:8545 anvil_setCode 0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 0x
# only while publicLaunch() is false: open it on the fork as the owner Safe
SAFE=0x2910117dd2cB431173Ae9Fb6eAF30726321d1693
cast rpc --rpc-url http://127.0.0.1:8545 anvil_impersonateAccount $SAFE
cast rpc --rpc-url http://127.0.0.1:8545 anvil_setBalance $SAFE 0xDE0B6B3A7640000
cast send --rpc-url http://127.0.0.1:8545 --unlocked --from $SAFE \
0xC78b6A83d75C5612a1A7323702B56038254a6459 'setPublicLaunch(bool)' true
RPC_URL=http://127.0.0.1:8545 CREATOR_KEY=<anvil account 0 key> npx tsx launch.mts

Parameter cheat sheet​

fieldmeaningalloweddefault or example
name, symbolthe token's name and tickersymbol + creator + nonce not launched before
stockthe listed stock the curve and pool trade againstlistings(stock).enabledTSLA
creatorreceives creatorBps of the collected tax; sends the registrationsan EOA or a Safethe launching wallet
taxBpstotal trading taxminTaxBps..maxTaxBps: 100..1500100 (release recommendation)
creatorBpsthe creator's share of the collected tax0..maxCreatorBps: 0..50000 (release recommendation)
tp1Bps, tp2Bps, dipBps, stopBps, lotBpsthe treasury's rule; meaning per kind in Conceptstp2Bps 0 or above tp1Bps (BadConfig); kinds 5 and 6: tp1Bps, dipBps at least the rung floor; kinds 1, 4, 5: a nonzero stopBps above slippage + pool fee + bounty (115 on TSLA)creator's choice; the script uses 300, 600, 300, 0, 5000
bandBpsPerHourthe staleness band0..bandCeiling(stock)0
nonceseparates launches of one symbol by one creatoruint960
maxFeethe most the launcher pays as feelaunchFeeAmount0.0005 ETH
expectedOpenPriceE18the listing's opening pricelistings(stock).openPriceE18read it
saleBpsshare of supply sold on the curveDEFAULT_SALE_BPS() only7931
snipeSecondsopening window with the surcharge0..1803 without a registration
exemptionswallets that skip the surchargeup to 40; not zero, not the creator, no duplicatesyour buyers
kindthe treasury's strategy0..kindCount()-1 (0..6)0
Spot (2)targetBps, bandBps, cooldown, payoutBps; maxTradeUsdg, maxDailyTurnoverUsdgtarget 2000..9000; band at least 2 × (slippage + pool fee + bounty), under the target, target + band under 10000; cooldown at least 60 s; payout 0..10000; max trade from minLotUsdg (5 USDG) to sellChunkUsdg (2,000 USDG); daily from max trade to 24 × max tradescript: 7000, 500, 600, 5000; 500, 5,000 USDG
Rebalance (3)targetBps, bandBps, cooldown, payoutBps; buyBps, sellBps; daily buy and sell bpstarget 2000..9000; band at least pool fee + bounty, under the target, target + band under 10000; cooldown at least 60 s; buy and sell 1..2500; daily 1..10000script: 7000, 300, 600, 5000; 1000, 1000; 3000, 3000
Lots with reserve (6)reserveBps, cashFloorBps, dipBudgetBps, maxDipBuysreserve 0..5000; floor and budget 0..10000; dip buys 0..65535; 0 disables each limitscript: 2000, 0, 0, 0

If it reverts​

errorcausefix
NotOpenpublic launching is closed and the sender is not an authorised launcherwait for publicLaunch()
BadCurveConfigsaleBps is not 7931, or snipeSeconds is over 180use DEFAULT_SALE_BPS() and 0..180
BadOpeningTaxExemptionsmore than 40 wallets, the zero address, the creator, or a duplicatetrim the list; leave the creator out
BadKindsetStrategyKind on an engine kind, or a kind that does not existuse setEngineConfig for kinds 2, 3, 6
BadPolicythe policy key does not belong to the kinduse the key for that kind
BadEngineConfigengine words outside the bounds in the cheat sheetfix the settings
StopInsideExecutionFriction(stop, min)a nonzero stopBps at or under slippage + pool fee + bounty0, or above min
BadConfigtp2Bps is neither 0 nor above tp1Bpsfix the rule
BadRequestat launch: taxBps or creatorBps out of bounds, bandBpsPerHour over the ceiling, or msg.value not equal to the feeread getDefaults(); send exactly launchFeeAmount
Restateda registration changed after predict, or maxFee or expectedOpenPriceE18 not the live valuesregister the quoted values again, or predict again
TreasuryDeployFailedkind 5 or 6 with tp1Bps or dipBps under the rung floor, or kind 6 with reserveBps over 5000raise the rungs; keep the reserve at or under 5000
TokenDeployFailedthis creator already launched this symbol with this noncechange NONCE
no reasonpredict on a stock that is not listedpick a listed stock