{"id":31505737,"url":"https://github.com/furydotbot/swap-api","last_synced_at":"2025-10-02T20:22:52.306Z","repository":{"id":315304840,"uuid":"1049878790","full_name":"furydotbot/swap-api","owner":"furydotbot","description":"Jupiter selfhosted alternative. Working with grpc and Helius Enhanced Websocket","archived":false,"fork":false,"pushed_at":"2025-09-17T19:54:25.000Z","size":313,"stargazers_count":8,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-17T21:44:42.691Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/furydotbot.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-03T16:16:32.000Z","updated_at":"2025-09-17T19:54:29.000Z","dependencies_parsed_at":"2025-09-17T21:45:05.872Z","dependency_job_id":"9038a613-e09a-4532-b0d2-f7c2a9b4002c","html_url":"https://github.com/furydotbot/swap-api","commit_stats":null,"previous_names":["furydotbot/swap-api"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/furydotbot/swap-api","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/furydotbot%2Fswap-api","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/furydotbot%2Fswap-api/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/furydotbot%2Fswap-api/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/furydotbot%2Fswap-api/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/furydotbot","download_url":"https://codeload.github.com/furydotbot/swap-api/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/furydotbot%2Fswap-api/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278064417,"owners_count":25923870,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-02T02:00:08.890Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-10-02T20:22:51.115Z","updated_at":"2025-10-02T20:22:52.301Z","avatar_url":"https://github.com/furydotbot.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# How to Create a Transaction Builder\n\nThis guide explains how to create a new transaction builder for the swap API system. Transaction builders are responsible for creating swap transactions for different DEX protocols on Solana.\n\n## Overview\n\nThe swap API uses a builder pattern to handle different DEX protocols. Each protocol has its own builder that implements the `ITransactionBuilder` interface and extends the `BaseTransactionBuilder` abstract class.\n\n## Architecture\n\n### Core Interfaces\n\n- **`ITransactionBuilder`**: Base interface that all builders must implement\n- **`BaseTransactionBuilder`**: Abstract base class providing common functionality\n- **`TransactionBuilderRegistry`**: Registry that manages and provides access to all builders\n\n### Key Types\n\n```typescript\n// Input parameters for swap transactions\ninterface SwapParams {\n  mint: string;           // Token mint address\n  signer: string;         // User's wallet address\n  type: 'buy' | 'sell';   // Transaction type\n  inputAmount?: number;   // Input amount in lamports/tokens\n  outputAmount?: number;  // Expected output amount\n  slippageBps: number;    // Slippage tolerance in basis points\n  trade: {\n    mint: string;         // Token mint (same as above)\n    pool: string;         // Pool address\n    avgPrice: number;     // Average price\n    programId: string;    // DEX program ID\n    slot: string;         // Slot information\n  };\n}\n\n// Output transaction structure\ninterface SwapTransaction {\n  transactionId: string;\n  status: 'pending' | 'confirmed' | 'failed';\n  instructions: SwapInstruction[];\n}\n\n// Individual instruction format\ninterface SwapInstruction {\n  programId: string;\n  accounts: Array\u003c{\n    pubkey: string;\n    isSigner: boolean;\n    isWritable: boolean;\n  }\u003e;\n  data: string; // Base64 encoded instruction data\n}\n```\n\n## Step-by-Step Guide\n\n### 1. Create Your Builder Class\n\nCreate a new file in the `src/builders/` directory:\n\n```typescript\n// src/builders/YourProtocolTransactionBuilder.ts\nimport { BaseTransactionBuilder, SwapParams, SwapTransaction, SwapInstruction } from '../TransactionBuilder';\nimport { Connection, PublicKey, TransactionInstruction } from '@solana/web3.js';\n// Import your protocol's SDK\n\nexport class YourProtocolTransactionBuilder extends BaseTransactionBuilder {\n  // Define your protocol's program ID\n  programId = 'YourProgramIdHere';\n  private connection: Connection;\n  \n  constructor(connection: Connection) {\n    super();\n    this.connection = connection;\n  }\n\n  async buildSwapTransaction(params: SwapParams): Promise\u003cSwapTransaction\u003e {\n    const timestamp = Date.now();\n    const random = Math.random().toString(36).substring(2, 15);\n    const transactionId = `tx_${timestamp}_${random}`;\n    const instructions = await this.createSwapInstructions(params);\n    \n    return {\n      transactionId,\n      status: 'pending',\n      instructions\n    };\n  }\n\n  private async createSwapInstructions(params: SwapParams): Promise\u003cSwapInstruction[]\u003e {\n    // Implement your protocol-specific logic here\n    // Return array of SwapInstruction objects\n  }\n\n  private convertToSwapInstruction(instruction: TransactionInstruction): SwapInstruction {\n    return {\n      programId: instruction.programId.toString(),\n      accounts: instruction.keys.map(key =\u003e ({\n        pubkey: key.pubkey.toString(),\n        isSigner: key.isSigner,\n        isWritable: key.isWritable\n      })),\n      data: Buffer.from(instruction.data).toString('base64')\n    };\n  }\n}\n```\n\n### 2. Implement Core Logic\n\nThe `createSwapInstructions` method should:\n\n1. **Parse input parameters**:\n   ```typescript\n   const mint = new PublicKey(params.mint);\n   const user = new PublicKey(params.signer);\n   const pool = new PublicKey(params.trade.pool);\n   ```\n\n2. **Handle token accounts**:\n   ```typescript\n   // Get or create associated token accounts\n   const userTokenAccount = await getAssociatedTokenAddress(mint, user);\n   \n   // Check if account exists and create if needed\n   const tokenAccountInfo = await this.connection.getAccountInfo(userTokenAccount);\n   if (!tokenAccountInfo) {\n     const createInstruction = createAssociatedTokenAccountInstruction(\n       user, userTokenAccount, user, mint\n     );\n     instructions.push(createInstruction);\n   }\n   ```\n\n3. **Create protocol-specific swap instructions**:\n   ```typescript\n   // Use your protocol's SDK to create swap instructions\n   const swapInstruction = await yourProtocolSdk.createSwapInstruction({\n     pool,\n     user,\n     inputAmount: params.inputAmount,\n     outputAmount: params.outputAmount,\n     slippage: params.slippageBps\n   });\n   \n   instructions.push(swapInstruction);\n   ```\n\n4. **Convert and return instructions**:\n   ```typescript\n   return instructions.map(ix =\u003e this.convertToSwapInstruction(ix));\n   ```\n\n### 3. Register Your Builder\n\nAdd your builder to the registry in `src/builders/TransactionBuilderRegistry.ts`:\n\n```typescript\n// Import your builder\nimport { YourProtocolTransactionBuilder } from './YourProtocolTransactionBuilder';\n\n// In the registerBuilders() method:\nprivate registerBuilders(): void {\n  // ... existing builders ...\n  \n  // Register your builder\n  const yourProtocolBuilder = new YourProtocolTransactionBuilder(this.connection);\n  this.builders.set(yourProtocolBuilder.programId, yourProtocolBuilder);\n}\n\n// Update getBuilderInfo() method to include your protocol:\ncase 'YourProgramIdHere':\n  name = 'YourProtocolName';\n  break;\n```\n\n### 4. Add Helper Methods (Optional)\n\nAdd protocol-specific helper methods:\n\n```typescript\n// Static method to check if a program ID belongs to your protocol\npublic static isYourProtocolProgram(programId: string): boolean {\n  return programId === 'YourProgramIdHere';\n}\n\n// Helper methods for your protocol\npublic getPoolAddress(mint: string): string {\n  // Implementation specific to your protocol\n}\n\npublic calculateSlippage(amount: number, slippageBps: number): number {\n  return amount * (10000 - slippageBps) / 10000;\n}\n```\n\n## Best Practices\n\n### Error Handling\n\n```typescript\nprivate async createSwapInstructions(params: SwapParams): Promise\u003cSwapInstruction[]\u003e {\n  try {\n    // Your implementation\n  } catch (error) {\n    console.error(`Error creating ${this.constructor.name} instructions:`, error);\n    throw new Error(`Failed to create swap instructions: ${error.message}`);\n  }\n}\n```\n\n### Input Validation\n\n```typescript\nprivate validateParams(params: SwapParams): void {\n  if (!params.mint || !PublicKey.isOnCurve(params.mint)) {\n    throw new Error('Invalid mint address');\n  }\n  if (!params.signer || !PublicKey.isOnCurve(params.signer)) {\n    throw new Error('Invalid signer address');\n  }\n  if (params.slippageBps \u003c 0 || params.slippageBps \u003e 10000) {\n    throw new Error('Invalid slippage value');\n  }\n}\n```\n\n### Performance Optimization\n\n```typescript\n// Cache SDK instances\nprivate protocolSdk: YourProtocolSdk | null = null;\n\nprivate async getProtocolSdk(): Promise\u003cYourProtocolSdk\u003e {\n  if (!this.protocolSdk) {\n    this.protocolSdk = new YourProtocolSdk(this.connection);\n  }\n  return this.protocolSdk;\n}\n```\n\n## Testing Your Builder\n\n1. **Unit Tests**: Create tests for your builder's methods\n2. **Integration Tests**: Test with actual Solana devnet\n3. **Manual Testing**: Use the API endpoints to test your builder\n\n```typescript\n// Example test\nconst builder = new YourProtocolTransactionBuilder(connection);\nconst params: SwapParams = {\n  mint: 'TokenMintAddress',\n  signer: 'UserWalletAddress',\n  type: 'buy',\n  inputAmount: 1000000, // 1 SOL in lamports\n  slippageBps: 100, // 1% slippage\n  trade: {\n    mint: 'TokenMintAddress',\n    pool: 'PoolAddress',\n    avgPrice: 0.001,\n    programId: 'YourProgramIdHere',\n    slot: '12345'\n  }\n};\n\nconst transaction = await builder.buildSwapTransaction(params);\nconsole.log('Transaction created:', transaction);\n```\n\n## Examples\n\nRefer to existing builders for implementation examples:\n\n- **PumpFunTransactionBuilder**: Simple bonding curve implementation\n- **DBCTransactionBuilder**: Dynamic bonding curve with complex account management\n- **LaunchpadTransactionBuilder**: Raydium launchpad integration\n- **PumpSwapTransactionBuilder**: AMM-style swaps\n\n## Common Patterns\n\n### Account Creation\nMost builders need to handle associated token account creation:\n\n```typescript\nconst userTokenAccount = await getAssociatedTokenAddress(mint, user);\nconst accountInfo = await this.connection.getAccountInfo(userTokenAccount);\nif (!accountInfo) {\n  instructions.push(createAssociatedTokenAccountInstruction(\n    user, userTokenAccount, user, mint\n  ));\n}\n```\n\n### WSOL Handling\nFor SOL-based swaps, you'll often need to wrap/unwrap SOL:\n\n```typescript\n// Create WSOL account\nconst wsolAccount = await getAssociatedTokenAddress(NATIVE_MINT, user);\n// Add wrap/unwrap instructions as needed\n```\n\n### Slippage Calculation\n```typescript\nconst minAmountOut = expectedAmount * (10000 - slippageBps) / 10000;\n```\n\n## Troubleshooting\n\n- **Account not found**: Ensure all required accounts are created\n- **Insufficient funds**: Validate input amounts and account balances\n- **Invalid instruction data**: Check SDK usage and parameter formatting\n- **Program errors**: Verify program IDs and account permissions\n\n## Contributing\n\nWhen contributing a new builder:\n\n1. Follow the existing code style and patterns\n2. Add comprehensive error handling\n3. Include helper methods for common operations\n4. Update the registry to include your builder\n5. Add tests for your implementation\n6. Document any protocol-specific requirements\n\nYour builder will automatically be available through the API once registered in the `TransactionBuilderRegistry`.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffurydotbot%2Fswap-api","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffurydotbot%2Fswap-api","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffurydotbot%2Fswap-api/lists"}