The keeper
The keeper is a Node service in keeper/. It takes launch requests and launches coins on the three chains. It talks to Solana through @solana/web3.js and to Robinhood Chain and BNB Chain through viem.
Modules
| File | What it does |
|---|---|
pump.mjs | pump.fun instructions: create_v2, fee sharing, with simulation (buy and sell exist but are not used in a launch) |
rh.mjs | Robinhood Chain: the launcher and vault ABIs, curve reads |
bnb.mjs | BNB Chain: the launcher ABI, the 7777 salt miner, the upload of the logo and metadata to Flap's gateway |
alt.mjs | The address lookup table that lets create_v2 and the creator's dev buy share one transaction on pump.fun |
treasury.mjs | The float of SOL, ETH and BNB the keeper needs, the floors below which requests pause, and what to top up |
fx.mjs | The SOL, ETH and BNB rates from Coinbase, Binance, OKX and Jupiter, the median, and a flag when they disagree |
pipeline.mjs | The launch as a saved state machine, with retries and refunds |
soldeposit.mjs | Verifies the creator's SOL deposit on Solana: signer, recipient, memo reference, success, amount |
metadata.mjs | Uploads the image and metadata to pump.fun's IPFS endpoint |
adapters.mjs | The real chain calls behind each pipeline step, dry or live |
server.mjs | The HTTP API for the launch page and the loops that watch deposits and run launches |
index.mjs | Dry-run reads of the chains |
The API
| Route | What it does |
|---|---|
GET /api/config | Deposit address, image limits |
POST /api/quote | The deposit for a set of options, itemised |
POST /api/metadata | Uploads the logo (base64, 1.5 MB at most) and the metadata to pump.fun's IPFS and to Flap's gateway, returns the metadata URI, the Pons logo and the Flap CID |
POST /api/requests | Validates the parameters, reads the rate, returns an id, the deposit address, the amount in SOL and the expiry |
POST /api/requests/:id/deposit | Takes the transaction signature of the SOL deposit, reads it on Solana and starts the launch if it is valid |
GET /api/requests/:id | Status and public log of a launch |
GET /api/health | Alive |
Commands
| Command | What it does |
|---|---|
node keeper/index.mjs newest | Dry run on the newest live pump.fun coin and the newest Pons coin: reads both curves and the rate. Sends nothing |
node keeper/launch.mjs params.json | Dry run of a whole launch through the pipeline: simulates the pump.fun creation, the Pons launch and the Flap launch, plans the fee sharing |
node keeper/treasury.mjs | Prints the keeper balances on the three chains, the floors and what to top up |
node keeper/launch.mjs params.json --live | The real launch, with keys |
node keeper/server.mjs | The API and the background loops, dry by default; --live for real launches |
node keeper/check-solana.mjs | Simulates a buy, a sell, a create_v2 and a create_v2 with the fee sharing config against mainnet state |
node keeper/test-rh-fork.mjs | Integration test of the Robinhood path on an anvil fork |
node keeper/test-devbuy-fork.mjs | Integration test of the creator's dev buy on Pons and Flap on anvil forks: tokens in the creator's hands, none left in the contracts, costs booked |
node keeper/alt.mjs | The lookup table for the pump.fun dev buy: addresses and transaction sizes, --create to create it |
node keeper/test-bnb-fork.mjs | Integration test of the BNB Chain path on an anvil fork: live Flap launch with the mined salt, trades, tax and split |
Keys and configuration
| Variable | Used for |
|---|---|
KEEPER_SOL_SECRET | The Solana keeper wallet: deposit address, creator of record on pump.fun |
KEEPER_EVM_KEY | The EVM keeper address, the same on Robinhood Chain and BNB Chain, the only one the contracts accept |
LAUNCHER | The address of PivotLauncher (Robinhood Chain) once it is deployed |
BNB_LAUNCHER | The address of PivotFlapLauncher (BNB Chain) once it is deployed |
SOLANA_RPC, RH_RPC, BNB_RPC | RPC endpoints, public ones by default |
PUMP_ALT | Address of the lookup table, if not in keeper/data/pump-alt.json |
MAX_DEV_BUY_SOL | Largest dev buy per chain, in SOL at the current rate, 10 by default |
SITE_ORIGIN, PORT | CORS origin and port of the API |
Without the keys the keeper stays in dry-run mode and can only read and simulate.
The address lookup table, for a dev buy on pump.fun
create_v2 alone takes 849 bytes of the 1232 a transaction holds, so create and buy do not fit together. pump.fun's own site solves it with an address lookup table that replaces the 24 fixed accounts by one-byte indexes; with it the create and the buy are 828 bytes. Pivot does the same with its own table.
node keeper/alt.mjsprints the accounts and the sizes, and sends nothing.node keeper/alt.mjs --createcreates the table with the keeper wallet, once, for about 0.01 SOL, and saves its address inkeeper/data/pump-alt.json(or setPUMP_ALT).- Without the table the dev buy on pump.fun is switched off: the API says so (
pumpDevBuy: falsein/api/config) and the page disables the box. Nothing is ever sent as a create followed by a separate buy, because a failure between the two followed by a retry would create a second coin. Dev buys on Pons and Flap do not need it.
The float: how one deposit in SOL pays for three chains
The creator pays in SOL. The keeper pays the creation fees and the gas in SOL, ETH and BNB from a float it already holds on each chain, so nobody waits for a swap or a bridge while a launch runs. The deposits bring SOL in; the operator tops up the ETH and BNB floats in batches with that SOL.
node keeper/treasury.mjsprints the three balances, the floors (1 SOL, 0.02 ETH, 0.04 BNB), the targets (5 SOL, 0.1 ETH, 0.2 BNB) and, below a floor, how much to top up and how much SOL of the deposits that is.- The API reads the three balances (cached for a minute). When one is below its floor,
POST /api/requestsanswers 503 "launches are paused for a few minutes while the keeper wallets are refilled", and no money is asked. - A dev buy on Pons or Flap is fronted from the ETH or BNB float, so a large one needs a large float: the API refuses a dev buy that would take the float below its gas reserve (0.005 ETH, 0.01 BNB) and says which chain.
- The top-up itself is done by the operator with an exchange or a bridge. It is not automated in the keeper, on purpose: a swap that fails in the middle of a creator's launch is worse than a launch that waits.
What the dry runs prove
- The Solana creation and fee sharing configuration are valid against the live pump.fun program and state.
- The Robinhood path works against the real Pons: launch, buy, sell and sweep, with the engine's prediction equal to the chain's result.
- The BNB path works against the real Flap: the keeper's mined salt gives the address the Portal creates, the tax is the creator's fee, and
harvestshares it between the creator's address and Pivot. - The whole pipeline runs from a request to a registered coin against a fork and live Solana state.
What they do not prove
- A live run with real funds, which has not been done.
- The behaviour of the keeper through a long outage, an RPC failure or a pump.fun program change.
update_fee_shares_v2on a new coin: its layout comes from the AuronV2 code that ran it on mainnet, and a single transaction with the creation and both fee sharing instructions would exceed the size limit.
Public limits
- 12 launch requests and 20 uploads per hour and address.
- A deposit counts only if it comes from the Solana address given in the request and carries the request's reference.
- Public requests cannot change the pump.fun fee share, and there is no alignment buy and no maker cash.
- Pipeline steps never overlap: a request already being worked on is skipped by the next pass, so a step cannot run twice.