From 778d2f5c40aad084c4c1f42f36d50d7100eed3d7 Mon Sep 17 00:00:00 2001 From: Ewan Sheldon Date: Fri, 13 Dec 2024 12:23:07 +0100 Subject: [PATCH] remove defunct jobs --- index.js | 4 - src/liquidation.js | 25 --- src/stats.js | 62 ------ src/transactions.js | 448 -------------------------------------------- src/yield.js | 55 ------ 5 files changed, 594 deletions(-) delete mode 100644 src/stats.js delete mode 100644 src/transactions.js delete mode 100644 src/yield.js diff --git a/index.js b/index.js index 05a2ac1..e57c13a 100644 --- a/index.js +++ b/index.js @@ -1,15 +1,11 @@ const http = require('http'); 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) => { diff --git a/src/liquidation.js b/src/liquidation.js index ffe5e99..fb7c1ef 100644 --- a/src/liquidation.js +++ b/src/liquidation.js @@ -59,31 +59,6 @@ const getVaultManager = async _ => { const scheduleLiquidation = async _ => { const network = getNetwork('arbitrum'); - // checks for undercollateralised vaults and liquidates - schedule.scheduleJob('6,36 * * * *', async _ => { - console.log('running liquidations') - const start = Math.floor(new Date / 1000); - 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()) { - console.log(`liquidating ${tokenID}`) - const RewardGateway = await getContract(network.name, 'RewardGateway'); - await RewardGateway.connect(wallet).liquidateVault(tokenID); - } - } catch (e) { - console.log('vault liquidation error', tokenID); - } - } - const end = Math.floor(new Date / 1000); - - console.log(`liquidations complete ${end - start}s`) - }); - // posts liquidation info to discord schedule.scheduleJob('55 7 * * *', async _ => { console.log('logging liquidation info'); diff --git a/src/stats.js b/src/stats.js deleted file mode 100644 index c5ca047..0000000 --- a/src/stats.js +++ /dev/null @@ -1,62 +0,0 @@ -const schedule = require('node-schedule'); -const ethers = require('ethers'); -const { createClient } = require('redis'); -const { getContract, getERC20 } = require('./contractFactory'); -const { getNetwork } = require('./networks'); - -const redisHost = process.env.REDIS_HOST || '127.0.0.1'; -const redisPort = process.env.REDIS_PORT || '6379'; - -const redis = createClient({ - url: `redis://${redisHost}:${redisPort}` -}); -redis.on('error', err => console.log('Redis Client Error', err)); - -const saveTvlDateToRedis = async data => { - await redis.connect(); - let multi = redis.MULTI() - .DEL('assets') - .SADD('assets', data.map(asset => asset.address)); - data.forEach(asset => { - multi = multi.SETEX(`tvl:${asset.address}`, 3600, asset.assetTvl); - }); - await multi.EXEC(); - await redis.disconnect(); -}; - -const indexStats = async _ => { - const network = getNetwork('arbitrum'); - const provider = new ethers.getDefaultProvider(network.rpc); - const wallet = new ethers.Wallet(process.env.WALLET_PRIVATE_KEY, provider); - const tokens = await (await getContract(network.name, 'TokenManager')).connect(wallet).getAcceptedTokens(); - const vaultAddresses = []; - - const tvl = []; - for (let i = 0; i < tokens.length; i++) { - const { addr } = tokens[i]; - let assetTvl = 0n; - for (let j = 0; j < vaultAddresses.length; j++) { - const vaultAddress = vaultAddresses[j]; - if (addr === ethers.constants.AddressZero) { - assetTvl += await provider.getBalance(vaultAddress); - } else { - assetTvl += await (await getERC20(addr)).connect(wallet).balanceOf(vaultAddress); - } - } - tvl.push({ address: addr, assetTvl: assetTvl.toString() }); - } - - await saveTvlDateToRedis(tvl); -}; - -const scheduleStatIndexing = async _ => { - schedule.scheduleJob('15 57 6 * * *', async _ => { - console.log(`indexing stats...`); - await indexStats(); - console.log(`indexed stats.`); - }); -}; - -module.exports = { - scheduleStatIndexing -}; \ No newline at end of file diff --git a/src/transactions.js b/src/transactions.js deleted file mode 100644 index 46c04e0..0000000 --- a/src/transactions.js +++ /dev/null @@ -1,448 +0,0 @@ -const fs = require('fs'); -const https = require('https'); -const schedule = require('node-schedule'); -const { ethers } = require("ethers"); -const { createClient } = require('redis'); -const { getArchiveNode, getNetwork } = require("./networks"); -const { getWallet } = require('./wallet'); -const { getContract } = require('./contractFactory'); - -const contracts = JSON.parse(fs.readFileSync('contracts.json', { encoding: 'utf8' })); -const redisHost = process.env.REDIS_HOST || '127.0.0.1'; -const redisPort = process.env.REDIS_PORT || '6379'; - -const redis = createClient({ - url: `redis://${redisHost}:${redisPort}` -}); -redis.on('error', err => console.log('Redis Client Error', err)); - -const LATEST_INDEXED_BLOCK_KEY = 'tx:latestBlock'; -const SMART_VAULT_LAUNCH_BLOCK = 117059962; -const VAULT_ADDRESSES_KEY = 'vaultAddresses'; -let startBlock; -let endBlock; -let tokenDecs = {}; - -const withBlockLimits = filter => { - return { - ...filter, - fromBlock: startBlock, - toBlock: endBlock - }; -}; - -const getStatusAt = async (tx, wallet) => { - try { - const vault = new ethers.Contract(tx.vaultAddress, contracts.SmartVault, wallet); - return await vault.status({ blockTag: tx.blockNumber }); - } catch (e) { - return await getStatusAt(tx, wallet); - } -} - -const addVaultStatus = async (transactions) => { - const transactionsWithStatus = []; - const { wallet } = getWallet(getArchiveNode('arbitrum')); - const statuses = await Promise.all(transactions.map(tx => getStatusAt(tx, wallet))) - for (let i = 0; i < transactions.length; i++) { - transactionsWithStatus.push({ - ... transactions[i], - minted: statuses[i].minted.toString(), - totalCollateralValue: statuses[i].totalCollateralValue.toString() - }) - } - return transactionsWithStatus; -}; - -const getDepositsForERC20 = async (vaults, token, wallet) => { - try { - const tokenContract = new ethers.Contract(token.addr, contracts.ERC20, wallet); - const filter = withBlockLimits(tokenContract.filters.Transfer(null, vaults)); - const events = await tokenContract.queryFilter(filter, startBlock, endBlock); - return events.map(event => { - return { - ... event, - tokenSymbol: ethers.utils.parseBytes32String(token.symbol), - tokenDec: token.dec - } - }); - } catch (e) { - return await getDepositsForERC20(vaults, token, wallet); - } -}; - -const getERC20DepositsForVaults = async (vaults, tokens, wallet, provider) => { - if (vaults.length === 0) return []; - const depositEvents = (await Promise.all(tokens.map(token => getDepositsForERC20(vaults, token, wallet)))).flat(); - const timestamps = (await Promise.all(depositEvents.map(e => provider.getBlock(e.blockNumber)))).map(block => block.timestamp); - return depositEvents.map((event, i) => { - return { - type: 'deposit', - txHash: event.transactionHash.toLowerCase(), - blockNumber: event.blockNumber, - asset: event.tokenSymbol, - vaultAddress: event.args.to.toLowerCase(), - amount: event.args.value.toString(), - assetDec: event.tokenDec, - timestamp: timestamps[i] - } - }); -}; - -const allTransactionsFor = async vault => { - const url = `https://api.arbiscan.io/api?module=account&action=txlist&address=${vault}&startblock=${startBlock}&endBlock=${endBlock}&sort=asc&apikey=${process.env.ARBISCAN_KEY}`; - return new Promise((resolve, reject) => { - https.get(url, res => { - let json = ''; - - res.on('data', data => { - json += data; - }); - - res.on('end', _ => { - try { - resolve(JSON.parse(json)); - } catch(e) { - reject(e); - } - }); - }); - }); -}; - -const vaultEthDeposits = async (vault) => { - try { - const allTransactions = await allTransactionsFor(vault); - const ethDeposits = allTransactions.result.filter(tx => tx.value !== '0'); - return ethDeposits.map(tx => { - const deposit = tx.to.toLowerCase() === vault.toLowerCase(); - return { - type: deposit ? 'deposit' : 'withdrawal', - txHash: tx.hash.toLowerCase(), - blockNumber: parseInt(tx.blockNumber), - asset: 'ETH', - vaultAddress: vault.toLowerCase(), - amount: tx.value, - assetDec: 18, - timestamp: tx.timeStamp - }; - }); - } catch (e) { - return await vaultEthDeposits(vault) - } -}; - -const requestRateLimit = async ms => { - return new Promise(resolve => { - setTimeout(() => resolve(), ms); - }); -}; - -const getAllEthDeposits = async (vaults) => { - let deposits = []; - - for (let i = 0; i < vaults.length; i++) { - await requestRateLimit(500); - deposits = [...deposits, ... await vaultEthDeposits(vaults[i])]; - } - - return deposits; -}; - -const getVaultWithdrawals = async (vault, wallet) => { - try { - const vaultContract = new ethers.Contract(vault, contracts.SmartVault, wallet); - const filter = withBlockLimits(vaultContract.filters.CollateralRemoved()); - return (await vaultContract.queryFilter(filter, startBlock, endBlock)).map(event => { - return { - ... event, - vaultAddress: vault - } - }); - } catch (e) { - return await getVaultWithdrawals(vault, wallet); - } -}; - -const getWithdrawals = async (vaults, wallet, provider) => { - const withdrawalEvents = (await Promise.all(vaults.map(vault => getVaultWithdrawals(vault, wallet)))).flat(); - const timestamps = (await Promise.all(withdrawalEvents.map(e => provider.getBlock(e.blockNumber)))).map(block => block.timestamp); - return withdrawalEvents.map((event, i) => { - return { - type: 'withdrawal', - txHash: event.transactionHash.toLowerCase(), - blockNumber: event.blockNumber, - asset: ethers.utils.parseBytes32String(event.args.symbol), - vaultAddress: event.vaultAddress.toLowerCase(), - amount: event.args.amount.toString(), - assetDec: tokenDecs[event.args.symbol], - timestamp: timestamps[i] - } - }); -}; - -const getVaultBorrows = async (vault, wallet) => { - try { - const vaultContract = new ethers.Contract(vault, contracts.SmartVault, wallet); - const filter = withBlockLimits(vaultContract.filters.EUROsMinted()); - return (await vaultContract.queryFilter(filter, startBlock, endBlock)).map(event => { - return { - ... event, - vaultAddress: vault - } - }); - } catch (e) { - return await getVaultBorrows(vault, wallet); - } -}; - -const getBorrows = async (vaults, wallet, provider) => { - const borrowEvents = (await Promise.all(vaults.map(vault => getVaultBorrows(vault, wallet)))).flat(); - const timestamps = (await Promise.all(borrowEvents.map(e => provider.getBlock(e.blockNumber)))).map(block => block.timestamp); - return borrowEvents.map((event, i) => { - return { - type: 'borrow', - txHash: event.transactionHash.toLowerCase(), - blockNumber: event.blockNumber, - asset: 'EUROs', - vaultAddress: event.vaultAddress.toLowerCase(), - amount: event.args.amount.toString(), - assetDec: 18, - timestamp: timestamps[i] - } - }); -}; - -const getVaultRepays = async (vault, wallet) => { - try { - const vaultContract = new ethers.Contract(vault, contracts.SmartVault, wallet); - const filter = withBlockLimits(vaultContract.filters.EUROsBurned()); - return (await vaultContract.queryFilter(filter, startBlock, endBlock)).map(event => { - return { - ... event, - vaultAddress: vault.toLowerCase() - } - }); - } catch (e) { - return await getVaultRepays(vault, wallet); - } -}; - -const getRepays = async (vaults, wallet, provider) => { - const repayEvents = (await Promise.all(vaults.map(vault => getVaultRepays(vault, wallet)))).flat(); - const timestamps = (await Promise.all(repayEvents.map(e => provider.getBlock(e.blockNumber)))).map(block => block.timestamp); - return repayEvents.map((event, i) => { - return { - type: 'repay', - txHash: event.transactionHash.toLowerCase(), - blockNumber: event.blockNumber, - asset: 'EUROs', - vaultAddress: event.vaultAddress.toLowerCase(), - amount: event.args.amount.toString(), - assetDec: 18, - timestamp: timestamps[i] - } - }); -}; - -const getLiquidations = async (smartVaultManagerContract, provider) => { - try { - const liquidations = []; - const filter = withBlockLimits(smartVaultManagerContract.filters.VaultLiquidated()); - const liquidationEvents = await smartVaultManagerContract.queryFilter(filter, startBlock, endBlock); - for (let i = 0; i < liquidationEvents.length; i++) { - const event = liquidationEvents[i]; - liquidations.push({ - type: 'liquidation', - txHash: event.transactionHash.toLowerCase(), - blockNumber: event.blockNumber, - asset: 'n/a', - vaultAddress: event.args.vaultAddress.toLowerCase(), - amount: '0', - assetDec: 0, - timestamp: (await provider.getBlock(event.blockNumber)).timestamp - }); - - const previousBlock = event.blockNumber - 1; - liquidations.push({ - type: 'pre-liquidation', - txHash: '0x', - blockNumber: event.blockNumber - 1, - asset: 'n/a', - vaultAddress: event.args.vaultAddress.toLowerCase(), - amount: '0', - assetDec: 0, - timestamp: (await provider.getBlock(previousBlock)).timestamp - }); - } - return liquidations; - } catch (e) { - return await getLiquidations(smartVaultManagerContract, provider); - } -}; - -const getCreations = async (smartVaultManagerContract, provider) => { - try { - const filter = withBlockLimits(smartVaultManagerContract.filters.VaultDeployed()); - const creationEvents = await smartVaultManagerContract.queryFilter(filter, startBlock, endBlock); - const timestamps = (await Promise.all(creationEvents.map(e => provider.getBlock(e.blockNumber)))).map(block => block.timestamp); - return creationEvents.map((event, i) => { - return { - type: 'creation', - txHash: event.transactionHash.toLowerCase(), - blockNumber: event.blockNumber, - asset: 'n/a', - vaultAddress: event.args.vaultAddress.toLowerCase(), - amount: '0', - assetDec: 0, - timestamp: timestamps[i] - } - }); - } catch (e) { - return await getCreations(smartVaultManagerContract, provider); - } -}; - -const getVaultAddressForTokenId = async (tokenId, wallet, network) => { - const smartVaultIndex = (await getContract(network.name, 'SmartVaultIndex')).connect(wallet); - return (await smartVaultIndex.getVaultAddress(tokenId)).toLowerCase(); -}; - -const getTransfers = async (smartVaultManagerContract, provider, wallet, network) => { - try { - const filter = withBlockLimits(smartVaultManagerContract.filters.Transfer()); - const transferEvents = (await smartVaultManagerContract.queryFilter(filter, startBlock, endBlock)) - .filter(event => event.args.from !== ethers.constants.AddressZero); - const vaultAddresses = await Promise.all(transferEvents.map(e => getVaultAddressForTokenId(e.args.tokenId.toString(), wallet, network))) - const timestamps = (await Promise.all(transferEvents.map(e => provider.getBlock(e.blockNumber)))).map(block => block.timestamp); - return transferEvents.map((event, i) => { - return { - type: 'transfer', - txHash: event.transactionHash.toLowerCase(), - blockNumber: event.blockNumber, - asset: 'n/a', - vaultAddress: vaultAddresses[i], - amount: '0', - assetDec: 0, - timestamp: timestamps[i] - } - }); - } catch (e) { - return await getTransfers(smartVaultManagerContract, provider, wallet, network); - } -}; - -const getTs = _ => { - return Math.floor(new Date() / 1000); -}; - -const setStartBlock = async _ => { - await redis.connect(); - const lastIndexed = await redis.get(LATEST_INDEXED_BLOCK_KEY); - await redis.disconnect(); - startBlock = lastIndexed ? parseInt(lastIndexed) + 1 : SMART_VAULT_LAUNCH_BLOCK; -}; - -const setEndBlock = async provider => { - const blockDiffLimit = 10_000; - const latest = await provider.getBlockNumber(); - if (latest - startBlock > blockDiffLimit) { - endBlock = startBlock + blockDiffLimit; - } else { - endBlock = latest; - } -}; - -const getAcceptedERC20s = async (network, wallet) => { - try { - return (await (await getContract(network.name, 'TokenManager')).connect(wallet).getAcceptedTokens()) - .filter(token => token.addr !== ethers.constants.AddressZero); - } catch (e) { - return await getAcceptedERC20s(network, wallet); - } -}; - -const setTokenDecs = tokens => { - tokenDecs[ethers.utils.formatBytes32String('ETH')] = 18; - tokens.forEach(token => { - tokenDecs[token.symbol] = token.dec; - }); -}; - -const getIndexedVaults = async _ => { - await redis.connect(); - const vaults = await redis.SMEMBERS(VAULT_ADDRESSES_KEY); - await redis.disconnect(); - return vaults; -}; - -const saveToRedis = async (transactions, vaults) => { - // SCHEMA: - // key: 'vaultTxs:0x...' - // score: timestamp - // value: 'type:hash:blockNo:asset:amount:assetDec:minted:collateralValue' - // e.g. 'deposit:0x8ae26a528861d3e6c08d4331885eaba2d1b5b55fc540234fc9b8c9c198a0d429:124132949:PAXG:8000000000000000:18:136175000000000000000:181087962079756018667' - const schema = ['type', 'txHash', 'blockNumber', 'asset', 'amount', 'assetDec', 'minted', 'totalCollateralValue'] - - let redisTx = await redis.MULTI(); - for (let i = 0; i < transactions.length; i++) { - const transaction = transactions[i]; - const key = `vaultTxs:${transaction.vaultAddress}`; - const score = transaction.timestamp; - const value = schema.map(key => transaction[key]).join(':'); - redisTx = redisTx.ZADD(key, [{ score, value }]); - } - - if (vaults.length > 0) { - redisTx = redisTx - .SADD(VAULT_ADDRESSES_KEY, vaults.map(addr => addr.toLowerCase())); - } - - await redis.connect(); - await redisTx - .SET(LATEST_INDEXED_BLOCK_KEY, endBlock) - .EXEC(); - await redis.disconnect(); -}; - -const indexVaultTransactions = async _ => { - const startTs = getTs(); - const network = getNetwork('arbitrum'); - const { wallet, provider } = getWallet(network); - await setStartBlock(); - await setEndBlock(provider); - console.log(`indexing transactions from block ${startBlock} to ${endBlock} ...`); - const smartVaultManagerContract = (await getContract(network.name, 'SmartVaultManager')).connect(wallet); - const erc20Tokens = await getAcceptedERC20s(network, wallet); - setTokenDecs(erc20Tokens); - const vaultCreations = (await getCreations(smartVaultManagerContract, provider)); - const unindexedVaults = vaultCreations.map(event => event.vaultAddress); - const vaults = [... await getIndexedVaults(), ...unindexedVaults]; - const transactions = await addVaultStatus(([... await Promise.all([ - getERC20DepositsForVaults(vaults, erc20Tokens, wallet, provider), - getAllEthDeposits(vaults), - getWithdrawals(vaults, wallet, provider), - getBorrows(vaults, wallet, provider), - getRepays(vaults, wallet, provider), - getLiquidations(smartVaultManagerContract, provider), - getTransfers(smartVaultManagerContract, provider, wallet, network) - ]), ...vaultCreations]).flat()); - await saveToRedis(transactions, unindexedVaults); - const endTs = getTs(); - console.log(`indexed transactions (${endTs - startTs}s)`); -}; - -const scheduleVaultTransactionIndexing = async _ => { - let running = false; - schedule.scheduleJob('*/10 * * * *', async _ => { - if (!running) { - running = true; - await indexVaultTransactions(); - running = false; - } - }); -}; - -module.exports = { - scheduleVaultTransactionIndexing -}; \ No newline at end of file diff --git a/src/yield.js b/src/yield.js deleted file mode 100644 index 994d7f0..0000000 --- a/src/yield.js +++ /dev/null @@ -1,55 +0,0 @@ -const https = require('https'); -const schedule = require('node-schedule'); -const { createClient } = require('redis'); - -const redisHost = process.env.REDIS_HOST || '127.0.0.1'; -const redisPort = process.env.REDIS_PORT || '6379'; -const CAMELOT_LIQUIDITY_API_URL = 'https://api.camelot.exchange/v2/liquidity-v3-data'; -const POOL_ADDRESS = '0xc9AA2fEB84F0134a38d5D1c56b1E787191327Cb0'; -const GRAIL_ADDRESS = '0x3d9907f9a368ad0a51be60f7da3b97cf940982d8'; -const TST_ADDRESS = '0xf5A27E55C748bCDdBfeA5477CB9Ae924f0f7fd2e'; - -const redis = createClient({ - url: `redis://${redisHost}:${redisPort}` -}); -redis.on('error', err => console.log('Redis Client Error', err)); - -const saveToRedis = async data => { - const ts = Math.floor(new Date() / 1000); - await redis.connect(); - await redis.set(`yield:${GRAIL_ADDRESS}`, `${ts}:${data.tvl}:${data.apy}`); - await redis.set(`yield:${TST_ADDRESS}`, `${ts}:${data.tvl}:5`); - await redis.disconnect(); -} - -const getCamelotData = async _ => { - return new Promise(resolve => { - https.get(CAMELOT_LIQUIDITY_API_URL, res => { - let json = ''; - - res.on('data', data => { - json += data; - }); - - res.on('end', _ => { - const poolData = JSON.parse(json).data.pools[POOL_ADDRESS]; - resolve({ - tvl: poolData.activeTvlUSD, - apy: poolData.activeTvlAverageAPR - }) - }); - }); - }) -} - -const scheduleIndexYieldData = _ => { - schedule.scheduleJob(`*/15 * * * *`, async _ => { - console.log('indexing yield data ...') - await saveToRedis(await getCamelotData()); - console.log('indexed yield data') - }); -} - -module.exports = { - scheduleIndexYieldData -} \ No newline at end of file