Skip to main content
Glama
index.ts19.1 kB
import { Account, AccountAddress, Aptos, AptosConfig as AptosSDKConfig, Ed25519PrivateKey, Secp256k1PrivateKey, Network, PrivateKey, PrivateKeyVariants } from "@aptos-labs/ts-sdk"; import { initTappSDK, TappSDK } from '@tapp-exchange/sdk'; import { getTappConfig } from "../config"; import { TappPool, TappPosition, SwapEstimate, TransactionResponse, SwapAMMParams, SwapCLMMParams, SwapStableParams, CreateAMMPoolAndAddLiquidityParams, CreateCLMMPoolAndAddLiquidityParams, CreateStablePoolAndAddLiquidityParams, RemoveSingleAMMLiquidityParams, RemoveMultipleAMMLiquidityParams, RemoveSingleCLMMLiquidityParams, RemoveMultipleCLMMLiquidityParams, RemoveSingleStableLiquidityParams, RemoveMultipleStableLiquidityParams, CollectFeeParams, AddAMMLiquidityParams, AddCLMMLiquidityParams, AddStableLiquidityParams } from "../types"; // Key format detection utility (reused from base Aptos MCP) class KeyFormatDetector { static detectAndCreateKey(privateKeyInput: string): Ed25519PrivateKey | Secp256k1PrivateKey { const cleanKey = this.cleanPrivateKey(privateKeyInput); const format = this.detectKeyFormat(privateKeyInput, cleanKey); console.error(`Auto-detected key format: ${format}`); switch (format) { case 'secp256k1': return new Secp256k1PrivateKey( PrivateKey.formatPrivateKey(cleanKey, PrivateKeyVariants.Secp256k1) ); case 'ed25519': default: return new Ed25519PrivateKey( PrivateKey.formatPrivateKey(cleanKey, PrivateKeyVariants.Ed25519) ); } } private static cleanPrivateKey(privateKey: string): string { let cleaned = privateKey.trim(); const prefixes = [ 'secp256k1-priv-', 'ed25519-priv-', 'private-key-', 'priv-', ]; for (const prefix of prefixes) { if (cleaned.toLowerCase().startsWith(prefix.toLowerCase())) { cleaned = cleaned.substring(prefix.length); break; } } if (!cleaned.startsWith('0x') && this.isHexString(cleaned)) { cleaned = '0x' + cleaned; } return cleaned; } private static detectKeyFormat(originalKey: string, cleanedKey: string): 'secp256k1' | 'ed25519' { if (originalKey.toLowerCase().includes('secp256k1')) { return 'secp256k1'; } if (originalKey.toLowerCase().includes('ed25519')) { return 'ed25519'; } const keyHex = cleanedKey.startsWith('0x') ? cleanedKey.slice(2) : cleanedKey; if (keyHex.length === 64) { return this.advancedKeyFormatDetection(keyHex); } if (keyHex.length === 66 && keyHex.startsWith('00')) { return 'secp256k1'; } return 'ed25519'; } private static advancedKeyFormatDetection(keyHex: string): 'secp256k1' | 'ed25519' { try { const secp256k1Order = BigInt('0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141'); const keyValue = BigInt('0x' + keyHex); if (keyValue >= secp256k1Order) { return 'ed25519'; } return 'ed25519'; } catch (error) { console.warn('Error in advanced key detection:', error); } return 'ed25519'; } private static isHexString(str: string): boolean { return /^[0-9a-fA-F]+$/.test(str); } static validateKey(privateKeyInput: string): { isValid: boolean; format: string; error?: string } { try { const key = this.detectAndCreateKey(privateKeyInput); const account = Account.fromPrivateKey({ privateKey: key }); return { isValid: true, format: key instanceof Secp256k1PrivateKey ? 'secp256k1' : 'ed25519', }; } catch (error) { return { isValid: false, format: 'unknown', error: error instanceof Error ? error.message : 'Unknown validation error' }; } } } export class TappAgent { public account: Account; public aptos: Aptos; public sdk: TappSDK; // Properly typed Tapp SDK instance public network: 'mainnet' | 'testnet' | 'devnet'; public keyFormat: 'secp256k1' | 'ed25519'; constructor() { const config = getTappConfig(); // Map network string to Network enum let networkEnum: Network; switch (config.network) { case 'mainnet': networkEnum = Network.MAINNET; break; case 'testnet': networkEnum = Network.TESTNET; break; case 'devnet': networkEnum = Network.DEVNET; break; default: networkEnum = Network.MAINNET; } this.network = config.network; // Initialize Aptos client const aptosConfig = new AptosSDKConfig({ network: networkEnum, ...(config.rpcUrl && { fullnode: config.rpcUrl }) }); this.aptos = new Aptos(aptosConfig); // Initialize Tapp SDK this.sdk = initTappSDK({ network: networkEnum, ...(config.rpcUrl && { url: config.rpcUrl }) }); // Setup account if (config.privateKey) { const validation = KeyFormatDetector.validateKey(config.privateKey); if (!validation.isValid) { throw new Error(`Invalid private key: ${validation.error}`); } } const privateKey = KeyFormatDetector.detectAndCreateKey( config.privateKey || `${(Account.generate().privateKey)}` ); this.keyFormat = privateKey instanceof Secp256k1PrivateKey ? 'secp256k1' : 'ed25519'; console.error(`✅ Successfully initialized TappAgent with ${this.keyFormat} key format`); this.account = Account.fromPrivateKey({ privateKey }); } async getAddress(): Promise<string> { return this.account.accountAddress.toString(); } async getKeyInfo(): Promise<{ address: string; format: string; publicKey: string; }> { return { address: await this.getAddress(), format: this.keyFormat, publicKey: this.account.publicKey.toString(), }; } // Pool Management async getPools(params: { page?: number; size?: number; sortBy?: string; type?: string; }): Promise<TappPool[]> { const pools = await this.sdk.Pool.getPools(params as any); return pools; } async getPoolInfo(poolId: string): Promise<TappPool> { const info = await this.sdk.Pool.getInfo(poolId); return info; } // Swap Operations async getEstimateSwapAmount(params: { amount: number; poolId: string; pair: [number, number]; a2b: boolean; field?: 'input' | 'output'; }): Promise<SwapEstimate> { const result = await this.sdk.Swap.getEstSwapAmount(params); return result; } async getSwapRoute(token0: string, token1: string): Promise<any> { const route = await this.sdk.Swap.getRoute(token0, token1); return route; } async swapAMM(params: SwapAMMParams): Promise<TransactionResponse> { try { const data = this.sdk.Swap.swapAMMTransactionPayload(params); const response = await this.aptos.transaction.submit.simple({ sender: this.account.accountAddress, data: data } as any); return { hash: response.hash, success: true }; } catch (error) { return { hash: '', success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async swapCLMM(params: SwapCLMMParams): Promise<TransactionResponse> { try { const data = this.sdk.Swap.swapCLMMTransactionPayload(params); const response = await this.aptos.transaction.submit.simple({ sender: this.account.accountAddress, data: data } as any); return { hash: response.hash, success: true }; } catch (error) { return { hash: '', success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async swapStable(params: SwapStableParams): Promise<TransactionResponse> { try { const data = this.sdk.Swap.swapStableTransactionPayload(params); const response = await this.aptos.transaction.submit.simple({ sender: this.account.accountAddress, data: data } as any); return { hash: response.hash, success: true }; } catch (error) { return { hash: '', success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } // Liquidity Operations async createAMMPoolAndAddLiquidity(params: CreateAMMPoolAndAddLiquidityParams): Promise<TransactionResponse> { try { const data = this.sdk.Position.createAMMPoolAndAddLiquidity(params); const response = await this.aptos.transaction.submit.simple({ sender: this.account.accountAddress, data: data } as any); return { hash: response.hash, success: true }; } catch (error) { return { hash: '', success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async createCLMMPoolAndAddLiquidity(params: CreateCLMMPoolAndAddLiquidityParams): Promise<TransactionResponse> { try { const data = this.sdk.Position.createCLMMPoolAndAddLiquidity(params); const response = await this.aptos.transaction.submit.simple({ sender: this.account.accountAddress, data: data } as any); return { hash: response.hash, success: true }; } catch (error) { return { hash: '', success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async createStablePoolAndAddLiquidity(params: CreateStablePoolAndAddLiquidityParams): Promise<TransactionResponse> { try { const data = this.sdk.Position.createStablePoolAndAddLiquidity(params); const response = await this.aptos.transaction.submit.simple({ sender: this.account.accountAddress, data: data } as any); return { hash: response.hash, success: true }; } catch (error) { return { hash: '', success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async addAMMLiquidity(params: AddAMMLiquidityParams): Promise<TransactionResponse> { try { const data = this.sdk.Position.addAMMLiquidity(params); const response = await this.aptos.transaction.submit.simple({ sender: this.account.accountAddress, data: data } as any); return { hash: response.hash, success: true }; } catch (error) { return { hash: '', success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async addCLMMLiquidity(params: AddCLMMLiquidityParams): Promise<TransactionResponse> { try { const data = this.sdk.Position.addCLMMLiquidity(params); const response = await this.aptos.transaction.submit.simple({ sender: this.account.accountAddress, data: data } as any); return { hash: response.hash, success: true }; } catch (error) { return { hash: '', success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async addStableLiquidity(params: AddStableLiquidityParams): Promise<TransactionResponse> { try { const data = this.sdk.Position.addStableLiquidity(params); const response = await this.aptos.transaction.submit.simple({ sender: this.account.accountAddress, data: data } as any); return { hash: response.hash, success: true }; } catch (error) { return { hash: '', success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } // Remove Liquidity Operations async removeSingleAMMLiquidity(params: RemoveSingleAMMLiquidityParams): Promise<TransactionResponse> { try { const data = this.sdk.Position.removeSingleAMMLiquidity(params); const response = await this.aptos.transaction.submit.simple({ sender: this.account.accountAddress, data: data } as any); return { hash: response.hash, success: true }; } catch (error) { return { hash: '', success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async removeMultipleAMMLiquidity(params: RemoveMultipleAMMLiquidityParams): Promise<TransactionResponse> { try { const data = this.sdk.Position.removeMultipleAMMLiquidity(params); const response = await this.aptos.transaction.submit.simple({ sender: this.account.accountAddress, data: data } as any); return { hash: response.hash, success: true }; } catch (error) { return { hash: '', success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async removeSingleCLMMLiquidity(params: RemoveSingleCLMMLiquidityParams): Promise<TransactionResponse> { try { const data = this.sdk.Position.removeSingleCLMMLiquidity(params); const response = await this.aptos.transaction.submit.simple({ sender: this.account.accountAddress, data: data } as any); return { hash: response.hash, success: true }; } catch (error) { return { hash: '', success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async removeMultipleCLMMLiquidity(params: RemoveMultipleCLMMLiquidityParams): Promise<TransactionResponse> { try { const data = this.sdk.Position.removeMultipleCLMMLiquidity(params); const response = await this.aptos.transaction.submit.simple({ sender: this.account.accountAddress, data: data } as any); return { hash: response.hash, success: true }; } catch (error) { return { hash: '', success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async removeSingleStableLiquidity(params: RemoveSingleStableLiquidityParams): Promise<TransactionResponse> { try { const data = this.sdk.Position.removeSingleStableLiquidity(params); const response = await this.aptos.transaction.submit.simple({ sender: this.account.accountAddress, data: data } as any); return { hash: response.hash, success: true }; } catch (error) { return { hash: '', success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } async removeMultipleStableLiquidity(params: RemoveMultipleStableLiquidityParams): Promise<TransactionResponse> { try { const data = this.sdk.Position.removeMultipleStableLiquidity(params); const response = await this.aptos.transaction.submit.simple({ sender: this.account.accountAddress, data: data } as any); return { hash: response.hash, success: true }; } catch (error) { return { hash: '', success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } // Position Management async getPositions(params: { userAddr?: string; page?: number; size?: number; }): Promise<TappPosition[]> { const userAddr = params.userAddr || await this.getAddress(); const positions = await this.sdk.Position.getPositions({ ...params, userAddr }); return positions; } async collectFee(params: CollectFeeParams): Promise<TransactionResponse> { try { const data = this.sdk.Position.collectFee(params); const response = await this.aptos.transaction.submit.simple({ sender: this.account.accountAddress, data: data } as any); return { hash: response.hash, success: true }; } catch (error) { return { hash: '', success: false, error: error instanceof Error ? error.message : 'Unknown error' }; } } } // Export the KeyFormatDetector for testing purposes export { KeyFormatDetector };

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/tamago-labs/tapp-exchange-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server