UNPKG

@okx-dex/okx-dex-sdk

Version:
512 lines (511 loc) 20.9 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.DexAPI = void 0; const factory_1 = require("./swap/factory"); const CryptoJS = __importStar(require("crypto-js")); const solana_instruction_1 = require("./swap/solana/solana-instruction"); class DexAPI { constructor(client, config) { this.client = client; this.config = config; this.defaultNetworkConfigs = { "501": { id: "501", explorer: "https://web3.okx.com/explorer/sol/tx", defaultSlippage: "0.005", maxSlippage: "1", computeUnits: 300000, confirmationTimeout: 60000, maxRetries: 3, }, "784": { id: "784", explorer: "https://web3.okx.com/explorer/sui/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "43114": { id: "43114", explorer: "https://web3.okx.com/explorer/avax/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "1": { id: "1", explorer: "https://web3.okx.com/explorer/ethereum/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "137": { id: "137", explorer: "https://web3.okx.com/explorer/polygon/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "146": { id: "146", explorer: "https://web3.okx.com/explorer/sonic/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "8453": { id: "8453", explorer: "https://web3.okx.com/explorer/base/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "196": { id: "196", explorer: "https://web3.okx.com/explorer/x-layer/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "10": { id: "10", explorer: "https://web3.okx.com/explorer/optimism/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "42161": { id: "42161", explorer: "https://web3.okx.com/explorer/arbitrum/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "56": { id: "56", explorer: "https://web3.okx.com/explorer/bsc/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "100": { id: "100", explorer: "https://web3.okx.com/explorer/gnosis/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "169": { id: "169", explorer: "https://web3.okx.com/explorer/manta/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "250": { id: "250", explorer: "https://web3.okx.com/explorer/ftm/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "324": { id: "324", explorer: "https://web3.okx.com/explorer/zksync/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "1101": { id: "1101", explorer: "https://web3.okx.com/explorer/polygon-zkevm/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "5000": { id: "5000", explorer: "https://web3.okx.com/explorer/mantle/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "25": { id: "25", explorer: "https://cronoscan.com/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "534352": { id: "534352", explorer: "https://web3.okx.com/explorer/scroll/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "59144": { id: "59144", explorer: "https://web3.okx.com/explorer/linea/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "1088": { id: "1088", explorer: "https://web3.okx.com/explorer/metis/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "1030": { id: "1030", explorer: "https://www.confluxscan.io/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "81457": { id: "81457", explorer: "https://web3.okx.com/explorer/blast/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "7000": { id: "7000", explorer: "https://explorer.zetachain.com/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, "66": { id: "66", explorer: "https://www.okx.com/web3/explorer/oktc/tx", defaultSlippage: "0.005", maxSlippage: "1", confirmationTimeout: 60000, maxRetries: 3, }, }; this.config.networks = { ...this.defaultNetworkConfigs, ...(config.networks || {}), }; } getNetworkConfig(chainIndex) { var _a; const networkConfig = (_a = this.config.networks) === null || _a === void 0 ? void 0 : _a[chainIndex]; if (!networkConfig) { throw new Error(`Network configuration not found for chain ${chainIndex}`); } return networkConfig; } // Convert params to API format toAPIParams(params) { const apiParams = {}; for (const [key, value] of Object.entries(params)) { if (value !== undefined) { if (key === "autoSlippage") { apiParams[key] = value ? "true" : "false"; } else { apiParams[key] = String(value); } } } return apiParams; } async getQuote(params) { return this.client.request("GET", "/api/v6/dex/aggregator/quote", this.toAPIParams(params)); } async getLiquidity(chainIndex) { return this.client.request("GET", "/api/v6/dex/aggregator/get-liquidity", this.toAPIParams({ chainIndex })); } async getChainData(chainIndex) { return this.client.request("GET", "/api/v6/dex/aggregator/supported/chain", this.toAPIParams({ chainIndex })); } async getSwapData(params) { // Validate slippage parameters if (!params.slippagePercent && !params.autoSlippage) { throw new Error("Either slippagePercent or autoSlippage must be provided"); } if (params.slippagePercent) { const slippageValue = parseFloat(params.slippagePercent); if (isNaN(slippageValue) || slippageValue < 0 || slippageValue > 1) { throw new Error("Slippage must be between 0 and 1"); } } if (params.autoSlippage && !params.maxAutoSlippagePercent) { throw new Error("maxAutoSlippagePercent must be provided when autoSlippage is enabled"); } return this.client.request("GET", "/api/v6/dex/aggregator/swap", this.toAPIParams(params)); } async getTokens(chainIndex) { return this.client.request("GET", "/api/v6/dex/aggregator/all-tokens", this.toAPIParams({ chainIndex })); } async getSolanaSwapInstruction(params) { if (!params.slippagePercent && !params.autoSlippage) { throw new Error("Either slippagePercent or autoSlippage must be provided"); } if (params.slippagePercent) { const slippageValue = parseFloat(params.slippagePercent); if (isNaN(slippageValue) || slippageValue < 0 || slippageValue > 1) { throw new Error("Slippage must be between 0 and 1"); } } if (params.autoSlippage && !params.maxAutoSlippagePercent) { throw new Error("maxAutoSlippagePercent must be provided when autoSlippage is enabled"); } return this.client.request("GET", "/api/v6/dex/aggregator/swap-instruction", this.toAPIParams(params)); } async executeSolanaSwapInstructions(params) { var _a; const instructionResp = await this.getSolanaSwapInstruction(params); const instructionData = instructionResp.data; if (!instructionData) { throw new Error("Empty instruction data from API"); } const networkConfig = this.getNetworkConfig(params.chainIndex); if (!((_a = this.config.solana) === null || _a === void 0 ? void 0 : _a.wallet)) { throw new Error("Solana wallet configuration required"); } const executor = new solana_instruction_1.SolanaInstructionExecutor(this.config, networkConfig); return executor.executeInstructions(instructionData); } async executeSwap(params) { const swapData = await this.getSwapData(params); const networkConfig = this.getNetworkConfig(params.chainIndex); const executor = factory_1.SwapExecutorFactory.createExecutor(params.chainIndex, this.config, networkConfig); return executor.executeSwap(swapData, params); } async executeApproval(params) { var _a, _b; try { // Get network configuration const networkConfig = this.getNetworkConfig(params.chainIndex); // Get the DEX approval address from supported chains const chainsData = await this.getChainData(params.chainIndex); const dexTokenApproveAddress = (_b = (_a = chainsData.data) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.dexTokenApproveAddress; if (!dexTokenApproveAddress) { throw new Error(`No dex contract address found for chain ${params.chainIndex}`); } // Create the approve executor const executor = factory_1.SwapExecutorFactory.createApproveExecutor(params.chainIndex, this.config, networkConfig); // Execute approval with the contract address from supported chains const result = await executor.handleTokenApproval(params.chainIndex, params.tokenContractAddress, params.approveAmount); // Return formatted result return { transactionHash: result.transactionHash, explorerUrl: `${networkConfig.explorer}/${result.transactionHash}` }; } catch (error) { // Check if it's an "already approved" error, which is not a real error if (error instanceof Error && error.message.includes("already approved")) { // Return a mock result for already approved tokens return { transactionHash: "", explorerUrl: "", alreadyApproved: true, message: "Token already approved for the requested amount" }; } // Otherwise, rethrow the error throw error; } } async simulateTransaction(params) { var _a; const requestPath = "/api/v5/dex/pre-transaction/simulate"; const timestamp = new Date().toISOString(); const requestBody = JSON.stringify(params); const headers = this.getHeaders(timestamp, "POST", requestPath, requestBody); const response = await fetch(`https://web3.okx.com${requestPath}`, { method: "POST", headers, body: requestBody }); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}, ${await response.text()}`); } const result = await response.json(); if (result.code !== "0" || !result.data || result.data.length === 0) { throw new Error(`Simulation failed: ${result.msg || 'Unknown error'}`); } const simData = result.data[0]; return { success: !simData.failReason, gasUsed: simData.gasUsed, error: simData.failReason, logs: simData.debug, assetChanges: ((_a = simData.assetChange) === null || _a === void 0 ? void 0 : _a.map((asset) => ({ direction: asset.rawVaule.startsWith('-') ? 'SEND' : 'RECEIVE', symbol: asset.symbol || 'Unknown', type: asset.assetType, amount: asset.rawVaule, decimals: asset.decimals, address: asset.address }))) || [], risks: simData.risks || [] }; } async getGasLimit(params) { const requestPath = "/api/v5/dex/pre-transaction/gas-limit"; const timestamp = new Date().toISOString(); const requestBody = JSON.stringify(params); const headers = this.getHeaders(timestamp, "POST", requestPath, requestBody); const response = await fetch(`https://web3.okx.com${requestPath}`, { method: "POST", headers, body: requestBody }); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}, ${await response.text()}`); } const result = await response.json(); if (result.code !== "0") { throw new Error(`Gas limit request failed: ${result.msg || 'Unknown error'}`); } return result; } async getGasPrice(chainIndex) { return this.client.request("GET", "/api/v5/dex/pre-transaction/gas-price", this.toAPIParams({ chainIndex })); } async broadcastTransaction(params) { const requestPath = "/api/v5/dex/pre-transaction/broadcast-transaction"; const timestamp = new Date().toISOString(); // Prepare request body const requestBody = { signedTx: params.signedTx, chainIndex: params.chainIndex, address: params.address }; // Handle extraData for MEV protection and Jito (for Solana) if (params.enableMevProtection || params.jitoSignedTx) { const extraData = {}; if (params.enableMevProtection) { extraData.enableMevProtection = params.enableMevProtection; } if (params.jitoSignedTx) { extraData.jitoSignedTx = params.jitoSignedTx; } requestBody.extraData = JSON.stringify(extraData); } else if (params.extraData) { requestBody.extraData = params.extraData; } const requestBodyString = JSON.stringify(requestBody); const headers = this.getHeaders(timestamp, "POST", requestPath, requestBodyString); const response = await fetch(`https://web3.okx.com${requestPath}`, { method: "POST", headers, body: requestBodyString }); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}, ${await response.text()}`); } const result = await response.json(); if (result.code !== "0") { throw new Error(`Broadcast transaction failed: ${result.msg || 'Unknown error'}`); } return result; } async getTransactionOrders(params) { const queryParams = new URLSearchParams(); queryParams.append('address', params.address); queryParams.append('chainIndex', params.chainIndex); if (params.txStatus) queryParams.append('txStatus', params.txStatus); if (params.orderId) queryParams.append('orderId', params.orderId); if (params.cursor) queryParams.append('cursor', params.cursor); if (params.limit) queryParams.append('limit', params.limit); const requestPath = `/api/v5/dex/post-transaction/orders?${queryParams.toString()}`; const timestamp = new Date().toISOString(); const headers = this.getHeaders(timestamp, "GET", requestPath); const response = await fetch(`https://web3.okx.com${requestPath}`, { method: "GET", headers }); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}, ${await response.text()}`); } const result = await response.json(); if (result.code !== "0") { throw new Error(`Get transaction orders failed: ${result.msg || 'Unknown error'}`); } return result; } getHeaders(timestamp, method, requestPath, requestBody = "") { const stringToSign = timestamp + method + requestPath + requestBody; return { "Content-Type": "application/json", "OK-ACCESS-KEY": this.config.apiKey, "OK-ACCESS-SIGN": CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(stringToSign, this.config.secretKey)), "OK-ACCESS-TIMESTAMP": timestamp, "OK-ACCESS-PASSPHRASE": this.config.apiPassphrase, }; } } exports.DexAPI = DexAPI;