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