Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

TRUST QA-4: Smoother exposedRefPrice in case of default #987

Merged
merged 2 commits into from
Oct 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion contracts/plugins/assets/AppreciatingFiatCollateral.sol
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ abstract contract AppreciatingFiatCollateral is FiatCollateral {

// uint192(<) is equivalent to Fix.lt
if (underlyingRefPerTok < exposedReferencePrice) {
exposedReferencePrice = hiddenReferencePrice;
exposedReferencePrice = underlyingRefPerTok;
markStatus(CollateralStatus.DISABLED);
} else if (hiddenReferencePrice > exposedReferencePrice) {
exposedReferencePrice = hiddenReferencePrice;
Expand Down
2 changes: 1 addition & 1 deletion contracts/plugins/assets/L2LSDCollateral.sol
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ abstract contract L2LSDCollateral is AppreciatingFiatCollateral {

// uint192(<) is equivalent to Fix.lt
if (underlyingRefPerTok < exposedReferencePrice) {
exposedReferencePrice = hiddenReferencePrice;
exposedReferencePrice = underlyingRefPerTok;
markStatus(CollateralStatus.DISABLED);
} else if (hiddenReferencePrice > exposedReferencePrice) {
exposedReferencePrice = hiddenReferencePrice;
Expand Down
2 changes: 1 addition & 1 deletion contracts/plugins/assets/compoundv3/CTokenV3Collateral.sol
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ contract CTokenV3Collateral is AppreciatingFiatCollateral {

// uint192(<) is equivalent to Fix.lt
if (underlyingRefPerTok < exposedReferencePrice) {
exposedReferencePrice = hiddenReferencePrice;
exposedReferencePrice = underlyingRefPerTok;
markStatus(CollateralStatus.DISABLED);
} else if (hiddenReferencePrice > exposedReferencePrice) {
exposedReferencePrice = hiddenReferencePrice;
Expand Down
2 changes: 1 addition & 1 deletion contracts/plugins/assets/curve/CurveStableCollateral.sol
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ contract CurveStableCollateral is AppreciatingFiatCollateral, PoolTokens {

// uint192(<) is equivalent to Fix.lt
if (underlyingRefPerTok < exposedReferencePrice) {
exposedReferencePrice = hiddenReferencePrice;
exposedReferencePrice = underlyingRefPerTok;
markStatus(CollateralStatus.DISABLED);
} else if (hiddenReferencePrice > exposedReferencePrice) {
exposedReferencePrice = hiddenReferencePrice;
Expand Down
15 changes: 15 additions & 0 deletions test/plugins/individual-collateral/collateralTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -339,14 +339,29 @@ export default function fn<X extends CollateralFixtureContext>(
})

// Should remain SOUND after a 1% decrease
let refPerTok = await ctx.collateral.refPerTok()
await reduceRefPerTok(ctx, 1) // 1% decrease
await ctx.collateral.refresh()
expect(await ctx.collateral.status()).to.equal(CollateralStatus.SOUND)

// refPerTok should be unchanged
expect(await ctx.collateral.refPerTok()).to.be.closeTo(
refPerTok,
refPerTok.div(bn('1e3'))
) // within 1-part-in-1-thousand

// Should become DISABLED if drops more than that
refPerTok = await ctx.collateral.refPerTok()
await reduceRefPerTok(ctx, 1) // another 1% decrease
await ctx.collateral.refresh()
expect(await ctx.collateral.status()).to.equal(CollateralStatus.DISABLED)

// refPerTok should have fallen 1%
refPerTok = refPerTok.sub(refPerTok.div(100))
expect(await ctx.collateral.refPerTok()).to.be.closeTo(
refPerTok,
refPerTok.div(bn('1e3'))
) // within 1-part-in-1-thousand
})

it('reverts if Chainlink feed reverts or runs out of gas, maintains status', async () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ const collateralSpecificStatusTests = () => {
})

// Should remain SOUND after a 1% decrease
let refPerTok = await collateral.refPerTok()
let currentExchangeRate = await wcusdcV3Mock.exchangeRate()
await wcusdcV3Mock.setMockExchangeRate(
true,
Expand All @@ -364,14 +365,22 @@ const collateralSpecificStatusTests = () => {
await collateral.refresh()
expect(await collateral.status()).to.equal(CollateralStatus.SOUND)

// refPerTok should be unchanged
expect(await collateral.refPerTok()).to.be.closeTo(refPerTok, refPerTok.div(bn('1e3'))) // within 1-part-in-1-thousand

// Should become DISABLED if drops more than that
refPerTok = await collateral.refPerTok()
currentExchangeRate = await wcusdcV3Mock.exchangeRate()
await wcusdcV3Mock.setMockExchangeRate(
true,
currentExchangeRate.sub(currentExchangeRate.mul(1).div(100))
)
await collateral.refresh()
expect(await collateral.status()).to.equal(CollateralStatus.DISABLED)

// refPerTok should have fallen 1%
refPerTok = refPerTok.sub(refPerTok.div(100))
expect(await collateral.refPerTok()).to.be.closeTo(refPerTok, refPerTok.div(bn('1e3'))) // within 1-part-in-1-thousand
})
}

Expand Down
7 changes: 7 additions & 0 deletions test/plugins/individual-collateral/curve/collateralTests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,7 @@ export default function fn<X extends CurveCollateralFixtureContext>(
expect(await ctx.collateral.whenDefault()).to.equal(MAX_UINT48)

// Decrease refPerTok by 1 part in a million
const refPerTok = await ctx.collateral.refPerTok()
const currentExchangeRate = await ctx.curvePool.get_virtual_price()
const newVirtualPrice = currentExchangeRate.sub(currentExchangeRate.div(bn('1e6')))
await ctx.curvePool.setVirtualPrice(newVirtualPrice)
Expand All @@ -650,11 +651,17 @@ export default function fn<X extends CurveCollateralFixtureContext>(
expect(await ctx.collateral.status()).to.equal(CollateralStatus.SOUND)
expect(await ctx.collateral.whenDefault()).to.equal(MAX_UINT48)

// refPerTok should be unchanged
expect(await ctx.collateral.refPerTok()).to.equal(refPerTok)

// One quanta more of decrease results in default
await ctx.curvePool.setVirtualPrice(newVirtualPrice.sub(2)) // sub 2 to compenstate for rounding
await expect(ctx.collateral.refresh()).to.emit(ctx.collateral, 'CollateralStatusChanged')
expect(await ctx.collateral.status()).to.equal(CollateralStatus.DISABLED)
expect(await ctx.collateral.whenDefault()).to.equal(await getLatestBlockTimestamp())

// refPerTok should have fallen exactly 2e-18
expect(await ctx.collateral.refPerTok()).to.equal(refPerTok.sub(2))
})

describe('collateral-specific tests', collateralSpecificStatusTests)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ const collateralSpecificConstructorTests = () => {}

// eslint-disable-next-line @typescript-eslint/no-empty-function
const collateralSpecificStatusTests = () => {
it('does revenue hiding', async () => {
it('does revenue hiding correctly', async () => {
const MockFactory = await ethers.getContractFactory('SfraxEthMock')
const erc20 = (await MockFactory.deploy()) as SfraxEthMock
let currentPPS = await (await ethers.getContractAt('IsfrxEth', SFRX_ETH)).pricePerShare()
Expand All @@ -215,14 +215,24 @@ const collateralSpecificStatusTests = () => {
})

// Should remain SOUND after a 1% decrease
await erc20.setPricePerShare(currentPPS.sub(currentPPS.div(100)))
let refPerTok = await collateral.refPerTok()
const newPPS = currentPPS.sub(currentPPS.div(100))
await erc20.setPricePerShare(newPPS)
await collateral.refresh()
expect(await collateral.status()).to.equal(CollateralStatus.SOUND)

// Should become DISABLED if drops more than that
await erc20.setPricePerShare(currentPPS.sub(currentPPS.div(99)))
// refPerTok should be unchanged
expect(await collateral.refPerTok()).to.be.closeTo(refPerTok, refPerTok.div(bn('1e3'))) // within 1-part-in-1-thousand

// Should become DISABLED if drops another 1%
refPerTok = await collateral.refPerTok()
await erc20.setPricePerShare(newPPS.sub(newPPS.div(100)))
await collateral.refresh()
expect(await collateral.status()).to.equal(CollateralStatus.DISABLED)

// refPerTok should have fallen 1%
refPerTok = refPerTok.sub(refPerTok.div(100))
expect(await collateral.refPerTok()).to.be.closeTo(refPerTok, refPerTok.div(bn('1e3'))) // within 1-part-in-1-thousand
})
}

Expand Down