UNPKG

@mstable/protocol

Version:
163 lines 9.26 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); /* eslint-disable no-await-in-loop */ /* eslint-disable no-restricted-syntax */ const units_1 = require("@ethersproject/units"); const constants_1 = require("@utils/constants"); const math_1 = require("@utils/math"); const ethers_multicall_1 = require("ethers-multicall"); const graphql_request_1 = require("graphql-request"); const config_1 = require("hardhat/config"); const generated_1 = require("types/generated"); const signerFactory_1 = require("./utils/signerFactory"); const deploy_utils_1 = require("./utils/deploy-utils"); const networkAddressFactory_1 = require("./utils/networkAddressFactory"); const tokens_1 = require("./utils/tokens"); const maxVMTA = math_1.simpleToExactAmount(600000, 18); const maxBoost = math_1.simpleToExactAmount(3, 18); const minBoost = math_1.simpleToExactAmount(1, 18); const floor = math_1.simpleToExactAmount(98, 16); const coeff = math_1.BN.from(9); const calcBoost = (raw, vMTA, priceCoefficient, decimals = 18) => { // min(m, max(d, (d * 0.95) + c * min(vMTA, f) / USD^b)) const scaledBalance = raw.mul(priceCoefficient).div(math_1.simpleToExactAmount(1, 18)); if (scaledBalance.lt(math_1.simpleToExactAmount(1, decimals))) return minBoost; let denom = parseFloat(units_1.formatUnits(scaledBalance)); denom **= 0.75; const scaledMTA = vMTA.div(12); const flooredMTA = scaledMTA.gt(maxVMTA) ? maxVMTA : scaledMTA; let rhs = floor.add(flooredMTA.mul(coeff).div(10).mul(constants_1.fullScale).div(math_1.simpleToExactAmount(denom))); rhs = rhs.gt(minBoost) ? rhs : minBoost; return rhs.gt(maxBoost) ? maxBoost : rhs; }; const getAccountBalanceMap = async (accounts, tokenAddress, signer) => { const abi = ["function balanceOf(address owner) view returns (uint256)", "function decimals() view returns (uint8)"]; const token = new ethers_multicall_1.Contract(tokenAddress, abi); const ethcallProvider = new ethers_multicall_1.Provider(signer.provider); await ethcallProvider.init(); const callPromises = accounts.map((account) => token.balanceOf(account)); console.log(`About to get balances for ${accounts.length} accounts`); const balances = (await ethcallProvider.all(callPromises)); const accountBalances = {}; balances.forEach((balance, i) => { accountBalances[accounts[i]] = balance; }); return accountBalances; }; config_1.task("over-boost", "Pokes accounts that are over boosted") .addFlag("update", "Will send a poke transactions to the Poker contract") .addOptionalParam("account", "Address of account to check or poke", undefined, config_1.types.string) .addOptionalParam("minMtaDiff", "Min amount of vMTA over boosted. 300 = 0.3 boost", 300, config_1.types.float) .addOptionalParam("speed", "Defender Relayer speed param: 'safeLow' | 'average' | 'fast' | 'fastest'", "fast", config_1.types.string) .setAction(async (taskArgs, hre) => { const chain = networkAddressFactory_1.getChain(hre); const signer = await signerFactory_1.getSigner(hre, taskArgs.speed); const stkMTA = await networkAddressFactory_1.getChainAddress("StakedTokenMTA", chain); const stkMBPT = await networkAddressFactory_1.getChainAddress("StakedTokenBPT", chain); let idFilter = ""; if (taskArgs.account) { const vaults = [tokens_1.mUSD, tokens_1.mBTC, tokens_1.GUSD, tokens_1.BUSD, tokens_1.HBTC, tokens_1.TBTC, tokens_1.TBTCv2, tokens_1.alUSD, tokens_1.RAI, tokens_1.FEI]; const vaultAddresses = vaults.map((v) => v.vault.toLowerCase()); const vaultAccountIds = vaultAddresses.map((vaultAddress) => `"${vaultAddress}.${taskArgs.account.toLowerCase()}" `); idFilter = `id_in: [${vaultAccountIds}] `; } const gqlClient = new graphql_request_1.GraphQLClient("https://api.studio.thegraph.com/query/948/mstable-feeder-pools-and-vaults/v0.0.8"); const query = graphql_request_1.gql ` { boostedSavingsVaults { id boostCoeff priceCoeff stakingToken { symbol } accounts(where: { ${idFilter} rawBalance_gt: "0" }) { id rawBalance boostedBalance rewardPerTokenPaid rewards lastClaim lastAction } } _meta { block { number } } } `; const gqlData = await gqlClient.request(query); // eslint-disable-next-line no-underscore-dangle const blockNumber = gqlData._meta.block.number; console.log(`Results for block number ${blockNumber}`); // Maps GQL to a list if accounts (addresses) in each vault const vaultAccounts = gqlData.boostedSavingsVaults.map((vault) => vault.accounts.map((account) => account.id.split(".")[1])); const accountsWithDuplicates = vaultAccounts.flat(); const accountsUnique = [...new Set(accountsWithDuplicates)]; const mtaBalances = await getAccountBalanceMap(accountsUnique, stkMTA, signer); const bptBalances = await getAccountBalanceMap(accountsUnique, stkMBPT, signer); const accountBalances = {}; Object.keys(mtaBalances).forEach((account) => { accountBalances[account] = mtaBalances[account].add(bptBalances[account]); }); const pokeVaultAccounts = []; // For each Boosted Vault const vaults = gqlData.boostedSavingsVaults.filter((vault) => vault.id !== tokens_1.mUSD.vault.toLocaleLowerCase()); for (const vault of vaults) { const boostVault = generated_1.BoostedVault__factory.connect(vault.id, signer); const priceCoeff = await boostVault.priceCoeff(); // eslint-disable-next-line @typescript-eslint/no-explicit-any const overBoosted = []; console.log(`\nVault with id ${vault.id} for token ${vault.stakingToken.symbol}, ${vault.accounts.length} accounts, price coeff ${priceCoeff}`); console.log("Account, Raw Balance, Boosted Balance, Boost Balance USD, vMTA balance, Boost Actual, Boost Expected, Boost Diff"); // For each account in the boosted savings vault vault.accounts.forEach((account) => { const boostActual = math_1.BN.from(account.boostedBalance).mul(1000).div(account.rawBalance).toNumber(); const accountId = account.id.split(".")[1]; const boostExpected = calcBoost(math_1.BN.from(account.rawBalance), accountBalances[accountId], priceCoeff) .div(math_1.simpleToExactAmount(1, 15)) .toNumber(); const boostDiff = boostActual - boostExpected; // Calculate how much the boost balance is in USD = balance balance * price coefficient / 1e18 const boostBalanceUsd = math_1.BN.from(account.boostedBalance).mul(priceCoeff).div(math_1.simpleToExactAmount(1)); // Identify accounts with more than 20% over their boost and boost balance > 50,000 USD if (boostDiff > taskArgs.minMtaDiff && boostBalanceUsd.gt(math_1.simpleToExactAmount(50000))) { overBoosted.push({ ...account, boostActual, boostExpected, boostDiff, boostBalanceUsd, }); } console.log(`${accountId}, ${units_1.formatUnits(account.rawBalance)}, ${units_1.formatUnits(account.boostedBalance)}, ${units_1.formatUnits(boostBalanceUsd)}, ${units_1.formatUnits(accountBalances[accountId])}, ${units_1.formatUnits(boostActual, 3)}, ${units_1.formatUnits(boostExpected, 3)}, ${units_1.formatUnits(boostDiff, 3)}`); }); console.log(`${overBoosted.length} of ${vault.accounts.length} over boosted for ${vault.stakingToken.symbol} vault ${vault.id}`); console.log("Account, Over Boosted by, Boost USD balance"); overBoosted.forEach((account) => { const accountId = account.id.split(".")[1]; console.log(`${accountId} ${units_1.formatUnits(account.boostDiff, 3)}, ${units_1.formatUnits(account.boostBalanceUsd)}`); }); const pokeAccounts = overBoosted.map((account) => account.id.split(".")[1]); pokeVaultAccounts.push({ boostVault: vault.id, accounts: pokeAccounts, }); } if (taskArgs.update) { const pokerAddress = networkAddressFactory_1.getChainAddress("Poker", chain); console.log(`About to poke ${pokeVaultAccounts.length} vaults`); const poker = generated_1.Poker__factory.connect(pokerAddress, signer); const tx = await poker.poke(pokeVaultAccounts); await deploy_utils_1.logTxDetails(tx, "poke Poker"); } }); config_1.task("deployPoker", "Deploys the Poker contract").setAction(async (_, hre) => { const deployer = await signerFactory_1.getSigner(hre); await deploy_utils_1.deployContract(new generated_1.Poker__factory(deployer), "Poker"); }); module.exports = {}; //# sourceMappingURL=poker.js.map