UNPKG

@okx-dex/okx-dex-sdk

Version:
139 lines (138 loc) 6.18 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.EVMApproveExecutor = void 0; // src/api/swap/evm/evm-approve.ts const web3_1 = __importDefault(require("web3")); const http_client_1 = require("../../../core/http-client"); // ERC20 ABI for approval const ERC20_ABI = [ { "constant": true, "inputs": [ { "name": "_owner", "type": "address" }, { "name": "_spender", "type": "address" } ], "name": "allowance", "outputs": [{ "name": "", "type": "uint256" }], "type": "function" }, { "constant": false, "inputs": [ { "name": "_spender", "type": "address" }, { "name": "_value", "type": "uint256" } ], "name": "approve", "outputs": [{ "name": "", "type": "bool" }], "type": "function" } ]; class EVMApproveExecutor { constructor(config, networkConfig) { var _a; this.config = config; this.networkConfig = networkConfig; this.DEFAULT_GAS_MULTIPLIER = BigInt(3) / BigInt(2); // 1.5x if (!this.config.evm) { throw new Error("EVM configuration required"); } this.web3 = new web3_1.default(((_a = this.config.evm.connection) === null || _a === void 0 ? void 0 : _a.rpcUrl) || ''); this.httpClient = new http_client_1.HTTPClient(this.config); } async executeSwap(swapData, params) { throw new Error("Swap execution not supported in approval executor"); } async getAllowance(tokenAddress, ownerAddress, spenderAddress) { const tokenContract = new this.web3.eth.Contract(ERC20_ABI, tokenAddress); const allowanceResult = await tokenContract.methods.allowance(ownerAddress, spenderAddress).call(); return BigInt(allowanceResult); } async handleTokenApproval(chainId, tokenAddress, amount) { var _a; if (!((_a = this.config.evm) === null || _a === void 0 ? void 0 : _a.walletAddress)) { throw new Error("Wallet address not configured"); } const dexContractAddress = await this.getDexContractAddress(chainId); // Check current allowance const currentAllowance = await this.getAllowance(tokenAddress, this.config.evm.walletAddress, dexContractAddress); if (currentAllowance >= BigInt(amount)) { throw new Error("Token already approved for the requested amount"); } try { // Execute the approval transaction const result = await this.executeApprovalTransaction(tokenAddress, dexContractAddress, amount); return { transactionHash: result.transactionHash.toString() }; } catch (error) { console.error("Approval execution failed:", error); throw error; } } async getDexContractAddress(chainId) { var _a, _b; try { const response = await this.httpClient.request('GET', '/api/v5/dex/aggregator/supported/chain', { chainId }); if (!((_b = (_a = response.data) === null || _a === void 0 ? void 0 : _a[0]) === null || _b === void 0 ? void 0 : _b.dexTokenApproveAddress)) { throw new Error(`No dex contract address found for chain ${chainId}`); } return response.data[0].dexTokenApproveAddress; } catch (error) { console.error('Error getting dex contract address:', error); throw error; } } async executeApprovalTransaction(tokenAddress, spenderAddress, amount) { var _a, _b; if (!((_a = this.config.evm) === null || _a === void 0 ? void 0 : _a.privateKey) || !((_b = this.config.evm) === null || _b === void 0 ? void 0 : _b.walletAddress)) { throw new Error("EVM private key and wallet address required"); } // Prepare approval data const approvalCallData = this.web3.eth.abi.encodeFunctionCall({ name: 'approve', type: 'function', inputs: [ { type: 'address', name: '_spender' }, { type: 'uint256', name: '_value' } ] }, [spenderAddress, amount]); let retryCount = 0; while (retryCount < (this.networkConfig.maxRetries || 3)) { try { const nonce = await this.web3.eth.getTransactionCount(this.config.evm.walletAddress, 'latest'); const gasPrice = await this.web3.eth.getGasPrice(); // Estimate gas const estimate = await this.web3.eth.estimateGas({ from: this.config.evm.walletAddress, to: tokenAddress, data: approvalCallData }); const gasLimit = BigInt(estimate) * BigInt(2); // Double to be safe const signTransactionParams = { to: tokenAddress, data: approvalCallData, gasPrice: BigInt(gasPrice) * this.DEFAULT_GAS_MULTIPLIER, gas: gasLimit, value: '0', nonce }; console.log("Signing approval transaction..."); const { rawTransaction } = await this.web3.eth.accounts.signTransaction(signTransactionParams, this.config.evm.privateKey); console.log("Sending approval transaction..."); return this.web3.eth.sendSignedTransaction(rawTransaction); } catch (error) { retryCount++; console.warn(`Approval attempt ${retryCount} failed, retrying in ${2000 * retryCount}ms...`); if (retryCount === this.networkConfig.maxRetries) throw error; await new Promise(resolve => setTimeout(resolve, 2000 * retryCount)); } } throw new Error('Max retries exceeded for approval transaction'); } } exports.EVMApproveExecutor = EVMApproveExecutor;