.jpg)
TL;DR
- Base changes speed on November 5, 2026. The Denim upgrade is scheduled to replace 2-second blocks and Flashblocks with a canonical block every 200 ms. Build your listener on standard block and log subscriptions, not on Flashblocks streams.
- There is no public mempool to watch. Base runs a single sequencer that orders transactions by priority fee and arrival time, so a Base bot reacts to events that are already included.
- Launchpads now tax snipers on purpose. Clanker v4.1 lets creators start the swap fee at 80% and decay it over up to two minutes. Entering in the first second can cost three quarters of your position.
- The safety check module matters more than speed. Simulate the buy and the sell before every entry, and refuse anything you cannot exit.
- RPC is part of the strategy. RPC Fast serves Base over HTTPS and WebSocket on every plan, with a free tier and 85 ms average global latency on its Base API.
A Base sniper bot is a program that listens for new pools or token launches on Base, filters and safety-checks them in milliseconds, and buys within the next block or two. On Base that means reacting to included events rather than a mempool, paying a priority fee to get ordered first, and enforcing hard limits on size, frequency and losses, because most launches are not worth entering.
When Jesse Pollak launched his JESSE creator coin on Base in November 2025, two wallets bought a large share of the pool in the same on-chain block as the launch. According to DL News, one of them spent about $190,000, paid more than $44,000 in transaction fees and sold for roughly $626,000 in profit 15 minutes later. That story gets retold as proof that sniping works. It also shows what you are competing with: bots ready to spend tens of thousands of dollars on priority fees for a single launch.
This guide is for building something more modest and much less likely to blow up: a Base sniper bot with a clear architecture, a real safety check module, and limits that stop it before a bad week becomes a lost account. We run RPC infrastructure, so RPC choices come up where they change outcomes. The rest is about the bot.
What a sniper bot is, and why most launches lose money
A sniper bot buys new tokens faster than humans can, and most of what it buys goes down. Your edge comes from filtering, not from speed alone.
What a sniper bot is
A sniper bot watches the chain for one kind of event, usually a new liquidity pool or a launchpad deployment, and submits a buy as soon as the event appears. The bet is simple: the first buyers get the lowest price, and if attention follows the launch, they can sell into it.
Everything else in a serious bot exists to answer two questions in under a few hundred milliseconds. Is this token safe to hold for a few minutes, and is this trade allowed under my risk rules? Speed without those answers is how sniper bots fund other people's exits.
The reality of launches
Most tokens launched on any memecoin launchpad lose nearly all of their value, and Base is no exception. Clanker alone had created more than 355,000 tokens by August 2025, according to Cointelegraph. A bot that buys a meaningful share of that flow buys mostly tokens nobody else will want an hour later.
Launch design has also moved against snipers. Clanker v4 ships MEV modules that delay trading by two blocks, auction the first swaps, or charge a descending fee that starts as high as 80%. Flaunch runs a fixed-price window at launch so early buyers all pay the same price. Each of these removes some of the advantage a pure speed race used to give you.
So the honest framing is this: a sniper bot is a filter with a fast trigger. If your filter is weak, a faster trigger only loses money sooner.
Who this is for and what it won't cover
This guide is for developers who can read TypeScript, run a Node.js process on a VPS, and debug a reverted transaction. You do not need to be a protocol engineer.
It covers architecture, an event listener, filters, a safety check placeholder you will extend, risk limits, a confirmation mode and testing. It does not cover multi-wallet bundling, wash trading, deployer-coordinated launches or anything designed to hide one actor behind many wallets. Those are covered briefly in the legal section, and the short answer is: don't.
How Base works for a bot
Base is an EVM L2 with a single sequencer, no public mempool and, from November 5, 2026, 200 ms canonical blocks. Those three facts shape every design choice below.
Base as an EVM L2
Base is Coinbase's Ethereum layer 2. It started on the OP Stack and, since the Azul upgrade in May 2026, ships its own network upgrades and clients such as base-reth-node. For a bot developer, the important part is that Base is fully EVM: the same Solidity contracts, ABIs, viem or ethers code and Uniswap deployments you know from Ethereum. Mainnet chain ID is 8453 and Base Sepolia is 84532.
Ordering is where Base differs from Ethereum. There is one sequencer, operated by Coinbase, and your transaction goes straight to it. Base's documentation says transactions are ordered by priority fee and arrival time. Nobody can see your pending buy and sandwich it on the way in, and you cannot see theirs either.
The timing is changing right now. Since July 2025 Base has produced a 2-second block split into ten 200 ms Flashblocks, which gave apps preconfirmations inside the block. The Denim upgrade, live on Base Sepolia since October 21, 2026 and targeting Base Mainnet on November 5, 2026 at 18:00 UTC, replaces that with a full canonical block every 200 ms, each with its own number, hash and receipts. Flashblocks streams stop at activation.
Two practical consequences. First, block numbers advance ten times faster, so any logic in your bot that converts blocks to time (cooldowns, timeouts, "wait three blocks") needs to be rewritten in milliseconds. Second, if you built on the Flashblocks pending state, migrate to newHeads and log subscriptions before activation. The code in this guide uses only those.
Which events matter
A Base sniper bot listens for a small set of contract events. Which ones depends on where you want to enter.
The Uniswap v4 PoolManager on Base mainnet lives at 0x498581fF718922c3f8e6A244956aF099B2652b2b, per Uniswap's deployment list. Take the Sepolia address from the same page rather than from a blog post, including this one.
Where the data comes from
Your bot gets everything through an RPC endpoint: a WebSocket for subscriptions (eth_subscribe to newHeads and logs) and HTTPS for calls, simulations and sending transactions. The public endpoint at mainnet.base.org is rate-limited and fine for reading docs, not for a bot.
With 200 ms blocks, the WebSocket carries five new heads per second plus every matching log, and your simulation calls compete for the same budget. A provider that drops subscriptions under load or adds 100 ms of jitter on eth_call eats most of a block. Measure it from the server your bot will run on, not from your laptop.
Bot architecture
Split the bot into six modules with clear inputs and outputs. You will rewrite the filter and safety check many times, and you don't want to touch the execution code each time.
Event listener
The listener keeps a WebSocket open, subscribes to the events from the table above, and turns each log into a small typed object: pool ID, currencies, hook, fee, block number, transaction hash and log index. It does no thinking. Its job is to stay connected, reconnect fast when the socket drops, and never emit the same event twice.
Filter
The filter throws away most events in under a millisecond using data you already have: is the pair WETH or native ETH, is the hook on your allowlist or blocklist, is the deployer on your blocklist, have you seen this pool before. Cheap checks first, network calls later.
Safety check module
This is where the bot reads the chain. It fetches bytecode, simulates a small buy and an immediate sell, checks the effective tax in both directions and looks for owner powers such as minting, pausing or blacklisting. It is the slowest stage and the one that saves you money.
Decision module
The decision module takes a safe candidate and applies risk rules: maximum size per trade, maximum trades per hour, daily loss limit, current exposure and a global kill switch. It returns either a sized order or a reason for refusing.
Execution
Execution builds the swap, sets the priority fee within your cap, signs, sends and waits for a receipt. On Base you compete on priority fee and on arrival time, so a warm connection and a pre-fetched nonce matter more than clever encoding.
Accounting and logs
Accounting records every decision, including refusals, and every fill with its real cost. Without this you cannot tell a bad filter from bad luck, and you will tune the wrong thing.
Environment setup
Use Node.js with TypeScript and viem, start on Base Sepolia, and keep every number that controls risk in configuration rather than code.
Tools
- Node.js 20+ and TypeScript: the examples use tsx to run TypeScript directly.
- viem: a typed Ethereum library with WebSocket subscriptions, simulation and Base chain definitions built in.
- Foundry (anvil, cast): for forking Base locally and replaying launches against real state.
- A Base RPC endpoint with WebSocket support: one for the bot and a second provider for failover.
- A dedicated hot wallet: funded only with what the bot is allowed to lose.
mkdir base-sniper && cd base-sniper
npm init -y
npm install viem dotenv
npm install -D typescript tsx @types/nodeTest network
Base Sepolia already runs Denim, so it is the closest you can get to post-upgrade mainnet behaviour today. Get test ETH from a Base Sepolia faucet, deploy your own test token and pool, and watch your bot detect and trade it. Deploying your own launches is the only way to test the full path on demand.
For realistic token behaviour, fork mainnet with anvil and replay specific blocks. A fork lets you run the safety check against tokens that actually were honeypots, without spending anything.
Configuration
Put endpoints, mode and limits in a .env file. The default mode is dry-run, so a fresh checkout cannot spend money by accident.
# .env
BASE_WSS_URL=wss://your-base-endpoint
BASE_HTTP_URL=https://your-base-endpoint
CHAIN_ID=84532 # 84532 Base Sepolia, 8453 Base Mainnet
POOL_MANAGER=0x... # Uniswap v4 PoolManager for this chain
UNIVERSAL_ROUTER=0x... # Uniswap Universal Router for this chain
MODE=dry-run # dry-run | confirm | live
MAX_BUY_ETH=0.01
MAX_BUYS_PER_HOUR=3
DAILY_LOSS_LIMIT_ETH=0.05
MAX_PRIORITY_FEE_GWEI=0.05
PRIVATE_KEY=0x... # hot wallet only, never your main wallet// config.ts
import 'dotenv/config';
import { parseEther, parseGwei } from 'viem';
export const CONFIG = {
wssUrl: process.env.BASE_WSS_URL!,
httpUrl: process.env.BASE_HTTP_URL!,
chainId: Number(process.env.CHAIN_ID ?? 84532),
poolManager: process.env.POOL_MANAGER as `0x${string}`,
router: process.env.UNIVERSAL_ROUTER as `0x${string}`,
mode: (process.env.MODE ?? 'dry-run') as 'dry-run' | 'confirm' | 'live',
maxBuy: parseEther(process.env.MAX_BUY_ETH ?? '0.01'),
maxBuysPerHour: Number(process.env.MAX_BUYS_PER_HOUR ?? 3),
dailyLossLimit: parseEther(process.env.DAILY_LOSS_LIMIT_ETH ?? '0.05'),
maxPriorityFee: parseGwei(process.env.MAX_PRIORITY_FEE_GWEI ?? '0.05'),
};Step-by-step implementation
Build the bot in five steps, and keep it in dry-run until each step has logged correct decisions on real events.
Event subscription
Subscribe to Initialize on the v4 PoolManager and assert the chain ID at startup. A bot pointed at mainnet with Sepolia addresses fails quietly, which is the worst way to fail.
// listener.ts
import { createPublicClient, webSocket, parseAbiItem } from 'viem';
import { base, baseSepolia } from 'viem/chains';
import { CONFIG } from './config';
import { onNewPool } from './pipeline';
export const client = createPublicClient({
chain: CONFIG.chainId === 8453 ? base : baseSepolia,
transport: webSocket(CONFIG.wssUrl, { reconnect: { attempts: 100, delay: 250 } }),
});
const initialize = parseAbiItem(
'event Initialize(bytes32 indexed id, address indexed currency0, address indexed currency1, ' +
'uint24 fee, int24 tickSpacing, address hooks, uint160 sqrtPriceX96, int24 tick)'
);
export async function start() {
const id = await client.getChainId();
if (id !== CONFIG.chainId) throw new Error(`Wrong chain: ${id}, expected ${CONFIG.chainId}`);
client.watchEvent({
address: CONFIG.poolManager,
event: initialize,
onLogs: (logs) => logs.forEach((log) => onNewPool(log).catch(console.error)),
onError: (err) => console.error('[listener]', err.message),
});
console.log(`[listener] watching PoolManager on chain ${id}, mode=${CONFIG.mode}`);
}Add a second subscription to newHeads and log the gap between consecutive heads. After Denim it should sit near 200 ms. When it jumps, your WebSocket is lagging, and you want to know that before you trust any fill timing.
Parsing and filtering
Parse the log into a candidate and run the cheap filters. On Uniswap v4, native ETH is address(0), and WETH on Base is 0x4200000000000000000000000000000000000006. Deduplicate on transaction hash plus log index, because reconnecting subscriptions can replay recent logs.
// filter.ts
import { zeroAddress } from 'viem';
const WETH = '0x4200000000000000000000000000000000000006';
const seen = new Set<string>();
const BLOCKED_HOOKS = new Set<string>([/* hook addresses you refuse to trade */]);
export type Candidate = { poolId: `0x${string}`; token: `0x${string}`; hooks: `0x${string}`; fee: number; block: bigint };
export function toCandidate(log: any): Candidate | null {
const key = `${log.transactionHash}:${log.logIndex}`;
if (seen.has(key)) return null;
seen.add(key);
const { id, currency0, currency1, hooks, fee } = log.args;
const isEth = (a: string) => a === zeroAddress || a.toLowerCase() === WETH.toLowerCase();
if (!isEth(currency0) && !isEth(currency1)) return null; // only ETH pairs
if (BLOCKED_HOOKS.has(hooks.toLowerCase())) return null;
const token = isEth(currency0) ? currency1 : currency0;
return { poolId: id, token, hooks, fee, block: log.blockNumber };
}The hook address is the most useful field on Base. A Clanker pool, a Flaunch pool and a plain v4 pool behave very differently at launch. Keep a small map from known hook addresses to launch rules, and refuse unknown hooks until you have read their code.
Safety check placeholder
This is a placeholder on purpose. It shows the shape of the module and the checks that belong in it, and it fails closed: if a check cannot run, the answer is no.
// safety.ts
import { client } from './listener';
import type { Candidate } from './filter';
export async function safetyCheck(c: Candidate): Promise<{ ok: boolean; reasons: string[] }> {
const reasons: string[] = [];
try {
const code = await client.getCode({ address: c.token });
if (!code || code === '0x') reasons.push('token has no bytecode');
// TODO 1: simulate a small buy, then an immediate sell of the received amount
// (eth_call with state overrides, or a local anvil fork). No sell = no buy.
// TODO 2: compute effective buy and sell tax from the simulation; compare to MAX_TAX_BPS.
// TODO 3: check for owner powers: mint, pause, blacklist, setFee, upgradeable proxy.
// TODO 4: read launchpad rules from the hook (descending fee, auction, delay).
// TODO 5: deployer history against your own blocklist.
} catch (e) {
reasons.push(`check failed: ${(e as Error).message}`); // fail closed
}
return { ok: reasons.length === 0, reasons };
}TODO 4 deserves its own paragraph, because it is specific to Base. Clanker's descending fee module lets a deployer start the LP fee as high as 80% and decay it over up to two minutes. The recommended configuration goes from 80% to 5% over 30 seconds, with the fee falling along a quadratic curve. If your bot ignores this, it will report a "successful" buy that lost most of its value to fees in the same transaction.
The practical rule: compute the fee at the moment your transaction is likely to land, not when you saw the event, and skip the trade if the expected fee is above your ceiling. Often the best entry on a Clanker launch is 20 to 30 seconds in, when the fee has dropped and the first wave of bots is already trying to sell.
Amount and frequency limits
Limits live in their own small class and every buy goes through it. If the gate says no, nothing upstream can override it.
// risk.ts
import { CONFIG } from './config';
export class RiskGate {
private buys: number[] = [];
private realizedLoss = 0n;
private halted = false;
allow(amount: bigint): { ok: boolean; reason?: string } {
if (this.halted) return { ok: false, reason: 'kill switch on' };
if (amount > CONFIG.maxBuy) return { ok: false, reason: 'above max buy' };
const hourAgo = Date.now() - 3_600_000;
this.buys = this.buys.filter((t) => t > hourAgo);
if (this.buys.length >= CONFIG.maxBuysPerHour) return { ok: false, reason: 'hourly cap' };
if (this.realizedLoss >= CONFIG.dailyLossLimit) {
this.halted = true;
return { ok: false, reason: 'daily loss limit hit' };
}
return { ok: true };
}
recordBuy() { this.buys.push(Date.now()); }
recordPnl(pnl: bigint) { if (pnl < 0n) this.realizedLoss += -pnl; }
halt() { this.halted = true; }
}Note what is missing: there is no "resume" method. Resetting the daily loss counter should require restarting the process, which means a human looked at what happened.
Confirmation mode
Run three modes. Dry-run logs the order it would send and stops. Confirm builds and simulates the transaction, prints it and waits for you to type yes. Live sends without asking. Confirm mode is too slow to win a first-block race, and that is fine: it is how you check, with real money and small size, that the bot's decisions match yours.
// execute.ts (sending part)
import { createWalletClient, http } from 'viem';
import { privateKeyToAccount } from 'viem/accounts';
import { base, baseSepolia } from 'viem/chains';
import readline from 'node:readline/promises';
import { CONFIG } from './config';
import { client } from './listener';
const wallet = createWalletClient({
account: privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`),
chain: CONFIG.chainId === 8453 ? base : baseSepolia,
transport: http(CONFIG.httpUrl),
});
export async function send(tx: { to: `0x${string}`; data: `0x${string}`; value: bigint }) {
const fees = await client.estimateFeesPerGas();
const tip = fees.maxPriorityFeePerGas! > CONFIG.maxPriorityFee ? CONFIG.maxPriorityFee : fees.maxPriorityFeePerGas!;
const request = { ...tx, maxPriorityFeePerGas: tip, maxFeePerGas: fees.maxFeePerGas! + tip };
if (CONFIG.mode === 'dry-run') { console.log('[dry-run]', request); return null; }
if (CONFIG.mode === 'confirm') {
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
const answer = await rl.question(`Send ${request.value} wei to ${request.to}? (yes/no) `);
rl.close();
if (answer.trim() !== 'yes') return null;
}
const hash = await wallet.sendTransaction(request);
return client.waitForTransactionReceipt({ hash, timeout: 10_000 });
}Building the swap calldata itself (the data field) depends on the pool type. For v4 pools that means a V4_SWAP command through the Universal Router; use Uniswap's v4 SDK rather than hand-encoding it, and simulate the full transaction with simulateContract or eth_call before every send. Always set a minimum output. A sniper bot without slippage protection is a donation contract.
The priority fee cap is a risk control, not just a cost setting. The JESSE sniper paid more than $44,000 in fees to win one launch. Your bot should never get into that auction by accident.
Risk management
Risk management for a Base token sniper is three things: limits that cap the damage, stop rules that end a bad run, and logs that tell you why.
Limits
These are starting points, not recommendations for your capital. Tighten them first and loosen them only after a few hundred logged decisions tell you the filter works.
Stop rules
Every position needs three exits defined at entry: a take-profit level, a stop-loss level and a maximum holding time. The time limit is the one people skip and the one that matters most on launches, because a token that has not moved in the first minutes rarely moves later.
The bot also needs stop rules for itself. Halt on three reverted transactions in a row, on a wallet balance below the gas floor, on RPC p99 latency above your threshold for a minute, and on any position the safety check cannot sell in simulation. Halting costs you a few missed launches. Not halting can cost the account.
What to log
- Per event: block number, timestamp in ms (use timestampMs after Denim), pool ID, hook, filter result and reason.
- Per safety check: each check's result, simulated buy and sell output, effective tax, and how long the check took.
- Per order: mode, size, priority fee, submit time, inclusion block, gas used, expected versus actual output.
- Per position: entry, exit, exit reason, hold time and net PnL after fees and gas.
Log refusals as carefully as fills. The tokens your bot skipped, and what they did next, are how you find out whether a filter is protecting you or just costing you trades.
Testing
Test in three stages: testnet launches you control, a mainnet dry run on real events, and a short checklist before any real money moves.
Testnet tests
On Base Sepolia, deploy a plain token with a v4 pool, then a token with a transfer tax, then one that blocks sells. Your bot should buy the first, refuse or size down the second, and refuse the third. If it buys the honeypot, the safety check is not finished, whatever the unit tests say.
Also test failure paths: kill the WebSocket mid-run, send a malformed log, drain the wallet below the gas floor, and trigger the hourly cap. Each should produce a clear log line and a safe state.
Mainnet dry run
Point the bot at Base Mainnet in dry-run mode for at least a few days. It will see real launches, run real safety checks and write down the orders it would have sent. Then compare: of the tokens it would have bought, how many could you have sold at a profit after fees, and how many of its refusals were right?
This step also shows you your real latency. Log the time from event to "order ready". If it is regularly over 200 ms, you are competing for the block after next, and your strategy should assume that.
Pre-launch checklist
- Chain ID asserted at startup, addresses taken from official deployment pages
- No Flashblocks or pending-state dependencies left after Denim
- Safety check fails closed and simulates a sell before every buy
- Launchpad fee or auction rules read from the hook before entry
- Slippage minimum set on every swap
- All limits in config, kill switch tested, no automatic resume
- Hot wallet funded with only the amount the bot may lose
- Second RPC provider configured for failover
- Confirm mode run on mainnet with minimum size before live
Legal and ethical limits
Buying a public launch quickly is legal in most places. Some of what gets called sniping is not, and the line matters more on a chain run by a regulated company.
Platform and token rules
Launchpads set rules on purpose. A descending fee, a two-block delay or a sniper auction is the creator telling you how early entry works on this token. Working within those rules is trading. Writing code to get around them, for example by splitting buys across many wallets to dodge per-wallet limits, is the kind of behaviour launchpads and analysts flag, and it tends to end in blocklists.
Also check the terms of every service you use, including your RPC provider and any API you call for token data. Automated trading is usually allowed. Abusive request patterns are usually not.
Law
Rules on automated crypto trading differ by country. Common problems for bot operators are taxes on every realized trade, market manipulation rules when a bot coordinates with a token deployer, and securities rules when the asset is a tokenized stock rather than a memecoin. Base is operated by Coinbase, a public company with regulatory obligations, and onchain activity is permanent and traceable. If you are trading meaningful size, pay for legal and tax advice in your jurisdiction. This article is not that advice.
Honest risk statement
A Base sniper bot can lose all of the capital you give it. Most launches lose value, competitors spend more on fees than you will, launchpad rules change without notice, and a single bug in a filter can drain a wallet in minutes. The architecture in this guide reduces those risks; it does not remove them. Start on testnet, move to mainnet in dry-run, trade with an amount you would be fine losing, and let the logs, not the occasional big win, decide whether the bot keeps running.
Ready to connect your bot to Base?
Create a free RPC Fast account, get Base HTTPS and WebSocket endpoints in minutes, and see the Base API details before you scale up.

.jpg)
.jpg)