@kaiachain/web3js-ext
Version:
web3.js extension for kaiachain blockchain
103 lines (102 loc) • 4.58 kB
JavaScript
import { Contract } from "web3-eth-contract";
import { GaslessSwapRouterAbi } from "../abi/GaslessSwapRouter.js";
import { RegistryAbi } from "../abi/Registry.js";
import { ERC20Abi } from "../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;
export async function getGaslessSwapRouter(context, address) {
let contractAddress;
if (!address) { // Read from the KIP-149 registry.
const registry = new Contract(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 Contract(GaslessSwapRouterAbi, contractAddress, context);
}
export 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;
}
export 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;
}
export 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);
}
export async function getApproveTx(context, eth, fromAddress, tokenAddress, routerAddress, gasPrice) {
const token = new Contract(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;
}
export 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 Contract(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;
}