@okx-dex/okx-dex-sdk
Version:
OKX DEX SDK
152 lines (151 loc) • 7.41 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.EVMSwapExecutor = void 0;
class EVMSwapExecutor {
constructor(config, networkConfig) {
var _a;
this.config = config;
this.networkConfig = networkConfig;
this.DEFAULT_GAS_MULTIPLIER = BigInt(150); // 1.5x
if (!((_a = this.config.evm) === null || _a === void 0 ? void 0 : _a.wallet)) {
throw new Error("EVM configuration required");
}
this.provider = this.config.evm.wallet.provider;
}
async executeSwap(swapData, params) {
var _a;
const quoteData = (_a = swapData.data) === null || _a === void 0 ? void 0 : _a[0];
if (!(quoteData === null || quoteData === void 0 ? void 0 : quoteData.routerResult)) {
throw new Error("Invalid swap data: missing router result");
}
const { routerResult } = quoteData;
const tx = quoteData.tx;
if (!tx) {
throw new Error("Missing transaction data");
}
try {
const result = await this.executeEvmTransaction(tx);
return this.formatSwapResult(result.hash, routerResult);
}
catch (error) {
console.error("Swap execution failed:", error);
throw error;
}
}
async executeEvmTransaction(tx) {
var _a;
if (!((_a = this.config.evm) === null || _a === void 0 ? void 0 : _a.wallet)) {
throw new Error("EVM wallet required");
}
let retryCount = 0;
while (retryCount < (this.networkConfig.maxRetries || 3)) {
try {
console.log("Preparing transaction...");
const gasMultiplier = BigInt(500); // 5x standard multiplier
// Get current nonce
const nonce = await this.provider.getTransactionCount(this.config.evm.wallet.address);
// Get current gas prices
const feeData = await this.provider.getFeeData();
const baseFee = feeData.maxFeePerGas || BigInt(0);
const priorityFee = feeData.maxPriorityFeePerGas || BigInt(3000000000); // 3 gwei minimum
const transaction = {
data: tx.data,
to: tx.to,
value: tx.value || '0',
nonce: nonce + retryCount, // Increment nonce for each retry
gasLimit: BigInt(tx.gas || 0) * gasMultiplier / BigInt(100),
maxFeePerGas: (baseFee * gasMultiplier) / BigInt(100),
maxPriorityFeePerGas: (priorityFee * gasMultiplier) / BigInt(100)
};
console.log("Transaction details:", {
to: transaction.to,
value: transaction.value,
nonce: transaction.nonce,
gasLimit: transaction.gasLimit.toString(),
maxFeePerGas: transaction.maxFeePerGas.toString(),
maxPriorityFeePerGas: transaction.maxPriorityFeePerGas.toString()
});
console.log("Sending transaction...");
const response = await this.config.evm.wallet.sendTransaction(transaction);
console.log("Transaction sent! Hash:", response.hash);
// Wait a bit before checking status to allow transaction to be mined
await new Promise(resolve => setTimeout(resolve, 5000));
console.log("Waiting for transaction confirmation...");
try {
// Poll for transaction status
let receipt = null;
let attempts = 0;
const maxAttempts = 30; // 30 attempts * 2 seconds = 60 seconds total
while (attempts < maxAttempts) {
receipt = await this.provider.getTransactionReceipt(response.hash);
if (receipt) {
console.log("Transaction confirmed! Block number:", receipt.blockNumber);
return receipt;
}
// Check if transaction is still pending
const tx = await this.provider.getTransaction(response.hash);
if (!tx) {
// Check if we're on a different network than expected
const network = await this.provider.getNetwork();
console.error(`Transaction dropped. Network: ${network.name} (${network.chainId})`);
throw new Error("Transaction dropped - check network and gas prices");
}
console.log(`Transaction still pending... (attempt ${attempts + 1}/${maxAttempts})`);
await new Promise(resolve => setTimeout(resolve, 2000)); // Wait 2 seconds between checks
attempts++;
}
throw new Error("Transaction confirmation timed out - check explorer for status");
}
catch (waitError) {
console.error("Error waiting for confirmation:", waitError.message);
throw waitError;
}
}
catch (error) {
retryCount++;
console.error(`Transaction attempt ${retryCount} failed:`, error.message);
if (error.code === 'INSUFFICIENT_FUNDS') {
throw new Error("Insufficient funds for transaction");
}
if (error.code === 'NONCE_EXPIRED') {
throw new Error("Transaction nonce expired");
}
if (retryCount === this.networkConfig.maxRetries) {
console.error("Max retries reached. Last error:", error);
throw error;
}
const delay = 2000 * retryCount;
console.log(`Retrying in ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
throw new Error('Max retries exceeded');
}
formatSwapResult(txHash, routerResult) {
const fromDecimals = parseInt(routerResult.fromToken.decimal);
const toDecimals = parseInt(routerResult.toToken.decimal);
const displayFromAmount = (Number(routerResult.fromTokenAmount) /
Math.pow(10, fromDecimals)).toFixed(6);
const displayToAmount = (Number(routerResult.toTokenAmount) /
Math.pow(10, toDecimals)).toFixed(6);
return {
success: true,
transactionId: txHash,
explorerUrl: `${this.networkConfig.explorer}/${txHash}`,
details: {
fromToken: {
symbol: routerResult.fromToken.tokenSymbol,
amount: displayFromAmount,
decimal: routerResult.fromToken.decimal,
},
toToken: {
symbol: routerResult.toToken.tokenSymbol,
amount: displayToAmount,
decimal: routerResult.toToken.decimal,
},
priceImpact: routerResult.priceImpactPercentage,
},
};
}
}
exports.EVMSwapExecutor = EVMSwapExecutor;