From 6e9a4853affe9baa96104c8ee80cc7c9e0741fe5 Mon Sep 17 00:00:00 2001 From: Ewan Sheldon Date: Mon, 24 Jun 2024 15:38:23 +0200 Subject: [PATCH] adds scheduling to run liquidations + post liquidation info to discord --- index.js | 2 + src/liquidation.js | 108 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) create mode 100644 src/liquidation.js diff --git a/index.js b/index.js index 5fb3766..9fe44cd 100644 --- a/index.js +++ b/index.js @@ -3,12 +3,14 @@ require('dotenv').config(); const { schedulePricingIndexing } = require('./src/pricing'); const { scheduleIndexYieldData } = require('./src/yield'); const { scheduleVaultTransactionIndexing } = require('./src/transactions'); +const { scheduleLiquidation } = require('./src/liquidation'); const port = process.env.PORT || 3000; schedulePricingIndexing(); scheduleIndexYieldData(); scheduleVaultTransactionIndexing(); +scheduleLiquidation(); const server = http.createServer(async (req, res) => { res.statusCode = 200; diff --git a/src/liquidation.js b/src/liquidation.js new file mode 100644 index 0000000..c57ae7d --- /dev/null +++ b/src/liquidation.js @@ -0,0 +1,108 @@ +const https = require('https'); +const schedule = require('node-schedule'); +const { getContract } = require("./contractFactory"); +const { ethers, BigNumber } = require('ethers'); +const { getNetwork } = require('./networks'); + +const getVaultSupply = async (wallet, manager) => { + try { + return await manager.connect(wallet).totalSupply(); + } catch (_) { + return await getVaultSupply(wallet, manager); + } +}; + +const postToDiscord = async content => { + return new Promise((resolve, reject) => { + const data = JSON.stringify({ + content, + }); + + const options = { + hostname: 'discord.com', + port: 443, + path: `/api/webhooks/1254770462186143816/${process.env.WEBHOOK_TOKEN}`, + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': data.length, + }, + }; + + const req = https.request(options, (res) => { + console.log(`statusCode: ${res.statusCode}`); + + res.on('data', (d) => { + process.stdout.write(d); + }); + }); + + req.on('error', (error) => { + console.error(error); + reject(); + }); + + req.write(data); + req.end(); + resolve(); + }); +}; + +const getVaultManager = async _ => { + const network = getNetwork('arbitrum'); + const manager = await getContract(network.name, 'SmartVaultManager'); + const provider = new ethers.getDefaultProvider(network.rpc); + const wallet = new ethers.Wallet(process.env.WALLET_PRIVATE_KEY, provider); + return { manager, wallet, provider }; +}; + +const scheduleLiquidation = async _ => { + // checks for undercollateralised vaults and liquidates + schedule.scheduleJob('6,36 * * * *', async _ => { + const network = getNetwork('arbitrum'); + const index = await getContract(network.name, 'SmartVaultIndex'); + const { manager, wallet } = await getVaultManager(); + const supply = Number((await getVaultSupply(wallet, manager)).toString()); + for (let tokenID = 1; tokenID <= supply; tokenID++) { + const vaultAddress = await index.connect(wallet).getVaultAddress(tokenID); + const vault = await getContract(network.name, 'SmartVault', vaultAddress); + try { + if (await vault.connect(wallet).undercollateralised()) { + const RewardGateway = await getContract(network.name, 'RewardGateway'); + await RewardGateway.connect(wallet).liquidateVault(tokenID); + } + } catch (e) { + console.log('vault data error', tokenID); + } + } + }); + + // posts liquidation info to discord + schedule.scheduleJob('55 9 * * *', async _ => { + const { manager, wallet, provider } = await getVaultManager(); + const EUROs = await getContract(network.name, 'EUROs'); + liquidatorETHBalance = ethers.utils.formatEther(await provider.getBalance(wallet.address)); + liquidatorEUROsBalance = ethers.utils.formatEther(await EUROs.connect(wallet).balanceOf(wallet.address)); + + const supply = Number((await getVaultSupply(wallet, manager)).toString()); + let content = `Liquidator wallet balance: **${liquidatorETHBalance} ETH**, **${liquidatorEUROsBalance} EUROs**\n--------------\n`; + for (let tokenID = 1; tokenID <= supply; tokenID++) { + try { + const { minted, totalCollateralValue, vaultAddress } = (await manager.connect(wallet).vaultData(tokenID)).status; + if (minted.gt(0)) { + const collateralPercentage = totalCollateralValue.mul(100).div(minted); + const formattedDebt = ethers.utils.formatEther(minted); + if (collateralPercentage.lt(125)) content += `ID: **${tokenID}**, address: **${vaultAddress}**, debt: **${formattedDebt} EUROs**, collateral: **${collateralPercentage}%**\n`; + } + } catch (e) { + console.log(`vault data error ${tokenID}`); + } + } + + await postToDiscord(content); + }); +}; + +module.exports = { + scheduleLiquidation +}; \ No newline at end of file