From 1c0e5326b3df055a0a44aaf0e8f1416e040f2dc2 Mon Sep 17 00:00:00 2001 From: darek Date: Thu, 16 Feb 2023 17:26:48 +0100 Subject: [PATCH] allow override of self-collateral factor --- contracts/modules/Liquidation.sol | 14 ++--- contracts/modules/RiskManager.sol | 4 +- test/liquidationWithOverrides.js | 90 +++++++++++++++++++++++++++++++ test/override.js | 51 ++++++++++++++++++ 4 files changed, 147 insertions(+), 12 deletions(-) diff --git a/contracts/modules/Liquidation.sol b/contracts/modules/Liquidation.sol index 5ba1a513..6c3f7de3 100644 --- a/contracts/modules/Liquidation.sol +++ b/contracts/modules/Liquidation.sol @@ -133,20 +133,12 @@ contract Liquidation is BaseLogic { // If override is active for the liquidated pair, assume the resulting liability will be fully covered by override collateral, and adjust inputs if (liqLocs.overrideCollateralValue > 0) { - if (liqLocs.underlying == liqLocs.collateral) { // liquidating self-collateral - collateralFactor = SELF_COLLATERAL_FACTOR; + overrideConfig = overrideLookup[liqLocs.underlying][liqLocs.collateral]; + if (overrideConfig.enabled || liqLocs.underlying == liqLocs.collateral) { // the liquidated collateral has active override with liability + collateralFactor = overrideConfig.enabled ? overrideConfig.collateralFactor : SELF_COLLATERAL_FACTOR; borrowFactor = CONFIG_FACTOR_SCALE; // adjust the whole liability for override BF = 1 liqLocs.liabilityValue = liqLocs.currentOwed * liqLocs.underlyingPrice / 1e18; - } else { - overrideConfig = overrideLookup[liqLocs.underlying][liqLocs.collateral]; - - if (overrideConfig.enabled) { // the liquidated collateral has active override with liability - collateralFactor = overrideConfig.collateralFactor; - borrowFactor = CONFIG_FACTOR_SCALE; - // adjust the whole liability for override BF = 1 - liqLocs.liabilityValue = liqLocs.currentOwed * liqLocs.underlyingPrice / 1e18; - } } } diff --git a/contracts/modules/RiskManager.sol b/contracts/modules/RiskManager.sol index 9ffd7f9c..9af60347 100644 --- a/contracts/modules/RiskManager.sol +++ b/contracts/modules/RiskManager.sol @@ -329,7 +329,9 @@ contract RiskManager is IRiskManager, BaseLogic { uint balanceInUnderlying = balanceToUnderlyingAmount(assetCache, balance); uint assetCollateral = balanceInUnderlying * price / 1e18; - assetCollateral = assetCollateral * SELF_COLLATERAL_FACTOR / CONFIG_FACTOR_SCALE; + OverrideConfig memory overrideConfig = overrideLookup[underlying][underlying]; + uint32 selfCollateralFactor = overrideConfig.enabled ? overrideConfig.collateralFactor : SELF_COLLATERAL_FACTOR; + assetCollateral = assetCollateral * selfCollateralFactor / CONFIG_FACTOR_SCALE; // self-collateralization is an implicit override status.overrideCollateralValue += assetCollateral; diff --git a/test/liquidationWithOverrides.js b/test/liquidationWithOverrides.js index ff37ede7..3563a732 100644 --- a/test/liquidationWithOverrides.js +++ b/test/liquidationWithOverrides.js @@ -1137,4 +1137,94 @@ et.testSet({ ], }) + + + +.test({ + desc: "XV Liquidate self-collateral with override on self-collateral factor", + dev: 1, + actions: ctx => [ + { action: 'setAssetConfig', tok: 'TST3', config: { collateralFactor: .5}, }, + { send: 'tokens.TST3.mint', args: [ctx.wallet2.address, et.eth(200)], }, + { from: ctx.wallet2, send: 'tokens.TST3.approve', args: [ctx.contracts.euler.address, et.MaxUint256,], }, + { from: ctx.wallet2, send: 'eTokens.eTST3.deposit', args: [0, et.eth(30)], }, + { from: ctx.wallet2, send: 'markets.enterMarket', args: [0, ctx.contracts.tokens.TST3.address], }, + + + { send: 'tokens.TST6.mint', args: [ctx.wallet2.address, et.eth(100)], }, + { from: ctx.wallet2, send: 'tokens.TST6.approve', args: [ctx.contracts.euler.address, et.MaxUint256,], }, + { from: ctx.wallet2, send: 'eTokens.eTST6.deposit', args: [0, et.eth(10)], }, + { from: ctx.wallet2, send: 'markets.enterMarket', args: [0, ctx.contracts.tokens.TST6.address], }, + + { send: 'governance.setOverride', args: [ + ctx.contracts.tokens.TST.address, + ctx.contracts.tokens.TST6.address, + { + enabled: true, + collateralFactor: Math.floor(0.6 * 4e9), + }, + ], }, + + { from: ctx.wallet2, send: 'eTokens.eTST.mint', args: [0, et.eth(45)], }, + + { send: 'governance.setOverride', args: [ + ctx.contracts.tokens.TST.address, + ctx.contracts.tokens.TST2.address, + { + enabled: true, + collateralFactor: Math.floor(0.9 * 4e9), + }, + ], }, + + + { action: 'updateUniswapPrice', pair: 'TST/WETH', price: '7.4', }, + + { send: 'governance.setOverride', args: [ + ctx.contracts.tokens.TST.address, + ctx.contracts.tokens.TST.address, + { + enabled: true, + collateralFactor: Math.floor(0.8 * 4e9), + }, + ], }, + + { call: 'exec.liquidity', args: [ctx.wallet2.address], onResult: r => { + et.equals(r.collateralValue / r.liabilityValue, 0.914, 0.001); + }, }, + testDetailedLiability(ctx, 0.914), + + { callStatic: 'liquidation.checkLiquidation', args: [ctx.wallet.address, ctx.wallet2.address, ctx.contracts.tokens.TST.address, ctx.contracts.tokens.TST.address], + onResult: r => { + et.equals(r.healthScore, 0.914, 0.001); + ctx.stash.repay = r.repay; + ctx.stash.yield = r.yield; + }, + }, + + // Successful liquidation + + { call: 'eTokens.eTST.reserveBalanceUnderlying', args: [], equals: [0, '0.000000000001'] }, + { call: 'dTokens.dTST.balanceOf', args: [ctx.wallet2.address], equals: et.eth('45'), }, + + { send: 'liquidation.liquidate', args: [ctx.wallet2.address, ctx.contracts.tokens.TST.address, ctx.contracts.tokens.TST.address, () => ctx.stash.repay, 0], }, + + // liquidator: + { call: 'dTokens.dTST.balanceOf', args: [ctx.wallet.address], equals: () => ctx.stash.repay, }, + { call: 'eTokens.eTST.balanceOfUnderlying', args: [ctx.wallet.address], equals: () => [ctx.stash.yield.add(et.eth(100)), '0.00001'], }, // 100 pre-existing depsit + + // reserves: + { call: 'eTokens.eTST.reserveBalanceUnderlying', onResult: (r) => ctx.stash.reserves = r, }, + + // violator: + { call: 'dTokens.dTST.balanceOf', args: [ctx.wallet2.address], equals: () => [et.units(45).sub(ctx.stash.repay).add(ctx.stash.reserves), '0.000000000001'], }, + { call: 'eTokens.eTST.balanceOfUnderlying', args: [ctx.wallet2.address], equals: () => [et.units(45).sub(ctx.stash.yield), '0.000000000001'], }, + + { call: 'exec.liquidity', args: [ctx.wallet2.address], onResult: async (r) => { + let targetHealth = (await ctx.contracts.liquidation.TARGET_HEALTH()) / 1e18; + et.equals(r.collateralValue / r.liabilityValue, targetHealth, 0.00000001); + }}, + testDetailedLiability(ctx, 1.25), + ], +}) + .run(); diff --git a/test/override.js b/test/override.js index 3e87e372..39b3bc6c 100644 --- a/test/override.js +++ b/test/override.js @@ -118,5 +118,56 @@ et.testSet({ +.test({ + desc: "override self-collateral factor", + actions: ctx => [ + // set collateral factor to 0 + + // { from: ctx.wallet2, send: 'dTokens.dTST3.borrow', args: [0, et.eth(1)], }, + { from: ctx.wallet2, send: 'eTokens.eTST2.mint', args: [0, et.eth(10)], }, + + + { call: 'exec.liquidity', args: [ctx.wallet2.address], onResult: r => { + et.equals(r.liabilityValue, 5, .001); // 10 * 0.5 (price) * 1 (BF) + et.equals(r.collateralValue, 9.5, .001); // 20 * 0.5 (price) * 0.95 (SCF) + et.equals(r.overrideCollateralValue, 9.5, .001); // whole collateral is in override + }, }, + + // Override is added for the self collateralisation + + { send: 'governance.setOverride', args: [ + ctx.contracts.tokens.TST2.address, + ctx.contracts.tokens.TST2.address, + { + enabled: true, + collateralFactor: Math.floor(0.8 * 4e9), + }, + ], }, + + { call: 'exec.liquidity', args: [ctx.wallet2.address], onResult: r => { + et.equals(r.liabilityValue, 5, .001); // 10 * 0.5 (price) * 1 (BF) + et.equals(r.collateralValue, 8, .001); // 20 * 0.5 (price) * 0.8 (CF) + et.equals(r.overrideCollateralValue, 8, .001); // whole collateral is in override + }, }, + + { send: 'governance.setOverride', args: [ + ctx.contracts.tokens.TST2.address, + ctx.contracts.tokens.TST2.address, + { + enabled: false, + collateralFactor:0, + }, + ], }, + + { call: 'exec.liquidity', args: [ctx.wallet2.address], onResult: r => { + et.equals(r.liabilityValue, 5, .001); // 10 * 0.5 (price) * 1 (BF) + et.equals(r.collateralValue, 9.5, .001); // 20 * 0.5 (price) * 0.95 (SCF) + et.equals(r.overrideCollateralValue, 9.5, .001); // whole collateral is in override + }, }, + ], +}) + + + .run();