Skip to content

A launch sniper with caps

Tested on BSC testnet before every release

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-sniper.mjs, exactly as our tests run it on testnet. Point it at testnet with MM_API, or leave it for mainnet.

recipe-sniper.mjs
// A launch sniper with caps: buys each new launch once, the moment it is indexed,
// never more than you allow, and takes profit on the way up.
// PRIVATE_KEY=0x… node recipe-sniper.mjs
//
// Every limit is yours to set, and there is no "unlimited":
// BUY=0.01 BNB per launch
// MAX_TOTAL=0.05 BNB across the whole run: no buys after this
// MAX_CAP_BNB=20 skip a launch already worth more than this (market cap, in BNB)
// TAKE_PROFIT=2 sell half a position once it is worth this many times what it cost
// STOP_LOSS=0.5 sell all of it once it is worth this fraction of what it cost
// SECONDS= stop after this long (default: run until stopped)
// ONCE=0x… treat this token as a new launch (to try it out)
import { MarketMayhem } from '@marketmayhem/sdk';
import { Contract, JsonRpcProvider } from 'ethers';
const buy = Number(process.env.BUY ?? '0.01');
const maxTotal = Number(process.env.MAX_TOTAL ?? '0.05');
const maxCapBnb = Number(process.env.MAX_CAP_BNB ?? '20');
const takeProfit = Number(process.env.TAKE_PROFIT ?? '2');
const stopLoss = Number(process.env.STOP_LOSS ?? '0.5');
const stopAt = process.env.SECONDS ? Date.now() + Number(process.env.SECONDS) * 1000 : Infinity;
if (!(buy > 0) || !(maxTotal >= buy)) throw new Error('BUY must be above 0 and no bigger than MAX_TOTAL.');
const mm = MarketMayhem.fromPrivateKey(process.env.PRIVATE_KEY, { api: process.env.MM_API });
const { chainId } = await mm.settings();
const provider = new JsonRpcProvider(chainId === 56 ? 'https://bsc-dataseed.bnbchain.org' : 'https://bsc-testnet-rpc.publicnode.com', chainId);
/** Market cap in BNB, from a tiny quote and the token's supply. */
async function marketCapBnb(token) {
const q = await mm.quote(token, 'buy', '0.001');
const supply = await new Contract(token, ['function totalSupply() view returns (uint256)'], provider).totalSupply();
return (0.001 / Number(q.receive.amount)) * (Number(supply) / 1e18);
}
/** What a position would sell for now, in BNB. */
const worth = async (token, tokens) => Number((await mm.quote(token, 'sell', tokens.toFixed(6))).receive.amount);
let spent = 0;
const positions = new Map(); // token -> { cost, tokens, tookProfit }
const log = [];
async function onLaunch(l) {
if (positions.has(l.token)) return;
if (spent + buy > maxTotal + 1e-12) { console.log(`skip $${l.symbol}: MAX_TOTAL reached`); return; }
const cap = await marketCapBnb(l.token);
if (cap > maxCapBnb) { console.log(`skip $${l.symbol}: worth ${cap.toFixed(2)} BNB, above MAX_CAP_BNB`); return; }
// What we really got: the balance before and after, not the quote.
const erc20 = new Contract(l.token, ['function balanceOf(address) view returns (uint256)'], provider);
const before = await erc20.balanceOf(mm.address);
const r = await mm.buy(l.token, { spend: String(buy) });
spent += buy;
const got = Number((await erc20.balanceOf(mm.address)) - before) / 1e18;
positions.set(l.token, { symbol: l.symbol, cost: buy, tokens: got, tookProfit: false });
log.push({ bought: l.token, hash: r.hash });
console.log(`bought $${l.symbol} for ${buy} BNB at a ${cap.toFixed(3)} BNB market cap: ${r.hash}`);
}
async function manage() {
for (const [token, p] of positions) {
const now = await worth(token, p.tokens);
if (!p.tookProfit && now >= p.cost * takeProfit) {
const r = await mm.sell(token, { percent: 50 });
p.tokens /= 2; p.cost /= 2; p.tookProfit = true;
log.push({ tookProfit: token, hash: r.hash });
console.log(`$${p.symbol} worth ${now.toFixed(4)} BNB: sold half, ${r.hash}`);
} else if (now <= p.cost * stopLoss) {
const r = await mm.sell(token, { percent: 100 });
positions.delete(token);
log.push({ stopped: token, hash: r.hash });
console.log(`$${p.symbol} worth ${now.toFixed(4)} BNB: sold all (stop loss), ${r.hash}`);
}
}
}
if (process.env.ONCE) await onLaunch({ token: process.env.ONCE, symbol: (await mm.token(process.env.ONCE)).symbol });
const ctrl = new AbortController();
const timer = Number.isFinite(stopAt) ? setTimeout(() => ctrl.abort(), stopAt - Date.now()) : null;
const managing = setInterval(() => manage().catch((e) => console.log(`manage: ${e.message}`)), 15_000);
try {
for await (const l of mm.watchLaunches({ signal: ctrl.signal })) await onLaunch(l).catch((e) => console.log(`$${l.symbol}: ${e.message}`));
} catch (e) { if (!ctrl.signal.aborted) throw e; }
clearInterval(managing); if (timer) clearTimeout(timer);
await manage();
console.log(`stopped: ${spent} BNB spent, ${positions.size} open position(s)`);
console.log('RESULT', JSON.stringify({ spent, open: positions.size, log }));