Getting Started¶
Goal of this guide: From a fresh clone to a working dry run in ~15 minutes. Last updated: 2026-05-13
1. Prerequisites¶
| Tool | Version | Check |
|---|---|---|
| Node.js | >=20.0.0 |
node --version |
| npm | >=10 |
npm --version |
| (optional) Docker | >=24 |
docker --version |
| (optional) Ledger device + Ethereum app | latest | only if USE_LEDGER=true |
You also need: - WebSocket RPC URLs for every chain you plan to use (Alchemy, Infura, QuickNode, or a self-hosted node). HTTP-only RPCs will not work — the bot is event-driven. - An EVM private key (for Uniswap/PancakeSwap) and/or a Solana secret key (for Raydium). Never paste these into a chat or shell history. - (Optional) Telegram bot token + chat ID, Discord webhook URL for alerts.
2. Clone & install¶
Optional dependencies (@solana/*, @raydium-io/raydium-sdk-v2, @ledgerhq/*) install automatically. If you only use EVM chains and want to skip Solana, run npm install --omit=optional instead.
3. Configure environment¶
Open .env and fill in the variables. The minimum set for a dry run:
LOG_LEVEL=info
DRY_RUN=true
NODE_ENV=development
# Pick ONE key source — see step 4
EVM_PRIVATE_KEY=0x... # dev only
# OR
KEYSTORE_PATH=./keystore.json
KEYSTORE_PASSWORD=
# At least one chain's WebSocket RPC
ETH_RPC_WS=wss://eth-mainnet.g.alchemy.com/v2/YOUR_KEY
ETH_RPC_HTTP=https://eth-mainnet.g.alchemy.com/v2/YOUR_KEY
# At least one position (see Usage docs for the schema)
POSITIONS_JSON=[{"chain":"ethereum","dex":"uniswap-v2","type":"v2","pair":"WETH/USDC","address":"0x..."}]
See Usage → Environment variables for the full list with explanations.
⚠️ Never commit
.env. It's in.gitignorefor a reason. If you ever paste a private key into a shared system, treat that key as burned and migrate funds immediately.
4. Set up your signer (pick ONE)¶
The bot reads signer config in priority order: Ledger > encrypted keystore > raw private key. Raw key is refused in NODE_ENV=production.
Option A — Encrypted keystore (recommended for solo prod use)¶
A brand-new wallet. The key is created inside the process and written out encrypted — it is never printed, never logged, and never touches disk in plaintext:
It prints only the address. Fund that address on the chain you intend to run. Because the key existed nowhere else, the password is your only backup — lose it and the funds are unrecoverable.
If that makes you nervous, take a paper backup now, while you still have the password. The keystore holds the wallet's mnemonic encrypted alongside the key, so you can recover the twelve words once and write them down offline:
node -e "require('ethers').Wallet.fromEncryptedJson(require('fs').readFileSync('./keystore.json','utf8'), process.argv[1]).then(w=>console.log(w.mnemonic.phrase))" 'your-password'
That is a deliberate trade: it gives you a recovery path that survives losing the password, at the cost of the phrase existing somewhere outside the keystore. Do it on paper, not in a file, and never for a wallet you did not generate yourself.
A dedicated wallet holding just the position and a little gas is the cheapest safety measure available: the bot signs unattended, so everything in that wallet is inside the blast radius.
Importing a key you already have:
Either way the script prompts for a password (required on every startup unless
KEYSTORE_PASSWORD is set in .env) and an output path. It refuses to overwrite
an existing keystore — pass --force only when you are certain the old one is
disposable, since its key may exist nowhere else.
Output: ./keystore.json (mode 0600). Add KEYSTORE_PATH=./keystore.json to .env.
Option B — Ledger hardware wallet (recommended for high-value use)¶
Plug the device in, unlock, open the Ethereum app, enable blind signing in settings. Every removal tx requires you to physically approve on the device.
Option C — Raw private key (dev only)¶
The bot refuses to start with a raw key when NODE_ENV=production. This is intentional.
Solana¶
Solana does not yet support Ledger or keystore here. Use a dedicated hot wallet with minimal SOL.
5. First dry run¶
This sets DRY_RUN=true and starts the bot. You should see:
[INFO] config loaded { chains: ['ethereum'], positions: 1, dryRun: true }
[INFO] WS connected { chain: 'ethereum' }
[INFO] monitor subscribed { pair: 'WETH/USDC', dex: 'uniswap-v2' }
If you see WS reconnect loops, your RPC URL is wrong or rate-limited. Fix that first.
Let it run for ≥5 minutes. You should see price updates flowing. If your position is configured with a sensitive trigger (e.g., TRIGGER_PRICE_DROP_PCT=0.1), you may see a simulated removal logged with full tx params but no broadcast.
6. Verify alerts (optional but recommended)¶
If you set TELEGRAM_BOT_TOKEN and TELEGRAM_CHAT_ID, you should receive a startup ping. To force a test trigger, set DRY_RUN=true and temporarily lower TRIGGER_PRICE_DROP_PCT to a value the market will hit quickly, then revert.
💡 Past gotcha:
TELEGRAM_CHAT_IDmust be the numeric chat ID, no leading colon. If alerts fail with HTTP 400, double-check this. (Seedocs/CHANGELOG.md2026-05-13.)
7. Next steps¶
- Read Architecture to understand what's happening under the hood.
- Read Safety before disabling dry-run mode. The checklist there is non-optional.
- Run the test suite:
npm test(see Testing). - Run a 24-hour dry run against live RPC before going live. This is the only manual validation step that exists.
Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
Error: No EVM signer configured |
None of Ledger / keystore / raw key set | Pick one option in step 4 |
Raw private key blocked in production |
EVM_PRIVATE_KEY set with NODE_ENV=production |
Use keystore or Ledger, or set NODE_ENV=development |
| WS keeps reconnecting | Bad RPC URL, wrong protocol (https:// instead of wss://), or rate limit |
Verify URL, upgrade RPC tier |
Invalid POSITIONS_JSON |
Schema mismatch | Compare against examples in Usage |
| Telegram HTTP 400 | Malformed TELEGRAM_CHAT_ID |
Strip leading colon; must be pure numeric |
| Solana modules fail to load | Optional deps skipped | npm install without --omit=optional |