feat(meteora): add DAMM v2 (cp-amm) AMM connector - #671
Conversation
Adds a Meteora DAMM v2 integration under /connectors/meteora/amm/*, mirroring the Raydium AMM connector and backed by @meteora-ag/cp-amm-sdk. Endpoints: pool-info, position-info, quote-swap, execute-swap, quote-liquidity, add-liquidity, remove-liquidity, and create-pool (with a generic CreatePool schema added to amm-schema.ts). DAMM v2's custom features are mapped onto the AMM interface (NFT positions, sqrt-price accounting, config-driven pool creation, base/dynamic fees, Token-2022, locks/vesting) and documented — with proposed enhancements — in docs/connectors/meteora-damm-v2.md. create-pool requires an explicit configAddress because many permissionless configs are launch configs with ~99% starting fees. Unit tests mirror the Raydium AMM suite (pool-info, quote-swap BUY/SELL, quote-liquidity, create-pool guard). typecheck + lint clean; full Meteora suite green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N1YS88ZAY2rXVLmgvyN4oY
create-pool now resolves the initial price in priority order: explicit initialPrice, then explicit quoteTokenAmount ratio, else the live market price fetched from the unified swap router (/trading/swap/quote → Jupiter on Solana). Only baseTokenAmount is required; the quote side is derived. If the base token has no market to price against, it throws a clear error asking for initialPrice/quoteTokenAmount rather than guessing. This prevents opening a pool off-market and being immediately arbitraged/sniped (as observed in live testing: a pool seeded at 0.001 was repriced to ~0.0039 by MEV bots within one slot; the router quote returns 0.00385, i.e. the correct on-market seed price). The seed price is returned as `price` in CreatePoolResponse. quoteTokenAmount is now optional; initialPrice added to the request schema. Doc updated. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N1YS88ZAY2rXVLmgvyN4oY
…ed /trading/amm/create-pool Adds pool creation across AMM connectors, matching the Meteora DAMM v2 create-pool: - Raydium: cpmm.createPool (CP-Swap), single-tx, dynamic fee-config from the Raydium API, mainnet/devnet program selection, market-price seeding (anti-snipe). - Uniswap: V2 router.addLiquidity auto-creates the pair (fixed 0.30% fee), pair address read from the factory; ETH/WETH via addLiquidityETH; allowance checks; market-price seeding. - Unified POST /trading/amm/create-pool dispatches on connector (meteora|raydium|uniswap), carrying per-connector optionals (configAddress / feeConfigIndex+openTime / gasPrice+maxGas). All three share the generic CreatePoolResponse. Seed price priority everywhere: initialPrice -> quoteTokenAmount ratio -> live market price from the unified swap router. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N1YS88ZAY2rXVLmgvyN4oY
…/* for all 7 operations
Standardizes Meteora/Raydium/Uniswap AMM route files to export a uniform network-based
function per operation (getPoolInfo, getPositionInfo, quoteSwap, executeSwap, quoteLiquidity,
addLiquidity, removeLiquidity), each returning the shared amm-schema type — mirroring how the
unified CLMM routes consume connectors.
Adds unified cross-connector routes under /trading/amm (pool-info, position-info, quote-swap,
execute-swap, quote-liquidity, add-liquidity, remove-liquidity) that dispatch on `connector`,
alongside the existing create-pool. Uniswap V2 wrappers derive the quote token from the pair
(token0/token1); Uniswap create-pool validation errors now return 400 (not 500).
Updates the unified swap router (trading/swap/{quote,execute}) to the new connector signatures.
Tests: adds create-pool unit tests for Raydium (CPMM) and Uniswap (V2) and a unified-dispatch
test; existing AMM + swap tests pass (54 total).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N1YS88ZAY2rXVLmgvyN4oY
…ess and drop quoteToken
CLMM swaps are pool-addressed, so the counter ("quote") token is fully determined by
poolAddress + baseToken. Standardizes the exported quoteSwap/executeSwap wrappers for
Meteora/Raydium/Uniswap CLMM to the signature (network, [walletAddress,] poolAddress,
baseToken, side, amount, slippagePct?) — matching the AMM connectors — and derives the
counter token from the pool via a per-connector resolveCounterToken helper
(dlmmPool.tokenX/Y, poolInfo.mintA/B, pool-info base/quote).
Updates the unified swap router (trading/swap/{quote,execute}) CLMM call sites accordingly,
and the Meteora/Raydium execute-swap route handlers + the Uniswap execute route (which now
resolves the pool via findDefaultPool before calling the poolAddress-required function).
An unknown base token not in the pool now returns 400 (was 404). Tests updated to
argument-based token mocks; all CLMM + trading suites pass (69).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N1YS88ZAY2rXVLmgvyN4oY
…3 + unified /trading/clmm/create-pool Adds pool creation to all three CLMM connectors. create-pool CREATES + INITIALIZES a pool at an initial price (no position seeded — concentrated-liquidity positions need a range, opened separately via open-position). Initial price priority: initialPrice -> live market price from the unified swap router (anti-off-market default). Reuses the shared CreatePoolResponse. - Meteora DLMM: DLMM.createCustomizablePermissionlessLbPair2 (Token-2022 aware); activeId derived from initialPrice via the SDK's own getPricePerLamport/getBinIdFromPrice math; canonical mint ordering with price inversion; binStep + feeBps required. - Raydium CLMM: raydiumSDK.clmm.createPool with an ammConfig from api.getClmmConfigs() (ammConfigIndex), initialPrice as Decimal; SDK handles mint ordering + price inversion. - Uniswap V3: NonfungiblePositionManager.createAndInitializePoolIfNecessary (added factory getPool, pool slot0, and NFT-manager ABIs); initialPrice -> sqrtPriceX96 with decimal + token0/token1 orientation handling; fee tier param (100/500/3000/10000). Unified POST /trading/clmm/create-pool dispatches on connector, carrying per-connector extras (binStep/feeBps, ammConfigIndex, fee/gasPrice/maxGas). Tests: per-connector validation + unified dispatch; full suite green (104). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N1YS88ZAY2rXVLmgvyN4oY
…p/executeSwap Extends the CLMM swap standardization to the remaining pool-addressed connectors: quoteSwap/ executeSwap now require poolAddress and drop quoteToken (derived from the pool via a per-connector resolveCounterToken) — orca via getWhirlpool tokenMintA/B, pancakeswap via getPancakeswapPoolInfo, pancakeswap-sol via getClmmPoolInfo. Swap-router call sites updated. Router-only connectors (0x/dflow/jupiter/okx/titan) keep quoteToken by design (no pool). All Gateway pool-based DEX swap functions now share one signature. tsc clean; 235 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N1YS88ZAY2rXVLmgvyN4oY
…l for orca/pancakeswap/pancakeswap-sol
Finishes standardizing every pool-based Gateway DEX so all share one interface.
Standardization:
- PancakeSwap AMM: standardized the 7 ops to network-based signatures (getPoolInfo, getPositionInfo,
quoteSwap, executeSwap, quoteLiquidity, addLiquidity, removeLiquidity) mirroring uniswap AMM,
and wired pancakeswap into the unified /trading/amm/* dispatch (+ swap-router AMM branches).
create-pool (create + initialize at price; no position seeded):
- Orca (Whirlpools): client.createPool with tickSpacing + initialTick (PriceMath), PoolUtil.orderMints.
- PancakeSwap AMM (V2 fork): router.addLiquidity auto-create; CLMM (V3 fork):
NonfungiblePositionManager.createAndInitializePoolIfNecessary, fee tiers 100/500/2500/10000
(+ V3 factory/pool/NFT-manager ABIs).
- pancakeswap-sol CLMM: manual create_pool instruction from the bundled IDL (discriminator + PDA
seeds decoded from IDL bytes), sqrt_price_x64 from priceToSqrtPriceX64, explicit ammConfig.
- Unified /trading/{amm,clmm}/create-pool dispatch extended to all six pool-based connectors.
Router-only connectors (0x/dflow/jupiter/okx/titan) are excluded by design (no pool). All connector
+ trading tests pass (511); tsc/lint/build clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N1YS88ZAY2rXVLmgvyN4oY
…s, add positions-owned) DAMM v2 positions are NFTs, not fungible LP; a wallet may hold several per pool, each an independent asset differing in size, lock state, and fees. The prior AMM routes aggregated on read but silently acted on the largest position on write, so "remove 100%" was not a true exit when several existed. Make the Meteora AMM routes position-addressed (mirroring CLMM): - remove-liquidity now REQUIRES positionAddress; percentage applies to that position (found via the owner-filtered getUserPositions, which also proves ownership + pool membership). - add-liquidity takes an OPTIONAL positionAddress: provide to add to that position, omit to open a NEW position NFT (never silently the largest). - position-info returns the pool aggregate PLUS a positions[] breakdown (positionAddress, lpTokenAmount, base/quote per NFT) — the discovery path. - new positions-owned route lists all of a wallet's DAMM v2 positions across pools (getPositionsByUser grouped by pool). Unified /trading/amm/* carry positionAddress (required for meteora on remove, optional on add) and expose positions-owned; fungible-LP AMMs (raydium, uniswap, pancakeswap) ignore positionAddress and reject positions-owned with a clear error, since a fungible LP balance has no enumerable positions. Shared PositionInfoSchema gains an optional positions[] (PositionDetail). Adds unified-dispatch tests (meteora remove without positionAddress -> 400; fungible-LP positions-owned -> 400). Updates the DAMM v2 connector doc. typecheck/lint/build clean; 84 meteora+trading tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01N1YS88ZAY2rXVLmgvyN4oY
Greptile SummaryAdds a Meteora DAMM v2 connector with swap, pool, and NFT-position liquidity operations, and extends the unified AMM interface to expose pool creation and owned-position discovery.
Confidence Score: 4/5The automatic pool-pricing path should be fixed before merging because a price-impacting seed amount can create a new pool at an immediately arbitrageable price. The explicit-price and explicit-quote-amount paths remain coherent, but the default path uses the full seed trade’s average execution price—including price impact and routing costs—as the pool’s initial market price. Files Needing Attention: src/connectors/meteora/amm-routes/createPool.ts
|
| Filename | Overview |
|---|---|
| src/connectors/meteora/amm-routes/createPool.ts | Implements config-driven pool creation and automatic pricing, but the fallback seeds from a size-dependent execution price that can open the pool off-market. |
| src/connectors/meteora/amm-routes/quoteSwap.ts | Adds exact-in and exact-out DAMM v2 quote construction with token ordering and SDK-derived amounts. |
| src/connectors/meteora/amm-routes/executeSwap.ts | Builds and submits DAMM v2 SELL and BUY transactions from the corresponding quote parameters. |
| src/connectors/meteora/amm-routes/addLiquidity.ts | Supports adding to an explicitly owned position or creating a new NFT position with an additional signer. |
| src/connectors/meteora/amm-routes/removeLiquidity.ts | Requires an owned position, accounts for vesting and activation time, and removes its requested unlocked-liquidity percentage. |
| src/trading/trading-amm-routes/create-pool.ts | Extends the unified AMM route and forwards Meteora-specific pool-creation inputs without dropping fields. |
| src/trading/trading-amm-routes/remove-liquidity.ts | Adds optional position addressing while retaining existing connector signatures and explicitly requiring it for Meteora. |
| src/connectors/meteora/meteora-damm.ts | Introduces shared DAMM v2 pool, token-program, ownership, and activation-clock helpers. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[Create-pool request] --> B{Price supplied?}
B -->|initialPrice| C[Use explicit price]
B -->|quoteTokenAmount| D[Derive seed ratio]
B -->|Neither| E[Quote full baseTokenAmount through unified swap router]
E --> F[Use average execution amountOut / amountIn]
C --> G[Scale token amounts]
D --> G
F --> G
G --> H[Prepare DAMM v2 pool parameters]
H --> I[Create and seed pool]
Reviews (1): Last reviewed commit: "feat(meteora): position-addressed DAMM v..." | Re-trigger Greptile
| `No market route found for ${baseToken}/${quoteToken}. Pass initialPrice or quoteTokenAmount explicitly.`, | ||
| ); | ||
| } | ||
| return quote.amountOut / quote.amountIn; // quote token per base token |
There was a problem hiding this comment.
Full-seed quote skews initial price
When the seed amount has material price impact, this ratio uses the average execution price for selling the entire baseTokenAmount, including routing fees and price impact, rather than a marginal market price. The derived quote deposit therefore opens the pool off-market and exposes its initial liquidity to immediate arbitrage.
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
…e impact into pool seed price Quoting the full seed amount returned the average execution price of that trade (impact + routing fees included), opening the pool below market for large seeds. Probe with a small fraction instead to approximate the marginal price. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Too many files changed for review (117 files, 100 file limit). Bypass the limit by tagging |
Summary
Adds a Meteora DAMM v2 (constant-product
cp-amm) integration under/connectors/meteora/amm/*, mirroring the Raydium AMM connector and backed by@meteora-ag/cp-amm-sdk. Meteora's existing DLMM connector stays asclmm; this addsammas a second trading type.Endpoints
pool-info,position-infoquote-swap,execute-swapgetQuote2/swap2quote-liquidity,add-liquidity,remove-liquiditycreate-poolCreatePoolschema added toamm-schema.ts)DAMM v2 custom features (see
docs/connectors/meteora-damm-v2.md)DAMM v2 diverges from a fungible-LP AMM; the doc records each difference, how it maps onto the shared AMM interface, and proposed follow-ups:
position-infoaggregates. Position-addressed routes proposed.create-poolrequires an explicitconfigAddressbecause many permissionless configs are launch configs with ~99% starting fees (Gateway won't auto-select and foot-gun users).initialPricenorquoteTokenAmountis given, the seed price is fetched from the unified swap router (/trading/swap/quote) so pools open on-market instead of being immediately arbitraged/sniped. No market to price against → clear error, never a guess.Testing
tsc+ lint clean.🤖 Generated with Claude Code
Related PRs — 4-layer AMM integration (merge bottom-up, each depends on the one below):
/trading/amm/*/gateway/amm/*routerclient.gateway_ammmanage_ammtool + Meteora Launch LP agent