A launch buyer with limits
Example
This is an example. Every value in it (names, amounts, addresses) is there to show you how; change any of them to suit you. It’s recipe-launch-buyer.mjs from developers/examples, as it is. Anything that sends waits for SEND=1: there is no testnet, so a sent transaction spends real ETH.
// A bot that buys new launches, within limits you set: only coins whose bundled wallets and snipers hold little,// a fixed amount each, at most MAX_BUYS, never a coin whose round trip loses more than MAX_ROUND_TRIP_LOSS_PCT.// It earns its builder a fee on every buy it makes (feeBps to FEE_RECIPIENT). Without SEND=1 it only reports.// PRIVATE_KEY=0x… SPEND=0.001 MAX_BUYS=3 FEE_RECIPIENT=0x… SEND=1 node recipe-launch-buyer.mjsimport { JsonRpcProvider, Wallet } from 'ethers';
const API = process.env.UPRALLY_API ?? 'https://uprally.co/api/v1';const SPEND = process.env.SPEND ?? '0.001';const MAX_BUYS = Number(process.env.MAX_BUYS ?? 3);const MAX_BUNDLED = Number(process.env.MAX_BUNDLED_PCT ?? 10);const MAX_SNIPERS = Number(process.env.MAX_SNIPERS_PCT ?? 5);const MAX_LOSS = Number(process.env.MAX_ROUND_TRIP_LOSS_PCT ?? 10);const stopAt = Date.now() + Number(process.env.SECONDS ?? 300) * 1000;
const chain = await (await fetch(`${API}/chain`)).json();const wallet = new Wallet(process.env.PRIVATE_KEY, new JsonRpcProvider(chain.rpc, chain.chainId));const fee = process.env.FEE_RECIPIENT ? { feeBps: 50, feeRecipient: process.env.FEE_RECIPIENT } : {};
let since = (await (await fetch(`${API}/coins/new?limit=1`)).json()).coins[0]?.createdAt ?? new Date().toISOString();let bought = 0;while (bought < MAX_BUYS && Date.now() < stopAt) { const { coins = [] } = await (await fetch(`${API}/coins/new?since=${encodeURIComponent(since)}&limit=60`)).json(); if (coins.length) since = coins[0].createdAt; for (const c of coins) { // Wait for its holders to be counted; skip a coin its bundled wallets or snipers already hold much of. const h = await (await fetch(`${API}/coins/${c.address}/holders`)).json(); if (!h.tracked || h.groups.bundled.heldPct > MAX_BUNDLED || h.groups.snipers.heldPct > MAX_SNIPERS) continue; const res = await fetch(`${API}/swap`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify({ wallet: wallet.address, token: c.address, side: 'buy', amount: SPEND, ...fee }) }); const built = await res.json(); if (!res.ok) { console.log('skip', c.ticker, built.error.code); continue; } // The API sold it straight back in the same simulation: what a round trip costs right now. const lossPct = 100 * (1 - Number(built.sellBack.eth.amount) / Number(SPEND)); if (lossPct > MAX_LOSS) { console.log('skip', c.ticker, `round trip loses ${lossPct.toFixed(1)}%`); continue; } console.log('buy', c.ticker, c.address, built.amountOut.amount, `(round trip -${lossPct.toFixed(1)}%)`); if (process.env.SEND === '1') { const sent = await wallet.sendTransaction({ to: built.tx.to, data: built.tx.data, value: BigInt(built.tx.value), gasLimit: BigInt(built.tx.gas), chainId: built.tx.chainId }); console.log(' sent', `${chain.explorer}/tx/${sent.hash}`); } bought += 1; if (bought >= MAX_BUYS) break; } await new Promise((r) => { setTimeout(r, 3000); });}console.log(`RESULT ${bought}`);