-
-
Notifications
You must be signed in to change notification settings - Fork 246
feat / Pancakeswap Solana connector #529
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Add new connector for PancakeSwap on Solana using manual buffer decoding instead of Anchor Program to avoid IDL compatibility issues. Key features: - CLMM pool info endpoint (/connectors/pancakeswap-sol/clmm/pool-info) - Position info endpoint (/connectors/pancakeswap-sol/clmm/position-info) - Manual account data decoding for PoolState and PersonalPositionState - Program ID: HpNfyc2Saw7RKkQd8nEL4khUcuPhQ7WwY1B2qjx8jxFq - Compatible with pools using same IDL as Raydium CLMM Technical implementation: - Direct buffer reading to decode Solana account data - No dependency on Anchor Program client to avoid IDL parsing errors - Reuses existing CLMM schemas for API compatibility - Configuration and template files for setup script Tests: - Integration tests for pool-info and position-info endpoints - Verified with real PancakeSwap SOL/USDC pool on mainnet 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add positions-owned endpoint to list all positions for a wallet in a pool. Rename IDL file from amm_v3.json to clmm.json for clarity. Add comprehensive README documenting implemented and pending routes. Implemented routes: - GET /pool-info (existing) - GET /position-info (existing) - GET /positions-owned (new) The connector uses manual buffer decoding for read operations. Transaction routes (openPosition, closePosition, etc.) are documented as pending implementation due to complexity of instruction building without a dedicated PancakeSwap SDK. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
PancakeSwap position NFTs use Token2022 program, not SPL Token. Update positionsOwned route to query both programs in parallel. Tested with wallet DRpaJDurGtinzUPWSYnripFsJTBXm4HG7AC3LSgJNtNB and successfully found position F1xRqqbWdg3vdMEsn9YjRU7RnFVn67MZhDVXrWoobii5 in pool DJNtGuBGEQiUCWE8F981M2C3ZghZt2XLD8f2sQdZ6rsZ. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add simplified quote-swap endpoint that uses current pool price. Limitations: - Uses spot price from pool state - Does NOT calculate price impact across ticks - Does NOT use tick array data for precise calculations - Does NOT account for liquidity depth This provides basic swap quoting without requiring complex concentrated liquidity math or SDK dependencies. Tested with SOL/USDC pool successfully. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Implement manual transaction building for swap_v2 instruction using Anchor's BorshCoder to encode instruction data. Key implementation: - Manual pool data parsing to extract vaults and observation state - Anchor instruction encoding without full Program client - Support for both SPL Token and Token2022 programs - Compute budget and priority fee handling - Transaction simulation before sending Features: - POST /execute-swap endpoint for executing swaps - Supports both BUY and SELL sides - Configurable slippage protection - Returns transaction signature and balance changes Technical approach: - Decodes pool account data to get required accounts - Uses @coral-xyz/anchor BorshCoder for instruction encoding - Builds VersionedTransaction with ComputeBudget instructions - Signs and sends transaction via Solana RPC Note: Requires testing with funded wallet (not tested yet due to unfunded test wallet). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Update documentation to reflect newly implemented routes: - quote-swap: Simplified quote using spot price - execute-swap: Manual transaction building for swaps Add technical details about: - Anchor instruction encoding - Transaction building process - Account resolution from pool data Update examples with swap usage and clarify limitations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
… routes Add complete test coverage for all implemented routes with real mainnet data: Tests Added: - Pool info route (2 tests) - Position info route (2 tests) - Positions owned route with Token2022 support (1 test) - Quote swap route for BUY and SELL (2 tests) - Fixture validation (3 tests) Total: 14 tests, all passing Fixtures Created: - pool-info.json: Real SOL/USDC pool data - position-info.json: Real position NFT data - positions-owned.json: Array of positions - quote-swap-sell.json: SELL quote response - quote-swap-buy.json: BUY quote response Key Test Features: - Tests connector methods directly (not HTTP routes) - Validates against real mainnet data - Verifies Token2022 NFT discovery - Tests both SPL Token and Token2022 programs - Validates fixture structure matches responses - Comprehensive logging for test visibility All fixtures fetched from live mainnet data for accurate testing. Test Results: 14/14 passing ✅ 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…PancakeSwap Solana Added three position management routes: 1. **removeLiquidity**: Remove liquidity from existing positions by percentage - Takes percentageToRemove (0-100) parameter - Uses decrease_liquidity_v2 instruction with manual buffer decoding - Extracts position data to calculate tick arrays and protocol PDAs - Returns balance changes extracted from transaction 2. **closePosition**: Close empty positions - Validates position has zero liquidity before closing - Uses close_position instruction - Refunds position NFT account rent 3. **collectFees**: Collect accumulated fees - Uses clever Raydium approach: removes 1% liquidity to collect fees - Calls removeLiquidity internally with 1% parameter Key implementation details: - Added tick array address calculation (60 ticks per array) - Added position data parsing to extract poolId, ticks, and liquidity - Added pool tick spacing parsing - Uses Token2022 for position NFTs with SPL Token fallback - Proper response schema compliance with TransactionStatus enum All routes build successfully and follow CLMM schema patterns. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Added final position management route: **addLiquidity**: - Takes base and quote token amounts - Uses increase_liquidity_v2 instruction - Lets program calculate liquidity from amounts - Returns balance changes via extractBalanceChangesAndFee Implementation details: - Added buildIncreaseLiquidityV2Instruction to utils - Simplified interface: users specify token amounts, not liquidity - Uses baseFlag to indicate which amount to base calculation on - Complete tick array and PDA derivation - Proper Token2022 NFT account handling Updated README: - Documented all 9 implemented routes - Moved position management routes from "Not Implemented" to "Implemented" - Added endpoint paths, parameters, and implementation notes - Only openPosition and quotePosition remain unimplemented Complete feature set now includes: - Pool/Position info routes (3) - Swap routes (2) - Position management routes (4) All routes build successfully and follow CLMM schema patterns. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…mentation Added the two essential routes for full CLMM functionality: **quotePosition**: Simplified position quoting using spot price - Takes price range and one token amount - Calculates required amounts based on current pool price - Returns baseLimited flag and amount estimates - Note: Simplified version without full tick math **openPosition**: Creates new positions with Token2022 NFT - Generates new Keypair for NFT mint - Converts prices to ticks with proper spacing - Uses open_position_with_token22_nft instruction - Creates NFT with metadata (name: 'Pancake Concentrated Liquidity', symbol: 'PCL') - Transaction signed by both wallet and NFT mint keypair - Returns NFT mint address as position identifier Key utilities added: - priceToTick(): Converts price to tick index - roundTickToSpacing(): Rounds tick to valid spacing - parsePoolTickSpacing(): Made public/exported - buildOpenPositionWithToken22NftInstruction(): Full instruction builder All 11 routes now implemented and building successfully: - Pool/Position Info (3) - Swap Operations (2) - Position Management (6) Updated README to reflect complete implementation. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…lt wallet Updated all PancakeSwap Solana CLMM schemas to match Raydium's pattern: - Made network parameter optional with default value from chain config - Made walletAddress optional with default from defaultWallet setting - Added examples to all schema fields for better documentation - Added ExecuteSwapRequest schema (was inline in route file) - Imported getSolanaChainConfig and PancakeswapSolConfig for defaults - Added example constants (SOL/USDC pool, tokens, amounts, prices) Routes updated: - All 11 CLMM routes now apply defaults for network and walletAddress - Added proper type assertions for 'side' parameter (BUY/SELL) - Moved executeSwap schema from inline to centralized schemas.ts Benefits: - Consistent API with Raydium connector - Better developer experience with sensible defaults - Improved Swagger documentation with examples - Eliminates need to specify network/wallet on every request 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Fixed two issues in getPositionInfo: 1. Added null check for token info before accessing decimals property - Prevents "Cannot read properties of null" error when token not found - Returns null gracefully if tokens can't be loaded 2. Convert fee amounts to decimal format using token decimals - baseFeeAmount: Number(tokenFeesOwed0) / 10^baseDecimals - quoteFeeAmount: Number(tokenFeesOwed1) / 10^quoteDecimals - Previously showed raw amounts (e.g. 50109 instead of 0.050109) This fixes the error seen in positions-owned route when iterating through positions where some tokens may not be in the token list. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…tion Enhanced quoteSwap to use actual pool data for realistic quotes: **Price Impact Calculation**: - Estimates impact based on swap amount vs pool token balances - Impact ratio: (swapAmount / poolBalance) * 100% - Price adjusts against trader: SELL lowers price, BUY raises price - Uses pool liquidity concentration for more accurate estimates **Fee Integration**: - Uses actual pool fee from AMM config (poolInfo.feePct) - SELL: Deducts fee from output amount - BUY: Increases required input to cover fee - Fee shown in response and logs **Execution Price**: - Spot price: Current pool price from sqrtPriceX64 - Execution price: Adjusted for estimated price impact - Effective price: Final price including fees and impact - Returned as amountOut/amountIn or amountIn/amountOut **Example Results** (SOL/USDC pool): - 0.01 SOL: ~0.0008% impact, price ≈ spot - 1 SOL: ~0.08% impact, slight price movement - 100 SOL: ~8% impact, significant slippage - 10000 SOL: ~800% impact, would drain pool **Limitations**: - Impact estimation is simplified (linear approximation) - Does not use tick-by-tick liquidity distribution - May underestimate for very large cross-tick swaps - Suitable for most practical swap sizes **Improvements over previous**: - ✅ Now uses actual pool fee (was 0%) - ✅ Calculates price impact (was always 0%) - ✅ Adjusts execution price (was spot price only) - ✅ More realistic quotes for large swaps - ✅ Better logging with impact and fee info 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Upgraded @coral-xyz/anchor from 0.29.0 to 0.30.1 to fix BorshCoder error:
"Type not found: InitializeRewardParam"
**Issue**:
The PancakeSwap CLMM IDL uses Anchor 0.30+ format for defined types:
```json
{ "type": { "defined": { "name": "InitializeRewardParam" } } }
```
Anchor 0.29 uses the old format:
```json
{ "type": { "defined": "InitializeRewardParam" } }
```
**Fix**:
- Upgraded to Anchor 0.30.1 (supports new IDL format)
- BorshCoder can now properly parse all type definitions
- All swap instructions (swap_v2, open_position, etc.) now work
**Affected instructions using defined types**:
- initialize_reward (InitializeRewardParam)
- PoolState (Observation arrays)
- PersonalPositionState (PositionRewardInfo arrays)
- RewardInfo arrays
- TickArrayState (TickState arrays)
This fixes execute-swap errors when encoding instructions.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed "AccountNotInitialized" error by properly handling token accounts: **Token Account Setup** (pancakeswap-sol-utils.ts:150-200): - Check if input/output token accounts exist before swap - Create associated token accounts if they don't exist - Prevents "account not initialized" errors **WSOL Wrapping for Native SOL** (pancakeswap-sol-utils.ts:175-187): When swapping from SOL: 1. Create WSOL associated token account 2. Transfer SOL lamports to WSOL account 3. Call syncNative to wrap SOL → WSOL 4. Close WSOL account after swap to recover remaining SOL **Transaction Flow**: ``` 1. Compute budget instructions 2. Create input ATA (if needed) 3. Wrap SOL to WSOL (if SOL input) 4. Create output ATA (if needed) 5. Execute swap instruction 6. Close WSOL account (if created) ``` **Imports Added**: - NATIVE_MINT - Native SOL mint address - createAssociatedTokenAccountInstruction - createSyncNativeInstruction - createCloseAccountInstruction **Error Fixed**: ``` Before: AnchorError: AccountNotInitialized (3012) After: Automatic ATA creation + WSOL wrapping ``` This enables SOL ↔ Token swaps without pre-created token accounts. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Add getTokenProgramForMint() helper to detect if mint uses Token2022 - Update buildSwapV2Instruction to detect and use correct token programs - Update buildSwapTransaction to use detected programs for ATA creation - Use correct program for SyncNative and CloseAccount instructions - This fixes potential swap failures when tokens use different programs
- Wrap SOL to WSOL before every swap when input is native SOL - Don't close WSOL account after swap (keep it for future use) - Add debug logging for pool tokens, vaults, and account addresses
- Add proper typing for catch blocks (e: any) - Re-throw httpErrors with original statusCode instead of converting to 500 - Include error message in internal server errors - Add descriptive error context logging - Apply pattern across all 11 CLMM routes
- Add MIN_SQRT_PRICE_X64 and MAX_SQRT_PRICE_X64 constants - Use MIN limit when isBaseInput=true (token0 -> token1) - Use MAX limit when isBaseInput=false (token1 -> token0) - Setting sqrtPriceLimitX64 to 0 was causing program panic
- Log all swap_v2 instruction details before execution - Show instruction data hex, amount, threshold, price limit - Display account count and isBaseInput flag - Help diagnose Option::unwrap() panic at swap.rs:182:64
- Read tick_current and tick_spacing from pool state - Calculate tick array start index for current tick - Derive tick array PDA address - Include tick array as 14th account (remaining account) - Program needs tick array to traverse liquidity, was panicking without it - Add tick info to debug logging
CRITICAL FIX: Anchor's BorshCoder requires exact IDL parameter names - Changed otherAmountThreshold -> other_amount_threshold - Changed sqrtPriceLimitX64 -> sqrt_price_limit_x64 - Changed isBaseInput -> is_base_input - Previous camelCase names caused parameters to encode as 0 - This was causing the Option::unwrap() panic in swap.rs
- Remove MIN/MAX_SQRT_PRICE_X64 constants (values were causing overflow) - Set sqrtPriceLimitX64 to 0 to indicate no price limit - Slippage protection is handled by otherAmountThreshold - Previous attempt with 0 failed due to parameter encoding bug (now fixed)
- Check if calculated tick array account exists before including it - Program panics when trying to load non-existent tick array account - Log warning when tick array is not initialized - TODO: Implement proper tick array discovery for swap path
Fixed critical PDA derivation issues by switching from little-endian to big-endian encoding for i32 seed values. Anchor programs use big-endian encoding for integer seeds in PDA derivation. Changes: - Protocol position PDA: use BE for tick_lower and tick_upper indices - Tick array PDA: use BE for start_index - Applied fix to all affected functions: - buildOpenPositionWithToken22NftInstruction - buildDecreaseLiquidityV2Instruction - buildIncreaseLiquidityV2Instruction - getTickArrayAddress (used by swap and all CLMM operations) Added comprehensive logging to openPosition route for debugging: - Pool and token information - Token program detection (SPL vs Token2022) - Token account addresses - Tick indices and spacing - Tick array start indices and addresses - PDA addresses (protocol position, personal position) - Amount parameters and base flag - Instruction data in hex Also improved token program detection to support Token2022 mints across all liquidity operations. Verified against actual PancakeSwap transaction: ReVKyVQDhGWmgC6TCdJp8AmFo59MkTEqQ93vfiaRbTcu6b2n51o8HNuaqHGtGDeTe9Kw5UEEMdKxQsJ83oTVnqD 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
The addLiquidity route was failing with PriceSlippageCheck error (6021) because it wasn't applying any slippage buffer to the token amounts. Changes: - Added slippagePct parameter to addLiquidity function - Apply slippage buffer to amounts (same pattern as openPosition): amount = baseAmount * (1 + slippagePct / 100) - Added logging to show slippage-adjusted amounts - Schema already had slippagePct field, just needed to wire it up The slippagePct parameter is treated as a percentage (e.g., 50 = 50%), matching the behavior in openPosition. This fixes the issue where adding liquidity would fail even with sufficient slippage tolerance specified in the request. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Changed addLiquidity to follow Raydium's approach by calling quotePosition to calculate proper token amounts based on the position's tick range and current pool price, rather than directly using user-provided amounts. This fixes the PriceSlippageCheck error by ensuring the amounts are calculated correctly for the position's price range: - Calls quotePosition with position's lowerPrice, upperPrice, and poolAddress - Uses quote.baseTokenAmountMax and quote.quoteTokenAmountMax - Applies slippage buffer on top of quoted max amounts - Uses quote.baseLimited to determine baseFlag This matches how Raydium CLMM handles addLiquidity and ensures proper token ratio based on where the current price is relative to the position's tick range. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Fixes InvalidRewardInputAccountNumber (6035) error when removing liquidity. The decrease_liquidity_v2 instruction requires 19 accounts including 3 reward vault addresses from the pool's reward system. Previous implementation only had 16 accounts. Changes: - Parse 3 reward vault addresses from pool state reward_infos array - Add reward vaults to instruction keys after vault_0_mint and vault_1_mint - Reward vaults parsed at offset 399 + (169 * index) + 89 from pool data 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Fixes AccountNotInitialized (3012) error when removing liquidity. The decrease_liquidity_v2 instruction requires additional accounts for active rewards. Each active reward (reward_state != 0) needs 3 accounts: reward vault, user's ATA for reward token, and reward token mint. Previous fix incorrectly added all 3 reward vaults directly. This caused AccountNotInitialized errors for inactive rewards (which have System Program as vault address). Changes: - Parse reward_state from pool's reward_infos array (offset 397) - Only process active rewards (reward_state != 0) - For each active reward, extract token_mint (offset +57) and token_vault (offset +89) - Dynamically add 3 accounts per active reward: vault, user ATA, mint - Detect correct token program for each reward mint 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…dity Matches the behavior of successful manual transactions that pass exact liquidity values instead of relying on the program to calculate from amounts. Changes: - Created pancakeswap-clmm-math.ts with CLMM liquidity calculation helpers - getLiquidityFromAmounts: Calculate liquidity from token amounts - getAmountsFromLiquidity: Calculate amounts from liquidity - getLiquidityFromSingleAmount: Calculate from one token amount - Based on Uniswap V3/CLMM mathematical formulas - Updated quotePosition to calculate and return actual liquidity - Get token decimals for accurate calculations - Use getLiquidityFromAmounts helper - Return liquidity.toString() instead of '0' - Updated addLiquidity to use actual liquidity value - Extract liquidity from quote response - Pass to buildAddLiquidityTransaction - Updated buildIncreaseLiquidityV2Instruction - Accept liquidity parameter (non-zero) - Use actual liquidity in instruction encoding - Set base_flag to null (not needed when liquidity specified) - Updated buildAddLiquidityTransaction signature - Accept liquidity as parameter - Remove baseFlag parameter (not needed) Now matches the exact behavior seen in successful manual transaction: 39wgR5dpfvJV7MnDvk1RTLavPEa9Gnx6QxM3Dg6uWAjw7s2kh4VyqenyirgnT1BXDtKAJ2QkBcGGCgP9QSB5zkuL 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…directory - Move pancakeswap-sol-routes.test.ts to test/lifecycle/ - Move pancakeswap-sol-all-routes.test.ts to test/lifecycle/ - Fix import paths from ../../../src/ to ../../src/ - Fix fixture imports to point to ../connectors/pancakeswap-sol/fixtures/ - Remove CI skip logic (tests run manually, not in CI) - Update jest.config.js to exclude test/lifecycle/ from CI test runs Integration tests require live RPC access and should be run manually to avoid rate limiting in CI/CD environments. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
WSL logs:
|
…r positions This commit fixes two critical issues in the PancakeSwap Solana CLMM connector: 1. Quote Position Endpoint - Tick Alignment - Added tickToPrice() helper to convert tick indices back to prices - Updated quotePosition to convert prices to ticks, round to valid tick spacing - Use tick-aligned prices for all liquidity calculations - Ensures quotes match PancakeSwap website precision 2. Position Info - Incorrect Amount Calculation - Fixed liquidity parsing: read full 16 bytes (u128) instead of 8 bytes (u64) - Replaced naive division with proper CLMM math using getAmountsFromLiquidity() - Use tickToPrice() for accurate tick-to-price conversion - Position amounts now match on-chain state (verified against Solscan) Changes: - pancakeswap-sol.parser.ts: Added tickToPrice() function - quotePosition.ts: Implement tick-aligned price calculations - pancakeswap-sol.ts: Fix getPositionInfo() liquidity reading and amount calculation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
|
@nikspz The last commit should fix the issues with opening positions and fetching the correct position holdings |
## Changes
### Status Response Schema
- Add required `swapProvider` field to StatusResponseSchema
- Returns the configured swap provider for each network
### Ethereum Chain
- Add `swapProvider` property to Ethereum class
- Load swapProvider from network config (defaults to empty string)
- Include swapProvider in status endpoint response
- Add swapProvider to EthereumNetworkConfig interface
### Solana Chain
- Include swapProvider from existing config in status endpoint response
- swapProvider was already in SolanaNetworkConfig
### Schema Updates
- Add `swapProvider` to ethereum-network-schema.json
- Add `swapProvider` to solana-network-schema.json
- Schemas now synchronized between src/templates and conf
### Tests
- Update all Ethereum status tests (10 tests) with swapProvider expectations
- Update all Solana status tests (8 tests) with swapProvider expectations
- All 18 status tests passing
### API Response Example
```json
{
"chain": "ethereum",
"network": "mainnet",
"rpcUrl": "https://eth.llamarpc.com",
"rpcProvider": "url",
"currentBlockNumber": 23329000,
"nativeCurrency": "ETH",
"swapProvider": "uniswap/router"
}
```
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
## Summary
Add new standardized chain-level endpoints `/chains/{chain}/quote-swap` and `/chains/{chain}/execute-swap` that abstract away the complexity of choosing and calling specific DEX connectors.
## New Chain Routes
### Ethereum Routes
- `POST /chains/ethereum/quote-swap` - Get swap quote from configured swap provider
- `POST /chains/ethereum/execute-swap` - Execute swap via configured swap provider
### Solana Routes
- `POST /chains/solana/quote-swap` - Get swap quote from configured swap provider
- `POST /chains/solana/execute-swap` - Execute swap via configured swap provider
## Features
- Automatically uses the `swapProvider` configured for each network
- Supports all swap types: router, amm, clmm
- Standard request/response interface across all chains
- Comprehensive error handling with specific error codes
- Full test coverage (4 new test files, 1,140+ lines of tests)
## Request Schema
```typescript
{
network: string; // Network name (mainnet, devnet, etc.)
base: string; // Base token symbol or address
quote: string; // Quote token symbol or address
amount: string; // Amount in base token units
side: 'BUY' | 'SELL'; // Trade side
address?: string; // Wallet address (required for execute-swap)
slippageTolerance?: string; // Optional slippage (default from connector config)
// Optional AMM-specific
poolAddress?: string;
// Optional CLMM-specific
poolId?: string;
tickLower?: number;
tickUpper?: number;
}
```
## Connector Updates
All DEX connectors now export `slippageTolerance`:
- ✅ Jupiter (router)
- ✅ 0x (router)
- ✅ Uniswap (router, amm, clmm)
- ✅ PancakeSwap (router, amm, clmm)
- ✅ Raydium (amm, clmm)
- ✅ Meteora (clmm)
- ✅ PancakeSwap Solana (clmm)
## Configuration Updates
- Added `swapProvider` to Solana chain config interface
- Added `swapProvider` field to Solana network configs
## Test Coverage
- `test/chains/ethereum/routes/quote-swap.test.ts` - 298 lines, 6 test cases
- `test/chains/ethereum/routes/execute-swap.test.ts` - 305 lines, 6 test cases
- `test/chains/solana/routes/quote-swap.test.ts` - 261 lines, 5 test cases
- `test/chains/solana/routes/execute-swap.test.ts` - 276 lines, 5 test cases
## Files Changed
- 33 files changed
- 1,823 insertions(+), 11 deletions(-)
- 4 new route implementation files
- 4 new test files
- 25 connector updates
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
…ation Implement Solana RPC rate limit detection and proper HTTP 429 error responses: **Rate Limit Interceptor (new file)** - Create proxy-based Connection wrapper to intercept all RPC methods - Detect 429 errors from multiple sources: statusCode, code, message patterns - Transform rate limit errors into actionable user messages with: - Current RPC URL causing the limit - Network-specific config file path (mainnet-beta.yml vs devnet.yml) - Instructions to update nodeURL or configure Helius **Error Propagation** - Add re-throw logic for 429 errors in: - getSolBalance and fetchTokenAccounts (balances route) - confirmTransaction (both getTransaction and getSignatureStatus) - _sendAndConfirmRawTransaction (polling loop, re-broadcast, getBlockHeight) - Non-429 errors continue graceful handling (return 0 for balances, retry for transient errors) **Bug Fixes** - Fix confirmTransaction promise executor to properly propagate errors - Add 429 re-throw in outer try-catch of _sendAndConfirmRawTransaction **Chain-Level Swap Schemas** - Create ChainQuoteSwapResponseSchema without quoteId (quotes not cached at chain level) - Create ChainExecuteSwapResponseSchema for standardized swap execution responses - Update Solana and Ethereum quote-swap routes to use new schemas **Comprehensive Test Coverage (40 tests)** - Interceptor tests: 429 detection patterns, error messages, method coverage - Propagation tests: Error flow through Solana class methods - Route tests: HTTP 429 responses, actionable error messages 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
…ror handling - Replace `maxFeePerGas` and `maxPriorityFeePerGas` with `baseFee` and `priorityFee` - Add configurable `baseFeeMultiplier` parameter (default: 1.2) - Transaction fee formula: `maxFeePerGas = baseFee * baseFeeMultiplier + priorityFee` - Default `priorityFee` changed from 0.01 to 0.001 GWEI - Support partial configuration: can override individual gas parameters - Fetch values from network/Etherscan API when not configured - Rename `minGasPrice` to `gasPrice` - Fetch from network RPC when not configured - Remove hardcoded fallback values - Fix cross-network cache contamination bug - Change from single static cache to per-network map - Each network maintains independent 10-second cache - Applies to both Ethereum and Solana chains - **503 Service Unavailable**: RPC provider/network errors - **400 Bad Request**: Invalid network configuration - **500 Internal Server Error**: Generic failures - Better error messages with context - Update `ethereum-network-schema.json` and `solana-network-schema.json` - Add type unions `["number", "null"]` for optional numeric fields - Update all 9 Ethereum network templates - Update 2 Solana network templates - Update estimate-gas route tests for new parameters - Add comprehensive error handling test cases - Remove deprecated `minGasPrice` from mock configs - All tests passing (6 estimate-gas tests, 10 infura tests, 17 etherscan tests) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Created test infrastructure to reduce duplication across chain route tests:
**New Helpers:**
- test/helpers/commonMocks.ts: Centralized logger & ConfigManagerV2 mocks
- test/helpers/connectorMocks.ts: createRouteMock() for connector mocks
- test/helpers/mockResponses.ts: Factory functions for mock responses
- test/constants/mockTokens.ts: Centralized token addresses & constants
**Improvements:**
- Reduced logger mock duplication by 89% (72 lines → 8 lines)
- Reduced ConfigManagerV2 mock duplication by 89%
- Reduced connector mock boilerplate by 75% (using require() in jest.mock)
- Replaced 20+ hardcoded wallet addresses with MOCK_WALLET_ADDRESSES
- Eliminated duplicate mock response objects with factory functions
**Key Pattern:**
Used require() inside jest.mock() factory functions to call helpers while
satisfying Jest's hoisting requirements:
```typescript
jest.mock('path', () => require('helpers').createRouteMock('executeSwap'));
```
**Files Refactored:**
- test/chains/ethereum/routes/execute-swap.test.ts
- test/chains/ethereum/routes/quote-swap.test.ts
- test/chains/solana/routes/execute-swap.test.ts
- test/chains/solana/routes/quote-swap.test.ts
**Test Status:** All 34 tests passing ✅
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Fixed failing universal router tests to reflect the new behavior where gas estimation is skipped during quote phase and deferred to execution: **Test Updates:** - test/connectors/uniswap/universal-router.test.ts: Updated expectation from 500000 to 0 for estimatedGasUsed during quote phase - test/connectors/pancakeswap/universal-router.test.ts: Updated expectation from 500000 to 0 for estimatedGasUsed during quote phase **Implementation Update:** - src/connectors/pancakeswap/universal-router.ts: Aligned with Uniswap implementation to skip gas estimation during quote phase for consistency **Rationale:** Gas estimation during quote phase was intentionally removed in commit 99f4022 to improve performance. Quote phase now returns estimatedGasUsed=0 as a placeholder, with actual estimation happening during execution phase. **Test Status:** - Uniswap universal router: ✅ All tests passing - PancakeSwap universal router: ✅ All tests passing 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Add unified /swap/quote and /swap/execute routes that accept chain-network parameter (e.g., "solana-mainnet-beta", "ethereum-mainnet") to enable cross-chain swap routing from a single endpoint. Changes: - Add src/routes/swap/quote.ts: GET /swap/quote endpoint with chain-network routing - Add src/routes/swap/execute.ts: POST /swap/execute endpoint with chain-network routing - Update src/app.ts: Register unified swap routes before chain-specific routes - Implement parseChainNetwork() utility to parse "chain-network" format - Route to appropriate chain implementation (Ethereum/EVM or Solana) based on chain parameter - Support all EVM chains (ethereum, arbitrum, avalanche, base, bsc, celo, optimism, polygon, sepolia) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Fix chain-network parameter format to properly route EVM networks: - Use "ethereum-polygon" not "polygon-mainnet" - Use "ethereum-mainnet", "ethereum-arbitrum", etc. - Only "ethereum" and "solana" as chain identifiers in switch statement - Networks like polygon, arbitrum, base are passed as network parameter to Ethereum handler Changes: - Remove individual EVM chain cases from switch statements - Update documentation and examples to show correct format - Verified with manual testing: ethereum-polygon, ethereum-mainnet, solana-mainnet-beta 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Major Changes:
- Moved swap logic from chain-specific routes to unified /trading/swap endpoints
- Removed poolAddress parameter from all positions-owned endpoints
- Added connector parameter support to swap quote and execute endpoints
- Fixed Meteora SDK bug handling with proper error responses
Swap Routes Refactoring:
- Deleted src/chains/{ethereum,solana}/routes/{execute,quote}-swap.ts
- Moved all logic into src/trading/swap/{execute,quote}.ts
- Created src/trading/trading.routes.ts for route registration
- Updated src/app.ts to register trading routes at /trading/swap prefix
- Routes now handle both Ethereum and Solana chains in single endpoint
Positions-Owned Refactoring:
- Updated Meteora, PancakeSwap-Sol, and Raydium positions-owned endpoints
- Removed poolAddress from request schemas (now fetch ALL positions)
- Updated implementations to return all positions across all pools
- Added comprehensive tests for all three connectors
Meteora Fixes:
- Added error handling for SDK bug in getAllLbPairPositionsByUser
- Returns 503 Service Unavailable with descriptive error message
- Fixed price decimal adjustment in quotePosition endpoint
- Set fee amounts to 0 for batch position fetching (SDK limitation)
- Filed GitHub issue: MeteoraAg/dlmm-sdk#245
Test Fixes:
- Fixed Raydium test with valid Solana public keys
- Fixed Solana RPC provider test to accept both 'url' and 'helius'
- Fixed PancakeSwap-Sol test to handle closed positions gracefully
- All 569 tests now passing (2 skipped)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
Enhancements to unified swap routes for better developer experience: Schema Improvements: - Add default values to all parameters for Swagger UI auto-population - Reorder parameters: walletAddress first in execute, connector second in both - Change side from Type.Union to Type.String with enum for proper dropdown in Swagger - Use Jupiter/router as default connector example (SOL/USDC swap) Dynamic Configuration: - Fetch defaultWallet from Solana chain config at runtime - Fallback to Ethereum chain config if Solana not configured - Replace hardcoded wallet address with dynamic config value Technical Fixes: - Add type assertions (as 'BUY' | 'SELL') for side parameter to satisfy TypeScript - Import getSolanaChainConfig and getEthereumChainConfig functions - Ensure build passes with proper type safety Swagger UI Defaults: - chainNetwork: solana-mainnet-beta - connector: jupiter/router - walletAddress: <from config> - baseToken: SOL - quoteToken: USDC - amount: 1 - side: SELL (dropdown shows BUY/SELL) - slippagePct: 1 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Extract business logic from Uniswap and PancakeSwap CLMM routes into reusable functions
- Extract business logic from Raydium, Meteora, and PancakeSwap-Sol CLMM routes
- Create unified transaction routes at /trading/clmm/* for cross-chain operations:
- POST /trading/clmm/open - Open new CLMM position
- POST /trading/clmm/add - Add liquidity to existing position
- POST /trading/clmm/remove - Remove liquidity from position
- POST /trading/clmm/collect-fees - Collect accumulated fees
- POST /trading/clmm/close - Close position completely
- Change /pools/{poolAddress} to /pool-info with query parameter
- Change /positions/{positionAddress} to /position-info with query parameter
- Add comprehensive test coverage for unified CLMM routes
- Fix pancakeswap-sol positionsOwned tests to properly mock Token2022 calls
All connectors (uniswap, pancakeswap, raydium, meteora, pancakeswap-sol) now support
unified routing through /trading/clmm/* endpoints with consistent request/response schemas.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
|
Commit 8717815 and setup w/ hummingbot dev branch
Adding log files from diff devices and endpoints used for trading/clmm and trading/swaps: |
nikspz
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- ethereum
- mainnet
- gateway balance ✅
- base
-
gateway config ethereum update
-
defaultNetwork to base
-
nodeURL to infura
-
gateway balance ✅
-
GET /trading/swap/quote ✅
-
POST /trading/swap/execute ✅
- correct fees, correct amounts
-
GET /connectors/uniswap/router/quote-swap ✅
-
POST /chains/ethereum/approve ✅
-
POST /connectors/uniswap/router/execute-swap ✅
-
GET /connectors/uniswap/amm/quote-swap ✅
-
POST /connectors/uniswap/amm/execute-swap ✅
-
GET /connectors/uniswap/clmm/quote-swap ✅
-
POST /connectors/uniswap/clmm/execute-swap ✅
-
gateway swap uniswap/amm: ok ✅
-
gateway swap uniswap/clmm: ok ✅
-
POST /trading/clmm/open
- failed to open position using POST /trading/clmm/open:
statusCode": 500
- failed to open position using POST /trading/clmm/open:
-
gateway lp uniswap/clmm add-liquidity ✅
-
gateway lp uniswap/clmm remove-liquidity ✅
-
- mainnet
Before submitting this PR, please make sure:
Description
This PR adds a complete PancakeSwap Solana CLMM (Concentrated Liquidity Market Maker) connector to Gateway, enabling automated trading strategies on PancakeSwap's Solana DEX.
Features Implemented
CLMM Position Management:
openPosition- Open new concentrated liquidity positions with custom price rangesaddLiquidity- Add liquidity to existing positionsremoveLiquidity- Remove liquidity from positions (percentage-based)closePosition- Close positions and burn NFTs (auto-removes remaining liquidity)collectFees- Collect trading fees and farming rewards (CAKE)positionsOwned- Query all positions owned by a wallet with full detailsSwap Operations:
quoteSwap- Get swap quotes with price impact and fee calculationsexecuteSwap- Execute swaps with automatic WSOL wrapping/unwrappingPool Information:
Unified Trading Routes:
/trading/swap- Cross-chain unified swap endpoints for all DEX connectors/trading/clmm/*- Unified CLMM endpoints supporting all CLMM connectors (Uniswap, PancakeSwap, Raydium, Meteora, PancakeSwap-Sol)Ethereum Gas Improvements:
baseFeeMultiplierconfiguration for EIP-1559 networksTechnical Highlights
WSOL Handling:
wrapSOL()andunwrapSOL()helpers in Solana classTransaction Building:
Position NFTs:
Reward Collection:
decrease_liquidity_v2instruction collects both trading fees and farming rewardsBug Fixes in Latest Commits
Architecture
Tests Performed by Developer
✅ All position lifecycle operations tested on mainnet-beta:
✅ Swap operations tested:
✅ Query operations:
✅ Unified trading routes tested:
QA Testing Instructions
Prerequisites
Configure wallets for both Solana and Ethereum:
Set Gateway passphrase:
Start Gateway in dev mode:
Access Swagger UI:
Test 1: Unified Swap Routes (
/trading/swap)These routes provide a single unified interface for swapping across all DEX connectors (Jupiter, Uniswap, PancakeSwap, etc.).
Test 1.1: Quote Swap - Solana (Jupiter)
Endpoint:
GET /trading/swap/quoteTest Case:
Via Swagger:
Expected:
expectedAmount,price,gasPriceTest 1.2: Execute Swap - Ethereum (Uniswap)
Endpoint:
POST /trading/swap/executeTest Case:
Expected:
Test 1.3: Execute Swap - Solana (PancakeSwap-Sol)
Endpoint:
POST /trading/swap/executeTest Case:
Expected:
What to Verify:
Test 2: Unified CLMM Routes (
/trading/clmm/*)These routes provide unified CLMM position management across all connectors (Uniswap, PancakeSwap, Raydium, Meteora, PancakeSwap-Sol).
Test 2.1: Get Pool Info
Endpoint:
GET /trading/clmm/pool-infoTest Case - Solana (PancakeSwap-Sol):
Test Case - Ethereum (Uniswap):
Expected:
Test 2.2: Open Position
Endpoint:
POST /trading/clmm/openTest Case - Solana (PancakeSwap-Sol):
Test Case - Ethereum (Uniswap on Sepolia):
Expected:
Test 2.3: Add Liquidity to Position
Endpoint:
POST /trading/clmm/addTest Case:
Expected:
Test 2.4: Query Positions Owned
Endpoint:
GET /trading/clmm/positions-ownedTest Case:
Expected:
Test 2.5: Remove Liquidity from Position
Endpoint:
POST /trading/clmm/removeTest Case:
Expected:
Test 2.6: Collect Fees
Endpoint:
POST /trading/clmm/collect-feesTest Case:
Expected:
Test 2.7: Close Position
Endpoint:
POST /trading/clmm/closeTest Case:
Expected:
What to Verify:
Test 3: Ethereum Gas Settings (EIP-1559)
Recent commits added improved gas configuration for Ethereum networks with
baseFeeMultipliersupport.Test 3.1: Verify Gas Configuration Files
Check network configs:
Expected config in these networks:
conf/chains/ethereum/mainnet.ymlconf/chains/ethereum/polygon.ymlconf/chains/ethereum/arbitrum.ymlconf/chains/ethereum/optimism.ymlconf/chains/ethereum/base.ymlAll should have
baseFeeMultiplier: 1.2by default.Test 3.2: Test Gas Estimation with baseFeeMultiplier
Make a swap on Ethereum mainnet:
Monitor the transaction on Etherscan:
Expected:
Max Base Feeshould be ~1.2x the current base feeMax Priority Feeshould be 1 gwei (from config)Test 3.3: Test Gas Settings on Different Networks
Polygon (typically low gas):
Arbitrum (L2 with different gas model):
Expected:
Test 3.4: Test During Network Congestion
Simulate high gas scenario:
Expected behavior:
baseFeeMultiplier: 1.2, transaction should confirm in next few blocksWhat to Verify:
Test 4: Complete Position Lifecycle (Integration Test)
This test runs REAL transactions on mainnet - costs ~0.02-0.05 SOL in fees
Run the automated lifecycle test:
Before running:
walletAddressin test file to your walletExpected output:
Test 5: Error Handling & Edge Cases
Test invalid connector:
Expected: 400 error with "Unsupported connector" message
Test invalid chain-network format:
Expected: 400 error with format guidance
Test missing required fields:
Expected: 400 error listing missing required fields
Test invalid wallet address:
Expected: 400 error with "Invalid wallet address"
Summary of What to Verify
Unified Swap Routes (
/trading/swap):Unified CLMM Routes (
/trading/clmm/*):Ethereum Gas Settings:
Error Handling:
Swagger Documentation:
/docsKnown Test Pools
Solana (mainnet-beta):
4QU2NpRaqmKMvPSwVKQDeW4V6JFEKJdkzbzdauumD9qN(PancakeSwap)Ethereum (mainnet):
0x8ad599c3A0ff1De082011EFDDc58f1908eb6e6D8(Uniswap V3)Related Files:
src/trading/swap.routes.tssrc/trading/trading-clmm-routes/conf/chains/ethereum/*.ymltest/trading/andtest/lifecycle/🤖 Generated with Claude Code