Skip to main content
Glama

sui-trader-mcp

index.js5.08 kB
require("dotenv").config(); const { McpServer } = require("@modelcontextprotocol/sdk/server/mcp.js"); const { StdioServerTransport } = require("@modelcontextprotocol/sdk/server/stdio.js"); const { AggregatorClient } = require("@cetusprotocol/aggregator-sdk"); const { SuiClient, getFullnodeUrl } = require("@mysten/sui/client"); const { Ed25519Keypair } = require("@mysten/sui/keypairs/ed25519") const { decodeSuiPrivateKey } = require("@mysten/sui/cryptography") const { Transaction } = require("@mysten/sui/transactions") const BN = require("bn.js"); const { z } = require("zod"); SUI_NETWORK = "mainnet" // Initialize Sui client and keypair const client = new SuiClient({ url: getFullnodeUrl(SUI_NETWORK) }); // Parse bech32 private key const privateKey = process.env.PRIVATE_KEY; if (!privateKey || !privateKey.startsWith("suiprivkey")) { throw new Error("Valid bech32 PRIVATE_KEY (starting with 'suiprivkey') not found in .env"); } const {schema, secretKey} = decodeSuiPrivateKey(privateKey) const keypair = Ed25519Keypair.fromSecretKey(secretKey); // Initialize the Cetus Aggregator Client const aggregatorClient = new AggregatorClient({client, signer: keypair.toSuiAddress()}); // Create MCP server const server = new McpServer({ name: "SuiTrader", version: "1.0.0", }); // Hardcoded decimals for common tokens const TOKEN_DECIMALS = { "0x2::sui::SUI": 9, "0x06864a6f921804860930db6ddbe2e16acdf8504495ea7481637a1c8b9a8fe54b::cetus::CETUS": 9, "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC": 6 }; // Function to fetch token decimals dynamically (if not hardcoded) async function getTokenDecimals(tokenAddress) { if (TOKEN_DECIMALS[tokenAddress]) { return TOKEN_DECIMALS[tokenAddress]; } try { const metadata = await client.getCoinMetadata({ coinType: tokenAddress }); if (!metadata) { throw new Error(`No metadata found for token ${tokenAddress}`); } return metadata.decimals; } catch (error) { console.error(`Error fetching decimals for ${tokenAddress}: ${error.message}`); return 9; // Fallback to 9 } } // Tool for executing a token swap server.tool( "swap", "Executes a token swap on the Sui blockchain using the Cetus Aggregator", { from: z .string() .describe("Source token address") .default("0x2::sui::SUI"), target: z .string() .describe("Target token address") .default("0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC"), // USDC amount: z .number() .positive("Amount must be positive") .describe("Amount to swap (in human-readable format, e.g., 1.5 SUI)") .default(1.0), byAmountIn: z .boolean() .describe("True for fixed input, false for fixed output") .default(true), slippage: z .number() .min(0) .max(1) .describe("Slippage tolerance (e.g., 0.01 for 1%)") .default(0.01), }, async({from, target, amount, byAmountIn, slippage}) => { try { // Fetch decimals for the source token const decimals = await getTokenDecimals(from); // Convert floating-point amount to smallest unit const amountInSmallestUnit = new BN( Math.floor(amount * Math.pow(10, decimals)).toString() ); // Find best trading routes const routers = await aggregatorClient.findRouters({ from, target, amount: amountInSmallestUnit, byAmountIn, }); if (!routers || routers.routes.length === 0) { return { content: [{ type: "text", text: "No trading routes found" }], isError: true, }; } // Create transaction const txb = new Transaction(); // Execute fast swap await aggregatorClient.fastRouterSwap({ routers, txb, slippage, }); // Simulate transaction const simResult = await aggregatorClient.devInspectTransactionBlock(txb, keypair); if (simResult.effects.status.status !== "success") { return { content: [ { type: "text", text: `Transaction simulation failed: ${JSON.stringify(simResult.effects.status)}`, }, ], isError: true, }; } // Sign and execute transaction const result = await aggregatorClient.signAndExecuteTransaction(txb, keypair); return { content: [ { type: "text", text: `Transaction ID: ${result.digest}. Status: ${result.effects.status.status}`, }, ], isError: false, }; } catch (error) { return { content: [ { type: "text", text: `Error executing swap: ${error.message}`, }, ], isError: true, }; } } ); // Start the server with stdio transport async function startServer() { const transport = new StdioServerTransport(); await server.connect(transport); } startServer().catch(console.error);

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/kukapay/sui-trader-mcp'

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