harb/services/txnBot/service.js

167 lines
5 KiB
JavaScript
Raw Normal View History

2024-09-17 11:23:43 +02:00
require('dotenv').config();
const { ethers } = require('ethers');
const express = require('express');
const { execute } = require('./.graphclient');
const { bytesToUint256 } = require('kraiken-lib');
2024-09-17 11:23:43 +02:00
const myQuery = `
query GetPositions {
positions(first: 5000, where: {status: "Active"}) {
id
share
lastTaxTime
taxRate
status
}
}
`
// Load environment variables
const PROVIDER_URL = process.env.PROVIDER_URL;
const PRIVATE_KEY = process.env.PRIVATE_KEY;
const LM_CONTRACT_ADDRESS = process.env.LM_CONTRACT_ADDRESS;
const STAKE_CONTRACT_ADDRESS = process.env.STAKE_CONTRACT_ADDRESS;
const LM_ABI = [
{"type":"function","name":"recenter","inputs":[],"outputs":[],"stateMutability":"nonpayable"}
];
const STAKE_ABI = [
{"inputs":[{"internalType":"uint256","name":"positionId","type":"uint256"}],"name":"payTax","outputs":[],"stateMutability":"nonpayable","type":"function"}
];
// Initialize the provider
const provider = new ethers.JsonRpcProvider(PROVIDER_URL);
const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
const liquidityManager = new ethers.Contract(LM_CONTRACT_ADDRESS, LM_ABI, wallet);
const stakeContract = new ethers.Contract(STAKE_CONTRACT_ADDRESS, STAKE_ABI, wallet);
let startTime = new Date();
let lastRecenterTime = null;
let lastLiquidationTime = null;
async function checkFunds() {
const balance = await provider.getBalance(wallet.address);
return ethers.formatEther(balance);
}
async function canCallFunction() {
try {
// this will throw if the function is not callable
await liquidityManager.recenter.estimateGas();
return true;
} catch (error) {
return false;
}
}
async function checkPosition(position) {
let taxRate = parseFloat(position.taxRate);
let lastTaxTime = position.lastTaxTime;
let passed = (Date.now() / 1000) - lastTaxTime;
if (passed > 365 * 24 * 60 * 60 / taxRate ) {
const hexString = position.id;
const cleanHexString = hexString.startsWith('0x') ? hexString.substring(2) : hexString;
const bytes = new Uint8Array(Math.ceil(cleanHexString.length / 2));
for (let i = 0, j = 0; i < cleanHexString.length; i += 2, j++) {
bytes[j] = parseInt(cleanHexString.slice(i, i + 2), 16);
}
let positionId = bytesToUint256(bytes);
console.log(`Calling payTax on ${positionId}`);
const tx = await stakeContract.payTax(positionId);
await tx.wait();
lastLiquidationTime = new Date();
return 1;
} else {
return 0;
}
}
function formatDuration(ms) {
let seconds = Math.floor(ms / 1000);
let minutes = Math.floor(seconds / 60);
let hours = Math.floor(minutes / 60);
let days = Math.floor(hours / 24);
seconds = seconds % 60;
minutes = minutes % 60;
hours = hours % 24;
return `${days} days, ${hours} hours, ${minutes} minutes`;
}
async function liquidityLoop() {
try {
if (await canCallFunction()) {
console.log('Calling recenter...');
const tx = await liquidityManager.recenter();
await tx.wait();
lastRecenterTime = new Date();
console.log('recenter called successfully.');
} else {
console.log(`No liquidity can be moved at the moment. - ${(new Date()).toISOString()}`);
}
} catch (error) {
console.error('Error in liquidity loop:', error);
}
}
async function liquidationLoop() {
let counter = 0;
try {
const result = await execute(myQuery, {});
for (const position of result.data.positions) {
counter = counter + await checkPosition(position);
}
if (counter == 0) {
console.log(`No tax can be claimed at the moment. - ${(new Date()).toISOString()}`);
}
} catch (error) {
console.error('Error in liquididation loop:', error);
}
}
async function main() {
console.log('Service started...');
await liquidityLoop();
await liquidationLoop();
setInterval(liquidityLoop, 3 * 60000); // 3 minute
setInterval(liquidationLoop, 20 * 60000); // 20 minutes
}
// Start the main loop
main().catch(async (error) => {
console.error('Fatal error:', error);
});
// Set up the Express server
const app = express();
const PORT = process.env.PORT || 3000;
app.get('/status', async (req, res) => {
try {
const balance = await checkFunds();
const uptime = formatDuration(new Date() - startTime);
const status = {
balance: `${balance} ETH`,
uptime: uptime,
2024-09-17 15:05:22 +02:00
lastRecenterTime: lastRecenterTime ? lastRecenterTime.toISOString() : 'Never',
lastLiquidationTime: lastLiquidationTime ? lastLiquidationTime.toISOString() : 'Never'
2024-09-17 11:23:43 +02:00
};
if (parseFloat(balance) < 0.1) {
res.status(500).send(`Low Ethereum Balance: ${balance} ETH`);
} else {
res.status(200).json(status);
}
} catch (error) {
res.status(500).send(`Error checking funds: ${error.message}`);
}
});
app.listen(PORT, () => {
console.log(`HTTP server running on port ${PORT}`);
});