From 02a0f1ccdcac6444d1963c4a1207764fa0d7a06f Mon Sep 17 00:00:00 2001 From: valia fetisov Date: Tue, 22 Oct 2024 18:12:07 +0200 Subject: [PATCH] Support LSE collateral auctions (#622) --- .vscode/settings.json | 70 + bot/src/keepers/collateral.ts | 4 +- bot/src/notify.ts | 2 +- core/README.md | 19 +- core/package.json | 4 +- .../configs/onboardNewCollateral.ts | 4 +- core/simulations/configs/specificBlockFork.ts | 2 +- core/simulations/configs/vaultLiquidation.ts | 94 +- .../helpers/createVaultWithCollateral.ts | 71 +- .../helpers/promptToGetBlockNumber.ts | 29 - core/simulations/helpers/promptToGetNumber.ts | 36 + core/src/abis/LOCKSTAKE_CLIP.json | 761 +++++++++ core/src/abis/LOCKSTAKE_ENGINE.json | 1516 +++++++++++++++++ core/src/addresses.ts | 6 +- .../CurveLpTokenUniv3Callee.ts | 6 +- core/src/calleeFunctions/OneInchCallee.ts | 6 +- .../src/calleeFunctions/UniswapV2CalleeDai.ts | 6 +- .../UniswapV2LockstakeCallee.ts | 51 + .../UniswapV2LpTokenCalleeDai.ts | 6 +- core/src/calleeFunctions/UniswapV3Callee.ts | 6 +- .../calleeFunctions/WstETHCurveUniv3Callee.ts | 6 +- core/src/calleeFunctions/helpers/pools.ts | 4 +- core/src/calleeFunctions/helpers/uniswapV2.ts | 10 +- core/src/calleeFunctions/index.ts | 2 + .../calleeFunctions/rETHCurveUniv3Callee.ts | 6 +- core/src/constants/CALLEES.ts | 1 + core/src/constants/COLLATERALS.ts | 544 ++++-- core/src/contracts.ts | 29 +- core/src/fetch.ts | 17 +- core/src/getCollateralPriceOracleConfig.ts | 160 -- core/src/oracles.ts | 2 +- core/src/params.ts | 5 +- core/src/tokens.ts | 6 + core/src/types.ts | 16 +- core/src/vaults.ts | 61 +- core/src/wallet.ts | 14 +- core/test/vault-test.ts | 216 ++- .../auction/collateral/CollateralAuction.vue | 23 +- .../collateral/CollateralAuctionBidBlock.vue | 2 +- .../CollateralAuctionBidTransactionTable.vue | 8 +- .../CollateralAuctionSwapTransactionTable.vue | 19 +- .../collateral/CollateralAuctionsTable.vue | 6 +- .../MarketPriceSelection.stories.js | 2 +- .../collateral/MarketPriceSelection.vue | 18 +- .../components/dashboard/CollateralTable.vue | 6 +- .../modals/ManageCollateralTable.vue | 2 +- .../panels/WithdrawCollateralPanel.vue | 2 +- frontend/helpers/generateFakeAuction.ts | 1 + frontend/helpers/generateFakeCollateral.ts | 1 + frontend/store/collaterals.ts | 4 + 50 files changed, 3301 insertions(+), 591 deletions(-) create mode 100644 .vscode/settings.json delete mode 100644 core/simulations/helpers/promptToGetBlockNumber.ts create mode 100644 core/simulations/helpers/promptToGetNumber.ts create mode 100644 core/src/abis/LOCKSTAKE_CLIP.json create mode 100644 core/src/abis/LOCKSTAKE_ENGINE.json create mode 100644 core/src/calleeFunctions/UniswapV2LockstakeCallee.ts delete mode 100644 core/src/getCollateralPriceOracleConfig.ts diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..7a1093c90 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,70 @@ +{ + // Disable the default formatter, use eslint instead + "prettier.enable": false, + "editor.formatOnSave": false, + // Auto fix + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit", + "source.organizeImports": "never" + }, + // Silent the stylistic rules in you IDE, but still auto fix them + "eslint.rules.customizations": [ + { + "rule": "style/*", + "severity": "off" + }, + { + "rule": "format/*", + "severity": "off" + }, + { + "rule": "*-indent", + "severity": "off" + }, + { + "rule": "*-spacing", + "severity": "off" + }, + { + "rule": "*-spaces", + "severity": "off" + }, + { + "rule": "*-order", + "severity": "off" + }, + { + "rule": "*-dangle", + "severity": "off" + }, + { + "rule": "*-newline", + "severity": "off" + }, + { + "rule": "*quotes", + "severity": "off" + }, + { + "rule": "*semi", + "severity": "off" + } + ], + // Enable eslint for all supported languages + "eslint.validate": [ + "javascript", + "javascriptreact", + "typescript", + "typescriptreact", + "vue", + "html", + "markdown", + "json", + "jsonc", + "yaml", + "toml", + "gql", + "graphql" + ], + "typescript.tsdk": "node_modules/typescript/lib", +} diff --git a/bot/src/keepers/collateral.ts b/bot/src/keepers/collateral.ts index 4c32dd1aa..e90bd6162 100644 --- a/bot/src/keepers/collateral.ts +++ b/bot/src/keepers/collateral.ts @@ -131,8 +131,8 @@ const checkAndParticipateIfPossible = async function (network: string, auction: // display profit const postVatBalanceDai = await fetchVATbalanceDAI(network, walletAddress); const postErcBalanceDai = await fetchBalanceDAI(network, walletAddress); - console.info(`DAI VAT profit from the transaction: ${postVatBalanceDai.minus(preVatBalanceDai).toFixed()}`); - console.info(`DAI ERC profit from the transaction: ${postErcBalanceDai.minus(preErcBalanceDai).toFixed()}`); + console.info(`DAI VAT profit from the transaction: ${postVatBalanceDai.minus(preVatBalanceDai).toFixed()}`); + console.info(`DAI ERC20 profit from the transaction: ${postErcBalanceDai.minus(preErcBalanceDai).toFixed()}`); }; const participateInAuction = async function (network: string, auction: AuctionInitialInfo) { diff --git a/bot/src/notify.ts b/bot/src/notify.ts index 0bd8ef02a..868562067 100644 --- a/bot/src/notify.ts +++ b/bot/src/notify.ts @@ -6,7 +6,7 @@ const generateNotificationTextCollateral = function (auction: AuctionInitialInfo const url = `${process.env.FRONTEND_ORIGIN}/collateral/?network=${auction.network}&auction=${auction.id}`; const formattedString = formatToAutomaticDecimalPointsString(auction.collateralAmount); - return `Collateral auction with ${formattedString} ${auction.collateralSymbol} just started. Follow the link to participate: ${url}`; + return `Collateral auction with ${formattedString} ${auction.tokenName} just started. Follow the link to participate: ${url}`; }; const generateNotificationTextDebt = function (auction: DebtAuctionActive): string { diff --git a/core/README.md b/core/README.md index e4ececf58..4ed128751 100644 --- a/core/README.md +++ b/core/README.md @@ -32,19 +32,12 @@ The process of adding new collaterals depends on the token type used. This is du 5. Add new exchange file to the [`calleeFunctions` folder](./src/calleeFunctions) - The file should be named using the name from `1.` - The file should export `CalleeFunctions` - - The file should be imported in the [`calleeFunctions/index.ts`](./src/calleeFunctions/index.ts) -3. Adding price oracle configurations for the token: - 1. Get the source code of the price oracle contract: - - read value `ilks(collateralType)` from [`Spot` contract](https://etherscan.io/address/0x65c79fcb50ca1594b025960e539ed7a9a6d434a3#code) via "Read Contract" tabl - and receive the address of the oracle for the specified collateral. The linked conract is responsible for updating the unit prices for collaterals. - 2. Read the contract and determine the slot address of the variable: - - Generally a slot number can be determined by counting definition of variables in the contract source code, but there are exceptions, [please read the docs on the solidity version the contract was compiled with](https://docs.soliditylang.org/en/v0.8.13/internals/layout_in_storage.html) - - Experimenting with blockchain fork (e.g. hardhat) helps: try to fetch the value you're looking for / overwrite it / ... and validate that it's correct via some public method or comparing against your expectation. See section [Overwriting values of price oracles](./README.md#overwriting-values-of-price-oracles) - 3. Extend collateral config with the proper slot addresses. - 4. If needed, add the oracle type to `types` file if the existing types are not sufficient to cover for the set of values you need. -4. Run `npm run collateral:onboard` to run the script that helps to choose the oracle config. - - when the script outputs the json with the config, add it to the `oracle` key of the collateral configuration in `COLLATERALS.ts` - - if the script terminates with an error, please submit the report to the repository at https://github.com/sidestream-tech/unified-auctions-ui via an issue so that the support could be added. - - Read more about the collateral oracle configurations at `./README.md#collateral-oracle-configs` + 6. Import exchange file inside [`calleeFunctions/index.ts`](./src/calleeFunctions/index.ts) and export under `allCalleeFunctions` + +3. Adding price oracle configurations for the new collateral type: + + 1. Get the source code of the price oracle contract. Read value `ilks(collateralType)` from the [`MCD_SPOT` contract](https://etherscan.io/address/0x65c79fcb50ca1594b025960e539ed7a9a6d434a3#code) via "Read Contract" tab and get the address of the oracle for the specified collateral. The linked conract is responsible for updating the unit prices for collaterals + 2. If the contract resembles OSM ([Oracle Security Module](https://github.com/makerdao/osm)) `ORACLE_WITH_DELAY` needs to be used, otherwise `ORACLE_WITHOUT_DELAY` ### Onboarding not yet deployed collateral diff --git a/core/package.json b/core/package.json index 7efdcf251..b83da0926 100644 --- a/core/package.json +++ b/core/package.json @@ -12,9 +12,7 @@ "hardhat:silent": "npx hardhat node &> /dev/null", "hardhat": "npx hardhat node", "hardhat:simulations": "npx hardhat --network testnetwork --config local.hardhat.config.ts run ./simulations/index.ts", - "simulate": "npm-run-all --parallel hardhat:silent hardhat:simulations", - "collateral:validate": "npx hardhat --network testnetwork --config local.hardhat.config.ts run ./src/getCollateralPriceOracleConfig.ts", - "collateral:onboard": "npm-run-all --parallel hardhat:silent collateral:validate" + "simulate": "npm-run-all --parallel hardhat:silent hardhat:simulations" }, "engines": { "node": ">=16.0.0", diff --git a/core/simulations/configs/onboardNewCollateral.ts b/core/simulations/configs/onboardNewCollateral.ts index b7b6bb640..deb64981d 100644 --- a/core/simulations/configs/onboardNewCollateral.ts +++ b/core/simulations/configs/onboardNewCollateral.ts @@ -50,10 +50,10 @@ const simulation: Simulation = { console.info(`New ${collateralType} oracle price is ${oraclePrice.toFixed()} DAI`); // create and liquidate vault const collateralOwned = await calculateMinCollateralAmountToOpenVault(collateralType); - const vaultId = await createVaultWithCollateral(collateralType, collateralOwned); + const { vaultIndex } = await createVaultWithCollateral(collateralType, collateralOwned); await warpTime(60 * 24 * 30, 60); await collectStabilityFees(TEST_NETWORK, collateralType); - const vault = await fetchVault(TEST_NETWORK, vaultId); + const vault = await fetchVault(TEST_NETWORK, vaultIndex); await liquidateVault(TEST_NETWORK, vault.collateralType, vault.address); }, }, diff --git a/core/simulations/configs/specificBlockFork.ts b/core/simulations/configs/specificBlockFork.ts index c669ae9af..a93e41faa 100644 --- a/core/simulations/configs/specificBlockFork.ts +++ b/core/simulations/configs/specificBlockFork.ts @@ -1,6 +1,6 @@ import { addDaiToBalance, addMkrToBalance } from '../../helpers/hardhat/balance'; import { warpTime, resetNetworkAndSetupWallet } from '../../helpers/hardhat/network'; -import promptToGetBlockNumber from '../helpers/promptToGetBlockNumber'; +import { promptToGetBlockNumber } from '../helpers/promptToGetNumber'; import { Simulation } from '../types'; const simulation: Simulation = { diff --git a/core/simulations/configs/vaultLiquidation.ts b/core/simulations/configs/vaultLiquidation.ts index e93dd31ef..caa14d4f4 100644 --- a/core/simulations/configs/vaultLiquidation.ts +++ b/core/simulations/configs/vaultLiquidation.ts @@ -1,21 +1,24 @@ import { warpTime, resetNetworkAndSetupWallet } from '../../helpers/hardhat/network'; import { addDaiToBalance, addMkrToBalance } from '../../helpers/hardhat/balance'; import { Simulation } from '../types'; -import { collectStabilityFees, fetchVault, liquidateVault } from '../../src/vaults'; +import { collectStabilityFees, liquidateVault } from '../../src/vaults'; import { TEST_NETWORK } from '../../helpers/constants'; import createVaultWithCollateral, { adjustLimitsAndRates, calculateMinCollateralAmountToOpenVault, } from '../helpers/createVaultWithCollateral'; import promptToSelectOneOption from '../helpers/promptToSelectOneOption'; -import promptToGetBlockNumber from '../helpers/promptToGetBlockNumber'; +import { promptToGetNumber, promptToGetBlockNumber } from '../helpers/promptToGetNumber'; import getProvider from '../../src/provider'; - import fetchAuctionsByCollateralType, { fetchMaximumAuctionDurationInSeconds } from '../../src/fetch'; -import { getAllCollateralTypes } from '../../src/constants/COLLATERALS'; +import { getAllCollateralTypes, getCollateralConfigByType } from '../../src/constants/COLLATERALS'; import { setCollateralDebtCeilingToGlobal } from '../../helpers/hardhat/contractParametrization'; - -const TWO_YEARS_IN_MINUTES = 60 * 24 * 30 * 12 * 2; +import { getCurrentOraclePriceByCollateralType } from '../../src/oracles'; +import { overwriteCurrentOraclePrice } from '../../helpers/hardhat/overwrites'; +import BigNumber from 'bignumber.js'; +import { enrichAuction } from '../../src/auctions'; +import { overwriteUintValue } from '../../helpers/hardhat/slotOverwrite'; +import { RAY } from '../../src/constants/UNITS'; const simulation: Simulation = { title: 'Create collateral auction', @@ -39,35 +42,51 @@ const simulation: Simulation = { { title: 'Create underwater vault', entry: async context => { + // set oracle price + await overwriteCurrentOraclePrice(TEST_NETWORK, context.collateralType, new BigNumber(1000)); + const initialOraclePrice = await getCurrentOraclePriceByCollateralType( + TEST_NETWORK, + context.collateralType + ); + console.info(`Initial oracle price is ${initialOraclePrice.toFixed()} DAI`); + await adjustLimitsAndRates(context.collateralType); const collateralOwned = await calculateMinCollateralAmountToOpenVault(context.collateralType); console.info(`Minimum collateral amount to open vault: ${collateralOwned.toFixed()}`); await setCollateralDebtCeilingToGlobal(context.collateralType); - const latestVaultId = await createVaultWithCollateral( + const { vaultIndex, vaultAddress } = await createVaultWithCollateral( context.collateralType, collateralOwned.multipliedBy(1) ); - console.info(`Created Vault id: ${latestVaultId}`); - - console.info(`Skipping ${TWO_YEARS_IN_MINUTES} minutes...`); - await warpTime(TWO_YEARS_IN_MINUTES, 60); + console.info(`Created Vault with id ${vaultIndex} and address ${vaultAddress}`); - console.info(`Collecting stability fees...`); - const vaultBefore = await fetchVault(TEST_NETWORK, latestVaultId); - console.info(`Stability fee before ${vaultBefore.stabilityFeeRate}`); + // drop oracle price + console.info(`Initial oracle price is ${initialOraclePrice.toFixed()} DAI`); + await overwriteCurrentOraclePrice(TEST_NETWORK, context.collateralType, initialOraclePrice.times(0.5)); + const newOraclePrice = await getCurrentOraclePriceByCollateralType( + TEST_NETWORK, + context.collateralType + ); + console.info(`New oracle price is ${newOraclePrice.toFixed()} DAI`); await collectStabilityFees(TEST_NETWORK, context.collateralType); - const vaultAfter = await fetchVault(TEST_NETWORK, latestVaultId); - console.info(`Stability fee after ${vaultAfter.stabilityFeeRate}`); - return { ...context, latestVaultId }; + return { ...context, vaultIndex, vaultAddress, initialOraclePrice }; }, }, { title: 'Liquidate the vault', entry: async context => { - const liquidatedId = context.latestVaultId; - const vault = await fetchVault(TEST_NETWORK, liquidatedId); - await liquidateVault(TEST_NETWORK, vault.collateralType, vault.address); + const collateralConfig = getCollateralConfigByType(context.collateralType); + try { + // overwrite calc.tau (linear auction price reduction duration) + await overwriteUintValue(collateralConfig.contracts.calc, '0x1', new BigNumber(3000)); + } catch {} + try { + // overwrite clip.buf (initial auction price multiplier) + await overwriteUintValue(collateralConfig.contracts.clip, '0x5', RAY.dividedBy(50)); + } catch {} + // liquidate + await liquidateVault(TEST_NETWORK, context.collateralType, context.vaultAddress); return context; }, }, @@ -78,25 +97,36 @@ const simulation: Simulation = { TEST_NETWORK, context.collateralType ); - const INITIAL_WARP_PARTS = 1 / 13; - const warpSeconds = Math.floor(auctionLifetime * INITIAL_WARP_PARTS); - console.info(`Initial warp of ${INITIAL_WARP_PARTS} of an auction time: ${warpSeconds} seconds`); - await warpTime(warpSeconds, 1); + let proposedSecondsToWarp = Math.floor(auctionLifetime / 12); const provider = await getProvider(TEST_NETWORK); - const STEP_SECONDS = 30; while (true) { + proposedSecondsToWarp = await promptToGetNumber({ + title: 'Number of seconds to warp', + initial: proposedSecondsToWarp, + max: auctionLifetime, + }); + if (proposedSecondsToWarp === 0) { + try { + await provider.send('evm_mine', []); + console.info(`Mined one block without skipping any time`); + } catch (error) { + console.error('evm_mine failed with', error); + } + } else { + await warpTime(proposedSecondsToWarp, 1); + console.info(`Skipped ${proposedSecondsToWarp} seconds`); + } const initialAuctions = await fetchAuctionsByCollateralType(TEST_NETWORK, context.collateralType); - if (!initialAuctions[0] || !initialAuctions[0].isActive) { + if (!initialAuctions[0]) { console.info('No active auctions are found, exiting the "evm_mine" loop'); break; } - console.info(`Gradually skipping time, one block every ${STEP_SECONDS} seconds`); - try { - await provider.send('evm_mine', []); - await new Promise(resolve => setTimeout(resolve, STEP_SECONDS * 1000)); - } catch (error) { - console.error('evm_mine failed with', error); + const auction = await enrichAuction(TEST_NETWORK, initialAuctions[0]); + if (!auction?.isActive) { + console.info('No active auctions are found, exiting the "evm_mine" loop'); + break; } + console.info(`One active auction is still present: ${auction.id}`); } }, }, diff --git a/core/simulations/helpers/createVaultWithCollateral.ts b/core/simulations/helpers/createVaultWithCollateral.ts index 1217e5378..2dcfaa7a9 100644 --- a/core/simulations/helpers/createVaultWithCollateral.ts +++ b/core/simulations/helpers/createVaultWithCollateral.ts @@ -8,9 +8,10 @@ import { openVault, fetchVaultCollateralParameters, openVaultWithProxiedContractAndDrawDebt, + collectStabilityFees, } from '../../src/vaults'; import { HARDHAT_PUBLIC_KEY, TEST_NETWORK } from '../../helpers/constants'; -import { +import getContract, { getContractValue, getContractAddressByName, getErc20Contract, @@ -23,7 +24,7 @@ import { fetchCollateralInVat, withdrawCollateralFromVat, } from '../../src/wallet'; -import { MAX } from '../../src/constants/UNITS'; +import { MAX, WAD_NUMBER_OF_DIGITS } from '../../src/constants/UNITS'; import { CollateralConfig, CollateralType } from '../../src/types'; import { roundDownToFirstSignificantDecimal, roundUpToFirstSignificantDecimal } from '../../helpers/hex'; import { determineBalanceSlot, setCollateralInWallet } from '../../helpers/hardhat/erc20'; @@ -36,9 +37,14 @@ import { } from '../../helpers/hardhat/contractParametrization'; import { overwriteStabilityFeeAccumulationRate } from '../../helpers/hardhat/overwrites'; import detectProxyTarget from '../../helpers/detectProxyTarget'; +import getSigner from '../../src/signer'; const UNSUPPORTED_COLLATERAL_TYPES = [ 'GNO-A', + 'GUSD-A', + 'USDC-A', + 'USDC-B', + 'RENBTC-A', 'RETH-A', // [temporary] this collateral is not yet deployed, tested via different flow 'TUSD-A', // [proxy] this collateral has a proxy-token contract and fallback solution does not work since JOIN contract does not have sufficient funds ]; @@ -61,11 +67,12 @@ const setAndCheckCollateralInVat = async (collateralType: CollateralType, collat }; const checkAndWithdrawCollateralFromVat = async (collateralConfig: CollateralConfig, collateralOwned: BigNumber) => { + const joinName = getJoinNameByCollateralType(collateralConfig.ilk); + if (!joinName) { + throw new Error('checkAndWithdrawCollateralFromVat: there is no vat collateral for joinless collateral'); + } const tokenContractAddress = await getContractAddressByName(TEST_NETWORK, collateralConfig.symbol); - const addressJoin = await getContractAddressByName( - TEST_NETWORK, - getJoinNameByCollateralType(collateralConfig.ilk) - ); + const addressJoin = await getContractAddressByName(TEST_NETWORK, joinName); const joinBalance = await fetchERC20TokenBalance( TEST_NETWORK, tokenContractAddress, @@ -96,7 +103,7 @@ const ensureWalletBalance = async (collateralConfig: CollateralConfig, collatera `Unexpected wallet balance. Expected ${collateralOwned.toFixed()}, Actual ${balance.toFixed()}` ); } - console.info(`Wallet has ${collateralOwned.toFixed()} ${collateralConfig.symbol}`); + console.info(`Wallet has ${collateralOwned.toFixed()} ${collateralConfig.tokenName}`); }; const putCollateralIntoVaultAndWithdrawDai = async (vaultId: number, collateralOwned: BigNumber) => { @@ -164,11 +171,12 @@ export const checkAvailableDebtForAmountAndMinUnitPrice = async ( }; const giveJoinContractAllowance = async (collateralConfig: CollateralConfig, amount?: BigNumber) => { + const joinName = getJoinNameByCollateralType(collateralConfig.ilk); + if (!joinName) { + throw new Error('giveJoinContractAllowance: joinless contract allowance can not be given'); + } const tokenContractAddress = await getContractAddressByName(TEST_NETWORK, collateralConfig.symbol); - const addressJoin = await getContractAddressByName( - TEST_NETWORK, - getJoinNameByCollateralType(collateralConfig.ilk) - ); + const addressJoin = await getContractAddressByName(TEST_NETWORK, joinName); const contract = await getErc20Contract(TEST_NETWORK, tokenContractAddress, true); const amountRaw = amount ? amount.shiftedBy(collateralConfig.decimals).toFixed(0) : MAX.toFixed(0); await contract.approve(addressJoin, amountRaw); @@ -194,7 +202,37 @@ const createDefaultVaultWithCollateral = async (collateralType: CollateralType, collateralOwned ); await putCollateralIntoVaultAndWithdrawDai(vaultId, roundedCollateralOwned); - return vaultId; + return { vaultIndex: vaultId, vaultAddress: vault.address }; +}; + +const createLockstakeVaultWithCollateral = async (collateralType: CollateralType, collateralOwned: BigNumber) => { + const collateralConfig = getCollateralConfigByType(collateralType); + const walletAddress = await (await getSigner(TEST_NETWORK)).getAddress(); + const refId = 0; + + // Open engine vault + const engine = await getContract(TEST_NETWORK, 'LOCKSTAKE_ENGINE', true); + const vaultIndex = parseInt(await engine.ownerUrnsCount(walletAddress)); + await engine.open(vaultIndex); + + // Lock + const collateralOwnedInt = collateralOwned.shiftedBy(collateralConfig.decimals).toFixed(0); + const mkr = await getContract(TEST_NETWORK, 'MCD_GOV', true); + await mkr['approve(address,uint256)'](engine.address, collateralOwnedInt); + await engine.lock(walletAddress, vaultIndex, collateralOwnedInt, refId); + + // Draw + await collectStabilityFees(TEST_NETWORK, collateralType); + const { minUnitPrice, stabilityFeeRate } = await fetchVaultCollateralParameters(TEST_NETWORK, collateralType); + const drawnDebtExact = collateralOwned.multipliedBy(minUnitPrice).dividedBy(stabilityFeeRate); + const drawnDebt = roundDownToFirstSignificantDecimal(drawnDebtExact); + const drawnDebtInt = drawnDebt.shiftedBy(WAD_NUMBER_OF_DIGITS).toFixed(0); + await engine.draw(walletAddress, vaultIndex, walletAddress, drawnDebtInt); + + // Get vault address + const vaultAddress = await engine.ownerUrns(walletAddress, vaultIndex); + + return { vaultIndex, vaultAddress }; }; const createVaultWithCollateral = async (collateralType: CollateralType, collateralOwned: BigNumber) => { @@ -209,10 +247,11 @@ const createVaultWithCollateral = async (collateralType: CollateralType, collate } await ensureWalletBalance(collateralConfig, collateralOwned); - const joinContractAddress = await getContractAddressByName( - TEST_NETWORK, - getJoinNameByCollateralType(collateralType) - ); + const joinName = getJoinNameByCollateralType(collateralType); + if (!joinName) { + return await createLockstakeVaultWithCollateral(collateralType, collateralOwned); + } + const joinContractAddress = await getContractAddressByName(TEST_NETWORK, joinName); const proxyTarget = await detectProxyTarget(TEST_NETWORK, joinContractAddress); if (!proxyTarget) { return await createDefaultVaultWithCollateral(collateralType, collateralOwned); diff --git a/core/simulations/helpers/promptToGetBlockNumber.ts b/core/simulations/helpers/promptToGetBlockNumber.ts deleted file mode 100644 index 4fc8a1dcc..000000000 --- a/core/simulations/helpers/promptToGetBlockNumber.ts +++ /dev/null @@ -1,29 +0,0 @@ -import prompts from 'prompts'; - -import hre from 'hardhat'; - -interface BlockNumberPromt { - title?: string; - initial?: number; - max?: number; - min?: number; -} -const promptToGetBlockNumber = async (params?: BlockNumberPromt) => { - const title: string = params?.title || 'Block number to fork from'; - const min = params?.min || 0; - const initial = params?.initial; - const max = params?.max ?? (await hre.ethers.provider.getBlockNumber()); - const { number } = await prompts([ - { - type: 'number', - name: 'number', - message: title, - initial: initial ?? max, - min, - max, - }, - ]); - return number; -}; - -export default promptToGetBlockNumber; diff --git a/core/simulations/helpers/promptToGetNumber.ts b/core/simulations/helpers/promptToGetNumber.ts new file mode 100644 index 000000000..efdda2f85 --- /dev/null +++ b/core/simulations/helpers/promptToGetNumber.ts @@ -0,0 +1,36 @@ +import prompts from 'prompts'; + +import hre from 'hardhat'; + +interface NumberPromt { + title: string; + initial?: number; + min?: number; + max?: number; +} + +export const promptToGetNumber = async (params: NumberPromt) => { + const { number } = await prompts([ + { + type: 'number', + name: 'number', + message: params.title, + initial: params?.initial ?? 0, + min: params?.min ?? 0, + max: params?.max, + }, + ]); + if (!number) { + throw new Error('No number was provided'); + } + return number; +}; + +export const promptToGetBlockNumber = async () => { + const latestBlock = await hre.ethers.provider.getBlockNumber(); + return promptToGetNumber({ + title: 'Block number to fork from', + initial: latestBlock, + max: latestBlock, + }); +}; diff --git a/core/src/abis/LOCKSTAKE_CLIP.json b/core/src/abis/LOCKSTAKE_CLIP.json new file mode 100644 index 000000000..725221f2e --- /dev/null +++ b/core/src/abis/LOCKSTAKE_CLIP.json @@ -0,0 +1,761 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "vat_", + "type": "address" + }, + { + "internalType": "address", + "name": "spotter_", + "type": "address" + }, + { + "internalType": "address", + "name": "dog_", + "type": "address" + }, + { + "internalType": "address", + "name": "engine_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "usr", + "type": "address" + } + ], + "name": "Deny", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "what", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "data", + "type": "uint256" + } + ], + "name": "File", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "what", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "data", + "type": "address" + } + ], + "name": "File", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "top", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tab", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lot", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "usr", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "kpr", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "coin", + "type": "uint256" + } + ], + "name": "Kick", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "top", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tab", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lot", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "usr", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "kpr", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "coin", + "type": "uint256" + } + ], + "name": "Redo", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "usr", + "type": "address" + } + ], + "name": "Rely", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "max", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "price", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "owe", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tab", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "lot", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "usr", + "type": "address" + } + ], + "name": "Take", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "Yank", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "active", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "buf", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "calc", + "outputs": [ + { + "internalType": "contract AbacusLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "chip", + "outputs": [ + { + "internalType": "uint64", + "name": "", + "type": "uint64" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "chost", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "count", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "cusp", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "usr", + "type": "address" + } + ], + "name": "deny", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "dog", + "outputs": [ + { + "internalType": "contract DogLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "engine", + "outputs": [ + { + "internalType": "contract LockstakeEngineLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "what", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "data", + "type": "uint256" + } + ], + "name": "file", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "what", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "data", + "type": "address" + } + ], + "name": "file", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getStatus", + "outputs": [ + { + "internalType": "bool", + "name": "needsRedo", + "type": "bool" + }, + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tab", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "ilk", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "tab", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lot", + "type": "uint256" + }, + { + "internalType": "address", + "name": "usr", + "type": "address" + }, + { + "internalType": "address", + "name": "kpr", + "type": "address" + } + ], + "name": "kick", + "outputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "kicks", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "list", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "kpr", + "type": "address" + } + ], + "name": "redo", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "usr", + "type": "address" + } + ], + "name": "rely", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "sales", + "outputs": [ + { + "internalType": "uint256", + "name": "pos", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tab", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "lot", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tot", + "type": "uint256" + }, + { + "internalType": "address", + "name": "usr", + "type": "address" + }, + { + "internalType": "uint96", + "name": "tic", + "type": "uint96" + }, + { + "internalType": "uint256", + "name": "top", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "spotter", + "outputs": [ + { + "internalType": "contract SpotterLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "stopped", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "tail", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "max", + "type": "uint256" + }, + { + "internalType": "address", + "name": "who", + "type": "address" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + } + ], + "name": "take", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "tip", + "outputs": [ + { + "internalType": "uint192", + "name": "", + "type": "uint192" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "upchost", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "vat", + "outputs": [ + { + "internalType": "contract VatLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vow", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "wards", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "yank", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/core/src/abis/LOCKSTAKE_ENGINE.json b/core/src/abis/LOCKSTAKE_ENGINE.json new file mode 100644 index 000000000..2df209ad3 --- /dev/null +++ b/core/src/abis/LOCKSTAKE_ENGINE.json @@ -0,0 +1,1516 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "voteDelegateFactory_", + "type": "address" + }, + { + "internalType": "address", + "name": "usdsJoin_", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "ilk_", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "mkrSky_", + "type": "address" + }, + { + "internalType": "address", + "name": "lsmkr_", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "farm", + "type": "address" + } + ], + "name": "AddFarm", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "farm", + "type": "address" + } + ], + "name": "DelFarm", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "usr", + "type": "address" + } + ], + "name": "Deny", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "wad", + "type": "uint256" + } + ], + "name": "Draw", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "what", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "data", + "type": "address" + } + ], + "name": "File", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "what", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "data", + "type": "uint256" + } + ], + "name": "File", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "wad", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "freed", + "type": "uint256" + } + ], + "name": "Free", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "wad", + "type": "uint256" + } + ], + "name": "FreeNoFee", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "skyWad", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "skyFreed", + "type": "uint256" + } + ], + "name": "FreeSky", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "farm", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "amt", + "type": "uint256" + } + ], + "name": "GetReward", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "usr", + "type": "address" + } + ], + "name": "Hope", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "wad", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "ref", + "type": "uint16" + } + ], + "name": "Lock", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "skyWad", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "ref", + "type": "uint16" + } + ], + "name": "LockSky", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "usr", + "type": "address" + } + ], + "name": "Nope", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "urn", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "wad", + "type": "uint256" + } + ], + "name": "OnKick", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "urn", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "sold", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "burn", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "refund", + "type": "uint256" + } + ], + "name": "OnRemove", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "urn", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "who", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "wad", + "type": "uint256" + } + ], + "name": "OnTake", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "address", + "name": "urn", + "type": "address" + } + ], + "name": "Open", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "usr", + "type": "address" + } + ], + "name": "Rely", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "farm", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint16", + "name": "ref", + "type": "uint16" + } + ], + "name": "SelectFarm", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "voteDelegate", + "type": "address" + } + ], + "name": "SelectVoteDelegate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "wad", + "type": "uint256" + } + ], + "name": "Wipe", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "farm", + "type": "address" + } + ], + "name": "addFarm", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "farm", + "type": "address" + } + ], + "name": "delFarm", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "usr", + "type": "address" + } + ], + "name": "deny", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "wad", + "type": "uint256" + } + ], + "name": "draw", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "farm", + "type": "address" + } + ], + "name": "farms", + "outputs": [ + { + "internalType": "enum LockstakeEngine.FarmStatus", + "name": "", + "type": "uint8" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "fee", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "what", + "type": "bytes32" + }, + { + "internalType": "uint256", + "name": "data", + "type": "uint256" + } + ], + "name": "file", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "what", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "data", + "type": "address" + } + ], + "name": "file", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "wad", + "type": "uint256" + } + ], + "name": "free", + "outputs": [ + { + "internalType": "uint256", + "name": "freed", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "wad", + "type": "uint256" + } + ], + "name": "freeNoFee", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "internalType": "uint256", + "name": "skyWad", + "type": "uint256" + } + ], + "name": "freeSky", + "outputs": [ + { + "internalType": "uint256", + "name": "skyFreed", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "address", + "name": "farm", + "type": "address" + }, + { + "internalType": "address", + "name": "to", + "type": "address" + } + ], + "name": "getReward", + "outputs": [ + { + "internalType": "uint256", + "name": "amt", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "address", + "name": "usr", + "type": "address" + } + ], + "name": "hope", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "ilk", + "outputs": [ + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "address", + "name": "usr", + "type": "address" + } + ], + "name": "isUrnAuth", + "outputs": [ + { + "internalType": "bool", + "name": "ok", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "jug", + "outputs": [ + { + "internalType": "contract JugLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "wad", + "type": "uint256" + }, + { + "internalType": "uint16", + "name": "ref", + "type": "uint16" + } + ], + "name": "lock", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "skyWad", + "type": "uint256" + }, + { + "internalType": "uint16", + "name": "ref", + "type": "uint16" + } + ], + "name": "lockSky", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "lsmkr", + "outputs": [ + { + "internalType": "contract GemLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mkr", + "outputs": [ + { + "internalType": "contract GemLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mkrSky", + "outputs": [ + { + "internalType": "contract MkrSkyLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "mkrSkyRate", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes[]", + "name": "data", + "type": "bytes[]" + } + ], + "name": "multicall", + "outputs": [ + { + "internalType": "bytes[]", + "name": "results", + "type": "bytes[]" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "address", + "name": "usr", + "type": "address" + } + ], + "name": "nope", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "urn", + "type": "address" + }, + { + "internalType": "uint256", + "name": "wad", + "type": "uint256" + } + ], + "name": "onKick", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "urn", + "type": "address" + }, + { + "internalType": "uint256", + "name": "sold", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "left", + "type": "uint256" + } + ], + "name": "onRemove", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "urn", + "type": "address" + }, + { + "internalType": "address", + "name": "who", + "type": "address" + }, + { + "internalType": "uint256", + "name": "wad", + "type": "uint256" + } + ], + "name": "onTake", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "open", + "outputs": [ + { + "internalType": "address", + "name": "urn", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "ownerUrns", + "outputs": [ + { + "internalType": "address", + "name": "urn", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "ownerUrnsCount", + "outputs": [ + { + "internalType": "uint256", + "name": "count", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "usr", + "type": "address" + } + ], + "name": "rely", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "address", + "name": "farm", + "type": "address" + }, + { + "internalType": "uint16", + "name": "ref", + "type": "uint16" + } + ], + "name": "selectFarm", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "address", + "name": "voteDelegate", + "type": "address" + } + ], + "name": "selectVoteDelegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "sky", + "outputs": [ + { + "internalType": "contract GemLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "urn", + "type": "address" + } + ], + "name": "urnAuctions", + "outputs": [ + { + "internalType": "uint256", + "name": "auctionsCount", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "urn", + "type": "address" + }, + { + "internalType": "address", + "name": "usr", + "type": "address" + } + ], + "name": "urnCan", + "outputs": [ + { + "internalType": "uint256", + "name": "allowed", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "urn", + "type": "address" + } + ], + "name": "urnFarms", + "outputs": [ + { + "internalType": "address", + "name": "farm", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "urnImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "urn", + "type": "address" + } + ], + "name": "urnOwners", + "outputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "urn", + "type": "address" + } + ], + "name": "urnVoteDelegates", + "outputs": [ + { + "internalType": "address", + "name": "voteDelegate", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "usds", + "outputs": [ + { + "internalType": "contract GemLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "usdsJoin", + "outputs": [ + { + "internalType": "contract UsdsJoinLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "vat", + "outputs": [ + { + "internalType": "contract VatLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "voteDelegateFactory", + "outputs": [ + { + "internalType": "contract VoteDelegateFactoryLike", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "usr", + "type": "address" + } + ], + "name": "wards", + "outputs": [ + { + "internalType": "uint256", + "name": "allowed", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "wad", + "type": "uint256" + } + ], + "name": "wipe", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "index", + "type": "uint256" + } + ], + "name": "wipeAll", + "outputs": [ + { + "internalType": "uint256", + "name": "wad", + "type": "uint256" + } + ], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/core/src/addresses.ts b/core/src/addresses.ts index 50d7821aa..f4be2a555 100644 --- a/core/src/addresses.ts +++ b/core/src/addresses.ts @@ -1,7 +1,7 @@ import type { Contract } from 'ethers'; import { ethers } from 'ethers'; import memoizee from 'memoizee'; -import COLLATERALS, { getAllCollateralTypes } from './constants/COLLATERALS'; +import COLLATERALS, { getAllCollateralTypes, getCollateralConfigByType } from './constants/COLLATERALS'; import getProvider from './provider'; import CHAINLOG from './abis/CHAINLOG.json'; @@ -48,8 +48,8 @@ export const isCollateralSymbolSupported = async function ( }; export const isCollateralTypeSupported = async function (network: string, collateralType: string): Promise { - const suffix = collateralType.toUpperCase().replace('-', '_'); - const clipContractName = `MCD_CLIP_${suffix}`; + const collateralConfig = getCollateralConfigByType(collateralType); + const clipContractName = collateralConfig.contracts.clip; try { const collateralAddress = await fetchContractAddressByNetwork(network, clipContractName); return !!collateralAddress; diff --git a/core/src/calleeFunctions/CurveLpTokenUniv3Callee.ts b/core/src/calleeFunctions/CurveLpTokenUniv3Callee.ts index 56478169b..d06aa4522 100644 --- a/core/src/calleeFunctions/CurveLpTokenUniv3Callee.ts +++ b/core/src/calleeFunctions/CurveLpTokenUniv3Callee.ts @@ -23,9 +23,13 @@ const getCalleeData = async function ( if (!preloadedPools) { throw new Error(`Can not encode route for the "${collateral.ilk}" without preloaded pools`); } + const joinName = getJoinNameByCollateralType(collateral.ilk); + if (!joinName) { + throw new Error(`Collateral "${collateral.ilk}" does not have join contract`); + } + const joinAdapterAddress = await getContractAddressByName(network, joinName); const route = await encodePools(network, preloadedPools); const curveData = [CURVE_POOL_ADDRESS, CURVE_COIN_INDEX]; - const joinAdapterAddress = await getContractAddressByName(network, getJoinNameByCollateralType(collateral.ilk)); const minProfit = 1; const typesArray = ['address', 'address', 'uint256', 'bytes', 'address', 'tuple(address,uint256)']; return ethers.utils.defaultAbiCoder.encode(typesArray, [ diff --git a/core/src/calleeFunctions/OneInchCallee.ts b/core/src/calleeFunctions/OneInchCallee.ts index 0aca3cde7..b71c8f766 100644 --- a/core/src/calleeFunctions/OneInchCallee.ts +++ b/core/src/calleeFunctions/OneInchCallee.ts @@ -20,7 +20,11 @@ const getCalleeData = async function ( if (!oneInchParams) { throw new Error(`getCalleeData called with invalid txData`); } - const joinAdapterAddress = await getContractAddressByName(network, getJoinNameByCollateralType(collateral.ilk)); + const joinName = getJoinNameByCollateralType(collateral.ilk); + if (!joinName) { + throw new Error(`Collateral "${collateral.ilk}" does not have join contract`); + } + const joinAdapterAddress = await getContractAddressByName(network, joinName); const minProfit = 1; const typesArray = ['address', 'address', 'uint256', 'address', 'address', 'bytes']; return ethers.utils.defaultAbiCoder.encode(typesArray, [ diff --git a/core/src/calleeFunctions/UniswapV2CalleeDai.ts b/core/src/calleeFunctions/UniswapV2CalleeDai.ts index d3a22cdfd..75c1e296e 100644 --- a/core/src/calleeFunctions/UniswapV2CalleeDai.ts +++ b/core/src/calleeFunctions/UniswapV2CalleeDai.ts @@ -15,7 +15,11 @@ const getCalleeData = async function ( if (marketData?.callee !== 'UniswapV2CalleeDai') { throw new Error(`getCalleeData called with invalid collateral type "${collateral.ilk}"`); } - const joinAdapterAddress = await getContractAddressByName(network, getJoinNameByCollateralType(collateral.ilk)); + const joinName = getJoinNameByCollateralType(collateral.ilk); + if (!joinName) { + throw new Error(`Collateral "${collateral.ilk}" does not have join contract`); + } + const joinAdapterAddress = await getContractAddressByName(network, joinName); const minProfit = 1; const typesArray = ['address', 'address', 'uint256', 'address[]']; return ethers.utils.defaultAbiCoder.encode(typesArray, [ diff --git a/core/src/calleeFunctions/UniswapV2LockstakeCallee.ts b/core/src/calleeFunctions/UniswapV2LockstakeCallee.ts new file mode 100644 index 000000000..2cedb0e95 --- /dev/null +++ b/core/src/calleeFunctions/UniswapV2LockstakeCallee.ts @@ -0,0 +1,51 @@ +import type { CalleeFunctions, CollateralConfig, Pool } from '../types'; +import { ethers } from 'ethers'; +import BigNumber from '../bignumber'; +import { getUniswapRouteAddressesBySymbol, getRegularTokenExchangeRateBySymbol } from './helpers/uniswapV2'; +import { routeToPool } from './helpers/pools'; + +const getCalleeData = async function ( + network: string, + collateral: CollateralConfig, + marketId: string, + profitAddress: string +): Promise { + const marketData = collateral.exchanges[marketId]; + if (marketData?.callee !== 'UniswapV2LockstakeCallee') { + throw new Error(`getCalleeData called with invalid collateral type "${collateral.ilk}"`); + } + const minProfit = 1; + const typesArray = ['address', 'uint256', 'address[]']; + return ethers.utils.defaultAbiCoder.encode(typesArray, [ + profitAddress, + minProfit, + await getUniswapRouteAddressesBySymbol(network, collateral.symbol, marketId), + ]); +}; + +const getMarketPrice = async function ( + network: string, + collateral: CollateralConfig, + marketId: string, + amount: BigNumber +): Promise<{ price: BigNumber; pools: Pool[] }> { + const marketData = collateral.exchanges[marketId]; + if (marketData.callee !== 'UniswapV2LockstakeCallee') { + throw new Error(`Can not get market price for the "${collateral.ilk}"`); + } + let price = await getRegularTokenExchangeRateBySymbol(network, collateral.symbol, marketId, amount); + if (marketData.route[0] === 'SKY') { + price = price.multipliedBy(24_000); + } + return { + price, + pools: await routeToPool(network, marketData.route, collateral.symbol), + }; +}; + +const UniswapV2CalleeDai: CalleeFunctions = { + getCalleeData, + getMarketPrice, +}; + +export default UniswapV2CalleeDai; diff --git a/core/src/calleeFunctions/UniswapV2LpTokenCalleeDai.ts b/core/src/calleeFunctions/UniswapV2LpTokenCalleeDai.ts index d50fa30f3..710b665b0 100644 --- a/core/src/calleeFunctions/UniswapV2LpTokenCalleeDai.ts +++ b/core/src/calleeFunctions/UniswapV2LpTokenCalleeDai.ts @@ -20,7 +20,11 @@ const getCalleeData = async function ( if (marketData?.callee !== 'UniswapV2LpTokenCalleeDai') { throw new Error(`getCalleeData called with invalid collateral type "${collateral.ilk}"`); } - const joinAdapterAddress = await getContractAddressByName(network, getJoinNameByCollateralType(collateral.ilk)); + const joinName = getJoinNameByCollateralType(collateral.ilk); + if (!joinName) { + throw new Error(`Collateral "${collateral.ilk}" does not have join contract`); + } + const joinAdapterAddress = await getContractAddressByName(network, joinName); const minProfit = 1; const typesArray = ['address', 'address', 'uint256', 'address[]', 'address[]']; return ethers.utils.defaultAbiCoder.encode(typesArray, [ diff --git a/core/src/calleeFunctions/UniswapV3Callee.ts b/core/src/calleeFunctions/UniswapV3Callee.ts index 9d292873c..21a16b81b 100644 --- a/core/src/calleeFunctions/UniswapV3Callee.ts +++ b/core/src/calleeFunctions/UniswapV3Callee.ts @@ -18,12 +18,16 @@ const getCalleeData = async function ( if (marketData?.callee !== 'UniswapV3Callee') { throw new Error(`getCalleeData called with invalid collateral type "${collateral.ilk}"`); } + const joinName = getJoinNameByCollateralType(collateral.ilk); + if (!joinName) { + throw new Error(`collateral "${collateral.ilk}" does not have join contract`); + } const preloadedPools = !!params && 'pools' in params ? params.pools : undefined; const pools = preloadedPools || (await getPools(network, collateral, marketId)); if (!pools) { throw new Error(`getCalleeData called with invalid pools`); } - const joinAdapterAddress = await getContractAddressByName(network, getJoinNameByCollateralType(collateral.ilk)); + const joinAdapterAddress = await getContractAddressByName(network, joinName); const minProfit = 1; const uniswapV3pools = await encodePools(network, pools); const typesArray = ['address', 'address', 'uint256', 'bytes', 'address']; diff --git a/core/src/calleeFunctions/WstETHCurveUniv3Callee.ts b/core/src/calleeFunctions/WstETHCurveUniv3Callee.ts index 7b81d0065..c3c42dbfc 100644 --- a/core/src/calleeFunctions/WstETHCurveUniv3Callee.ts +++ b/core/src/calleeFunctions/WstETHCurveUniv3Callee.ts @@ -13,7 +13,11 @@ const getCalleeData = async function ( _marketId: string, profitAddress: string ): Promise { - const joinAdapterAddress = await getContractAddressByName(network, getJoinNameByCollateralType(collateral.ilk)); + const joinName = getJoinNameByCollateralType(collateral.ilk); + if (!joinName) { + throw new Error(`Collateral "${collateral.ilk}" does not have join contract`); + } + const joinAdapterAddress = await getContractAddressByName(network, joinName); const minProfit = 1; const typesArray = ['address', 'address', 'uint256', 'uint24', 'address']; return ethers.utils.defaultAbiCoder.encode(typesArray, [ diff --git a/core/src/calleeFunctions/helpers/pools.ts b/core/src/calleeFunctions/helpers/pools.ts index 85d948320..26a6ceea9 100644 --- a/core/src/calleeFunctions/helpers/pools.ts +++ b/core/src/calleeFunctions/helpers/pools.ts @@ -16,8 +16,8 @@ export const routeToPool = async ( uniswapFees?: number[] ): Promise => { const fees = uniswapFees || Array.from({ length: route.length + 2 }, () => 3000); - const fullRoute = [collateralSymbol, ...route, 'DAI']; - const routeSteps = getRouteSteps(fullRoute, fees); + collateralSymbol; + const routeSteps = getRouteSteps(route, fees); return await Promise.all( routeSteps.map(async step => ({ addresses: await Promise.all( diff --git a/core/src/calleeFunctions/helpers/uniswapV2.ts b/core/src/calleeFunctions/helpers/uniswapV2.ts index f8e3fec57..2ac9d6e94 100644 --- a/core/src/calleeFunctions/helpers/uniswapV2.ts +++ b/core/src/calleeFunctions/helpers/uniswapV2.ts @@ -21,12 +21,14 @@ const getCalleeConfig = function (collateral: CollateralConfig, _marketId: strin // TODO: remove _marketId from the all uniswapV2 functions, since they have to always use 'Uniswap V2' config const marketData = collateral.exchanges['Uniswap V2']; const isUniswapTokenNonAutoRouted = - (marketData?.callee === 'UniswapV2CalleeDai' || marketData?.callee === 'UniswapV3Callee') && + (marketData?.callee === 'UniswapV2CalleeDai' || + marketData?.callee === 'UniswapV3Callee' || + marketData?.callee === 'UniswapV2LockstakeCallee') && !('automaticRouter' in marketData); if (isUniswapTokenNonAutoRouted) { return marketData; } - throw new Error(`"${collateral.symbol}" is not an UniSwap token`); + throw new Error(`"${collateral.symbol}" is not an Uniswap token`); }; export const getCompleteExchangePathBySymbol = function (symbol: string, marketId: string, useExchangeRoute = true) { @@ -35,7 +37,7 @@ export const getCompleteExchangePathBySymbol = function (symbol: string, marketI return ['DAI']; } const collateral = getCollateralConfigBySymbol(symbol); - return !useExchangeRoute ? [symbol, 'DAI'] : [symbol, ...getCalleeConfig(collateral, marketId).route, 'DAI']; + return !useExchangeRoute ? [symbol, 'DAI'] : getCalleeConfig(collateral, marketId).route; }; export const getUniswapRouteAddressesBySymbol = async function ( @@ -122,7 +124,7 @@ export const getRegularTokenExchangeRateBySymbol = async function ( const completeExchangePath = getCompleteExchangePathBySymbol(symbol, marketId); const pairs = splitArrayIntoPairs(completeExchangePath); const uniswapPairs = await Promise.all(pairs.map(pair => getUniswapPairBySymbols(network, pair[0], pair[1]))); - const exchangeToken = await getUniswapTokenBySymbol(network, symbol); + const exchangeToken = await getUniswapTokenBySymbol(network, completeExchangePath[0]); const uniswapRoute = new Route(uniswapPairs, exchangeToken); const uniswapTrade = new Trade( uniswapRoute, diff --git a/core/src/calleeFunctions/index.ts b/core/src/calleeFunctions/index.ts index 785e6bffc..91b5180af 100644 --- a/core/src/calleeFunctions/index.ts +++ b/core/src/calleeFunctions/index.ts @@ -17,6 +17,7 @@ import CurveLpTokenUniv3Callee from './CurveLpTokenUniv3Callee'; import UniswapV3Callee from './UniswapV3Callee'; import OneInchCallee from './OneInchCallee'; import rETHCurveUniv3Callee from './rETHCurveUniv3Callee'; +import UniswapV2LockstakeCallee from './UniswapV2LockstakeCallee'; import { getCollateralConfigByType, getCollateralConfigBySymbol } from '../constants/COLLATERALS'; import { routeToPool } from './helpers/pools'; import { getOneInchMarketData } from './helpers/oneInch'; @@ -31,6 +32,7 @@ const allCalleeFunctions: Record = { UniswapV3Callee, rETHCurveUniv3Callee, OneInchCallee, + UniswapV2LockstakeCallee, }; export const getCalleeData = async function ( diff --git a/core/src/calleeFunctions/rETHCurveUniv3Callee.ts b/core/src/calleeFunctions/rETHCurveUniv3Callee.ts index 9f7d0e5fe..9094b2d74 100644 --- a/core/src/calleeFunctions/rETHCurveUniv3Callee.ts +++ b/core/src/calleeFunctions/rETHCurveUniv3Callee.ts @@ -24,8 +24,12 @@ const getCalleeData = async function ( if (!preloadedPools) { throw new Error(`Can not encode route for the "${collateral.ilk}" without preloaded pools`); } + const joinName = getJoinNameByCollateralType(collateral.ilk); + if (!joinName) { + throw new Error(`Collateral "${collateral.ilk}" does not have join contract`); + } const route = await encodePools(network, preloadedPools.slice(1)); - const joinAdapterAddress = await getContractAddressByName(network, getJoinNameByCollateralType(collateral.ilk)); + const joinAdapterAddress = await getContractAddressByName(network, joinName); const minProfit = 1; const typesArray = ['address', 'address', 'uint256', 'bytes', 'address']; return ethers.utils.defaultAbiCoder.encode(typesArray, [ diff --git a/core/src/constants/CALLEES.ts b/core/src/constants/CALLEES.ts index 45303f1df..3b84310d6 100644 --- a/core/src/constants/CALLEES.ts +++ b/core/src/constants/CALLEES.ts @@ -11,6 +11,7 @@ const CALLEES: Record = { UniswapV3Callee: '0xdB9C76109d102d2A1E645dCa3a7E671EBfd8e11A', rETHCurveUniv3Callee: '0x7cdAb0fE16efb1EFE89e53B141347D7F299d6610', OneInchCallee: '0x19c916CDAFB41FAdd4CEd3dCf412e0302291563A', + UniswapV2LockstakeCallee: '0xf68424845e4Af5b771356d504965A3c9257805f3', }, '0x5': { UniswapV2CalleeDai: '0x6d9139ac89ad2263f138633de20e47bcae253938', diff --git a/core/src/constants/COLLATERALS.ts b/core/src/constants/COLLATERALS.ts index f2e06032e..8d5f4fc4a 100644 --- a/core/src/constants/COLLATERALS.ts +++ b/core/src/constants/COLLATERALS.ts @@ -1,18 +1,18 @@ import type { CollateralConfig, OracleCurrentAndNextPrices, OracleCurrentPriceOnly } from '../types'; -export const CONFIG_WITH_NEXT_PRICE: OracleCurrentAndNextPrices = { +export const ORACLE_WITH_DELAY: OracleCurrentAndNextPrices = { type: 'CurrentAndNextPrice', - currentPriceSlotAddress: '0x3', - nextPriceSlotAddress: '0x4', hasDelay: true, + currentPriceSlotAddress: '0x3', slotPriceValueBeginsAtPosition: 34, + nextPriceSlotAddress: '0x4', }; -export const CONFIG_WITHOUT_NEXT_PRICE: OracleCurrentPriceOnly = { +export const ORACLE_WITHOUT_DELAY: OracleCurrentPriceOnly = { type: 'CurrentPriceOnly', - currentPriceSlotAddress: '0x2', hasDelay: false, - currentPriceValiditySlotAndOffset: { slot: '0x1', offset: 25 }, + currentPriceSlotAddress: '0x2', slotPriceValueBeginsAtPosition: 0, + currentPriceValiditySlotAndOffset: { slot: '0x1', offset: 25 }, }; const COLLATERALS: Record = { @@ -20,7 +20,15 @@ const COLLATERALS: Record = { title: 'AAVE', ilk: 'AAVE-A', symbol: 'AAVE', + tokenName: 'AAVE', decimals: 18, + contracts: { + token: 'AAVE', + pip: 'PIP_AAVE', + join: 'MCD_JOIN_AAVE_A', + clip: 'MCD_CLIP_AAVE_A', + calc: 'MCD_CLIP_CALC_AAVE_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -28,20 +36,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['AAVE', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['AAVE', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'BAL-A': { title: 'Balancer', ilk: 'BAL-A', symbol: 'BAL', + tokenName: 'BAL', decimals: 18, + contracts: { + token: 'BAL', + pip: 'PIP_BAL', + join: 'MCD_JOIN_BAL_A', + clip: 'MCD_CLIP_BAL_A', + calc: 'MCD_CLIP_CALC_BAL_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -49,20 +65,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['BAL', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['BAL', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'BAT-A': { title: 'Basic Attention Token', ilk: 'BAT-A', symbol: 'BAT', + tokenName: 'BAT', decimals: 18, + contracts: { + token: 'BAT', + pip: 'PIP_BAT', + join: 'MCD_JOIN_BAT_A', + clip: 'MCD_CLIP_BAT_A', + calc: 'MCD_CLIP_CALC_BAT_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -70,20 +94,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['BAL', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['BAL', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'COMP-A': { title: 'Compound', ilk: 'COMP-A', symbol: 'COMP', + tokenName: 'COMP', decimals: 18, + contracts: { + token: 'COMP', + pip: 'PIP_COMP', + join: 'MCD_JOIN_COMP_A', + clip: 'MCD_CLIP_COMP_A', + calc: 'MCD_CLIP_CALC_COMP_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -91,37 +123,53 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['COMP', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['COMP', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'GNO-A': { title: 'Gnosis Token', ilk: 'GNO-A', symbol: 'GNO', + tokenName: 'GNO', decimals: 18, + contracts: { + token: 'GNO', + pip: 'PIP_GNO', + join: 'MCD_JOIN_GNO_A', + clip: 'MCD_CLIP_GNO_A', + calc: 'MCD_CLIP_CALC_GNO_A', + }, exchanges: { 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['GNO', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['GNO', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'ETH-A': { title: 'Ether', ilk: 'ETH-A', symbol: 'ETH', + tokenName: 'ETH', decimals: 18, + contracts: { + token: 'ETH', + pip: 'PIP_ETH', + join: 'MCD_JOIN_ETH_A', + clip: 'MCD_CLIP_ETH_A', + calc: 'MCD_CLIP_CALC_ETH_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -129,20 +177,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: [], + route: ['ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: [], + route: ['ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'ETH-B': { title: 'Ether', ilk: 'ETH-B', symbol: 'ETH', + tokenName: 'ETH', decimals: 18, + contracts: { + token: 'ETH', + pip: 'PIP_ETH', + join: 'MCD_JOIN_ETH_B', + clip: 'MCD_CLIP_ETH_B', + calc: 'MCD_CLIP_CALC_ETH_B', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -150,20 +206,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: [], + route: ['ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: [], + route: ['ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'ETH-C': { title: 'Ether', ilk: 'ETH-C', symbol: 'ETH', + tokenName: 'ETH', decimals: 18, + contracts: { + token: 'ETH', + pip: 'PIP_ETH', + join: 'MCD_JOIN_ETH_C', + clip: 'MCD_CLIP_ETH_C', + calc: 'MCD_CLIP_CALC_ETH_C', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -171,20 +235,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: [], + route: ['ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: [], + route: ['ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'GUSD-A': { title: 'Gemini Dollar', ilk: 'GUSD-A', symbol: 'GUSD', + tokenName: 'GUSD', decimals: 2, + contracts: { + token: 'GUSD', + pip: 'PIP_GUSD', + join: 'MCD_JOIN_GUSD_A', + clip: 'MCD_CLIP_GUSD_A', + calc: 'MCD_CLIP_CALC_GUSD_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -192,20 +264,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['GUSD', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['GUSD', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITHOUT_NEXT_PRICE, + oracle: ORACLE_WITHOUT_DELAY, }, 'KNC-A': { title: 'Kyber Network Crystal', ilk: 'KNC-A', symbol: 'KNC', + tokenName: 'KNC', decimals: 18, + contracts: { + token: 'KNC', + pip: 'PIP_KNC', + join: 'MCD_JOIN_KNC_A', + clip: 'MCD_CLIP_KNC_A', + calc: 'MCD_CLIP_CALC_KNC_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -213,20 +293,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['KNC', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['KNC', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'LINK-A': { title: 'Chainlink', ilk: 'LINK-A', symbol: 'LINK', + tokenName: 'LINK', decimals: 18, + contracts: { + token: 'LINK', + pip: 'PIP_LINK', + join: 'MCD_JOIN_LINK_A', + clip: 'MCD_CLIP_LINK_A', + calc: 'MCD_CLIP_CALC_LINK_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -234,20 +322,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['LINK', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['LINK', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'LRC-A': { title: 'Loopring', ilk: 'LRC-A', symbol: 'LRC', + tokenName: 'LRC', decimals: 18, + contracts: { + token: 'LRC', + pip: 'PIP_LRC', + join: 'MCD_JOIN_LRC_A', + clip: 'MCD_CLIP_LRC_A', + calc: 'MCD_CLIP_CALC_LRC_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -255,20 +351,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['LRC', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['LRC', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'MANA-A': { title: 'Decentraland', ilk: 'MANA-A', symbol: 'MANA', + tokenName: 'MANA', decimals: 18, + contracts: { + token: 'MANA', + pip: 'PIP_MANA', + join: 'MCD_JOIN_MANA_A', + clip: 'MCD_CLIP_MANA_A', + calc: 'MCD_CLIP_CALC_MANA_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -276,20 +380,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['MANA', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['MANA', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'PAXUSD-A': { title: 'Paxos Standard', ilk: 'PAXUSD-A', symbol: 'PAXUSD', + tokenName: 'PAXUSD', decimals: 18, + contracts: { + token: 'PAXUSD', + pip: 'PIP_PAXUSD', + join: 'MCD_JOIN_PAXUSD_A', + clip: 'MCD_CLIP_PAXUSD_A', + calc: 'MCD_CLIP_CALC_PAXUSD_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -297,21 +409,29 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['PAXUSD', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['PAXUSD', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITHOUT_NEXT_PRICE, + oracle: ORACLE_WITHOUT_DELAY, }, 'RENBTC-A': { title: 'renBTC', ilk: 'RENBTC-A', symbol: 'RENBTC', + tokenName: 'RENBTC', decimals: 8, + contracts: { + token: 'RENBTC', + pip: 'PIP_RENBTC', + join: 'MCD_JOIN_RENBTC_A', + clip: 'MCD_CLIP_RENBTC_A', + calc: 'MCD_CLIP_CALC_RENBTC_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -319,20 +439,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['RENBTC', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['RENBTC', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'TUSD-A': { title: 'True USD', ilk: 'TUSD-A', symbol: 'TUSD', + tokenName: 'TUSD', decimals: 18, + contracts: { + token: 'TUSD', + pip: 'PIP_TUSD', + join: 'MCD_JOIN_TUSD_A', + clip: 'MCD_CLIP_TUSD_A', + calc: 'MCD_CLIP_CALC_TUSD_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -340,20 +468,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['TUSD', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['TUSD', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITHOUT_NEXT_PRICE, + oracle: ORACLE_WITHOUT_DELAY, }, 'UNI-A': { title: 'Uniswap', ilk: 'UNI-A', symbol: 'UNI', + tokenName: 'UNI', decimals: 18, + contracts: { + token: 'UNI', + pip: 'PIP_UNI', + join: 'MCD_JOIN_UNI_A', + clip: 'MCD_CLIP_UNI_A', + calc: 'MCD_CLIP_CALC_UNI_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -361,20 +497,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['UNI', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['UNI', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'USDC-A': { title: 'USD Coin', ilk: 'USDC-A', symbol: 'USDC', + tokenName: 'USDC', decimals: 6, + contracts: { + token: 'USDC', + pip: 'PIP_USDC', + join: 'MCD_JOIN_USDC_A', + clip: 'MCD_CLIP_USDC_A', + calc: 'MCD_CLIP_CALC_USDC_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -382,20 +526,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['USDC', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['USDC', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITHOUT_NEXT_PRICE, + oracle: ORACLE_WITHOUT_DELAY, }, 'USDC-B': { title: 'USD Coin', ilk: 'USDC-B', symbol: 'USDC', + tokenName: 'USDC', decimals: 6, + contracts: { + token: 'USDC', + pip: 'PIP_USDC', + join: 'MCD_JOIN_USDC_A', + clip: 'MCD_CLIP_USDC_B', + calc: 'MCD_CLIP_CALC_USDC_B', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -403,20 +555,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['USDC', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['USDC', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITHOUT_NEXT_PRICE, + oracle: ORACLE_WITHOUT_DELAY, }, 'USDT-A': { title: 'Tether USD', ilk: 'USDT-A', symbol: 'USDT', + tokenName: 'USDT', decimals: 6, + contracts: { + token: 'USDT', + pip: 'PIP_USDT', + join: 'MCD_JOIN_USDT_A', + clip: 'MCD_CLIP_USDT_A', + calc: 'MCD_CLIP_CALC_USDT_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -424,20 +584,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['USDT', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['USDT', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'WBTC-A': { title: 'Wrapped Bitcoin', ilk: 'WBTC-A', symbol: 'WBTC', + tokenName: 'WBTC', decimals: 8, + contracts: { + token: 'WBTC', + pip: 'PIP_WBTC', + join: 'MCD_JOIN_WBTC_A', + clip: 'MCD_CLIP_WBTC_A', + calc: 'MCD_CLIP_CALC_WBTC_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -445,20 +613,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['WBTC', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['WBTC', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'WBTC-B': { title: 'Wrapped BTC', ilk: 'WBTC-B', symbol: 'WBTC', + tokenName: 'WBTC', decimals: 8, + contracts: { + token: 'WBTC', + pip: 'PIP_WBTC', + join: 'MCD_JOIN_WBTC_B', + clip: 'MCD_CLIP_WBTC_B', + calc: 'MCD_CLIP_CALC_WBTC_B', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -466,20 +642,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['WBTC', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['WBTC', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'WBTC-C': { title: 'Wrapped BTC', ilk: 'WBTC-C', symbol: 'WBTC', + tokenName: 'WBTC', decimals: 8, + contracts: { + token: 'WBTC', + pip: 'PIP_WBTC', + join: 'MCD_JOIN_WBTC_C', + clip: 'MCD_CLIP_WBTC_C', + calc: 'MCD_CLIP_CALC_WBTC_C', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -487,20 +671,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['WBTC', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['WBTC', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'YFI-A': { title: 'yearn.finance', ilk: 'YFI-A', symbol: 'YFI', + tokenName: 'YFI', decimals: 18, + contracts: { + token: 'YFI', + pip: 'PIP_YFI', + join: 'MCD_JOIN_YFI_A', + clip: 'MCD_CLIP_YFI_A', + calc: 'MCD_CLIP_CALC_YFI_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -508,20 +700,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['YFI', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['YFI', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'ZRX-A': { title: '0x', ilk: 'ZRX-A', symbol: 'ZRX', + tokenName: 'ZRX', decimals: 18, + contracts: { + token: 'ZRX', + pip: 'PIP_ZRX', + join: 'MCD_JOIN_ZRX_A', + clip: 'MCD_CLIP_ZRX_A', + calc: 'MCD_CLIP_CALC_ZRX_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -529,20 +729,28 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['ZRX', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['ZRX', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'MATIC-A': { title: 'Matic', ilk: 'MATIC-A', symbol: 'MATIC', + tokenName: 'MATIC', decimals: 18, + contracts: { + token: 'MATIC', + pip: 'PIP_MATIC', + join: 'MCD_JOIN_MATIC_A', + clip: 'MCD_CLIP_MATIC_A', + calc: 'MCD_CLIP_CALC_MATIC_A', + }, exchanges: { 'Uniswap V3 Autorouter': { callee: 'UniswapV3Callee', @@ -550,59 +758,91 @@ const COLLATERALS: Record = { }, 'Uniswap V3': { callee: 'UniswapV3Callee', - route: ['ETH'], + route: ['MATIC', 'ETH', 'DAI'], }, 'Uniswap V2': { callee: 'UniswapV2CalleeDai', - route: ['ETH'], + route: ['MATIC', 'ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'WSTETH-A': { title: 'Lido wstETH', ilk: 'WSTETH-A', symbol: 'WSTETH', + tokenName: 'WSTETH', decimals: 18, + contracts: { + token: 'WSTETH', + pip: 'PIP_WSTETH', + join: 'MCD_JOIN_WSTETH_A', + clip: 'MCD_CLIP_WSTETH_A', + calc: 'MCD_CLIP_CALC_WSTETH_A', + }, exchanges: { 'Curve wstETH V3': { callee: 'WstETHCurveUniv3Callee', - route: [], + route: ['WSTETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'WSTETH-B': { title: 'Lido wstETH', ilk: 'WSTETH-B', symbol: 'WSTETH', + tokenName: 'WSTETH', decimals: 18, + contracts: { + token: 'WSTETH', + pip: 'PIP_WSTETH', + join: 'MCD_JOIN_WSTETH_B', + clip: 'MCD_CLIP_WSTETH_B', + calc: 'MCD_CLIP_CALC_WSTETH_B', + }, exchanges: { 'Curve wstETH V3': { callee: 'WstETHCurveUniv3Callee', - route: [], + route: ['WSTETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'CRVV1ETHSTETH-A': { title: 'Curve stETH', ilk: 'CRVV1ETHSTETH-A', symbol: 'CRVV1ETHSTETH', + tokenName: 'CRVV1ETHSTETH', decimals: 18, + contracts: { + token: 'CRVV1ETHSTETH', + pip: 'PIP_CRVV1ETHSTETH', + join: 'MCD_JOIN_CRVV1ETHSTETH_A', + clip: 'MCD_CLIP_CRVV1ETHSTETH_A', + calc: 'MCD_CLIP_CALC_CRVV1ETHSTETH_A', + }, exchanges: { 'Curve Token V3': { callee: 'CurveLpTokenUniv3Callee', - route: ['ETH'], + route: ['ETH', 'DAI'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'UNIV2DAIETH-A': { title: 'UNIV2DAIETH LP', ilk: 'UNIV2DAIETH-A', symbol: 'UNIV2DAIETH', + tokenName: 'UNIV2DAIETH', decimals: 18, + contracts: { + token: 'UNIV2DAIETH', + pip: 'PIP_UNIV2DAIETH', + join: 'MCD_JOIN_UNIV2DAIETH_A', + clip: 'MCD_CLIP_UNIV2DAIETH_A', + calc: 'MCD_CLIP_CALC_UNIV2DAIETH_A', + }, exchanges: { 'Uniswap Token V2': { callee: 'UniswapV2LpTokenCalleeDai', @@ -610,13 +850,21 @@ const COLLATERALS: Record = { token1: 'ETH', }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'UNIV2USDCETH-A': { title: 'UNIV2USDCETH LP', ilk: 'UNIV2USDCETH-A', symbol: 'UNIV2USDCETH', + tokenName: 'UNIV2USDCETH', decimals: 18, + contracts: { + token: 'UNIV2USDCETH', + pip: 'PIP_UNIV2USDCETH', + join: 'MCD_JOIN_UNIV2USDCETH_A', + clip: 'MCD_CLIP_UNIV2USDCETH_A', + calc: 'MCD_CLIP_CALC_UNIV2USDCETH_A', + }, exchanges: { 'Uniswap Token V2': { callee: 'UniswapV2LpTokenCalleeDai', @@ -624,13 +872,21 @@ const COLLATERALS: Record = { token1: 'ETH', }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'UNIV2ETHUSDT-A': { title: 'UNIV2ETHUSDT LP', ilk: 'UNIV2ETHUSDT-A', symbol: 'UNIV2ETHUSDT', + tokenName: 'UNIV2ETHUSDT', decimals: 18, + contracts: { + token: 'UNIV2ETHUSDT', + pip: 'PIP_UNIV2ETHUSDT', + join: 'MCD_JOIN_UNIV2ETHUSDT_A', + clip: 'MCD_CLIP_UNIV2ETHUSDT_A', + calc: 'MCD_CLIP_CALC_UNIV2ETHUSDT_A', + }, exchanges: { 'Uniswap Token V2': { callee: 'UniswapV2LpTokenCalleeDai', @@ -638,13 +894,21 @@ const COLLATERALS: Record = { token1: 'USDT', }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'UNIV2WBTCDAI-A': { title: 'UNIV2WBTCDAI LP', ilk: 'UNIV2WBTCDAI-A', symbol: 'UNIV2WBTCDAI', + tokenName: 'UNIV2WBTCDAI', decimals: 18, + contracts: { + token: 'UNIV2WBTCDAI', + pip: 'PIP_UNIV2WBTCDAI', + join: 'MCD_JOIN_UNIV2WBTCDAI_A', + clip: 'MCD_CLIP_UNIV2WBTCDAI_A', + calc: 'MCD_CLIP_CALC_UNIV2WBTCDAI_A', + }, exchanges: { 'Uniswap Token V2': { callee: 'UniswapV2LpTokenCalleeDai', @@ -652,13 +916,21 @@ const COLLATERALS: Record = { token1: 'DAI', }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'UNIV2WBTCETH-A': { title: 'UNIV2WBTCETH LP', ilk: 'UNIV2WBTCETH-A', symbol: 'UNIV2WBTCETH', + tokenName: 'UNIV2WBTCETH', decimals: 18, + contracts: { + token: 'UNIV2WBTCETH', + pip: 'PIP_UNIV2WBTCETH', + join: 'MCD_JOIN_UNIV2WBTCETH_A', + clip: 'MCD_CLIP_UNIV2WBTCETH_A', + calc: 'MCD_CLIP_CALC_UNIV2WBTCETH_A', + }, exchanges: { 'Uniswap Token V2': { callee: 'UniswapV2LpTokenCalleeDai', @@ -666,13 +938,21 @@ const COLLATERALS: Record = { token1: 'ETH', }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'UNIV2LINKETH-A': { title: 'UNIV2LINKETH LP', ilk: 'UNIV2LINKETH-A', symbol: 'UNIV2LINKETH', + tokenName: 'UNIV2LINKETH', decimals: 18, + contracts: { + token: 'UNIV2LINKETH', + pip: 'PIP_UNIV2LINKETH', + join: 'MCD_JOIN_UNIV2LINKETH_A', + clip: 'MCD_CLIP_UNIV2LINKETH_A', + calc: 'MCD_CLIP_CALC_UNIV2LINKETH_A', + }, exchanges: { 'Uniswap Token V2': { callee: 'UniswapV2LpTokenCalleeDai', @@ -680,13 +960,21 @@ const COLLATERALS: Record = { token1: 'ETH', }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'UNIV2UNIETH-A': { title: 'UNIV2UNIETH LP', ilk: 'UNIV2UNIETH-A', symbol: 'UNIV2UNIETH', + tokenName: 'UNIV2UNIETH', decimals: 18, + contracts: { + token: 'UNIV2UNIETH', + pip: 'PIP_UNIV2UNIETH', + join: 'MCD_JOIN_UNIV2UNIETH_A', + clip: 'MCD_CLIP_UNIV2UNIETH_A', + calc: 'MCD_CLIP_CALC_UNIV2UNIETH_A', + }, exchanges: { 'Uniswap Token V2': { callee: 'UniswapV2LpTokenCalleeDai', @@ -694,13 +982,21 @@ const COLLATERALS: Record = { token1: 'ETH', }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'UNIV2AAVEETH-A': { title: 'UNIV2AAVEETH LP', ilk: 'UNIV2AAVEETH-A', symbol: 'UNIV2AAVEETH', + tokenName: 'UNIV2AAVEETH', decimals: 18, + contracts: { + token: 'UNIV2AAVEETH', + pip: 'PIP_UNIV2AAVEETH', + join: 'MCD_JOIN_UNIV2AAVEETH_A', + clip: 'MCD_CLIP_UNIV2AAVEETH_A', + calc: 'MCD_CLIP_CALC_UNIV2AAVEETH_A', + }, exchanges: { 'Uniswap Token V2': { callee: 'UniswapV2LpTokenCalleeDai', @@ -708,13 +1004,21 @@ const COLLATERALS: Record = { token1: 'ETH', }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'UNIV2DAIUSDT-A': { title: 'UNIV2DAIUSDT LP', ilk: 'UNIV2DAIUSDT-A', symbol: 'UNIV2DAIUSDT', + tokenName: 'UNIV2DAIUSDT', decimals: 18, + contracts: { + token: 'UNIV2DAIUSDT', + pip: 'PIP_UNIV2DAIUSDT', + join: 'MCD_JOIN_UNIV2DAIUSDT_A', + clip: 'MCD_CLIP_UNIV2DAIUSDT_A', + calc: 'MCD_CLIP_CALC_UNIV2DAIUSDT_A', + }, exchanges: { 'Uniswap Token V2': { callee: 'UniswapV2LpTokenCalleeDai', @@ -722,13 +1026,21 @@ const COLLATERALS: Record = { token1: 'USDT', }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'UNIV2DAIUSDC-A': { title: 'UNIV2DAIUSDC LP', ilk: 'UNIV2DAIUSDC-A', symbol: 'UNIV2DAIUSDC', + tokenName: 'UNIV2DAIUSDC', decimals: 18, + contracts: { + token: 'UNIV2DAIUSDC', + pip: 'PIP_UNIV2DAIUSDC', + join: 'MCD_JOIN_UNIV2DAIUSDC_A', + clip: 'MCD_CLIP_UNIV2DAIUSDC_A', + calc: 'MCD_CLIP_CALC_UNIV2DAIUSDC_A', + }, exchanges: { 'Uniswap Token V2': { callee: 'UniswapV2LpTokenCalleeDai', @@ -736,20 +1048,48 @@ const COLLATERALS: Record = { token1: 'USDC', }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, 'RETH-A': { title: 'Rocket Pool ETH', ilk: 'RETH-A', symbol: 'RETH', + tokenName: 'RETH', decimals: 18, + contracts: { + token: 'RETH', + pip: 'PIP_RETH', + join: 'MCD_JOIN_RETH_A', + clip: 'MCD_CLIP_RETH_A', + calc: 'MCD_CLIP_CALC_RETH_A', + }, exchanges: { 'Curve rETH V3': { callee: 'rETHCurveUniv3Callee', - route: ['ETH'], + route: ['RETH', 'ETH', 'DAI'], + }, + }, + oracle: ORACLE_WITH_DELAY, + }, + 'LSE-MKR-A': { + title: 'Lockstake MKR', + ilk: 'LSE-MKR-A', + symbol: 'MCD_GOV', + tokenName: 'MKR', + decimals: 18, + contracts: { + token: 'MCD_GOV', + pip: 'PIP_MKR', + clip: 'LOCKSTAKE_CLIP', + calc: 'LOCKSTAKE_CLIP_CALC', + }, + exchanges: { + 'Uniswap V2': { + callee: 'UniswapV2LockstakeCallee', + route: ['SKY', 'USDS'], }, }, - oracle: CONFIG_WITH_NEXT_PRICE, + oracle: ORACLE_WITH_DELAY, }, }; diff --git a/core/src/contracts.ts b/core/src/contracts.ts index 8f9e2e079..23d969b8a 100644 --- a/core/src/contracts.ts +++ b/core/src/contracts.ts @@ -6,6 +6,7 @@ import getSigner from './signer'; import BigNumber from '../src/bignumber'; import { RAD_NUMBER_OF_DIGITS, RAY_NUMBER_OF_DIGITS, WAD_NUMBER_OF_DIGITS } from '../src/constants/UNITS'; import { fetchContractAddressByNetwork } from './addresses'; +import { getCollateralConfigByType } from './constants/COLLATERALS'; import MCD_DAI from './abis/MCD_DAI.json'; import MCD_VAT from './abis/MCD_VAT.json'; import MCD_JOIN_DAI from './abis/MCD_JOIN_DAI.json'; @@ -33,17 +34,19 @@ import MCD_ADM from './abis/MCD_ADM.json'; import PROXY_FACTORY from './abis/PROXY_FACTORY.json'; import PROXY_ACTIONS from './abis/PROXY_ACTIONS.json'; import MCD_PAUSE from './abis/MCD_PAUSE.json'; +import LOCKSTAKE_ENGINE from './abis/LOCKSTAKE_ENGINE.json'; +import LOCKSTAKE_CLIP from './abis/LOCKSTAKE_CLIP.json'; const ERC20_SYMBOL_CALL_CACHE_TIME_MS = 1000 * 60 * 60 * 24; // 1 day export const getClipperNameByCollateralType = function (collateralType: string): string { - const suffix = collateralType.toUpperCase().replace('-', '_'); - return `MCD_CLIP_${suffix}`; + const config = getCollateralConfigByType(collateralType); + return config.contracts.clip; }; -export const getJoinNameByCollateralType = function (collateralType: string): string { - const suffix = collateralType.toUpperCase().replace('-', '_'); - return `MCD_JOIN_${suffix}`; +export const getJoinNameByCollateralType = function (collateralType: string): string | undefined { + const config = getCollateralConfigByType(collateralType); + return config.contracts.join; }; export const getContractAddressByName = async function (network: string, contractName: string): Promise { @@ -74,6 +77,8 @@ export const getContractInterfaceByName = async function (contractName: string): MCD_ADM, PROXY_FACTORY, MCD_PAUSE, + LOCKSTAKE_ENGINE, + LOCKSTAKE_CLIP, }; if (Object.keys(ABIs).includes(contractName)) { return ABIs[contractName]; @@ -81,7 +86,7 @@ export const getContractInterfaceByName = async function (contractName: string): if (contractName.startsWith('MCD_JOIN_')) { return MCD_JOIN; } - if (contractName.startsWith('MCD_CLIP_CALC_')) { + if (contractName.startsWith('MCD_CLIP_CALC_') || contractName.endsWith('_CLIP_CALC')) { return MCD_CLIP_CALC; } if (contractName.startsWith('MCD_CLIP_')) { @@ -101,12 +106,6 @@ const _getContract = async function (network: string, contractName: string, useS return contract; }; -export const getErc20Contract = async function (network: string, contractAddress: string, useSigner = false) { - const signerOrProvider = useSigner ? await getSigner(network) : await getProvider(network); - const contract = await new ethers.Contract(contractAddress, ERC20, signerOrProvider); - return contract; -}; - const getContract = memoizee(_getContract, { promise: true, length: 3, @@ -136,6 +135,12 @@ export const getContractValue = async function ( return new BigNumber(variableHex._hex).shiftedBy(-decimals); }; +export const getErc20Contract = async function (network: string, contractAddress: string, useSigner = false) { + const signerOrProvider = useSigner ? await getSigner(network) : await getProvider(network); + const contract = await new ethers.Contract(contractAddress, ERC20, signerOrProvider); + return contract; +}; + const _getErc20SymbolByAddress = async function (network: string, address: string): Promise { const contract = await getErc20Contract(network, address); return await contract.symbol(); diff --git a/core/src/fetch.ts b/core/src/fetch.ts index e71617159..20ebb5aa8 100644 --- a/core/src/fetch.ts +++ b/core/src/fetch.ts @@ -41,8 +41,8 @@ export const fetchAuctionStatus = async function ( collateralType: string, auctionIndex: number ): Promise { - const contract = await getContract(network, getClipperNameByCollateralType(collateralType)); - const statusData = await contract.getStatus(convertNumberTo32Bytes(auctionIndex)); + const clipper = await getContract(network, getClipperNameByCollateralType(collateralType)); + const statusData = await clipper.getStatus(convertNumberTo32Bytes(auctionIndex)); const unitPrice = new BigNumber(statusData.price._hex).div(RAY); const collateralAmount = new BigNumber(statusData.lot._hex).div(WAD); const debtDAI = new BigNumber(statusData.tab._hex).div(RAD); @@ -64,28 +64,27 @@ export const fetchAuctionByCollateralTypeAndAuctionIndex = async function ( const contract = await getContract(network, getClipperNameByCollateralType(collateralType)); const auctionData = await contract.sales(auctionIndex); const startUnixTimestamp = new BigNumber(auctionData.tic._hex).toNumber(); + const collateralConfig = getCollateralConfigByType(collateralType); if (startUnixTimestamp === 0) { throw new Error('No active auction found with this id'); } - const startTimestamp = new BigNumber(auctionData.tic._hex).times(1000).toNumber(); - const endDate = new Date(startTimestamp + maximumAuctionDurationInSeconds * 1000); - const fetchedAt = new Date(); return { network, id: `${collateralType}:${auctionIndex}`, index: auctionIndex, collateralType, - collateralSymbol: getCollateralConfigByType(collateralType).symbol, + collateralSymbol: collateralConfig.symbol, + tokenName: collateralConfig.tokenName, collateralAmount: new BigNumber(auctionData.lot._hex).div(WAD), initialPrice: new BigNumber(auctionData.top._hex).div(RAY), vaultAddress: auctionData.usr, debtDAI: new BigNumber(auctionData.tab._hex).div(RAD), - startDate: new Date(startTimestamp), - endDate, + startDate: new Date(startUnixTimestamp * 1000), + endDate: new Date((startUnixTimestamp + maximumAuctionDurationInSeconds) * 1000), isActive: true, isFinished: false, isRestarting: false, - fetchedAt, + fetchedAt: new Date(), }; }; diff --git a/core/src/getCollateralPriceOracleConfig.ts b/core/src/getCollateralPriceOracleConfig.ts deleted file mode 100644 index fe84ae849..000000000 --- a/core/src/getCollateralPriceOracleConfig.ts +++ /dev/null @@ -1,160 +0,0 @@ -import prompts from 'prompts'; -import { getContractAddressByName } from './contracts'; -import contractCollateralToOracleInterface from './abis/MCD_SPOT.json'; -import contractOracleInterface from './abis/OSM.json'; -import { ethers } from 'ethers'; -import BigNumber from './bignumber'; -import { HARDHAT_PUBLIC_KEY, TEST_NETWORK } from '../helpers/constants'; -import { CONFIG_WITH_NEXT_PRICE, CONFIG_WITHOUT_NEXT_PRICE } from './constants/COLLATERALS'; -import { CollateralPriceSourceConfig, CollateralType } from './types'; -import getSigner from './signer'; -import keypress from '../helpers/keypress'; -import { resetNetworkAndSetupWallet } from '../helpers/hardhat/network'; -import { generateMappingSlotAddress, overwriteUintValueInAddress } from '../helpers/hardhat/slotOverwrite'; - -const choicesYesNo = [ - { title: 'yes', value: true }, - { title: 'no', value: false }, -]; -const choicesNetwork = [{ title: 'custom', value: 'custom' }]; - -const promptCollateralType = async () => { - const { collateralType }: { collateralType: CollateralType } = await prompts([ - { - type: 'text', - name: 'collateralType', - message: 'State the collateral type. E.g. ETH-A', - choices: choicesNetwork, - }, - ]); - return collateralType; -}; - -const promptBasicInformation = async () => { - const { hasNextPrice }: { hasNextPrice: string } = await prompts([ - { - type: 'select', - name: 'hasNextPrice', - choices: choicesYesNo, - message: 'Does contract provide future price?', - }, - ]); - return { - hasNextPrice, - }; -}; - -const getOracleAddressAndContract = async (collateralType: string) => { - const signer = await getSigner(TEST_NETWORK); - const contractOracleMapAdderss = await getContractAddressByName(TEST_NETWORK, 'MCD_SPOT'); - const contractOracleMap = new ethers.Contract( - contractOracleMapAdderss, - contractCollateralToOracleInterface, - signer - ); - const address: string = (await contractOracleMap.ilks(ethers.utils.formatBytes32String(collateralType))).pip; - const contract = new ethers.Contract(address, contractOracleInterface, signer); - return { contract, address }; -}; - -const overwriteValue = async (contractAddress: string, newValue: BigNumber, slot: string) => { - await overwriteUintValueInAddress(contractAddress, slot, newValue); -}; - -const callContractFunctionOrThrow = async (contract: ethers.Contract, functionName: string, ...args: any[]) => { - try { - return await contract[functionName](...args); - } catch (e) { - throw new Error(`Failed to run the function ${functionName}: ${e}`); - } -}; - -const callFunction = async (contract: ethers.Contract, functionName: string, ...args: any[]): Promise => { - const returnedValueHex = await callContractFunctionOrThrow(contract, functionName, ...args); - return returnedValueHex; -}; - -const addToWhitelist = async (contractAddress: string, whitelistSlot: number) => { - const slotAddress = generateMappingSlotAddress(`0x${whitelistSlot.toString(16)}`, HARDHAT_PUBLIC_KEY); - await overwriteUintValueInAddress(contractAddress, slotAddress, new BigNumber(1)); -}; - -const runOverwriteStep = async ( - contract: ethers.Contract, - address: string, - functionName: string, - slot: string, - valueIndex?: number, - valueToWrite?: BigNumber -) => { - const previousValueRaw = await callFunction(contract, functionName); - const previousValue = new BigNumber(valueIndex !== undefined ? previousValueRaw[valueIndex] : previousValueRaw); - await overwriteValue(address, valueToWrite || previousValue.plus(1), slot); - const currentValueRaw = await callFunction(contract, functionName); - const currentValue = new BigNumber(valueIndex !== undefined ? currentValueRaw[valueIndex] : currentValueRaw); - const isUnchanged = previousValue.eq(currentValue); - if (isUnchanged) { - throw new Error('Slot was not specified correctly.'); - } -}; - -const validateConfigWithNextPriceIsValid = async (contract: ethers.Contract, address: string) => { - const priceValiditySlotValue = await contract.provider.getStorageAt( - address, - CONFIG_WITH_NEXT_PRICE.currentPriceSlotAddress - ); - if (priceValiditySlotValue[CONFIG_WITH_NEXT_PRICE.slotPriceValueBeginsAtPosition - 1] !== '1') { - throw new Error('Failed to discover the price validity boolean position'); - } - await runOverwriteStep(contract, address, 'peek', CONFIG_WITH_NEXT_PRICE.currentPriceSlotAddress, 0); - await runOverwriteStep(contract, address, 'peep', CONFIG_WITH_NEXT_PRICE.nextPriceSlotAddress, 0); -}; - -const validateConfigWithoutNextPriceIsValid = async (contract: ethers.Contract, address: string) => { - const valueAtSlot = await contract.provider.getStorageAt( - address, - CONFIG_WITHOUT_NEXT_PRICE.currentPriceValiditySlotAndOffset.slot - ); - if (valueAtSlot[CONFIG_WITHOUT_NEXT_PRICE.currentPriceValiditySlotAndOffset.offset] !== '1') { - throw new Error('Failed to discover the price validity boolean position'); - } - await runOverwriteStep( - contract, - address, - 'peek', - CONFIG_WITHOUT_NEXT_PRICE.currentPriceSlotAddress, - CONFIG_WITHOUT_NEXT_PRICE.slotPriceValueBeginsAtPosition - ); -}; - -const run = async () => { - await keypress('Press enter to start collateral config generation'); - console.info('Resetting network and setting the wallet up...'); - await resetNetworkAndSetupWallet(); - const basicInfo = await promptBasicInformation(); - const config: CollateralPriceSourceConfig = basicInfo.hasNextPrice - ? CONFIG_WITH_NEXT_PRICE - : CONFIG_WITHOUT_NEXT_PRICE; - console.info(`Config ${basicInfo.hasNextPrice ? 'with' : 'without'} next price is chosen.`); - const collateralType = await promptCollateralType(); - const { contract, address } = await getOracleAddressAndContract(collateralType); - console.info(`Price oracle contract address: ${address}`); - if (basicInfo.hasNextPrice) { - console.info('Whitelisting the wallet address...'); - const whitelistSlot = 5; - const whitelistFunction = 'bud'; - await addToWhitelist(address, whitelistSlot); - const isWhitelisted = await callFunction(contract, whitelistFunction, HARDHAT_PUBLIC_KEY); - if (isWhitelisted === '0x00') { - throw new Error('Failed to whitelist the wallet on the fork'); - } - } - console.info('Validating the correctness of the config...'); - if (basicInfo.hasNextPrice) { - await validateConfigWithNextPriceIsValid(contract, address); - } else { - await validateConfigWithoutNextPriceIsValid(contract, address); - } - console.info(`The config is validated: \n ${JSON.stringify(config)}`); -}; -run(); diff --git a/core/src/oracles.ts b/core/src/oracles.ts index 47cc79941..a4a04f6fe 100644 --- a/core/src/oracles.ts +++ b/core/src/oracles.ts @@ -18,7 +18,7 @@ const CACHE_EXPIRY_MS = 60 * 1000; export const getOracleAddressByCollateralType = async function (network: string, collateralType: string) { const collateralConfig = getCollateralConfigByType(collateralType); - return await getContractAddressByName(network, `PIP_${collateralConfig.symbol}`); + return await getContractAddressByName(network, collateralConfig.contracts.pip); }; const getOraclePriceSameSlotValidity = async ( diff --git a/core/src/params.ts b/core/src/params.ts index 84435060e..ca2f5fb9f 100644 --- a/core/src/params.ts +++ b/core/src/params.ts @@ -6,12 +6,13 @@ import getProvider from './provider'; import MCD_CLIP_CALC from './abis/MCD_CLIP_CALC.json'; import { RAY_NUMBER_OF_DIGITS } from './constants/UNITS'; import { fetchContractAddressByNetwork, getSupportedCollateralTypes } from './addresses'; +import { getCollateralConfigByType } from './constants/COLLATERALS'; const PARAMS_CACHE = 24 * 60 * 60 * 1000; const getCalcAddressByCollateralType = async function (network: string, collateralType: string): Promise { - const suffix = collateralType.replace('-', '_'); - const calcAddress = await fetchContractAddressByNetwork(network, `MCD_CLIP_CALC_${suffix}`); + const config = getCollateralConfigByType(collateralType); + const calcAddress = await fetchContractAddressByNetwork(network, config.contracts.calc); if (!calcAddress) { throw new Error(`"${collateralType}" contract is not found on the "${network}" network`); } diff --git a/core/src/tokens.ts b/core/src/tokens.ts index fa7d12aa5..08dd0105a 100644 --- a/core/src/tokens.ts +++ b/core/src/tokens.ts @@ -22,9 +22,15 @@ export const getTokenDecimalsBySymbol = function (symbol: string): number { if (tokenName === 'DAI') { return DAI_NUMBER_OF_DIGITS; } + if (tokenName === 'USDS') { + return DAI_NUMBER_OF_DIGITS; + } if (tokenName === 'MKR') { return MKR_NUMBER_OF_DIGITS; } + if (tokenName === 'SKY') { + return MKR_NUMBER_OF_DIGITS; + } const collateral = getCollateralConfigBySymbol(tokenName); return collateral && collateral.decimals; }; diff --git a/core/src/types.ts b/core/src/types.ts index d6c3944f3..cbbf2d98f 100644 --- a/core/src/types.ts +++ b/core/src/types.ts @@ -12,6 +12,7 @@ export declare interface AuctionInitialInfo { index: number; collateralType: string; collateralSymbol: string; + tokenName: string; collateralAmount: BigNumber; debtDAI: BigNumber; startDate: Date; @@ -84,7 +85,8 @@ export declare interface RegularCalleeConfig { | 'WstETHCurveUniv3Callee' | 'CurveLpTokenUniv3Callee' | 'UniswapV3Callee' - | 'rETHCurveUniv3Callee'; + | 'rETHCurveUniv3Callee' + | 'UniswapV2LockstakeCallee'; route: string[]; } @@ -148,6 +150,14 @@ export declare interface ValueSlotAddressAndOffset { offset: number; } +export declare interface CollateralAddresses { + token: string; + join?: string; + pip: string; + clip: string; + calc: string; +} + export type CalleeConfig = | RegularCalleeConfig | AutoRouterCalleeConfig @@ -157,9 +167,11 @@ export declare interface CollateralConfig { title: string; ilk: string; symbol: string; + tokenName: string; decimals: number; exchanges: Record; oracle: CollateralPriceSourceConfig; + contracts: CollateralAddresses; } interface OracleConfigBase { @@ -198,6 +210,7 @@ export declare interface CalleeAddresses { UniswapV3Callee?: string; rETHCurveUniv3Callee?: string; OneInchCallee?: string; + UniswapV2LockstakeCallee?: string; } export type CalleeNames = keyof CalleeAddresses; @@ -255,6 +268,7 @@ export declare interface WalletBalances { export declare interface CollateralStatus { type: string; symbol: string; + tokenName: string; isAuthorized: boolean; isAuthorizing: boolean; isDepositingOrWithdrawing: boolean; diff --git a/core/src/vaults.ts b/core/src/vaults.ts index 6b6df3770..fea5fbe0d 100644 --- a/core/src/vaults.ts +++ b/core/src/vaults.ts @@ -103,9 +103,9 @@ export const fetchVaultCollateralParameters = async ( network: string, collateralType: CollateralType ): Promise => { - const contract = await getContract(network, 'MCD_VAT'); + const vat = await getContract(network, 'MCD_VAT'); const typeHex = ethers.utils.formatBytes32String(collateralType); - const { rate, spot } = await contract.ilks(typeHex); + const { rate, spot } = await vat.ilks(typeHex); return { stabilityFeeRate: new BigNumber(rate._hex).shiftedBy(-RAY_NUMBER_OF_DIGITS), minUnitPrice: new BigNumber(spot._hex).shiftedBy(-RAY_NUMBER_OF_DIGITS), @@ -117,9 +117,9 @@ export const fetchVaultAmount = async ( collateralType: CollateralType, vaultAddress: string ): Promise => { - const contract = await getContract(network, 'MCD_VAT'); + const vat = await getContract(network, 'MCD_VAT'); const typeHex = ethers.utils.formatBytes32String(collateralType); - const { ink, art } = await contract.urns(typeHex, vaultAddress); + const { ink, art } = await vat.urns(typeHex, vaultAddress); return { initialDebtDai: new BigNumber(art._hex).shiftedBy(-DAI_NUMBER_OF_DIGITS), collateralAmount: new BigNumber(ink._hex).shiftedBy(-WAD_NUMBER_OF_DIGITS), @@ -401,36 +401,39 @@ export const openVaultWithProxiedContractAndDrawDebt = async ( const proxyAddress = existingProxyAddress ? existingProxyAddress : await createProxyAndGiveAllowance(network, collateralType, collateralAmount); - const signer = await getSigner(network); const proxyContract = new ethers.Contract(proxyAddress, DS_PROXY, signer); const method = getMethodSignature('openLockGemAndDraw(address,address,address,bytes32,uint256,uint256)'); const jugContractAddress = await getContractAddressByName(network, 'MCD_JUG'); - const joinContractCollateralAddress = await getContractAddressByName( - network, - getJoinNameByCollateralType(collateralType) - ); const joinContractDaiAddress = await getContractAddressByName(network, 'MCD_JOIN_DAI'); - const args = [ - jugContractAddress, - joinContractCollateralAddress, - joinContractDaiAddress, - ethers.utils.formatBytes32String(collateralType), - collateralAmount.shiftedBy(WAD_NUMBER_OF_DIGITS).toFixed(0), - debtAmountDai.shiftedBy(DAI_NUMBER_OF_DIGITS).toFixed(0), - ]; - const typesArray = ['address', 'address', 'address', 'bytes32', 'uint256', 'uint256']; - const encodedArgs = ethers.utils.defaultAbiCoder.encode(typesArray, args); - const transactionData = method + encodedArgs.substring(2); - const target = (await getContract(network, `PROXY_ACTIONS_${proxyType}`)).address; - const transaction = await proxyContract['execute(address,bytes)'](target, transactionData); - const events = await extractEventFromTransaction( - transaction, - 'NewCdpRegistered(address,address,uint256)', - new ethers.utils.Interface(CDP_REGISTRY) - ); - const vaultId = events[0].args.cdp.toNumber(); - return vaultId; + const joinName = getJoinNameByCollateralType(collateralType); + if (joinName) { + const joinContractCollateralAddress = await getContractAddressByName(network, joinName); + const encodedArgs = ethers.utils.defaultAbiCoder.encode( + ['address', 'address', 'address', 'bytes32', 'uint256', 'uint256'], + [ + jugContractAddress, + joinContractCollateralAddress, + joinContractDaiAddress, + ethers.utils.formatBytes32String(collateralType), + collateralAmount.shiftedBy(WAD_NUMBER_OF_DIGITS).toFixed(0), + debtAmountDai.shiftedBy(DAI_NUMBER_OF_DIGITS).toFixed(0), + ] + ); + const transactionData = method + encodedArgs.substring(2); + const target = (await getContract(network, `PROXY_ACTIONS_${proxyType}`)).address; + const transaction = await proxyContract['execute(address,bytes)'](target, transactionData); + const events = await extractEventFromTransaction( + transaction, + 'NewCdpRegistered(address,address,uint256)', + new ethers.utils.Interface(CDP_REGISTRY) + ); + const vaultIndex = events[0].args.cdp.toNumber(); + const vault = await fetchVault(network, vaultIndex); + return { vaultIndex, vaultAddress: vault.address }; + } else { + throw new Error('openVaultWithProxiedContractAndDrawDebt: joinless vault creation is not yet implemented'); + } }; export const openVault = async (network: string, ownerAddress: string, collateralType: CollateralType) => { diff --git a/core/src/wallet.ts b/core/src/wallet.ts index 101bb1844..4b33bd13e 100644 --- a/core/src/wallet.ts +++ b/core/src/wallet.ts @@ -103,8 +103,11 @@ export const withdrawCollateralFromVat = async function ( const withdrawalAmount = amount || (await fetchCollateralVatBalance(network, walletAddress, collateralType)); const decimals = COLLATERALS[collateralType].decimals; const withdrawalAmountRounded = withdrawalAmount.shiftedBy(decimals).toFixed(0, BigNumber.ROUND_DOWN); - const contractName = getJoinNameByCollateralType(collateralType); - await executeTransaction(network, contractName, 'exit', [walletAddress, withdrawalAmountRounded], { + const joinName = getJoinNameByCollateralType(collateralType); + if (!joinName) { + throw new Error(`Collateral "${collateralType}" does not store tokens inside vat`); + } + await executeTransaction(network, joinName, 'exit', [walletAddress, withdrawalAmountRounded], { notifier, confirmTransaction: true, }); @@ -120,8 +123,11 @@ export const depositCollateralToVat = async function ( console.info(`Deposit ${amount.toFixed(2)} ${collateralType} to the VAT`); const collateralConfig = getCollateralConfigByType(collateralType); const depositRounded = amount.shiftedBy(collateralConfig.decimals).toFixed(0, BigNumber.ROUND_DOWN); - const contractName = getJoinNameByCollateralType(collateralType); - await executeTransaction(network, contractName, 'join', [walletAddress, depositRounded], { + const joinName = getJoinNameByCollateralType(collateralType); + if (!joinName) { + throw new Error(`Collateral "${collateralType}" can not deposit tokens into vat`); + } + await executeTransaction(network, joinName, 'join', [walletAddress, depositRounded], { notifier, confirmTransaction: true, }); diff --git a/core/test/vault-test.ts b/core/test/vault-test.ts index df9b8a6c5..4796644c1 100644 --- a/core/test/vault-test.ts +++ b/core/test/vault-test.ts @@ -23,6 +23,7 @@ import createVaultWithCollateral, { getLiquidatableCollateralTypes, } from '../simulations/helpers/createVaultWithCollateral'; import { NULL_ADDRESS } from '../src/constants/UNITS'; +import { overwriteCurrentOraclePrice } from '../helpers/hardhat/overwrites'; chai.use(deepEqualInAnyOrder); const compareVaultTransactionsNotLiquidated = ( @@ -204,7 +205,7 @@ describe('Vaults', () => { describe('Sound values are extracted', () => { before(async () => { await setupRpcUrlAndGetNetworks(LOCAL_RPC_URL); - await resetNetwork(15533000); + await resetNetwork(21019453); }); const expectedReturn: Record< CollateralType, @@ -213,22 +214,22 @@ describe('Sound values are extracted', () => { 'AAVE-A': { nextUnitPrice: '150.79237464128204', currentUnitPrice: '150.79237464128204', - nextPriceChange: new Date('2022-01-23T22:00:00.000Z'), + nextPriceChange: new Date('2024-01-30T01:00:00.000Z'), }, 'BAL-A': { - currentUnitPrice: '11.80800329930906', + currentUnitPrice: '11.721140147069267', nextUnitPrice: '11.721140147069267', - nextPriceChange: new Date('2022-01-23T19:00:00.000Z'), + nextPriceChange: new Date('2024-01-30T01:00:00.000Z'), }, 'BAT-A': { nextUnitPrice: '0.73', - currentUnitPrice: '0.71910538308', - nextPriceChange: new Date('2022-01-23T19:00:00.000Z'), + currentUnitPrice: '0.73', + nextPriceChange: new Date('2024-01-30T01:00:00.000Z'), }, 'COMP-A': { nextUnitPrice: '124.5640565401381', currentUnitPrice: '124.5640565401381', - nextPriceChange: new Date('2022-01-23T22:00:00.000Z'), + nextPriceChange: new Date('2024-01-30T01:00:00.000Z'), }, 'ETH-A': { nextUnitPrice: '1712.2106886', @@ -236,9 +237,9 @@ describe('Sound values are extracted', () => { nextPriceChange: new Date('2022-09-09T10:00:00.000Z'), }, 'ETH-B': { - nextUnitPrice: '1602.1803800999999', - currentUnitPrice: '1602.1803800999999', - nextPriceChange: new Date('2022-09-14T13:00:00.000Z'), + nextUnitPrice: '2643.19', + currentUnitPrice: '2643.19', + nextPriceChange: new Date('2024-10-22T07:00:00.000Z'), }, 'ETH-C': { nextUnitPrice: '1208.0159951', @@ -251,24 +252,24 @@ describe('Sound values are extracted', () => { nextPriceChange: new Date(NaN), }, 'KNC-A': { - nextUnitPrice: '1.5108', - currentUnitPrice: '1.4869', - nextPriceChange: new Date('2021-10-28T05:00:00.000Z'), + nextUnitPrice: '1.8601360490999999', + currentUnitPrice: '1.8601360490999999', + nextPriceChange: new Date('2023-04-06T19:00:00.000Z'), }, 'LINK-A': { - nextUnitPrice: '7.2245', - currentUnitPrice: '7.159627035', - nextPriceChange: new Date('2022-09-14T13:00:00.000Z'), + nextUnitPrice: '14.56509', + currentUnitPrice: '7.2', + nextPriceChange: new Date('2024-01-30T01:00:00.000Z'), }, 'LRC-A': { nextUnitPrice: '0.8117493344000001', - currentUnitPrice: '0.7270649846852948', - nextPriceChange: new Date('2022-01-23T19:00:00.000Z'), + currentUnitPrice: '0.8117493344000001', + nextPriceChange: new Date('2024-01-30T01:00:00.000Z'), }, 'MANA-A': { - nextUnitPrice: '0.73075', - currentUnitPrice: '0.73075', - nextPriceChange: new Date('2022-09-14T13:00:00.000Z'), + nextUnitPrice: '0.36741132408456323', + currentUnitPrice: '0.36741132408456323', + nextPriceChange: new Date('2024-01-30T01:00:00.000Z'), }, 'PAXUSD-A': { nextUnitPrice: 'NaN', @@ -276,9 +277,9 @@ describe('Sound values are extracted', () => { nextPriceChange: new Date(NaN), }, 'RENBTC-A': { - nextUnitPrice: '20388.465', - currentUnitPrice: '20388.465', - nextPriceChange: new Date('2022-09-14T13:00:00.000Z'), + nextUnitPrice: '66992.455', + currentUnitPrice: '66992.455', + nextPriceChange: new Date('2024-10-22T07:00:00.000Z'), }, 'TUSD-A': { nextUnitPrice: 'NaN', @@ -286,9 +287,9 @@ describe('Sound values are extracted', () => { nextPriceChange: new Date(NaN), }, 'UNI-A': { - nextUnitPrice: '6.1622020819', - currentUnitPrice: '5.103855836', - nextPriceChange: new Date('2022-07-11T16:00:00.000Z'), + nextUnitPrice: '5.845563067104212', + currentUnitPrice: '5.845563067104212', + nextPriceChange: new Date('2024-01-30T01:00:00.000Z'), }, 'USDC-A': { nextUnitPrice: 'NaN', @@ -301,104 +302,109 @@ describe('Sound values are extracted', () => { nextPriceChange: new Date(NaN), }, 'USDT-A': { - nextUnitPrice: '1.0007712772150232', - currentUnitPrice: '1.0007712772150232', - nextPriceChange: new Date('2021-10-28T05:00:00.000Z'), + nextUnitPrice: '1.000413775900346', + currentUnitPrice: '1.000413775900346', + nextPriceChange: new Date('2023-04-06T19:00:00.000Z'), }, 'WBTC-A': { - nextUnitPrice: '20388.465', - currentUnitPrice: '20388.465', - nextPriceChange: new Date('2022-09-14T13:00:00.000Z'), + nextUnitPrice: '66992.455', + currentUnitPrice: '66992.455', + nextPriceChange: new Date('2024-10-22T07:00:00.000Z'), }, 'WBTC-B': { - nextUnitPrice: '20388.465', - currentUnitPrice: '20388.465', - nextPriceChange: new Date('2022-09-14T13:00:00.000Z'), + nextUnitPrice: '66992.455', + currentUnitPrice: '66992.455', + nextPriceChange: new Date('2024-10-22T07:00:00.000Z'), }, 'WBTC-C': { - nextUnitPrice: '20388.465', - currentUnitPrice: '20388.465', - nextPriceChange: new Date('2022-09-14T13:00:00.000Z'), + nextUnitPrice: '66992.455', + currentUnitPrice: '66992.455', + nextPriceChange: new Date('2024-10-22T07:00:00.000Z'), }, 'YFI-A': { - nextUnitPrice: '9150', - currentUnitPrice: '9185.8368', - nextPriceChange: new Date('2022-09-14T13:00:00.000Z'), + nextUnitPrice: '6274.912194791102', + currentUnitPrice: '6274.912194791102', + nextPriceChange: new Date('2024-01-30T01:00:00.000Z'), }, 'ZRX-A': { nextUnitPrice: '0.54008', - currentUnitPrice: '0.50224', - nextPriceChange: new Date('2022-01-23T19:00:00.000Z'), + currentUnitPrice: '0.54008', + nextPriceChange: new Date('2024-01-30T01:00:00.000Z'), }, 'MATIC-A': { - nextUnitPrice: '0.859281', - currentUnitPrice: '0.859281', - nextPriceChange: new Date('2022-09-14T13:00:00.000Z'), + nextUnitPrice: '0.6834569949358978', + currentUnitPrice: '0.6834569949358978', + nextPriceChange: new Date('2024-01-30T01:00:00.000Z'), }, 'WSTETH-A': { - nextUnitPrice: '1687.84305547955', - currentUnitPrice: '1687.84305547955', - nextPriceChange: new Date('2022-09-14T13:00:00.000Z'), + nextUnitPrice: '3095.153407214590908078', + currentUnitPrice: '3095.153407214590908078', + nextPriceChange: new Date('2024-10-22T07:00:00.000Z'), }, 'WSTETH-B': { - nextUnitPrice: '1687.84305547955', - currentUnitPrice: '1687.84305547955', - nextPriceChange: new Date('2022-09-14T13:00:00.000Z'), + nextUnitPrice: '3095.153407214590908078', + currentUnitPrice: '3095.153407214590908078', + nextPriceChange: new Date('2024-10-22T07:00:00.000Z'), }, 'CRVV1ETHSTETH-A': { - nextUnitPrice: '1633.547452878850724671', - currentUnitPrice: '1645.994835125363651071', - nextPriceChange: new Date('2022-09-14T12:01:47.000Z'), + nextUnitPrice: '2949.792480904069309162', + currentUnitPrice: '2898.680178316106908798', + nextPriceChange: new Date('2024-10-21T16:00:11.000Z'), }, 'UNIV2DAIETH-A': { - nextUnitPrice: '111.536694644490420563', - currentUnitPrice: '115.716445113160358543', - nextPriceChange: new Date('2022-07-13T17:00:03.000Z'), + nextUnitPrice: '180.027413016904332609', + currentUnitPrice: '156.864657339046759104', + nextPriceChange: new Date('2024-01-30T01:43:59.000Z'), }, 'UNIV2USDCETH-A': { - nextUnitPrice: '164916785.176105260363204747', - currentUnitPrice: '165534555.801045344024651968', - nextPriceChange: new Date('2022-09-14T12:01:47.000Z'), + nextUnitPrice: '217940294.382329574841183087', + currentUnitPrice: '188666866.181749331159918865', + nextPriceChange: new Date('2024-01-30T01:43:59.000Z'), }, 'UNIV2ETHUSDT-A': { - nextUnitPrice: '228116379.650719357480121186', - currentUnitPrice: '230190196.687438171219371005', - nextPriceChange: new Date('2021-10-28T04:01:15.000Z'), + nextUnitPrice: '197722335.963263628773031269', + currentUnitPrice: '228116379.650719357480121186', + nextPriceChange: new Date('2023-04-06T18:44:23.000Z'), }, 'UNIV2WBTCDAI-A': { - nextUnitPrice: '31892330.725591269457933196', - currentUnitPrice: '32196608.076982062517168305', - nextPriceChange: new Date('2022-07-11T16:09:40.000Z'), + nextUnitPrice: '47113092.314935817960683604', + currentUnitPrice: '37716763.676534507210426868', + nextPriceChange: new Date('2024-01-30T01:43:59.000Z'), }, 'UNIV2WBTCETH-A': { - nextUnitPrice: '1102874511.239936578199968093', - currentUnitPrice: '1175008223.21111667836061111', - nextPriceChange: new Date('2022-07-13T17:00:03.000Z'), + nextUnitPrice: '2517374355.748241170169672756', + currentUnitPrice: '1794044631.240253383841931772', + nextPriceChange: new Date('2024-01-30T01:43:59.000Z'), }, 'UNIV2LINKETH-A': { - nextUnitPrice: '567.672642511519510798', - currentUnitPrice: '566.984073123537837073', - nextPriceChange: new Date('2022-01-23T19:01:00.000Z'), + nextUnitPrice: '632.725670378659259608', + currentUnitPrice: '375.213195603332086751', + nextPriceChange: new Date('2024-01-30T01:43:59.000Z'), }, 'UNIV2UNIETH-A': { - nextUnitPrice: '368.441439820843276851', - currentUnitPrice: '327.117645258478197955', - nextPriceChange: new Date('2022-07-11T16:09:40.000Z'), + nextUnitPrice: '546.022046787969721817', + currentUnitPrice: '478.806858210117892322', + nextPriceChange: new Date('2024-01-30T01:43:59.000Z'), }, 'UNIV2AAVEETH-A': { - nextUnitPrice: '2503.767176345875098159', - currentUnitPrice: '2529.450702467409881126', - nextPriceChange: new Date('2021-11-22T23:04:46.000Z'), + nextUnitPrice: '1435.896796202841677092', + currentUnitPrice: '2503.767176345875098159', + nextPriceChange: new Date('2023-04-06T18:44:23.000Z'), }, 'UNIV2DAIUSDT-A': { - nextUnitPrice: '2274141.032812901837192665', - currentUnitPrice: '2274109.765292120125373109', - nextPriceChange: new Date('2021-10-28T04:01:15.000Z'), + nextUnitPrice: '2333803.165539906895807342', + currentUnitPrice: '2274141.032812901837192665', + nextPriceChange: new Date('2023-04-06T18:44:23.000Z'), }, 'UNIV2DAIUSDC-A': { - nextUnitPrice: '2252657.773463853818331742', - currentUnitPrice: '2252657.773463853818331742', - nextPriceChange: new Date('2022-09-14T12:01:47.000Z'), + nextUnitPrice: '2265919.675663883138436151', + currentUnitPrice: '2265919.675663883138436151', + nextPriceChange: new Date('2024-10-22T06:53:35.000Z'), + }, + 'LSE-MKR-A': { + nextUnitPrice: '1177.52796', + currentUnitPrice: '1177.52796', + nextPriceChange: new Date('2024-10-22T07:00:00.000Z'), }, }; for (const type of getLiquidatableCollateralTypes()) { @@ -420,42 +426,18 @@ describe('Sound values are extracted', () => { describe(`Collateral vault simulation liquidation `, () => { before(async () => { await createWalletForRpc(); - await resetNetwork(16066000); // TODO: remove hardcoded block number and investigate `Dog/not-unsafe` error + await resetNetwork(21019453); // TODO: remove hardcoded block number and investigate `Dog/not-unsafe` error }); for (const collateralType of getLiquidatableCollateralTypes()) { it(`runs the simulaton for ${collateralType}`, async () => { - let collateralOwned: BigNumber; + await overwriteCurrentOraclePrice(TEST_NETWORK, collateralType, new BigNumber(1000)); await adjustLimitsAndRates(collateralType); - try { - collateralOwned = await calculateMinCollateralAmountToOpenVault(collateralType); - } catch (e) { - if (e instanceof Error && e.message.endsWith('max debt set to zero')) { - return; - } - throw e; - } - let vaultId: number; - try { - vaultId = await createVaultWithCollateral(collateralType, collateralOwned); - } catch (e) { - if (e instanceof Error && e.message === 'Could not borrow dai because debt ceiling is exceeded.') { - return; - } - throw e; - } - - const vault = await fetchVault(TEST_NETWORK, vaultId); - expect(vault.collateralAmount.toFixed(0)).to.eq(collateralOwned.toFixed(0)); - - const previousStabilityFee = vault.stabilityFeeRate; + const collateralOwned = (await calculateMinCollateralAmountToOpenVault(collateralType)).multipliedBy(10); + const vaultAddress = (await createVaultWithCollateral(collateralType, collateralOwned)).vaultAddress; await warpTime(60 * 24 * 30 * 12 * 2, 60); - await collectStabilityFees(TEST_NETWORK, vault.collateralType); - const currentStabilityFee = (await fetchVault(TEST_NETWORK, vaultId)).stabilityFeeRate; - if (!currentStabilityFee.gt(previousStabilityFee)) { - throw new Error('Successful vault creation, but stability fees did not change after time warp'); - } - - await liquidateVault(TEST_NETWORK, vault.collateralType, vault.address); + await overwriteCurrentOraclePrice(TEST_NETWORK, collateralType, new BigNumber(500)); + await collectStabilityFees(TEST_NETWORK, collateralType); + await liquidateVault(TEST_NETWORK, collateralType, vaultAddress); }); } }); diff --git a/frontend/components/auction/collateral/CollateralAuction.vue b/frontend/components/auction/collateral/CollateralAuction.vue index a69f24c73..5392e2cac 100644 --- a/frontend/components/auction/collateral/CollateralAuction.vue +++ b/frontend/components/auction/collateral/CollateralAuction.vue @@ -27,10 +27,7 @@ Auction Amount - + @@ -39,7 +36,7 @@ Unknown @@ -50,7 +47,7 @@ Unknown @@ -119,7 +116,7 @@ @@ -170,14 +167,14 @@ @@ -379,7 +376,7 @@ export default Vue.extend({ typeof this.auction?.marketUnitPriceToUnitPriceRatio === 'undefined' ) { return `Swap transaction is not possible, - because we can't get value of ${this.auction?.collateralSymbol} on UniSwap`; + because we can't get value of ${this.auction?.tokenName} on UniSwap`; } return null; }, diff --git a/frontend/components/auction/collateral/CollateralAuctionBidBlock.vue b/frontend/components/auction/collateral/CollateralAuctionBidBlock.vue index 4c18935e7..837060717 100644 --- a/frontend/components/auction/collateral/CollateralAuctionBidBlock.vue +++ b/frontend/components/auction/collateral/CollateralAuctionBidBlock.vue @@ -20,7 +20,7 @@ Bid for diff --git a/frontend/components/auction/collateral/CollateralAuctionBidTransactionTable.vue b/frontend/components/auction/collateral/CollateralAuctionBidTransactionTable.vue index 2510247b5..4006fa154 100644 --- a/frontend/components/auction/collateral/CollateralAuctionBidTransactionTable.vue +++ b/frontend/components/auction/collateral/CollateralAuctionBidTransactionTable.vue @@ -11,7 +11,7 @@
@@ -21,7 +21,7 @@ Unknown @@ -94,11 +94,11 @@
Unknown - {{ auctionTransaction.collateralSymbol }} + {{ auctionTransaction.tokenName }}
diff --git a/frontend/components/auction/collateral/CollateralAuctionSwapTransactionTable.vue b/frontend/components/auction/collateral/CollateralAuctionSwapTransactionTable.vue index 912d1ceb2..73d67190a 100644 --- a/frontend/components/auction/collateral/CollateralAuctionSwapTransactionTable.vue +++ b/frontend/components/auction/collateral/CollateralAuctionSwapTransactionTable.vue @@ -9,7 +9,7 @@
@@ -17,14 +17,15 @@
Auction Price
- per - {{ auctionTransaction.collateralSymbol }} + per + {{ auctionTransaction.tokenName }}
@@ -49,7 +50,7 @@ v-if="transactionGrossProfit" show-sign :value="transactionGrossProfit" - currency="DAI" + :currency="profitToken" /> Unknown
@@ -71,7 +72,7 @@ Unknown @@ -83,7 +84,7 @@ v-if="transactionNetProfit" show-sign :value="transactionNetProfit" - currency="DAI" + :currency="profitToken" class="font-extrabold" /> Unknown @@ -133,6 +134,12 @@ export default Vue.extend({ }; }, computed: { + profitToken(): string { + const marketId = this.currentMarketId || this.auctionTransaction.suggestedMarketId; + const route = this.auctionTransaction.marketDataRecords[marketId].route || []; + const profitToken = route[route.length - 1]; + return profitToken || 'DAI'; + }, marketUnitPriceToUnitPriceRatio(): BigNumber | undefined { if (!this.currentMarketId || !this.auctionTransaction.marketDataRecords) { return this.auctionTransaction.marketUnitPriceToUnitPriceRatio; diff --git a/frontend/components/auction/collateral/CollateralAuctionsTable.vue b/frontend/components/auction/collateral/CollateralAuctionsTable.vue index ae5a54cc3..5dd7aa09e 100644 --- a/frontend/components/auction/collateral/CollateralAuctionsTable.vue +++ b/frontend/components/auction/collateral/CollateralAuctionsTable.vue @@ -15,13 +15,13 @@ class="AuctionsTable relative overflow-visible" >
- - + +
Unknown
diff --git a/frontend/components/auction/collateral/MarketPriceSelection.stories.js b/frontend/components/auction/collateral/MarketPriceSelection.stories.js index c866663ac..699d4e07a 100644 --- a/frontend/components/auction/collateral/MarketPriceSelection.stories.js +++ b/frontend/components/auction/collateral/MarketPriceSelection.stories.js @@ -12,7 +12,7 @@ const common = { auctionTransaction: fakeAuctionTransaction, marketId: '', }), - template: ``, + template: ``, }; storiesOf('Auction/Collateral/MarketPriceSelection', module).add('Default', () => ({ ...common })); diff --git a/frontend/components/auction/collateral/MarketPriceSelection.vue b/frontend/components/auction/collateral/MarketPriceSelection.vue index bb1f07544..5e1530e62 100644 --- a/frontend/components/auction/collateral/MarketPriceSelection.vue +++ b/frontend/components/auction/collateral/MarketPriceSelection.vue @@ -8,8 +8,8 @@ ({{ suggestionOrSelection }})
- per - {{ auctionTransaction.collateralSymbol }} + per + {{ auctionTransaction.tokenName }}
@@ -31,8 +31,9 @@ Select - per - {{ auctionTransaction.collateralSymbol }} + + per + {{ auctionTransaction.tokenName }}
@@ -87,6 +88,10 @@ export default Vue.extend({ type: Object as Vue.PropType, required: true, }, + profitToken: { + type: String, + required: true, + }, marketId: { type: String, default: '', @@ -138,8 +143,9 @@ export default Vue.extend({ if (!pools || !pools?.length) { return ''; } - const fullRoute = [...pools.map(pool => pool.routes[0]), 'DAI']; - return fullRoute.join(' → '); + const route = pools.map(pool => pool.routes[0]); + route.push(pools[pools.length - 1].routes[1]); + return route.join(' → '); }, getRouteFromMarketData(marketData: MarketData) { if (!marketData) { diff --git a/frontend/components/dashboard/CollateralTable.vue b/frontend/components/dashboard/CollateralTable.vue index 11940575a..bb46300cc 100644 --- a/frontend/components/dashboard/CollateralTable.vue +++ b/frontend/components/dashboard/CollateralTable.vue @@ -8,12 +8,12 @@ :pagination="{ pageSize: collateralAmount, hideOnSinglePage: true }" >
- +
{{ ilk }}
-
{{ symbol }}
+
{{ tokenName }}
@@ -144,7 +144,7 @@ export default Vue.extend({ }, { title: 'Currency', - dataIndex: 'symbol', + dataIndex: 'tokenName', scopedSlots: { customRender: 'symbol' }, }, { diff --git a/frontend/components/modals/ManageCollateralTable.vue b/frontend/components/modals/ManageCollateralTable.vue index 02fae22b1..8d395dc33 100644 --- a/frontend/components/modals/ManageCollateralTable.vue +++ b/frontend/components/modals/ManageCollateralTable.vue @@ -15,7 +15,7 @@
- + {{ collateralStatus.type }}
diff --git a/frontend/components/panels/WithdrawCollateralPanel.vue b/frontend/components/panels/WithdrawCollateralPanel.vue index 88c81871d..d265bfc0f 100644 --- a/frontend/components/panels/WithdrawCollateralPanel.vue +++ b/frontend/components/panels/WithdrawCollateralPanel.vue @@ -87,7 +87,7 @@ export default Vue.extend({ collateralSymbol(): string { try { const collateral = getCollateralConfigByType(this.collateralType); - return collateral.symbol; + return collateral.tokenName; } catch { return ''; } diff --git a/frontend/helpers/generateFakeAuction.ts b/frontend/helpers/generateFakeAuction.ts index 06fb3ee36..ba7854948 100644 --- a/frontend/helpers/generateFakeAuction.ts +++ b/frontend/helpers/generateFakeAuction.ts @@ -115,6 +115,7 @@ export const generateFakeAuction = function () { index, collateralType: collateralObject.ilk, collateralSymbol: collateralObject.symbol, + tokenName: collateralObject.tokenName, collateralAmount, totalPrice, debtDAI, diff --git a/frontend/helpers/generateFakeCollateral.ts b/frontend/helpers/generateFakeCollateral.ts index 07fa6637c..b46e8cf1a 100644 --- a/frontend/helpers/generateFakeCollateral.ts +++ b/frontend/helpers/generateFakeCollateral.ts @@ -27,6 +27,7 @@ export const generateFakeCollateralStatuses = function () { collateralStatuses.push({ type: collateral.ilk, symbol: collateral.symbol, + tokenName: collateral.tokenName, address: faker.finance.ethereumAddress(), isAuthorized: faker.datatype.boolean(), isAuthorizing: false, diff --git a/frontend/store/collaterals.ts b/frontend/store/collaterals.ts index c4ed17fde..fd3d345d1 100644 --- a/frontend/store/collaterals.ts +++ b/frontend/store/collaterals.ts @@ -22,6 +22,7 @@ const getInitialState = (): State => ({ ({ ilk: collateral.ilk, symbol: collateral.symbol, + tokenName: collateral.tokenName, } as CollateralRow) ), collateralStatusesStorage: {}, @@ -131,6 +132,7 @@ export const actions = { commit('setCollateralStatus', { type: collateral.ilk, symbol: collateral.symbol, + tokenName: collateral.tokenName, }); }, async fetchCollateralStatus( @@ -152,12 +154,14 @@ export const actions = { commit('setCollateralStatus', { type: collateral.ilk, symbol: collateral.symbol, + tokenName: collateral.tokenName, address: tokenAddress, }); } catch { commit('setCollateralStatus', { type: collateral.ilk, symbol: collateral.symbol, + tokenName: collateral.tokenName, address: null, }); }