Skip to content

Commit

Permalink
Support LSE collateral auctions (#622)
Browse files Browse the repository at this point in the history
  • Loading branch information
valiafetisov authored Oct 22, 2024
1 parent 19622c5 commit 02a0f1c
Show file tree
Hide file tree
Showing 50 changed files with 3,301 additions and 591 deletions.
70 changes: 70 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -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",
}
4 changes: 2 additions & 2 deletions bot/src/keepers/collateral.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion bot/src/notify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
19 changes: 6 additions & 13 deletions core/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 1 addition & 3 deletions core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions core/simulations/configs/onboardNewCollateral.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
},
},
Expand Down
2 changes: 1 addition & 1 deletion core/simulations/configs/specificBlockFork.ts
Original file line number Diff line number Diff line change
@@ -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 = {
Expand Down
94 changes: 62 additions & 32 deletions core/simulations/configs/vaultLiquidation.ts
Original file line number Diff line number Diff line change
@@ -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',
Expand All @@ -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;
},
},
Expand All @@ -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}`);
}
},
},
Expand Down
Loading

0 comments on commit 02a0f1c

Please sign in to comment.