Skip to content

Commit 02a0f1c

Browse files
authored
Support LSE collateral auctions (#622)
1 parent 19622c5 commit 02a0f1c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

50 files changed

+3301
-591
lines changed

.vscode/settings.json

+70
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
{
2+
// Disable the default formatter, use eslint instead
3+
"prettier.enable": false,
4+
"editor.formatOnSave": false,
5+
// Auto fix
6+
"editor.codeActionsOnSave": {
7+
"source.fixAll.eslint": "explicit",
8+
"source.organizeImports": "never"
9+
},
10+
// Silent the stylistic rules in you IDE, but still auto fix them
11+
"eslint.rules.customizations": [
12+
{
13+
"rule": "style/*",
14+
"severity": "off"
15+
},
16+
{
17+
"rule": "format/*",
18+
"severity": "off"
19+
},
20+
{
21+
"rule": "*-indent",
22+
"severity": "off"
23+
},
24+
{
25+
"rule": "*-spacing",
26+
"severity": "off"
27+
},
28+
{
29+
"rule": "*-spaces",
30+
"severity": "off"
31+
},
32+
{
33+
"rule": "*-order",
34+
"severity": "off"
35+
},
36+
{
37+
"rule": "*-dangle",
38+
"severity": "off"
39+
},
40+
{
41+
"rule": "*-newline",
42+
"severity": "off"
43+
},
44+
{
45+
"rule": "*quotes",
46+
"severity": "off"
47+
},
48+
{
49+
"rule": "*semi",
50+
"severity": "off"
51+
}
52+
],
53+
// Enable eslint for all supported languages
54+
"eslint.validate": [
55+
"javascript",
56+
"javascriptreact",
57+
"typescript",
58+
"typescriptreact",
59+
"vue",
60+
"html",
61+
"markdown",
62+
"json",
63+
"jsonc",
64+
"yaml",
65+
"toml",
66+
"gql",
67+
"graphql"
68+
],
69+
"typescript.tsdk": "node_modules/typescript/lib",
70+
}

bot/src/keepers/collateral.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,8 @@ const checkAndParticipateIfPossible = async function (network: string, auction:
131131
// display profit
132132
const postVatBalanceDai = await fetchVATbalanceDAI(network, walletAddress);
133133
const postErcBalanceDai = await fetchBalanceDAI(network, walletAddress);
134-
console.info(`DAI VAT profit from the transaction: ${postVatBalanceDai.minus(preVatBalanceDai).toFixed()}`);
135-
console.info(`DAI ERC profit from the transaction: ${postErcBalanceDai.minus(preErcBalanceDai).toFixed()}`);
134+
console.info(`DAI VAT profit from the transaction: ${postVatBalanceDai.minus(preVatBalanceDai).toFixed()}`);
135+
console.info(`DAI ERC20 profit from the transaction: ${postErcBalanceDai.minus(preErcBalanceDai).toFixed()}`);
136136
};
137137

138138
const participateInAuction = async function (network: string, auction: AuctionInitialInfo) {

bot/src/notify.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const generateNotificationTextCollateral = function (auction: AuctionInitialInfo
66
const url = `${process.env.FRONTEND_ORIGIN}/collateral/?network=${auction.network}&auction=${auction.id}`;
77
const formattedString = formatToAutomaticDecimalPointsString(auction.collateralAmount);
88

9-
return `Collateral auction with ${formattedString} ${auction.collateralSymbol} just started. Follow the link to participate: ${url}`;
9+
return `Collateral auction with ${formattedString} ${auction.tokenName} just started. Follow the link to participate: ${url}`;
1010
};
1111

1212
const generateNotificationTextDebt = function (auction: DebtAuctionActive): string {

core/README.md

+6-13
Original file line numberDiff line numberDiff line change
@@ -32,19 +32,12 @@ The process of adding new collaterals depends on the token type used. This is du
3232
5. Add new exchange file to the [`calleeFunctions` folder](./src/calleeFunctions)
3333
- The file should be named using the name from `1.`
3434
- The file should export `CalleeFunctions`
35-
- The file should be imported in the [`calleeFunctions/index.ts`](./src/calleeFunctions/index.ts)
36-
3. Adding price oracle configurations for the token:
37-
1. Get the source code of the price oracle contract:
38-
- 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.
39-
2. Read the contract and determine the slot address of the variable:
40-
- 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)
41-
- 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)
42-
3. Extend collateral config with the proper slot addresses.
43-
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.
44-
4. Run `npm run collateral:onboard` to run the script that helps to choose the oracle config.
45-
- when the script outputs the json with the config, add it to the `oracle` key of the collateral configuration in `COLLATERALS.ts`
46-
- 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.
47-
- Read more about the collateral oracle configurations at `./README.md#collateral-oracle-configs`
35+
6. Import exchange file inside [`calleeFunctions/index.ts`](./src/calleeFunctions/index.ts) and export under `allCalleeFunctions`
36+
37+
3. Adding price oracle configurations for the new collateral type:
38+
39+
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
40+
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`
4841

4942
### Onboarding not yet deployed collateral
5043

core/package.json

+1-3
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,7 @@
1212
"hardhat:silent": "npx hardhat node &> /dev/null",
1313
"hardhat": "npx hardhat node",
1414
"hardhat:simulations": "npx hardhat --network testnetwork --config local.hardhat.config.ts run ./simulations/index.ts",
15-
"simulate": "npm-run-all --parallel hardhat:silent hardhat:simulations",
16-
"collateral:validate": "npx hardhat --network testnetwork --config local.hardhat.config.ts run ./src/getCollateralPriceOracleConfig.ts",
17-
"collateral:onboard": "npm-run-all --parallel hardhat:silent collateral:validate"
15+
"simulate": "npm-run-all --parallel hardhat:silent hardhat:simulations"
1816
},
1917
"engines": {
2018
"node": ">=16.0.0",

core/simulations/configs/onboardNewCollateral.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,10 @@ const simulation: Simulation = {
5050
console.info(`New ${collateralType} oracle price is ${oraclePrice.toFixed()} DAI`);
5151
// create and liquidate vault
5252
const collateralOwned = await calculateMinCollateralAmountToOpenVault(collateralType);
53-
const vaultId = await createVaultWithCollateral(collateralType, collateralOwned);
53+
const { vaultIndex } = await createVaultWithCollateral(collateralType, collateralOwned);
5454
await warpTime(60 * 24 * 30, 60);
5555
await collectStabilityFees(TEST_NETWORK, collateralType);
56-
const vault = await fetchVault(TEST_NETWORK, vaultId);
56+
const vault = await fetchVault(TEST_NETWORK, vaultIndex);
5757
await liquidateVault(TEST_NETWORK, vault.collateralType, vault.address);
5858
},
5959
},

core/simulations/configs/specificBlockFork.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { addDaiToBalance, addMkrToBalance } from '../../helpers/hardhat/balance';
22
import { warpTime, resetNetworkAndSetupWallet } from '../../helpers/hardhat/network';
3-
import promptToGetBlockNumber from '../helpers/promptToGetBlockNumber';
3+
import { promptToGetBlockNumber } from '../helpers/promptToGetNumber';
44
import { Simulation } from '../types';
55

66
const simulation: Simulation = {

core/simulations/configs/vaultLiquidation.ts

+62-32
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,24 @@
11
import { warpTime, resetNetworkAndSetupWallet } from '../../helpers/hardhat/network';
22
import { addDaiToBalance, addMkrToBalance } from '../../helpers/hardhat/balance';
33
import { Simulation } from '../types';
4-
import { collectStabilityFees, fetchVault, liquidateVault } from '../../src/vaults';
4+
import { collectStabilityFees, liquidateVault } from '../../src/vaults';
55
import { TEST_NETWORK } from '../../helpers/constants';
66
import createVaultWithCollateral, {
77
adjustLimitsAndRates,
88
calculateMinCollateralAmountToOpenVault,
99
} from '../helpers/createVaultWithCollateral';
1010
import promptToSelectOneOption from '../helpers/promptToSelectOneOption';
11-
import promptToGetBlockNumber from '../helpers/promptToGetBlockNumber';
11+
import { promptToGetNumber, promptToGetBlockNumber } from '../helpers/promptToGetNumber';
1212
import getProvider from '../../src/provider';
13-
1413
import fetchAuctionsByCollateralType, { fetchMaximumAuctionDurationInSeconds } from '../../src/fetch';
15-
import { getAllCollateralTypes } from '../../src/constants/COLLATERALS';
14+
import { getAllCollateralTypes, getCollateralConfigByType } from '../../src/constants/COLLATERALS';
1615
import { setCollateralDebtCeilingToGlobal } from '../../helpers/hardhat/contractParametrization';
17-
18-
const TWO_YEARS_IN_MINUTES = 60 * 24 * 30 * 12 * 2;
16+
import { getCurrentOraclePriceByCollateralType } from '../../src/oracles';
17+
import { overwriteCurrentOraclePrice } from '../../helpers/hardhat/overwrites';
18+
import BigNumber from 'bignumber.js';
19+
import { enrichAuction } from '../../src/auctions';
20+
import { overwriteUintValue } from '../../helpers/hardhat/slotOverwrite';
21+
import { RAY } from '../../src/constants/UNITS';
1922

2023
const simulation: Simulation = {
2124
title: 'Create collateral auction',
@@ -39,35 +42,51 @@ const simulation: Simulation = {
3942
{
4043
title: 'Create underwater vault',
4144
entry: async context => {
45+
// set oracle price
46+
await overwriteCurrentOraclePrice(TEST_NETWORK, context.collateralType, new BigNumber(1000));
47+
const initialOraclePrice = await getCurrentOraclePriceByCollateralType(
48+
TEST_NETWORK,
49+
context.collateralType
50+
);
51+
console.info(`Initial oracle price is ${initialOraclePrice.toFixed()} DAI`);
52+
4253
await adjustLimitsAndRates(context.collateralType);
4354
const collateralOwned = await calculateMinCollateralAmountToOpenVault(context.collateralType);
4455
console.info(`Minimum collateral amount to open vault: ${collateralOwned.toFixed()}`);
4556
await setCollateralDebtCeilingToGlobal(context.collateralType);
46-
const latestVaultId = await createVaultWithCollateral(
57+
const { vaultIndex, vaultAddress } = await createVaultWithCollateral(
4758
context.collateralType,
4859
collateralOwned.multipliedBy(1)
4960
);
50-
console.info(`Created Vault id: ${latestVaultId}`);
51-
52-
console.info(`Skipping ${TWO_YEARS_IN_MINUTES} minutes...`);
53-
await warpTime(TWO_YEARS_IN_MINUTES, 60);
61+
console.info(`Created Vault with id ${vaultIndex} and address ${vaultAddress}`);
5462

55-
console.info(`Collecting stability fees...`);
56-
const vaultBefore = await fetchVault(TEST_NETWORK, latestVaultId);
57-
console.info(`Stability fee before ${vaultBefore.stabilityFeeRate}`);
63+
// drop oracle price
64+
console.info(`Initial oracle price is ${initialOraclePrice.toFixed()} DAI`);
65+
await overwriteCurrentOraclePrice(TEST_NETWORK, context.collateralType, initialOraclePrice.times(0.5));
66+
const newOraclePrice = await getCurrentOraclePriceByCollateralType(
67+
TEST_NETWORK,
68+
context.collateralType
69+
);
70+
console.info(`New oracle price is ${newOraclePrice.toFixed()} DAI`);
5871
await collectStabilityFees(TEST_NETWORK, context.collateralType);
59-
const vaultAfter = await fetchVault(TEST_NETWORK, latestVaultId);
60-
console.info(`Stability fee after ${vaultAfter.stabilityFeeRate}`);
6172

62-
return { ...context, latestVaultId };
73+
return { ...context, vaultIndex, vaultAddress, initialOraclePrice };
6374
},
6475
},
6576
{
6677
title: 'Liquidate the vault',
6778
entry: async context => {
68-
const liquidatedId = context.latestVaultId;
69-
const vault = await fetchVault(TEST_NETWORK, liquidatedId);
70-
await liquidateVault(TEST_NETWORK, vault.collateralType, vault.address);
79+
const collateralConfig = getCollateralConfigByType(context.collateralType);
80+
try {
81+
// overwrite calc.tau (linear auction price reduction duration)
82+
await overwriteUintValue(collateralConfig.contracts.calc, '0x1', new BigNumber(3000));
83+
} catch {}
84+
try {
85+
// overwrite clip.buf (initial auction price multiplier)
86+
await overwriteUintValue(collateralConfig.contracts.clip, '0x5', RAY.dividedBy(50));
87+
} catch {}
88+
// liquidate
89+
await liquidateVault(TEST_NETWORK, context.collateralType, context.vaultAddress);
7190
return context;
7291
},
7392
},
@@ -78,25 +97,36 @@ const simulation: Simulation = {
7897
TEST_NETWORK,
7998
context.collateralType
8099
);
81-
const INITIAL_WARP_PARTS = 1 / 13;
82-
const warpSeconds = Math.floor(auctionLifetime * INITIAL_WARP_PARTS);
83-
console.info(`Initial warp of ${INITIAL_WARP_PARTS} of an auction time: ${warpSeconds} seconds`);
84-
await warpTime(warpSeconds, 1);
100+
let proposedSecondsToWarp = Math.floor(auctionLifetime / 12);
85101
const provider = await getProvider(TEST_NETWORK);
86-
const STEP_SECONDS = 30;
87102
while (true) {
103+
proposedSecondsToWarp = await promptToGetNumber({
104+
title: 'Number of seconds to warp',
105+
initial: proposedSecondsToWarp,
106+
max: auctionLifetime,
107+
});
108+
if (proposedSecondsToWarp === 0) {
109+
try {
110+
await provider.send('evm_mine', []);
111+
console.info(`Mined one block without skipping any time`);
112+
} catch (error) {
113+
console.error('evm_mine failed with', error);
114+
}
115+
} else {
116+
await warpTime(proposedSecondsToWarp, 1);
117+
console.info(`Skipped ${proposedSecondsToWarp} seconds`);
118+
}
88119
const initialAuctions = await fetchAuctionsByCollateralType(TEST_NETWORK, context.collateralType);
89-
if (!initialAuctions[0] || !initialAuctions[0].isActive) {
120+
if (!initialAuctions[0]) {
90121
console.info('No active auctions are found, exiting the "evm_mine" loop');
91122
break;
92123
}
93-
console.info(`Gradually skipping time, one block every ${STEP_SECONDS} seconds`);
94-
try {
95-
await provider.send('evm_mine', []);
96-
await new Promise(resolve => setTimeout(resolve, STEP_SECONDS * 1000));
97-
} catch (error) {
98-
console.error('evm_mine failed with', error);
124+
const auction = await enrichAuction(TEST_NETWORK, initialAuctions[0]);
125+
if (!auction?.isActive) {
126+
console.info('No active auctions are found, exiting the "evm_mine" loop');
127+
break;
99128
}
129+
console.info(`One active auction is still present: ${auction.id}`);
100130
}
101131
},
102132
},

0 commit comments

Comments
 (0)