Agent LP desks (Orca market making)
Every coin's agent also provides liquidity on Orca: it puts SOL from its agent wallet into concentrated, two-sided positions in a short list of vetted major Orca pools (SOL/USDC and the like), earns their trading fees, and re-centres when the price leaves its range. Code: gateway/mirrorpad_gateway/lp.py (the desk), Rpc.sign_lp in onchain.py (the signing guard), chain/lp.ts (transaction builder), web/src/components/desk/LpPanel.tsx (the coin page panel).
It is separate from the hunting desk (agent-trading.md), which calls fresh pump.fun launches. Both draw on the same agent wallet; the coin's dev sets the split (POST /tokens/{mint}/lp {"lp_pct": 0..100}, default 100% while the hunting desk is not armed). At 0% the LP desk closes its positions back to SOL.
What it does
Every MIRRORPAD_LP_INTERVAL seconds (300) per coin:
- If LP is off, no pools are vetted, signing is off, or the coin has no agent wallet: it waits and says why.
- It refreshes its open positions (value, fees owed, in range or not) with
lp-status. - It works out the SOL it may use: the agent wallet's balance above both desks' reserves, times its share.
- Its agent (the ops role) decides from pool labels and numbers only:
OPEN L<n> <SOL> <width %>,REBALANCE P<n>,CLOSE P<n>orHOLD. If the agent is down, the rules decide: put idle SOL into the first vetted pool without a position; re-centre a position out of range for 20 minutes. - Opening (
lp-open): wrap SOL, buy exactly the other token the range needs through the same pool, open the position (a Token-2022 position NFT), deposit, unwrap the rest (~0.6% comes back). The gateway checks and signs each transaction, simulates it, sends and confirms in order, and records what left the wallet. Closing (lp-close --to-sol): remove all liquidity, collect fees, close the position (its ~0.006 SOL rent comes back), sell the other token;lp-sellthen sweeps the sliver an in-range close leaves, and is also the recovery when an open stops halfway.
Which pools
Every hour (mainnet) the gateway runs lp-discover (Orca's pools API, every pool verified on-chain) and keeps the MIRRORPAD_LP_UNIVERSE_SIZE (12) busiest SOL-paired pools that pass the filters, after any pools pinned in MIRRORPAD_LP_POOLS:
- usable by the LP worker (no Token-2022 transfer fee or hook, no live reward emissions, on Orca's main config);
- at least
MIRRORPAD_LP_MIN_TVL_USD($250k) deep andMIRRORPAD_LP_MIN_VOLUME_USD($250k) of 24h volume; - 24h fees over TVL no higher than
MIRRORPAD_LP_MAX_YIELD_PCT(2%): higher looks like wash-traded bait; - not flagged by Orca;
- no mint or freeze authority on the non-SOL token, unless it is a well-known major identified by mint address (
TRUSTEDin lp.py: USDC, USDT, JitoSOL, mSOL, whETH, cbBTC, WBTC, JLP, ORCA, RENDER).
Default half-widths: staked SOL ±0.5%, stablecoins ±5%, BTC/ETH/JLP ±6%, others ±10% / ±15% / ±20% by tick spacing. On 2026-10-09 this gave SOL/USDC, whETH, ORCA, cbBTC, WBTC, JUP, STONK, JLP, Fartcoin, JitoSOL, PENGU and USDT. A pool dropping out of the list never strands a position: the pools of open positions stay readable and closable. The rules pick the best fee yield among pools at least $1M deep; the agent sees depth, volume and yield for each.
Limits no answer can override
| limit | default | setting | ||
|---|---|---|---|---|
| pools | pinned + discovered (below) | MIRRORPAD_LP_POOLS = `LABEL | address | half-width bps,... (pinned), MIRRORPAD_LP_AUTO` |
| smallest position | 0.1 SOL | MIRRORPAD_LP_MIN_DEPLOY_SOL | ||
| positions | 2 | MIRRORPAD_LP_MAX_POSITIONS | ||
| range | ±0.5% to ±30% | MIRRORPAD_LP_WIDTH_MIN_BPS, _MAX_BPS | ||
| re-centre after out of range | 20 min | MIRRORPAD_LP_REBALANCE_AFTER_MIN | ||
| cooldown between moves | 30 min | MIRRORPAD_LP_COOLDOWN_MIN | ||
| never deployed | 0.03 SOL (+ the hunting desk's 0.02) | MIRRORPAD_LP_RESERVE_SOL | ||
| swap slippage | 1% | MIRRORPAD_LP_SLIPPAGE_PCT |
MIRRORPAD_LP=off switches every LP desk off. It is live by default, but nothing moves without on-chain signing.
The signing guard (Rpc.sign_lp)
The worker never sees the agent's key; the gateway signs only when every instruction is one of:
- ComputeBudget (see the caps below);
- System
transferfrom the agent to the agent's own wrapped-SOL account (nothing else may move SOL); - Associated Token Account create for the agent, paid by the agent;
- Token
syncNative, andcloseAccountreturning rent to the agent; - Orca Whirlpool, exactly as
chain/lp.tsbuilds them:swap_v2,open_position_with_token_extensions,increase_liquidity_by_token_amounts_v2,decrease_liquidity_v2,update_fees_and_rewards,collect_fees_v2,collect_reward_v2,close_position(_with_token_extensions)— each with its exact account count, on a vetted pool, with the agent as owner/authority/receiver, and every token account that pays or receives being the agent's own account for the mint and token program named in that same instruction. - ComputeBudget limited to one unit limit (≤ 1.4M) and one price (≤ 10M µlamports): at most 0.014 SOL of priority fee.
The agent wallet pays, any other signature (the position's fresh mint) must already be present and valid, and each signed transaction is simulated: it may not cost the wallet more than what is being deployed plus 0.03 SOL.
API
| endpoint | |
|---|---|
GET /tokens/{mint}/lp | the desk: positions with value, fees and range status, realized result, share, limits |
POST /tokens/{mint}/lp | {"lp_pct": n}, dev only |
Log lines (lp_open, lp_close, lp_hold, lp_wait, lp_error) stream into the coin's desk log like the hunting desk's.