@okx-dex/okx-dex-sdk
Version:
OKX DEX SDK
512 lines (511 loc) • 20.9 kB
JavaScript
;
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;