UNPKG

@kaiachain/web3js-ext

Version:
111 lines (110 loc) 5.03 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.getGaslessSwapRouter = getGaslessSwapRouter; exports.getAmountRepay = getAmountRepay; exports.getMinAmountOut = getMinAmountOut; exports.getAmountIn = getAmountIn; exports.getApproveTx = getApproveTx; exports.getSwapTx = getSwapTx; const web3_eth_contract_1 = require("web3-eth-contract"); const GaslessSwapRouter_js_1 = require("../abi/GaslessSwapRouter.js"); const Registry_js_1 = require("../abi/Registry.js"); const ERC20_js_1 = require("../abi/ERC20.js"); // GaslessSwapRouterAddress registry key // https://github.com/kaiachain/kaia/blob/v2.0.0/contracts/contracts/system_contracts/multicall/MultiCallContract.sol#L140 const GASLESS_SWAP_ROUTER_NAME = "GaslessSwapRouter"; const REGISTRY_ADDRESS = "0x0000000000000000000000000000000000000401"; const MAX_UINT256 = '0x' + 'f'.repeat(64); // Using fixed constants to simplify amountRepay calculation. const GAS_LIMIT_LEND = 21000; const GAS_LIMIT_APPROVE = 100000; const GAS_LIMIT_SWAP = 500000; async function getGaslessSwapRouter(context, address) { let contractAddress; if (!address) { // Read from the KIP-149 registry. const registry = new web3_eth_contract_1.Contract(Registry_js_1.RegistryAbi, REGISTRY_ADDRESS, context); const address = await registry.methods.getActiveAddr(GASLESS_SWAP_ROUTER_NAME).call(); if (!address) { throw new Error("GaslessSwapRouter not found in the registry"); } contractAddress = address; } else { // Use the custom address. contractAddress = address; } return new web3_eth_contract_1.Contract(GaslessSwapRouter_js_1.GaslessSwapRouterAbi, contractAddress, context); } function getAmountRepay(approveRequired, gasPrice) { const gasPriceBN = BigInt(gasPrice); const lendTxGas = BigInt(GAS_LIMIT_LEND); const approveTxGas = approveRequired ? BigInt(GAS_LIMIT_APPROVE) : BigInt(0); const swapTxGas = BigInt(GAS_LIMIT_SWAP); const R1 = gasPriceBN * lendTxGas; const R2 = gasPriceBN * approveTxGas; const R3 = gasPriceBN * swapTxGas; return R1 + R2 + R3; } function getMinAmountOut(amountRepay, appTxFee, commissionRateBps) { const amountRepayBN = BigInt(amountRepay); const appTxFeeBN = BigInt(appTxFee); const commissionRateBpsBN = BigInt(commissionRateBps); const denominator = BigInt(10000); if (commissionRateBpsBN < 0 || commissionRateBpsBN >= 10000) { throw new Error("Commission rate must be between 0 and 9999 basis points"); } // minAmountOut = appTxFee / (1 - commissionRate) + amountRepay // i.e. (amountOut - amountRepay) * (1 - commissionRate) >= appTxFee // because the swap output has to be enough to repay and pay the commission. const beforeCommission = appTxFeeBN * denominator / (denominator - commissionRateBpsBN); return beforeCommission + amountRepayBN; } async function getAmountIn(router, tokenAddress, minAmountOut, slippageBps) { const minAmountOutBN = BigInt(minAmountOut); const slippageBpsBN = BigInt(slippageBps); const denominator = BigInt(10000); // minAmountIn = DEX.getAmountIn(tokenAddress, minAmountOut * (1 + slippage)) // Have DEX calculate the required input to produce minAmountOut plus slippage. const withSlippage = minAmountOutBN * (denominator + slippageBpsBN) / denominator; const amountIn = await router.methods.getAmountIn(tokenAddress, withSlippage).call(); return BigInt(amountIn); } async function getApproveTx(context, eth, fromAddress, tokenAddress, routerAddress, gasPrice) { const token = new web3_eth_contract_1.Contract(ERC20_js_1.ERC20Abi, tokenAddress, context); const data = token.methods.approve(routerAddress, MAX_UINT256).encodeABI(); const nonce = await eth.getTransactionCount(fromAddress); const tx = { type: 0, from: fromAddress, to: tokenAddress, nonce: nonce, gas: GAS_LIMIT_APPROVE, gasPrice: gasPrice, value: 0n, data: data, }; return tx; } async function getSwapTx(eth, fromAddress, tokenAddress, routerAddress, amountIn, minAmountOut, amountRepay, gasPrice, approveRequired, deadlineBuffer) { const latestBlock = await eth.getBlock("latest"); if (!latestBlock) { throw new Error("Failed to get latest block"); } const deadlineTimestamp = latestBlock.timestamp + BigInt(deadlineBuffer); let nonce = await eth.getTransactionCount(fromAddress); if (approveRequired) { nonce += 1n; } const routerInterface = new web3_eth_contract_1.Contract(GaslessSwapRouter_js_1.GaslessSwapRouterAbi); const data = routerInterface.methods.swapForGas(tokenAddress, amountIn, minAmountOut, amountRepay, deadlineTimestamp).encodeABI(); const tx = { type: 0, from: fromAddress, to: routerAddress, nonce: nonce, gas: GAS_LIMIT_SWAP, gasPrice: gasPrice, value: 0n, data: data, }; return tx; }