From 3293a1de095e508121af834d0871bc90a67d58b8 Mon Sep 17 00:00:00 2001 From: Patrick McKelvy Date: Mon, 11 Dec 2023 21:06:36 -0500 Subject: [PATCH 01/11] setup test suit, fix SFraxETHCollatera. --- common/configuration.ts | 3 + .../assets/AppreciatingFiatCollateral.sol | 5 +- contracts/plugins/assets/FraxOracleLib.sol | 60 +++++++++++++ contracts/plugins/assets/OracleErrors.sol | 7 ++ contracts/plugins/assets/OracleLib.sol | 4 +- .../assets/frax-eth/SFraxEthCollateral.sol | 42 +++++---- contracts/plugins/mocks/ChainlinkMock.sol | 13 +++ .../individual-collateral/collateralTests.ts | 6 +- .../frax-eth/SFrxEthTestSuite.test.ts | 90 +++++++++++++++---- .../frax-eth/constants.ts | 3 +- test/utils/oracles.ts | 29 +++++- 11 files changed, 220 insertions(+), 42 deletions(-) create mode 100644 contracts/plugins/assets/FraxOracleLib.sol create mode 100644 contracts/plugins/assets/OracleErrors.sol diff --git a/common/configuration.ts b/common/configuration.ts index 0bd5d8ff2e..7ce53302f6 100644 --- a/common/configuration.ts +++ b/common/configuration.ts @@ -219,6 +219,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = { stETHUSD: '0xCfE54B5cD566aB89272946F602D76Ea879CAb4a8', // stETH/USD rETH: '0x536218f9E9Eb48863970252233c8F271f554C2d0', // rETH/ETH cbETH: '0xf017fcb346a1885194689ba23eff2fe6fa5c483b', // cbETH/ETH + sfrxETH: '0xc58f3385fbc1c8ad2c0c9a061d7c13b141d7a5df' // sfrxETH/ETH }, AAVE_LENDING_POOL: '0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9', AAVE_INCENTIVES: '0xd784927Ff2f95ba542BfC824c8a8a98F3495f6b5', @@ -326,6 +327,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = { stETHUSD: '0xCfE54B5cD566aB89272946F602D76Ea879CAb4a8', // stETH/USD rETH: '0x536218f9E9Eb48863970252233c8F271f554C2d0', // rETH/ETH cbETH: '0xf017fcb346a1885194689ba23eff2fe6fa5c483b', // cbETH/ETH + sfrxETH: '0xc58f3385fbc1c8ad2c0c9a061d7c13b141d7a5df' // sfrxETH/ETH }, AAVE_LENDING_POOL: '0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9', AAVE_RESERVE_TREASURY: '0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c', @@ -425,6 +427,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = { stETHUSD: '0xCfE54B5cD566aB89272946F602D76Ea879CAb4a8', // stETH/USD rETH: '0x536218f9E9Eb48863970252233c8F271f554C2d0', // rETH/ETH cbETH: '0xf017fcb346a1885194689ba23eff2fe6fa5c483b', // cbETH/ETH + sfrxETH: '0xc58f3385fbc1c8ad2c0c9a061d7c13b141d7a5df' // sfrxETH/ETH }, AAVE_LENDING_POOL: '0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9', AAVE_RESERVE_TREASURY: '0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c', diff --git a/contracts/plugins/assets/AppreciatingFiatCollateral.sol b/contracts/plugins/assets/AppreciatingFiatCollateral.sol index 60e575cf71..4adbda4efc 100644 --- a/contracts/plugins/assets/AppreciatingFiatCollateral.sol +++ b/contracts/plugins/assets/AppreciatingFiatCollateral.sol @@ -7,7 +7,7 @@ import "../../libraries/Fixed.sol"; import "./FiatCollateral.sol"; import "./Asset.sol"; import "./OracleLib.sol"; - +import "hardhat/console.sol"; /** * @title AppreciatingFiatCollateral * Collateral that may need revenue hiding to become truly "up only" @@ -112,11 +112,14 @@ abstract contract AppreciatingFiatCollateral is FiatCollateral { // If the price is below the default-threshold price, default eventually // uint192(+/-) is the same as Fix.plus/minus if (pegPrice < pegBottom || pegPrice > pegTop || low == 0) { + console.log("here", pegTop, pegBottom, pegPrice); markStatus(CollateralStatus.IFFY); } else { markStatus(CollateralStatus.SOUND); } } catch (bytes memory errData) { + console.log("over ther"); + console.logBytes(errData); // see: docs/solidity-style.md#Catching-Empty-Data if (errData.length == 0) revert(); // solhint-disable-line reason-string markStatus(CollateralStatus.IFFY); diff --git a/contracts/plugins/assets/FraxOracleLib.sol b/contracts/plugins/assets/FraxOracleLib.sol new file mode 100644 index 0000000000..3b99842880 --- /dev/null +++ b/contracts/plugins/assets/FraxOracleLib.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: BlueOak-1.0.0 +pragma solidity 0.8.19; + +import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; +import "../../libraries/Fixed.sol"; +import "./OracleErrors.sol"; +import "hardhat/console.sol"; +interface FraxAggregatorV3Interface is AggregatorV3Interface { + function priceSource() external view returns (address); + function addRoundData(bool _isBadData, uint104 _priceLow, uint104 _priceHigh, uint40 _timestamp) external; +} + +/// Used by asset plugins to price their collateral +library FraxOracleLib { + /// @dev Use for nested calls that should revert when there is a problem + /// @param timeout The number of seconds after which oracle values should be considered stale + /// @return {UoA/tok} + function price(FraxAggregatorV3Interface chainlinkFeed, uint48 timeout) + internal + view + returns (uint192) + { + try chainlinkFeed.latestRoundData() returns ( + uint80 roundId, + int256 p, + uint256, + uint256 updateTime, + uint80 answeredInRound + ) { + if (updateTime == 0 || answeredInRound < roundId) { + revert StalePrice(); + } + + // Downcast is safe: uint256(-) reverts on underflow; block.timestamp assumed < 2^48 + uint48 secondsSince = uint48(block.timestamp - updateTime); + if (secondsSince > timeout) revert StalePrice(); + + if (p == 0) revert ZeroPrice(); + + // {UoA/tok} + return shiftl_toFix(uint256(p), -int8(chainlinkFeed.decimals())); + } catch (bytes memory errData) { + // Check if the priceSource was not set: if so, the chainlink feed has been deprecated + // and a _specific_ error needs to be raised in order to avoid looking like OOG + if (errData.length == 0) { + if (chainlinkFeed.priceSource() == address(0)) { + revert StalePrice(); + } + // solhint-disable-next-line reason-string + revert(); + } + + // Otherwise, preserve the error bytes + // solhint-disable-next-line no-inline-assembly + assembly { + revert(add(32, errData), mload(errData)) + } + } + } +} diff --git a/contracts/plugins/assets/OracleErrors.sol b/contracts/plugins/assets/OracleErrors.sol new file mode 100644 index 0000000000..ddb96dd9ce --- /dev/null +++ b/contracts/plugins/assets/OracleErrors.sol @@ -0,0 +1,7 @@ +// SPDX-License-Identifier: BlueOak-1.0.0 +pragma solidity 0.8.19; + +// 0x19abf40e +error StalePrice(); +// 0x4dfba023 +error ZeroPrice(); diff --git a/contracts/plugins/assets/OracleLib.sol b/contracts/plugins/assets/OracleLib.sol index ff9bd0ef59..87db68e2b3 100644 --- a/contracts/plugins/assets/OracleLib.sol +++ b/contracts/plugins/assets/OracleLib.sol @@ -3,9 +3,7 @@ pragma solidity 0.8.19; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import "../../libraries/Fixed.sol"; - -error StalePrice(); -error ZeroPrice(); +import "./OracleErrors.sol"; interface EACAggregatorProxy { function aggregator() external view returns (address); diff --git a/contracts/plugins/assets/frax-eth/SFraxEthCollateral.sol b/contracts/plugins/assets/frax-eth/SFraxEthCollateral.sol index c3dbe91379..8b86104ff5 100644 --- a/contracts/plugins/assets/frax-eth/SFraxEthCollateral.sol +++ b/contracts/plugins/assets/frax-eth/SFraxEthCollateral.sol @@ -5,7 +5,9 @@ import "@openzeppelin/contracts/utils/math/Math.sol"; import "../../../libraries/Fixed.sol"; import "../AppreciatingFiatCollateral.sol"; import "../OracleLib.sol"; +import "../FraxOracleLib.sol"; import "./vendor/IsfrxEth.sol"; +import "hardhat/console.sol"; /** * ************************************************************ @@ -23,17 +25,27 @@ import "./vendor/IsfrxEth.sol"; */ contract SFraxEthCollateral is AppreciatingFiatCollateral { using OracleLib for AggregatorV3Interface; + using FraxOracleLib for FraxAggregatorV3Interface; using FixLib for uint192; - // solhint-disable no-empty-blocks - /// @param config.chainlinkFeed Feed units: {UoA/target} - constructor(CollateralConfig memory config, uint192 revenueHiding) - AppreciatingFiatCollateral(config, revenueHiding) - { + FraxAggregatorV3Interface public immutable targetPerTokChainlinkFeed; // {target/tok} + uint48 public immutable targetPerTokChainlinkTimeout; + + /// @param config.chainlinkFeed {UoA/target} price of ETH in USD terms + /// @param _targetPerTokChainlinkFeed {target/tok} price of cbETH in ETH terms + constructor( + CollateralConfig memory config, + uint192 revenueHiding, + FraxAggregatorV3Interface _targetPerTokChainlinkFeed, + uint48 _targetPerTokChainlinkTimeout + ) AppreciatingFiatCollateral(config, revenueHiding) { require(config.defaultThreshold > 0, "defaultThreshold zero"); - } + require(address(_targetPerTokChainlinkFeed) != address(0), "missing targetPerTok feed"); + require(_targetPerTokChainlinkTimeout != 0, "targetPerTokChainlinkTimeout zero"); - // solhint-enable no-empty-blocks + targetPerTokChainlinkFeed = _targetPerTokChainlinkFeed; + targetPerTokChainlinkTimeout = _targetPerTokChainlinkTimeout; + } /// Can revert, used by other contract functions in order to catch errors /// @return low {UoA/tok} The low price estimate @@ -49,22 +61,22 @@ contract SFraxEthCollateral is AppreciatingFiatCollateral { uint192 pegPrice ) { - // {UoA/tok} = {UoA/target} * {ref/tok} * {target/ref} (1) - uint192 p = chainlinkFeed.price(oracleTimeout).mul(_underlyingRefPerTok()); + console.log(address(targetPerTokChainlinkFeed), address(chainlinkFeed)); + uint192 targetPerTok = targetPerTokChainlinkFeed.price(targetPerTokChainlinkTimeout); + // {UoA/tok} = {UoA/target} * {target/tok} + uint192 p = chainlinkFeed.price(oracleTimeout).mul(targetPerTok); uint192 err = p.mul(oracleError, CEIL); - low = p - err; high = p + err; + low = p - err; // assert(low <= high); obviously true just by inspection - // TODO: Currently not checking for depegs between `frxETH` and `ETH` - // Should be modified to use a `frxETH/ETH` oracle when available - pegPrice = targetPerRef(); + // {target/ref} = {target/tok} / {ref/tok} + pegPrice = targetPerTok.div(_underlyingRefPerTok()); } /// @return {ref/tok} Quantity of whole reference units per whole collateral tokens function _underlyingRefPerTok() internal view override returns (uint192) { - uint256 rate = IsfrxEth(address(erc20)).pricePerShare(); - return _safeWrap(rate); + return _safeWrap(IsfrxEth(address(erc20)).pricePerShare()); } } diff --git a/contracts/plugins/mocks/ChainlinkMock.sol b/contracts/plugins/mocks/ChainlinkMock.sol index 6e0fee6785..045461297c 100644 --- a/contracts/plugins/mocks/ChainlinkMock.sol +++ b/contracts/plugins/mocks/ChainlinkMock.sol @@ -24,6 +24,7 @@ contract MockV3Aggregator is AggregatorV3Interface { // Additional variable to be able to test invalid behavior uint256 public latestAnsweredRound; address public aggregator; + address public priceSource; mapping(uint256 => int256) public getAnswer; mapping(uint256 => uint256) public getTimestamp; @@ -32,6 +33,7 @@ contract MockV3Aggregator is AggregatorV3Interface { constructor(uint8 _decimals, int256 _initialAnswer) { decimals = _decimals; aggregator = address(this); + priceSource = address(this); updateAnswer(_initialAnswer); } @@ -49,6 +51,17 @@ contract MockV3Aggregator is AggregatorV3Interface { latestAnsweredRound = latestRound; } + // used by Frax oracl + function addRoundData(bool isBadData, uint104 low, uint104 high, uint40 timestamp) public { + latestAnswer = int104(low + high) / 2; + latestTimestamp = block.timestamp; + latestRound++; + getAnswer[latestRound] = latestAnswer; + getTimestamp[latestRound] = block.timestamp; + getStartedAt[latestRound] = block.timestamp; + latestAnsweredRound = latestRound; + } + // Additional function to be able to test invalid Chainlink behavior function setInvalidTimestamp() public { getTimestamp[latestRound] = 0; diff --git a/test/plugins/individual-collateral/collateralTests.ts b/test/plugins/individual-collateral/collateralTests.ts index dcb238c332..b895dfea01 100644 --- a/test/plugins/individual-collateral/collateralTests.ts +++ b/test/plugins/individual-collateral/collateralTests.ts @@ -91,7 +91,7 @@ export default function fn( } = fixtures getDescribeFork(targetNetwork)(`Collateral: ${collateralName}`, () => { - before(resetFork) + beforeEach(resetFork) describe('constructor validation', () => { it('validates targetName', async () => { @@ -394,7 +394,6 @@ export default function fn( const invalidChainlinkFeed = ( await InvalidMockV3AggregatorFactory.deploy(8, chainlinkDefaultAnswer) ) - const invalidCollateral = await deployCollateral({ erc20: ctx.tok.address, chainlinkFeed: invalidChainlinkFeed.address, @@ -520,7 +519,9 @@ export default function fn( const delayUntilDefault = await collateral.delayUntilDefault() // Check initial state + console.log("start") expect(await collateral.status()).to.equal(CollateralStatus.SOUND) + console.log("1") expect(await collateral.whenDefault()).to.equal(MAX_UINT48) // Depeg - Reducing price by 20% @@ -809,6 +810,7 @@ export default function fn( // Should issue await collateralERC20.connect(addr1).approve(rToken.address, MAX_UINT256) await pairedERC20.connect(addr1).approve(rToken.address, MAX_UINT256) + console.log("is ready?", await collateral.status()) await rToken.connect(addr1).issue(supply) }) diff --git a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts index 7ac77c01d1..761563c3d3 100644 --- a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts +++ b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts @@ -12,7 +12,7 @@ import { TestICollateral, IsfrxEth, } from '../../../../typechain' -import { pushOracleForward } from '../../../utils/oracles' +import { pushFraxOracleForward, pushOracleForward } from '../../../utils/oracles' import { bn, fp } from '../../../../common/numbers' import { CollateralStatus } from '../../../../common/constants' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' @@ -27,6 +27,7 @@ import { FRX_ETH, SFRX_ETH, ETH_USD_PRICE_FEED, + SFRXETH_ETH_PRICE_FEED, } from './constants' import { advanceTime, @@ -42,13 +43,19 @@ import { interface SFrxEthCollateralFixtureContext extends CollateralFixtureContext { frxEth: ERC20Mock sfrxEth: IsfrxEth + targetPerTokChainlinkFeed: MockV3Aggregator } /* Define deployment functions */ -export const defaultRethCollateralOpts: CollateralOpts = { +interface SfrxEthCollateralOpts extends CollateralOpts { + targetPerTokChainlinkFeed?: string + targetPerTokChainlinkTimeout?: BigNumberish +} + +export const defaultRethCollateralOpts: SfrxEthCollateralOpts = { erc20: SFRX_ETH, targetName: ethers.utils.formatBytes32String('ETH'), rewardERC20: WETH, @@ -60,9 +67,11 @@ export const defaultRethCollateralOpts: CollateralOpts = { defaultThreshold: DEFAULT_THRESHOLD, delayUntilDefault: DELAY_UNTIL_DEFAULT, revenueHiding: fp('0'), + targetPerTokChainlinkFeed: SFRXETH_ETH_PRICE_FEED, + targetPerTokChainlinkTimeout: ORACLE_TIMEOUT } -export const deployCollateral = async (opts: CollateralOpts = {}): Promise => { +export const deployCollateral = async (opts: SfrxEthCollateralOpts = {}): Promise => { opts = { ...defaultRethCollateralOpts, ...opts } const SFraxEthCollateralFactory: ContractFactory = await ethers.getContractFactory( @@ -82,21 +91,29 @@ export const deployCollateral = async (opts: CollateralOpts = {}): Promise = () => Promise @@ -116,6 +133,13 @@ const makeCollateralFixtureContext = ( ) collateralOpts.chainlinkFeed = chainlinkFeed.address + const targetPerTokChainlinkFeed = ( + await MockV3AggregatorFactory.deploy(18, targetPerTokChainlinkDefaultAnswer) + ) + + collateralOpts.targetPerTokChainlinkFeed = targetPerTokChainlinkFeed.address + collateralOpts.targetPerTokChainlinkTimeout = ORACLE_TIMEOUT + const frxEth = (await ethers.getContractAt('ERC20Mock', FRX_ETH)) as ERC20Mock const sfrxEth = (await ethers.getContractAt('IsfrxEth', SFRX_ETH)) as IsfrxEth const collateral = await deployCollateral(collateralOpts) @@ -126,6 +150,7 @@ const makeCollateralFixtureContext = ( chainlinkFeed, frxEth, sfrxEth, + targetPerTokChainlinkFeed, tok: sfrxEth, } } @@ -146,11 +171,28 @@ const mintCollateralTo: MintCollateralFunc = as await mintSfrxETH(ctx.sfrxEth, user, amount, recipient, ctx.chainlinkFeed) } -// eslint-disable-next-line @typescript-eslint/no-empty-function -const reduceTargetPerRef = async () => {} +const changeTargetPerRef = async (ctx: SFrxEthCollateralFixtureContext, percentChange: BigNumber) => { + // We leave the actual refPerTok exchange where it is and just change {target/tok} + { + const lastRound = await ctx.targetPerTokChainlinkFeed.latestRoundData() + const nextAnswer = lastRound.answer.add(lastRound.answer.mul(percentChange).div(100)) + await ctx.targetPerTokChainlinkFeed.updateAnswer(nextAnswer) + } +} -// eslint-disable-next-line @typescript-eslint/no-empty-function -const increaseTargetPerRef = async () => {} +const reduceTargetPerRef = async ( + ctx: SFrxEthCollateralFixtureContext, + pctDecrease: BigNumberish +) => { + await changeTargetPerRef(ctx, bn(pctDecrease).mul(-1)) +} + +const increaseTargetPerRef = async ( + ctx: SFrxEthCollateralFixtureContext, + pctDecrease: BigNumberish +) => { + await changeTargetPerRef(ctx, bn(pctDecrease)) +} // prettier-ignore const reduceRefPerTok = async () => { @@ -172,11 +214,15 @@ const increaseRefPerTok = async ( await hre.network.provider.send('evm_mine', []) } await ctx.sfrxEth.syncRewards() - await advanceBlocks(1200 / 12) - await advanceTime(1200) + await advanceBlocks(86400 / 12) + await advanceTime(86400) // push chainlink oracle forward so that tryPrice() still works - const lastAnswer = await ctx.chainlinkFeed.latestAnswer() - await ctx.chainlinkFeed.updateAnswer(lastAnswer) + const latestRoundData = await ctx.chainlinkFeed.latestRoundData() + const nextAnswer = latestRoundData.answer.add(latestRoundData.answer.mul(pctIncrease).div(100)) + await ctx.chainlinkFeed.updateAnswer(nextAnswer) + const latestRoundDataTpR = await ctx.targetPerTokChainlinkFeed.latestRoundData() + const nextAnswerTpR = latestRoundDataTpR.answer.add(latestRoundDataTpR.answer.mul(pctIncrease).div(100)) + await ctx.targetPerTokChainlinkFeed.updateAnswer(nextAnswerTpR) } const getExpectedPrice = async (ctx: SFrxEthCollateralFixtureContext): Promise => { @@ -184,9 +230,13 @@ const getExpectedPrice = async (ctx: SFrxEthCollateralFixtureContext): Promise { const chainlinkFeed = ( await (await ethers.getContractFactory('MockV3Aggregator')).deploy(8, chainlinkDefaultAnswer) ) + const targetPerTokenChainlinkFeed = ( + await (await ethers.getContractFactory('MockV3Aggregator')).deploy(18, targetPerTokChainlinkDefaultAnswer) + ) + const collateral = await deployCollateral({ erc20: erc20.address, revenueHiding: fp('0.01'), chainlinkFeed: chainlinkFeed.address, + targetPerTokChainlinkFeed: targetPerTokenChainlinkFeed.address, }) // Should remain SOUND after a 1% decrease @@ -256,7 +311,7 @@ const opts = { increaseRefPerTok, getExpectedPrice, itClaimsRewards: it.skip, - itChecksTargetPerRefDefault: it.skip, + itChecksTargetPerRefDefault: it, itChecksRefPerTokDefault: it.skip, itChecksPriceChanges: it, itChecksNonZeroDefaultThreshold: it, @@ -264,6 +319,7 @@ const opts = { resetFork, collateralName: 'SFraxEthCollateral', chainlinkDefaultAnswer, + itIsPricedByPeg: true, } collateralTests(opts) diff --git a/test/plugins/individual-collateral/frax-eth/constants.ts b/test/plugins/individual-collateral/frax-eth/constants.ts index 8ae4cfc38a..9b306d309e 100644 --- a/test/plugins/individual-collateral/frax-eth/constants.ts +++ b/test/plugins/individual-collateral/frax-eth/constants.ts @@ -7,6 +7,7 @@ export const FRX_ETH = networkConfig['31337'].tokens.frxETH as string export const SFRX_ETH = networkConfig['31337'].tokens.sfrxETH as string export const WETH = networkConfig['31337'].tokens.WETH as string export const FRX_ETH_MINTER = '0xbAFA44EFE7901E04E39Dad13167D089C559c1138' +export const SFRXETH_ETH_PRICE_FEED = networkConfig['31337'].chainlinkFeeds.sfrxETH as string export const PRICE_TIMEOUT = bn('604800') // 1 week export const ORACLE_TIMEOUT = bn(86400) // 24 hours in seconds @@ -15,4 +16,4 @@ export const DEFAULT_THRESHOLD = bn(5).mul(bn(10).pow(16)) // 0.05 export const DELAY_UNTIL_DEFAULT = bn(86400) export const MAX_TRADE_VOL = bn(1000) -export const FORK_BLOCK = 16773193 +export const FORK_BLOCK = 18705637 diff --git a/test/utils/oracles.ts b/test/utils/oracles.ts index 2444878fe4..f7f973dabf 100644 --- a/test/utils/oracles.ts +++ b/test/utils/oracles.ts @@ -5,6 +5,8 @@ import { ethers, network } from 'hardhat' import { expect } from 'chai' import { fp, bn, divCeil } from '../../common/numbers' import { MAX_UINT192 } from '../../common/constants' +import { getLatestBlockNumber, getLatestBlockTimestamp } from './time' +import { whileImpersonating } from './impersonation' const toleranceDivisor = bn('1e15') // 1 part in 1000 trillions @@ -141,9 +143,14 @@ export const overrideOracle = async (oracleAddress: string): Promise { const chainlinkFeed = await ethers.getContractAt('MockV3Aggregator', await chainlinkAddr) - const initPrice = await chainlinkFeed.latestAnswer() + let initPrice; + // awkward workaround for sfrxETH oracle + try { + initPrice = await chainlinkFeed.latestAnswer() + } catch { + initPrice = (await chainlinkFeed.latestRoundData()).answer + } try { // Try to update as if it's a mock already await chainlinkFeed.updateAnswer(initPrice) @@ -163,3 +176,13 @@ export const pushOracleForward = async (chainlinkAddr: string) => { await oracle.updateAnswer(initPrice) } } + +export const pushFraxOracleForward = async (chainlinkAddr: string) => { + const [user] = await ethers.getSigners() + const chainlinkFeed = await ethers.getContractAt('FraxAggregatorV3Interface', chainlinkAddr) + let initPrice = (await chainlinkFeed.latestRoundData()).answer + await whileImpersonating(await chainlinkFeed.priceSource(), async (owner) => { + // await user.sendTransaction({ to: owner.address, value: ethers.utils.parseEther('1') }) + await chainlinkFeed.connect(owner).addRoundData(false, initPrice, initPrice, await getLatestBlockTimestamp() + 1) + }) +} \ No newline at end of file From 5f6b4aed5770901546186396d3d0b731f7c2fde9 Mon Sep 17 00:00:00 2001 From: Patrick McKelvy Date: Mon, 11 Dec 2023 22:34:16 -0500 Subject: [PATCH 02/11] remove consoles. --- .../plugins/assets/AppreciatingFiatCollateral.sol | 5 +---- contracts/plugins/assets/FraxOracleLib.sol | 2 +- .../plugins/assets/frax-eth/SFraxEthCollateral.sol | 2 -- test/plugins/individual-collateral/collateralTests.ts | 7 ++----- .../frax-eth/SFrxEthTestSuite.test.ts | 4 +--- test/plugins/individual-collateral/frax-eth/helpers.ts | 10 +++++++--- 6 files changed, 12 insertions(+), 18 deletions(-) diff --git a/contracts/plugins/assets/AppreciatingFiatCollateral.sol b/contracts/plugins/assets/AppreciatingFiatCollateral.sol index 4adbda4efc..60e575cf71 100644 --- a/contracts/plugins/assets/AppreciatingFiatCollateral.sol +++ b/contracts/plugins/assets/AppreciatingFiatCollateral.sol @@ -7,7 +7,7 @@ import "../../libraries/Fixed.sol"; import "./FiatCollateral.sol"; import "./Asset.sol"; import "./OracleLib.sol"; -import "hardhat/console.sol"; + /** * @title AppreciatingFiatCollateral * Collateral that may need revenue hiding to become truly "up only" @@ -112,14 +112,11 @@ abstract contract AppreciatingFiatCollateral is FiatCollateral { // If the price is below the default-threshold price, default eventually // uint192(+/-) is the same as Fix.plus/minus if (pegPrice < pegBottom || pegPrice > pegTop || low == 0) { - console.log("here", pegTop, pegBottom, pegPrice); markStatus(CollateralStatus.IFFY); } else { markStatus(CollateralStatus.SOUND); } } catch (bytes memory errData) { - console.log("over ther"); - console.logBytes(errData); // see: docs/solidity-style.md#Catching-Empty-Data if (errData.length == 0) revert(); // solhint-disable-line reason-string markStatus(CollateralStatus.IFFY); diff --git a/contracts/plugins/assets/FraxOracleLib.sol b/contracts/plugins/assets/FraxOracleLib.sol index 3b99842880..d759d522a9 100644 --- a/contracts/plugins/assets/FraxOracleLib.sol +++ b/contracts/plugins/assets/FraxOracleLib.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.19; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import "../../libraries/Fixed.sol"; import "./OracleErrors.sol"; -import "hardhat/console.sol"; + interface FraxAggregatorV3Interface is AggregatorV3Interface { function priceSource() external view returns (address); function addRoundData(bool _isBadData, uint104 _priceLow, uint104 _priceHigh, uint40 _timestamp) external; diff --git a/contracts/plugins/assets/frax-eth/SFraxEthCollateral.sol b/contracts/plugins/assets/frax-eth/SFraxEthCollateral.sol index 8b86104ff5..8eb1725085 100644 --- a/contracts/plugins/assets/frax-eth/SFraxEthCollateral.sol +++ b/contracts/plugins/assets/frax-eth/SFraxEthCollateral.sol @@ -7,7 +7,6 @@ import "../AppreciatingFiatCollateral.sol"; import "../OracleLib.sol"; import "../FraxOracleLib.sol"; import "./vendor/IsfrxEth.sol"; -import "hardhat/console.sol"; /** * ************************************************************ @@ -61,7 +60,6 @@ contract SFraxEthCollateral is AppreciatingFiatCollateral { uint192 pegPrice ) { - console.log(address(targetPerTokChainlinkFeed), address(chainlinkFeed)); uint192 targetPerTok = targetPerTokChainlinkFeed.price(targetPerTokChainlinkTimeout); // {UoA/tok} = {UoA/target} * {target/tok} uint192 p = chainlinkFeed.price(oracleTimeout).mul(targetPerTok); diff --git a/test/plugins/individual-collateral/collateralTests.ts b/test/plugins/individual-collateral/collateralTests.ts index b895dfea01..3acdfcd527 100644 --- a/test/plugins/individual-collateral/collateralTests.ts +++ b/test/plugins/individual-collateral/collateralTests.ts @@ -519,9 +519,7 @@ export default function fn( const delayUntilDefault = await collateral.delayUntilDefault() // Check initial state - console.log("start") expect(await collateral.status()).to.equal(CollateralStatus.SOUND) - console.log("1") expect(await collateral.whenDefault()).to.equal(MAX_UINT48) // Depeg - Reducing price by 20% @@ -635,7 +633,7 @@ export default function fn( }) }) - describe('integration tests', () => { + describe.only('integration tests', () => { before(resetFork) let ctx: X @@ -725,7 +723,7 @@ export default function fn( ;({ ctx, protocol } = await loadFixture(integrationFixture)) ;({ collateral } = ctx) ;({ deployer, facadeWrite, govParams } = protocol) - + await collateral.refresh() supply = fp('1') // Create a paired collateral of the same targetName @@ -810,7 +808,6 @@ export default function fn( // Should issue await collateralERC20.connect(addr1).approve(rToken.address, MAX_UINT256) await pairedERC20.connect(addr1).approve(rToken.address, MAX_UINT256) - console.log("is ready?", await collateral.status()) await rToken.connect(addr1).issue(supply) }) diff --git a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts index 761563c3d3..1902c522d4 100644 --- a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts +++ b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts @@ -105,9 +105,7 @@ export const deployCollateral = async (opts: SfrxEthCollateralOpts = {}): Promis // await pushOracleForward(opts.targetPerTokChainlinkFeed!) // sometimes we are trying to test a negative test case and we want this to fail silently // fortunately this syntax fails silently because our tools are terrible - console.log('fail here refresh') await expect(collateral.refresh()) - console.log('failed', await collateral.status()) return collateral } @@ -168,7 +166,7 @@ const mintCollateralTo: MintCollateralFunc = as user: SignerWithAddress, recipient: string ) => { - await mintSfrxETH(ctx.sfrxEth, user, amount, recipient, ctx.chainlinkFeed) + await mintSfrxETH(ctx.sfrxEth, user, amount, recipient, ctx.chainlinkFeed, ctx.targetPerTokChainlinkFeed) } const changeTargetPerRef = async (ctx: SFrxEthCollateralFixtureContext, percentChange: BigNumber) => { diff --git a/test/plugins/individual-collateral/frax-eth/helpers.ts b/test/plugins/individual-collateral/frax-eth/helpers.ts index 50a24bb9bc..62a5b6795c 100644 --- a/test/plugins/individual-collateral/frax-eth/helpers.ts +++ b/test/plugins/individual-collateral/frax-eth/helpers.ts @@ -11,7 +11,8 @@ export const mintSfrxETH = async ( account: SignerWithAddress, amount: BigNumberish, recipient: string, - chainlinkFeed: MockV3Aggregator + chainlinkFeed: MockV3Aggregator, + targetPerTokChainlinkFeed: MockV3Aggregator ) => { const frxEthMinter: IfrxEthMinter = ( await ethers.getContractAt('IfrxEthMinter', FRX_ETH_MINTER) @@ -28,8 +29,11 @@ export const mintSfrxETH = async ( await frxEthMinter.connect(account).submitAndDeposit(recipient, { value: depositAmount }) // push chainlink oracle forward so that tryPrice() still works - const lastAnswer = await chainlinkFeed.latestAnswer() - await chainlinkFeed.updateAnswer(lastAnswer) + const lastAnswer = await chainlinkFeed.latestRoundData() + await chainlinkFeed.updateAnswer(lastAnswer.answer) + + const lastAnswerTpR = await targetPerTokChainlinkFeed.latestRoundData() + await targetPerTokChainlinkFeed.updateAnswer(lastAnswerTpR.answer) } export const mintFrxETH = async ( From 52bb0f7506991667b15bbfce1949c5371ad8b30e Mon Sep 17 00:00:00 2001 From: Patrick McKelvy Date: Mon, 11 Dec 2023 22:38:52 -0500 Subject: [PATCH 03/11] fix integration tests. --- test/plugins/individual-collateral/collateralTests.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/plugins/individual-collateral/collateralTests.ts b/test/plugins/individual-collateral/collateralTests.ts index 3acdfcd527..2ae2b1f582 100644 --- a/test/plugins/individual-collateral/collateralTests.ts +++ b/test/plugins/individual-collateral/collateralTests.ts @@ -91,7 +91,7 @@ export default function fn( } = fixtures getDescribeFork(targetNetwork)(`Collateral: ${collateralName}`, () => { - beforeEach(resetFork) + before(resetFork) describe('constructor validation', () => { it('validates targetName', async () => { @@ -633,7 +633,7 @@ export default function fn( }) }) - describe.only('integration tests', () => { + describe('integration tests', () => { before(resetFork) let ctx: X From a57b077a6ec4380679e23cf8800eca2f02f50927 Mon Sep 17 00:00:00 2001 From: Patrick McKelvy Date: Tue, 12 Dec 2023 16:39:51 -0500 Subject: [PATCH 04/11] fix most. --- hardhat.config.ts | 6 ++++++ test/plugins/individual-collateral/collateralTests.ts | 2 ++ .../frax-eth/SFrxEthTestSuite.test.ts | 8 ++++---- test/utils/oracles.ts | 1 - 4 files changed, 12 insertions(+), 5 deletions(-) diff --git a/hardhat.config.ts b/hardhat.config.ts index 87a53dbcf0..798c3d2dc2 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -14,6 +14,7 @@ import { useEnv } from '#/utils/env' import { forkRpcs, Network } from '#/utils/fork' import { HardhatUserConfig } from 'hardhat/types' import forkBlockNumber from '#/test/integration/fork-block-numbers' +import { fp } from './common/numbers' // eslint-disable-next-line node/no-missing-require require('#/tasks') @@ -45,6 +46,11 @@ const config: HardhatUserConfig = { gas: 0x1ffffffff, blockGasLimit: 0x1fffffffffffff, allowUnlimitedContractSize: true, + accounts: [ + { privateKey: new Array(65).join('1'), balance: fp(1e6).toString() }, + { privateKey: new Array(65).join('2'), balance: fp(1e6).toString() }, + { privateKey: new Array(65).join('3'), balance: fp(1e6).toString() }, + ] }, localhost: { // network for long-lived mainnet forks diff --git a/test/plugins/individual-collateral/collateralTests.ts b/test/plugins/individual-collateral/collateralTests.ts index 2ae2b1f582..7b99ab5eec 100644 --- a/test/plugins/individual-collateral/collateralTests.ts +++ b/test/plugins/individual-collateral/collateralTests.ts @@ -452,6 +452,8 @@ export default function fn( }) describe('status', () => { + before(resetFork) + it('maintains status in normal situations', async () => { // Check initial state expect(await collateral.status()).to.equal(CollateralStatus.SOUND) diff --git a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts index 1902c522d4..d68046fe6d 100644 --- a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts +++ b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts @@ -99,10 +99,10 @@ export const deployCollateral = async (opts: SfrxEthCollateralOpts = {}): Promis // Push forward chainlink fee await pushOracleForward(opts.chainlinkFeed!) - await pushOracleForward(opts.targetPerTokChainlinkFeed!) - // opts.targetPerTokChainlinkFeed?.toLocaleLowerCase() == SFRXETH_ETH_PRICE_FEED.toLocaleLowerCase() ? - // await pushFraxOracleForward(opts.targetPerTokChainlinkFeed ?? SFRXETH_ETH_PRICE_FEED) : - // await pushOracleForward(opts.targetPerTokChainlinkFeed!) + // await pushOracleForward(opts.targetPerTokChainlinkFeed!) + opts.targetPerTokChainlinkFeed?.toLocaleLowerCase() == SFRXETH_ETH_PRICE_FEED.toLocaleLowerCase() ? + await pushFraxOracleForward(opts.targetPerTokChainlinkFeed ?? SFRXETH_ETH_PRICE_FEED) : + await pushOracleForward(opts.targetPerTokChainlinkFeed!) // sometimes we are trying to test a negative test case and we want this to fail silently // fortunately this syntax fails silently because our tools are terrible await expect(collateral.refresh()) diff --git a/test/utils/oracles.ts b/test/utils/oracles.ts index f7f973dabf..c1336542dc 100644 --- a/test/utils/oracles.ts +++ b/test/utils/oracles.ts @@ -182,7 +182,6 @@ export const pushFraxOracleForward = async (chainlinkAddr: string) => { const chainlinkFeed = await ethers.getContractAt('FraxAggregatorV3Interface', chainlinkAddr) let initPrice = (await chainlinkFeed.latestRoundData()).answer await whileImpersonating(await chainlinkFeed.priceSource(), async (owner) => { - // await user.sendTransaction({ to: owner.address, value: ethers.utils.parseEther('1') }) await chainlinkFeed.connect(owner).addRoundData(false, initPrice, initPrice, await getLatestBlockTimestamp() + 1) }) } \ No newline at end of file From ee448ea4cb5813270ed07e5cbcdbec4f0f84a011 Mon Sep 17 00:00:00 2001 From: Patrick McKelvy Date: Wed, 13 Dec 2023 00:11:38 -0500 Subject: [PATCH 05/11] needed to move iffy test, hopefully didnt break anything else. fuck this nondeterminism... --- .../assets/AppreciatingFiatCollateral.sol | 1 - hardhat.config.ts | 7 +------ .../individual-collateral/collateralTests.ts | 16 ++++++++-------- .../frax-eth/SFrxEthTestSuite.test.ts | 5 ++--- .../individual-collateral/frax-eth/helpers.ts | 3 +++ 5 files changed, 14 insertions(+), 18 deletions(-) diff --git a/contracts/plugins/assets/AppreciatingFiatCollateral.sol b/contracts/plugins/assets/AppreciatingFiatCollateral.sol index 60e575cf71..9f665d6424 100644 --- a/contracts/plugins/assets/AppreciatingFiatCollateral.sol +++ b/contracts/plugins/assets/AppreciatingFiatCollateral.sol @@ -85,7 +85,6 @@ abstract contract AppreciatingFiatCollateral is FiatCollateral { // {ref/tok} = {ref/tok} * {1} uint192 hiddenReferencePrice = underlyingRefPerTok.mul(revenueShowing); - // uint192(<) is equivalent to Fix.lt if (underlyingRefPerTok < exposedReferencePrice) { exposedReferencePrice = underlyingRefPerTok; diff --git a/hardhat.config.ts b/hardhat.config.ts index 798c3d2dc2..71ce7789bc 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -45,12 +45,7 @@ const config: HardhatUserConfig = { : undefined, gas: 0x1ffffffff, blockGasLimit: 0x1fffffffffffff, - allowUnlimitedContractSize: true, - accounts: [ - { privateKey: new Array(65).join('1'), balance: fp(1e6).toString() }, - { privateKey: new Array(65).join('2'), balance: fp(1e6).toString() }, - { privateKey: new Array(65).join('3'), balance: fp(1e6).toString() }, - ] + allowUnlimitedContractSize: true }, localhost: { // network for long-lived mainnet forks diff --git a/test/plugins/individual-collateral/collateralTests.ts b/test/plugins/individual-collateral/collateralTests.ts index 7b99ab5eec..710c0f4cba 100644 --- a/test/plugins/individual-collateral/collateralTests.ts +++ b/test/plugins/individual-collateral/collateralTests.ts @@ -223,6 +223,14 @@ export default function fn( describe('prices', () => { before(resetFork) // important for getting prices/refPerToks to behave predictably + it('enters IFFY state when price becomes stale', async () => { + const oracleTimeout = await collateral.oracleTimeout() + await setNextBlockTimestamp((await getLatestBlockTimestamp()) + oracleTimeout) + await advanceBlocks(oracleTimeout / 12) + await collateral.refresh() + expect(await collateral.status()).to.equal(CollateralStatus.IFFY) + }) + itChecksPriceChanges('prices change as USD feed price changes', async () => { const oracleError = await collateral.oracleError() const expectedPrice = await getExpectedPrice(ctx) @@ -410,14 +418,6 @@ export default function fn( expect(await invalidCollateral.status()).to.equal(CollateralStatus.SOUND) }) - it('enters IFFY state when price becomes stale', async () => { - const oracleTimeout = await collateral.oracleTimeout() - await setNextBlockTimestamp((await getLatestBlockTimestamp()) + oracleTimeout) - await advanceBlocks(oracleTimeout / 12) - await collateral.refresh() - expect(await collateral.status()).to.equal(CollateralStatus.IFFY) - }) - it('decays price over priceTimeout period', async () => { await collateral.refresh() const savedLow = await collateral.savedLowPrice() diff --git a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts index d68046fe6d..d498283edc 100644 --- a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts +++ b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts @@ -216,8 +216,7 @@ const increaseRefPerTok = async ( await advanceTime(86400) // push chainlink oracle forward so that tryPrice() still works const latestRoundData = await ctx.chainlinkFeed.latestRoundData() - const nextAnswer = latestRoundData.answer.add(latestRoundData.answer.mul(pctIncrease).div(100)) - await ctx.chainlinkFeed.updateAnswer(nextAnswer) + await ctx.chainlinkFeed.updateAnswer(latestRoundData.answer) const latestRoundDataTpR = await ctx.targetPerTokChainlinkFeed.latestRoundData() const nextAnswerTpR = latestRoundDataTpR.answer.add(latestRoundDataTpR.answer.mul(pctIncrease).div(100)) await ctx.targetPerTokChainlinkFeed.updateAnswer(nextAnswerTpR) @@ -312,8 +311,8 @@ const opts = { itChecksTargetPerRefDefault: it, itChecksRefPerTokDefault: it.skip, itChecksPriceChanges: it, - itChecksNonZeroDefaultThreshold: it, itHasRevenueHiding: it.skip, // implemnted in this file + itChecksNonZeroDefaultThreshold: it, resetFork, collateralName: 'SFraxEthCollateral', chainlinkDefaultAnswer, diff --git a/test/plugins/individual-collateral/frax-eth/helpers.ts b/test/plugins/individual-collateral/frax-eth/helpers.ts index 62a5b6795c..95f852b544 100644 --- a/test/plugins/individual-collateral/frax-eth/helpers.ts +++ b/test/plugins/individual-collateral/frax-eth/helpers.ts @@ -5,6 +5,8 @@ import { BigNumberish } from 'ethers' import { FORK_BLOCK, FRX_ETH_MINTER } from './constants' import { getResetFork } from '../helpers' import { setNextBlockTimestamp, getLatestBlockTimestamp } from '../../../utils/time' +import { fp } from '#/common/numbers' +import { setBalance } from '@nomicfoundation/hardhat-network-helpers' export const mintSfrxETH = async ( sfrxEth: IsfrxEth, @@ -14,6 +16,7 @@ export const mintSfrxETH = async ( chainlinkFeed: MockV3Aggregator, targetPerTokChainlinkFeed: MockV3Aggregator ) => { + await setBalance(account.address, fp(100000)); const frxEthMinter: IfrxEthMinter = ( await ethers.getContractAt('IfrxEthMinter', FRX_ETH_MINTER) ) From 1fab21a17dcfd33ab296177ac37e166ef5602aae Mon Sep 17 00:00:00 2001 From: Patrick McKelvy Date: Wed, 13 Dec 2023 08:52:21 -0500 Subject: [PATCH 06/11] cleanup linting. --- .../assets/AppreciatingFiatCollateral.sol | 1 + contracts/plugins/assets/FraxOracleLib.sol | 8 ++++- hardhat.config.ts | 1 - .../individual-collateral/collateralTests.ts | 5 +-- .../curve/cvx/CvxStableTestSuite.test.ts | 2 -- .../frax-eth/SFrxEthTestSuite.test.ts | 32 +++++++++++++------ .../individual-collateral/frax-eth/helpers.ts | 2 +- test/utils/oracles.ts | 17 +++++----- 8 files changed, 44 insertions(+), 24 deletions(-) diff --git a/contracts/plugins/assets/AppreciatingFiatCollateral.sol b/contracts/plugins/assets/AppreciatingFiatCollateral.sol index 9f665d6424..60e575cf71 100644 --- a/contracts/plugins/assets/AppreciatingFiatCollateral.sol +++ b/contracts/plugins/assets/AppreciatingFiatCollateral.sol @@ -85,6 +85,7 @@ abstract contract AppreciatingFiatCollateral is FiatCollateral { // {ref/tok} = {ref/tok} * {1} uint192 hiddenReferencePrice = underlyingRefPerTok.mul(revenueShowing); + // uint192(<) is equivalent to Fix.lt if (underlyingRefPerTok < exposedReferencePrice) { exposedReferencePrice = underlyingRefPerTok; diff --git a/contracts/plugins/assets/FraxOracleLib.sol b/contracts/plugins/assets/FraxOracleLib.sol index d759d522a9..67374c8de1 100644 --- a/contracts/plugins/assets/FraxOracleLib.sol +++ b/contracts/plugins/assets/FraxOracleLib.sol @@ -7,7 +7,13 @@ import "./OracleErrors.sol"; interface FraxAggregatorV3Interface is AggregatorV3Interface { function priceSource() external view returns (address); - function addRoundData(bool _isBadData, uint104 _priceLow, uint104 _priceHigh, uint40 _timestamp) external; + + function addRoundData( + bool _isBadData, + uint104 _priceLow, + uint104 _priceHigh, + uint40 _timestamp + ) external; } /// Used by asset plugins to price their collateral diff --git a/hardhat.config.ts b/hardhat.config.ts index 71ce7789bc..61bac4e916 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -14,7 +14,6 @@ import { useEnv } from '#/utils/env' import { forkRpcs, Network } from '#/utils/fork' import { HardhatUserConfig } from 'hardhat/types' import forkBlockNumber from '#/test/integration/fork-block-numbers' -import { fp } from './common/numbers' // eslint-disable-next-line node/no-missing-require require('#/tasks') diff --git a/test/plugins/individual-collateral/collateralTests.ts b/test/plugins/individual-collateral/collateralTests.ts index 710c0f4cba..1ecfb0d657 100644 --- a/test/plugins/individual-collateral/collateralTests.ts +++ b/test/plugins/individual-collateral/collateralTests.ts @@ -402,6 +402,7 @@ export default function fn( const invalidChainlinkFeed = ( await InvalidMockV3AggregatorFactory.deploy(8, chainlinkDefaultAnswer) ) + const invalidCollateral = await deployCollateral({ erc20: ctx.tok.address, chainlinkFeed: invalidChainlinkFeed.address, @@ -453,7 +454,7 @@ export default function fn( describe('status', () => { before(resetFork) - + it('maintains status in normal situations', async () => { // Check initial state expect(await collateral.status()).to.equal(CollateralStatus.SOUND) @@ -725,7 +726,7 @@ export default function fn( ;({ ctx, protocol } = await loadFixture(integrationFixture)) ;({ collateral } = ctx) ;({ deployer, facadeWrite, govParams } = protocol) - await collateral.refresh() + supply = fp('1') // Create a paired collateral of the same targetName diff --git a/test/plugins/individual-collateral/curve/cvx/CvxStableTestSuite.test.ts b/test/plugins/individual-collateral/curve/cvx/CvxStableTestSuite.test.ts index 6ff50ba47f..8cbdd58345 100644 --- a/test/plugins/individual-collateral/curve/cvx/CvxStableTestSuite.test.ts +++ b/test/plugins/individual-collateral/curve/cvx/CvxStableTestSuite.test.ts @@ -9,7 +9,6 @@ import { ethers } from 'hardhat' import { ContractFactory, BigNumberish } from 'ethers' import { ERC20Mock, - IERC20, MockV3Aggregator, MockV3Aggregator__factory, TestICollateral, @@ -43,7 +42,6 @@ import { CRV, THREE_POOL_HOLDER, } from '../constants' -import { whileImpersonating } from '#/test/utils/impersonation' type Fixture = () => Promise diff --git a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts index d498283edc..a46a41e9e3 100644 --- a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts +++ b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts @@ -68,10 +68,12 @@ export const defaultRethCollateralOpts: SfrxEthCollateralOpts = { delayUntilDefault: DELAY_UNTIL_DEFAULT, revenueHiding: fp('0'), targetPerTokChainlinkFeed: SFRXETH_ETH_PRICE_FEED, - targetPerTokChainlinkTimeout: ORACLE_TIMEOUT + targetPerTokChainlinkTimeout: ORACLE_TIMEOUT, } -export const deployCollateral = async (opts: SfrxEthCollateralOpts = {}): Promise => { +export const deployCollateral = async ( + opts: SfrxEthCollateralOpts = {} +): Promise => { opts = { ...defaultRethCollateralOpts, ...opts } const SFraxEthCollateralFactory: ContractFactory = await ethers.getContractFactory( @@ -97,12 +99,12 @@ export const deployCollateral = async (opts: SfrxEthCollateralOpts = {}): Promis ) await collateral.deployed() - // Push forward chainlink fee + // Push forward chainlink feed await pushOracleForward(opts.chainlinkFeed!) // await pushOracleForward(opts.targetPerTokChainlinkFeed!) - opts.targetPerTokChainlinkFeed?.toLocaleLowerCase() == SFRXETH_ETH_PRICE_FEED.toLocaleLowerCase() ? - await pushFraxOracleForward(opts.targetPerTokChainlinkFeed ?? SFRXETH_ETH_PRICE_FEED) : - await pushOracleForward(opts.targetPerTokChainlinkFeed!) + opts.targetPerTokChainlinkFeed?.toLocaleLowerCase() == SFRXETH_ETH_PRICE_FEED.toLocaleLowerCase() + ? await pushFraxOracleForward(opts.targetPerTokChainlinkFeed ?? SFRXETH_ETH_PRICE_FEED) + : await pushOracleForward(opts.targetPerTokChainlinkFeed!) // sometimes we are trying to test a negative test case and we want this to fail silently // fortunately this syntax fails silently because our tools are terrible await expect(collateral.refresh()) @@ -166,10 +168,20 @@ const mintCollateralTo: MintCollateralFunc = as user: SignerWithAddress, recipient: string ) => { - await mintSfrxETH(ctx.sfrxEth, user, amount, recipient, ctx.chainlinkFeed, ctx.targetPerTokChainlinkFeed) + await mintSfrxETH( + ctx.sfrxEth, + user, + amount, + recipient, + ctx.chainlinkFeed, + ctx.targetPerTokChainlinkFeed + ) } -const changeTargetPerRef = async (ctx: SFrxEthCollateralFixtureContext, percentChange: BigNumber) => { +const changeTargetPerRef = async ( + ctx: SFrxEthCollateralFixtureContext, + percentChange: BigNumber +) => { // We leave the actual refPerTok exchange where it is and just change {target/tok} { const lastRound = await ctx.targetPerTokChainlinkFeed.latestRoundData() @@ -256,7 +268,9 @@ const collateralSpecificStatusTests = () => { await (await ethers.getContractFactory('MockV3Aggregator')).deploy(8, chainlinkDefaultAnswer) ) const targetPerTokenChainlinkFeed = ( - await (await ethers.getContractFactory('MockV3Aggregator')).deploy(18, targetPerTokChainlinkDefaultAnswer) + await ( + await ethers.getContractFactory('MockV3Aggregator') + ).deploy(18, targetPerTokChainlinkDefaultAnswer) ) const collateral = await deployCollateral({ diff --git a/test/plugins/individual-collateral/frax-eth/helpers.ts b/test/plugins/individual-collateral/frax-eth/helpers.ts index 95f852b544..3323c07939 100644 --- a/test/plugins/individual-collateral/frax-eth/helpers.ts +++ b/test/plugins/individual-collateral/frax-eth/helpers.ts @@ -16,7 +16,7 @@ export const mintSfrxETH = async ( chainlinkFeed: MockV3Aggregator, targetPerTokChainlinkFeed: MockV3Aggregator ) => { - await setBalance(account.address, fp(100000)); + await setBalance(account.address, fp(100000)) const frxEthMinter: IfrxEthMinter = ( await ethers.getContractAt('IfrxEthMinter', FRX_ETH_MINTER) ) diff --git a/test/utils/oracles.ts b/test/utils/oracles.ts index c1336542dc..6dde6dec02 100644 --- a/test/utils/oracles.ts +++ b/test/utils/oracles.ts @@ -5,7 +5,7 @@ import { ethers, network } from 'hardhat' import { expect } from 'chai' import { fp, bn, divCeil } from '../../common/numbers' import { MAX_UINT192 } from '../../common/constants' -import { getLatestBlockNumber, getLatestBlockTimestamp } from './time' +import { getLatestBlockTimestamp } from './time' import { whileImpersonating } from './impersonation' const toleranceDivisor = bn('1e15') // 1 part in 1000 trillions @@ -143,9 +143,9 @@ export const overrideOracle = async (oracleAddress: string): Promise { const chainlinkFeed = await ethers.getContractAt('MockV3Aggregator', await chainlinkAddr) - let initPrice; + let initPrice // awkward workaround for sfrxETH oracle try { initPrice = await chainlinkFeed.latestAnswer() @@ -178,10 +178,11 @@ export const pushOracleForward = async (chainlinkAddr: string) => { } export const pushFraxOracleForward = async (chainlinkAddr: string) => { - const [user] = await ethers.getSigners() const chainlinkFeed = await ethers.getContractAt('FraxAggregatorV3Interface', chainlinkAddr) - let initPrice = (await chainlinkFeed.latestRoundData()).answer + const initPrice = (await chainlinkFeed.latestRoundData()).answer await whileImpersonating(await chainlinkFeed.priceSource(), async (owner) => { - await chainlinkFeed.connect(owner).addRoundData(false, initPrice, initPrice, await getLatestBlockTimestamp() + 1) + await chainlinkFeed + .connect(owner) + .addRoundData(false, initPrice, initPrice, (await getLatestBlockTimestamp()) + 1) }) -} \ No newline at end of file +} From efc21e31e32a4aa44994d12509159b61d85cb64b Mon Sep 17 00:00:00 2001 From: Patrick McKelvy Date: Wed, 13 Dec 2023 20:56:30 -0500 Subject: [PATCH 07/11] fix...frxETH not sfrxETH oracle. --- common/configuration.ts | 6 +- contracts/plugins/assets/frax-eth/README.md | 7 +-- .../assets/frax-eth/SFraxEthCollateral.sol | 35 +++++------ contracts/plugins/mocks/ChainlinkMock.sol | 2 +- .../frax-eth/SFrxEthTestSuite.test.ts | 62 ++++++++++--------- .../frax-eth/constants.ts | 2 +- 6 files changed, 54 insertions(+), 60 deletions(-) diff --git a/common/configuration.ts b/common/configuration.ts index 7ce53302f6..a5960ac404 100644 --- a/common/configuration.ts +++ b/common/configuration.ts @@ -219,7 +219,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = { stETHUSD: '0xCfE54B5cD566aB89272946F602D76Ea879CAb4a8', // stETH/USD rETH: '0x536218f9E9Eb48863970252233c8F271f554C2d0', // rETH/ETH cbETH: '0xf017fcb346a1885194689ba23eff2fe6fa5c483b', // cbETH/ETH - sfrxETH: '0xc58f3385fbc1c8ad2c0c9a061d7c13b141d7a5df' // sfrxETH/ETH + frxETH: '0xc58f3385fbc1c8ad2c0c9a061d7c13b141d7a5df' // frxETH/ETH }, AAVE_LENDING_POOL: '0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9', AAVE_INCENTIVES: '0xd784927Ff2f95ba542BfC824c8a8a98F3495f6b5', @@ -327,7 +327,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = { stETHUSD: '0xCfE54B5cD566aB89272946F602D76Ea879CAb4a8', // stETH/USD rETH: '0x536218f9E9Eb48863970252233c8F271f554C2d0', // rETH/ETH cbETH: '0xf017fcb346a1885194689ba23eff2fe6fa5c483b', // cbETH/ETH - sfrxETH: '0xc58f3385fbc1c8ad2c0c9a061d7c13b141d7a5df' // sfrxETH/ETH + frxETH: '0xc58f3385fbc1c8ad2c0c9a061d7c13b141d7a5df' // frxETH/ETH }, AAVE_LENDING_POOL: '0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9', AAVE_RESERVE_TREASURY: '0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c', @@ -427,7 +427,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = { stETHUSD: '0xCfE54B5cD566aB89272946F602D76Ea879CAb4a8', // stETH/USD rETH: '0x536218f9E9Eb48863970252233c8F271f554C2d0', // rETH/ETH cbETH: '0xf017fcb346a1885194689ba23eff2fe6fa5c483b', // cbETH/ETH - sfrxETH: '0xc58f3385fbc1c8ad2c0c9a061d7c13b141d7a5df' // sfrxETH/ETH + frxETH: '0xc58f3385fbc1c8ad2c0c9a061d7c13b141d7a5df' // frxETH/ETH }, AAVE_LENDING_POOL: '0x7d2768dE32b0b80b7a3454c06BdAc94A69DDc7A9', AAVE_RESERVE_TREASURY: '0x464C71f6c2F760DdA6093dCB91C24c39e5d6e18c', diff --git a/contracts/plugins/assets/frax-eth/README.md b/contracts/plugins/assets/frax-eth/README.md index 7d32cc254a..81ed35cd9b 100644 --- a/contracts/plugins/assets/frax-eth/README.md +++ b/contracts/plugins/assets/frax-eth/README.md @@ -1,7 +1,5 @@ # Staked-Frax-ETH Collateral Plugin -**NOTE: The SFraxEthCollateral plugin SHOULD NOT be deployed and used until a `frxETH/ETH` chainlink oracle can be integrated with the plugin. As of 3/14/23, there is no chainlink oracle, but the FRAX team is working on getting one.** - ## Summary This plugin allows `sfrxETH` ((Staked-Frax-ETH)[https://docs.frax.finance/frax-ether/overview]) holders use their tokens as collateral in the Reserve Protocol. @@ -16,8 +14,6 @@ You can get the `frxETH/sfrxETH` exchange rate from [`sfrxETH.pricePerShare()`]( `frxETH` contract: -`wstETH` and `stETH` can be always swapped at any time to each other without any risk and limitation (Except smart contract risk), like `wETH` and `ETH`. Wrap & Unwrap app can be found here: - ## Implementation ### Units @@ -32,6 +28,9 @@ You can get the `frxETH/sfrxETH` exchange rate from [`sfrxETH.pricePerShare()`]( This function returns rate of `frxETH/sfrxETH`, getting from [pricePerShare()](https://github.com/FraxFinance/frxETH-public/blob/master/src/sfrxETH.sol#L82) function in sfrxETH contract. +#### target-per-ref price {tar/ref} + +The targetPerRef price of `ETH/frxETH` is received from the frxETH/ETH FRAX-managed oracle ([details here](https://docs.frax.finance/frax-oracle/frax-oracle-overview)). #### tryPrice This function uses `refPerTok` and the chainlink price of `USD/ETH` to return the current price range of the collateral. Once an oracle becomes available for `frxETH/ETH`, this function should be modified to use it and return the appropiate `pegPrice`. diff --git a/contracts/plugins/assets/frax-eth/SFraxEthCollateral.sol b/contracts/plugins/assets/frax-eth/SFraxEthCollateral.sol index 8eb1725085..d9083e7e73 100644 --- a/contracts/plugins/assets/frax-eth/SFraxEthCollateral.sol +++ b/contracts/plugins/assets/frax-eth/SFraxEthCollateral.sol @@ -8,12 +8,6 @@ import "../OracleLib.sol"; import "../FraxOracleLib.sol"; import "./vendor/IsfrxEth.sol"; -/** - * ************************************************************ - * WARNING: this plugin is not ready to be used in Production - * ************************************************************ - */ - /** * @title SFraxEthCollateral * @notice Collateral plugin for Frax-ETH, @@ -27,23 +21,23 @@ contract SFraxEthCollateral is AppreciatingFiatCollateral { using FraxOracleLib for FraxAggregatorV3Interface; using FixLib for uint192; - FraxAggregatorV3Interface public immutable targetPerTokChainlinkFeed; // {target/tok} - uint48 public immutable targetPerTokChainlinkTimeout; + FraxAggregatorV3Interface public immutable targetPerRefChainlinkFeed; // {target/tok} + uint48 public immutable targetPerRefChainlinkTimeout; /// @param config.chainlinkFeed {UoA/target} price of ETH in USD terms - /// @param _targetPerTokChainlinkFeed {target/tok} price of cbETH in ETH terms + /// @param _targetPerRefChainlinkFeed {target/tok} price of frxETH in ETH terms constructor( CollateralConfig memory config, uint192 revenueHiding, - FraxAggregatorV3Interface _targetPerTokChainlinkFeed, - uint48 _targetPerTokChainlinkTimeout + FraxAggregatorV3Interface _targetPerRefChainlinkFeed, + uint48 _targetPerRefChainlinkTimeout ) AppreciatingFiatCollateral(config, revenueHiding) { require(config.defaultThreshold > 0, "defaultThreshold zero"); - require(address(_targetPerTokChainlinkFeed) != address(0), "missing targetPerTok feed"); - require(_targetPerTokChainlinkTimeout != 0, "targetPerTokChainlinkTimeout zero"); + require(address(_targetPerRefChainlinkFeed) != address(0), "missing targetPerRef feed"); + require(_targetPerRefChainlinkTimeout != 0, "targetPerRefChainlinkTimeout zero"); - targetPerTokChainlinkFeed = _targetPerTokChainlinkFeed; - targetPerTokChainlinkTimeout = _targetPerTokChainlinkTimeout; + targetPerRefChainlinkFeed = _targetPerRefChainlinkFeed; + targetPerRefChainlinkTimeout = _targetPerRefChainlinkTimeout; } /// Can revert, used by other contract functions in order to catch errors @@ -60,17 +54,16 @@ contract SFraxEthCollateral is AppreciatingFiatCollateral { uint192 pegPrice ) { - uint192 targetPerTok = targetPerTokChainlinkFeed.price(targetPerTokChainlinkTimeout); - // {UoA/tok} = {UoA/target} * {target/tok} - uint192 p = chainlinkFeed.price(oracleTimeout).mul(targetPerTok); + // {target/ref} Get current market peg ({eth/sfrxeth}) + pegPrice = targetPerRefChainlinkFeed.price(targetPerRefChainlinkTimeout); + + // {UoA/tok} = {UoA/target} * {target/ref} * {ref/tok} + uint192 p = chainlinkFeed.price(oracleTimeout).mul(pegPrice).mul(_underlyingRefPerTok()); uint192 err = p.mul(oracleError, CEIL); high = p + err; low = p - err; // assert(low <= high); obviously true just by inspection - - // {target/ref} = {target/tok} / {ref/tok} - pegPrice = targetPerTok.div(_underlyingRefPerTok()); } /// @return {ref/tok} Quantity of whole reference units per whole collateral tokens diff --git a/contracts/plugins/mocks/ChainlinkMock.sol b/contracts/plugins/mocks/ChainlinkMock.sol index 045461297c..17cd3b6edd 100644 --- a/contracts/plugins/mocks/ChainlinkMock.sol +++ b/contracts/plugins/mocks/ChainlinkMock.sol @@ -51,7 +51,7 @@ contract MockV3Aggregator is AggregatorV3Interface { latestAnsweredRound = latestRound; } - // used by Frax oracl + // used by Frax oracle function addRoundData(bool isBadData, uint104 low, uint104 high, uint40 timestamp) public { latestAnswer = int104(low + high) / 2; latestTimestamp = block.timestamp; diff --git a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts index a46a41e9e3..0c558284b0 100644 --- a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts +++ b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts @@ -27,7 +27,7 @@ import { FRX_ETH, SFRX_ETH, ETH_USD_PRICE_FEED, - SFRXETH_ETH_PRICE_FEED, + FRXETH_ETH_PRICE_FEED, } from './constants' import { advanceTime, @@ -43,7 +43,7 @@ import { interface SFrxEthCollateralFixtureContext extends CollateralFixtureContext { frxEth: ERC20Mock sfrxEth: IsfrxEth - targetPerTokChainlinkFeed: MockV3Aggregator + targetPerRefChainlinkFeed: MockV3Aggregator } /* @@ -51,8 +51,8 @@ interface SFrxEthCollateralFixtureContext extends CollateralFixtureContext { */ interface SfrxEthCollateralOpts extends CollateralOpts { - targetPerTokChainlinkFeed?: string - targetPerTokChainlinkTimeout?: BigNumberish + targetPerRefChainlinkFeed?: string + targetPerRefChainlinkTimeout?: BigNumberish } export const defaultRethCollateralOpts: SfrxEthCollateralOpts = { @@ -67,8 +67,8 @@ export const defaultRethCollateralOpts: SfrxEthCollateralOpts = { defaultThreshold: DEFAULT_THRESHOLD, delayUntilDefault: DELAY_UNTIL_DEFAULT, revenueHiding: fp('0'), - targetPerTokChainlinkFeed: SFRXETH_ETH_PRICE_FEED, - targetPerTokChainlinkTimeout: ORACLE_TIMEOUT, + targetPerRefChainlinkFeed: FRXETH_ETH_PRICE_FEED, + targetPerRefChainlinkTimeout: ORACLE_TIMEOUT, } export const deployCollateral = async ( @@ -93,18 +93,17 @@ export const deployCollateral = async ( delayUntilDefault: opts.delayUntilDefault, }, opts.revenueHiding, - opts.targetPerTokChainlinkFeed ?? SFRXETH_ETH_PRICE_FEED, - opts.targetPerTokChainlinkTimeout ?? ORACLE_TIMEOUT, + opts.targetPerRefChainlinkFeed ?? FRXETH_ETH_PRICE_FEED, + opts.targetPerRefChainlinkTimeout ?? ORACLE_TIMEOUT, { gasLimit: 2000000000 } ) await collateral.deployed() // Push forward chainlink feed await pushOracleForward(opts.chainlinkFeed!) - // await pushOracleForward(opts.targetPerTokChainlinkFeed!) - opts.targetPerTokChainlinkFeed?.toLocaleLowerCase() == SFRXETH_ETH_PRICE_FEED.toLocaleLowerCase() - ? await pushFraxOracleForward(opts.targetPerTokChainlinkFeed ?? SFRXETH_ETH_PRICE_FEED) - : await pushOracleForward(opts.targetPerTokChainlinkFeed!) + opts.targetPerRefChainlinkFeed?.toLocaleLowerCase() == FRXETH_ETH_PRICE_FEED.toLocaleLowerCase() + ? await pushFraxOracleForward(opts.targetPerRefChainlinkFeed ?? FRXETH_ETH_PRICE_FEED) + : await pushOracleForward(opts.targetPerRefChainlinkFeed!) // sometimes we are trying to test a negative test case and we want this to fail silently // fortunately this syntax fails silently because our tools are terrible await expect(collateral.refresh()) @@ -113,7 +112,7 @@ export const deployCollateral = async ( } const chainlinkDefaultAnswer = bn('1600e8') -const targetPerTokChainlinkDefaultAnswer = fp('1.026349814867976366') +const targetPerRefChainlinkDefaultAnswer = fp('1.026349814867976366') type Fixture = () => Promise @@ -133,12 +132,12 @@ const makeCollateralFixtureContext = ( ) collateralOpts.chainlinkFeed = chainlinkFeed.address - const targetPerTokChainlinkFeed = ( - await MockV3AggregatorFactory.deploy(18, targetPerTokChainlinkDefaultAnswer) + const targetPerRefChainlinkFeed = ( + await MockV3AggregatorFactory.deploy(18, targetPerRefChainlinkDefaultAnswer) ) - collateralOpts.targetPerTokChainlinkFeed = targetPerTokChainlinkFeed.address - collateralOpts.targetPerTokChainlinkTimeout = ORACLE_TIMEOUT + collateralOpts.targetPerRefChainlinkFeed = targetPerRefChainlinkFeed.address + collateralOpts.targetPerRefChainlinkTimeout = ORACLE_TIMEOUT const frxEth = (await ethers.getContractAt('ERC20Mock', FRX_ETH)) as ERC20Mock const sfrxEth = (await ethers.getContractAt('IsfrxEth', SFRX_ETH)) as IsfrxEth @@ -150,7 +149,7 @@ const makeCollateralFixtureContext = ( chainlinkFeed, frxEth, sfrxEth, - targetPerTokChainlinkFeed, + targetPerRefChainlinkFeed, tok: sfrxEth, } } @@ -174,7 +173,7 @@ const mintCollateralTo: MintCollateralFunc = as amount, recipient, ctx.chainlinkFeed, - ctx.targetPerTokChainlinkFeed + ctx.targetPerRefChainlinkFeed ) } @@ -184,9 +183,9 @@ const changeTargetPerRef = async ( ) => { // We leave the actual refPerTok exchange where it is and just change {target/tok} { - const lastRound = await ctx.targetPerTokChainlinkFeed.latestRoundData() + const lastRound = await ctx.targetPerRefChainlinkFeed.latestRoundData() const nextAnswer = lastRound.answer.add(lastRound.answer.mul(percentChange).div(100)) - await ctx.targetPerTokChainlinkFeed.updateAnswer(nextAnswer) + await ctx.targetPerRefChainlinkFeed.updateAnswer(nextAnswer) } } @@ -229,9 +228,8 @@ const increaseRefPerTok = async ( // push chainlink oracle forward so that tryPrice() still works const latestRoundData = await ctx.chainlinkFeed.latestRoundData() await ctx.chainlinkFeed.updateAnswer(latestRoundData.answer) - const latestRoundDataTpR = await ctx.targetPerTokChainlinkFeed.latestRoundData() - const nextAnswerTpR = latestRoundDataTpR.answer.add(latestRoundDataTpR.answer.mul(pctIncrease).div(100)) - await ctx.targetPerTokChainlinkFeed.updateAnswer(nextAnswerTpR) + const latestRoundDataTpR = await ctx.targetPerRefChainlinkFeed.latestRoundData() + await ctx.targetPerRefChainlinkFeed.updateAnswer(latestRoundDataTpR.answer) } const getExpectedPrice = async (ctx: SFrxEthCollateralFixtureContext): Promise => { @@ -239,12 +237,16 @@ const getExpectedPrice = async (ctx: SFrxEthCollateralFixtureContext): Promise { const chainlinkFeed = ( await (await ethers.getContractFactory('MockV3Aggregator')).deploy(8, chainlinkDefaultAnswer) ) - const targetPerTokenChainlinkFeed = ( + const targetPerRefenChainlinkFeed = ( await ( await ethers.getContractFactory('MockV3Aggregator') - ).deploy(18, targetPerTokChainlinkDefaultAnswer) + ).deploy(18, targetPerRefChainlinkDefaultAnswer) ) const collateral = await deployCollateral({ erc20: erc20.address, revenueHiding: fp('0.01'), chainlinkFeed: chainlinkFeed.address, - targetPerTokChainlinkFeed: targetPerTokenChainlinkFeed.address, + targetPerRefChainlinkFeed: targetPerRefenChainlinkFeed.address, }) // Should remain SOUND after a 1% decrease diff --git a/test/plugins/individual-collateral/frax-eth/constants.ts b/test/plugins/individual-collateral/frax-eth/constants.ts index 9b306d309e..f9a1c72752 100644 --- a/test/plugins/individual-collateral/frax-eth/constants.ts +++ b/test/plugins/individual-collateral/frax-eth/constants.ts @@ -7,7 +7,7 @@ export const FRX_ETH = networkConfig['31337'].tokens.frxETH as string export const SFRX_ETH = networkConfig['31337'].tokens.sfrxETH as string export const WETH = networkConfig['31337'].tokens.WETH as string export const FRX_ETH_MINTER = '0xbAFA44EFE7901E04E39Dad13167D089C559c1138' -export const SFRXETH_ETH_PRICE_FEED = networkConfig['31337'].chainlinkFeeds.sfrxETH as string +export const FRXETH_ETH_PRICE_FEED = networkConfig['31337'].chainlinkFeeds.frxETH as string export const PRICE_TIMEOUT = bn('604800') // 1 week export const ORACLE_TIMEOUT = bn(86400) // 24 hours in seconds From 825b74653ee364390cf76073bd193039bf9d5298 Mon Sep 17 00:00:00 2001 From: Patrick McKelvy Date: Mon, 15 Jan 2024 19:48:12 -0500 Subject: [PATCH 08/11] refactor oracle to use the curve ema. --- common/configuration.ts | 4 ++ .../assets/frax-eth/SFraxEthCollateral.sol | 31 ++++----- .../CurvePoolEmaPriceOracleWithMinMax.sol | 67 +++++++++++++++++++ .../ICurvePoolEmaPriceOracleWithMinMax.sol | 14 ++++ .../frax-eth/SFrxEthTestSuite.test.ts | 53 +++++---------- .../frax-eth/constants.ts | 2 + .../individual-collateral/frax-eth/helpers.ts | 6 +- 7 files changed, 122 insertions(+), 55 deletions(-) create mode 100644 contracts/plugins/assets/frax-eth/vendor/CurvePoolEmaPriceOracleWithMinMax.sol create mode 100644 contracts/plugins/assets/frax-eth/vendor/ICurvePoolEmaPriceOracleWithMinMax.sol diff --git a/common/configuration.ts b/common/configuration.ts index a5960ac404..71ee05a011 100644 --- a/common/configuration.ts +++ b/common/configuration.ts @@ -123,6 +123,7 @@ interface INetworkConfig { AAVE_V3_INCENTIVES_CONTROLLER?: string AAVE_V3_POOL?: string STARGATE_STAKING_CONTRACT?: string + CURVE_POOL_ETH_FRXETH?: string } export const networkConfig: { [key: string]: INetworkConfig } = { @@ -239,6 +240,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = { AAVE_V3_POOL: '0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2', AAVE_V3_INCENTIVES_CONTROLLER: '0x8164Cc65827dcFe994AB23944CBC90e0aa80bFcb', STARGATE_STAKING_CONTRACT: '0xB0D502E938ed5f4df2E681fE6E419ff29631d62b', + CURVE_POOL_ETH_FRXETH: '0xa1F8A6807c402E4A15ef4EBa36528A3FED24E577' }, '1': { name: 'mainnet', @@ -344,6 +346,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = { AAVE_V3_POOL: '0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2', AAVE_V3_INCENTIVES_CONTROLLER: '0x8164Cc65827dcFe994AB23944CBC90e0aa80bFcb', STARGATE_STAKING_CONTRACT: '0xB0D502E938ed5f4df2E681fE6E419ff29631d62b', + CURVE_POOL_ETH_FRXETH: '0xa1F8A6807c402E4A15ef4EBa36528A3FED24E577' }, '3': { name: 'tenderly', @@ -444,6 +447,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = { AAVE_V3_POOL: '0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2', AAVE_V3_INCENTIVES_CONTROLLER: '0x8164Cc65827dcFe994AB23944CBC90e0aa80bFcb', STARGATE_STAKING_CONTRACT: '0xB0D502E938ed5f4df2E681fE6E419ff29631d62b', + CURVE_POOL_ETH_FRXETH: '0xa1F8A6807c402E4A15ef4EBa36528A3FED24E577' }, '5': { name: 'goerli', diff --git a/contracts/plugins/assets/frax-eth/SFraxEthCollateral.sol b/contracts/plugins/assets/frax-eth/SFraxEthCollateral.sol index d9083e7e73..ccafd1163d 100644 --- a/contracts/plugins/assets/frax-eth/SFraxEthCollateral.sol +++ b/contracts/plugins/assets/frax-eth/SFraxEthCollateral.sol @@ -7,6 +7,7 @@ import "../AppreciatingFiatCollateral.sol"; import "../OracleLib.sol"; import "../FraxOracleLib.sol"; import "./vendor/IsfrxEth.sol"; +import "./vendor/CurvePoolEmaPriceOracleWithMinMax.sol"; /** * @title SFraxEthCollateral @@ -16,28 +17,28 @@ import "./vendor/IsfrxEth.sol"; * tar = ETH * UoA = USD */ -contract SFraxEthCollateral is AppreciatingFiatCollateral { +contract SFraxEthCollateral is AppreciatingFiatCollateral, CurvePoolEmaPriceOracleWithMinMax { using OracleLib for AggregatorV3Interface; using FraxOracleLib for FraxAggregatorV3Interface; using FixLib for uint192; - FraxAggregatorV3Interface public immutable targetPerRefChainlinkFeed; // {target/tok} - uint48 public immutable targetPerRefChainlinkTimeout; - /// @param config.chainlinkFeed {UoA/target} price of ETH in USD terms - /// @param _targetPerRefChainlinkFeed {target/tok} price of frxETH in ETH terms + /// @param revenueHiding {1e18} percent amount of revenue to hide constructor( CollateralConfig memory config, uint192 revenueHiding, - FraxAggregatorV3Interface _targetPerRefChainlinkFeed, - uint48 _targetPerRefChainlinkTimeout - ) AppreciatingFiatCollateral(config, revenueHiding) { + address curvePoolEmaPriceOracleAddress, + uint256 _minimumCurvePoolEma, + uint256 _maximumCurvePoolEma + ) + AppreciatingFiatCollateral(config, revenueHiding) + CurvePoolEmaPriceOracleWithMinMax( + curvePoolEmaPriceOracleAddress, + _minimumCurvePoolEma, + _maximumCurvePoolEma + ) + { require(config.defaultThreshold > 0, "defaultThreshold zero"); - require(address(_targetPerRefChainlinkFeed) != address(0), "missing targetPerRef feed"); - require(_targetPerRefChainlinkTimeout != 0, "targetPerRefChainlinkTimeout zero"); - - targetPerRefChainlinkFeed = _targetPerRefChainlinkFeed; - targetPerRefChainlinkTimeout = _targetPerRefChainlinkTimeout; } /// Can revert, used by other contract functions in order to catch errors @@ -54,8 +55,8 @@ contract SFraxEthCollateral is AppreciatingFiatCollateral { uint192 pegPrice ) { - // {target/ref} Get current market peg ({eth/sfrxeth}) - pegPrice = targetPerRefChainlinkFeed.price(targetPerRefChainlinkTimeout); + // {target/ref} Get current market peg ({eth/frxeth}) + pegPrice = _safeWrap(_getCurvePoolToken1EmaPrice()); // {UoA/tok} = {UoA/target} * {target/ref} * {ref/tok} uint192 p = chainlinkFeed.price(oracleTimeout).mul(pegPrice).mul(_underlyingRefPerTok()); diff --git a/contracts/plugins/assets/frax-eth/vendor/CurvePoolEmaPriceOracleWithMinMax.sol b/contracts/plugins/assets/frax-eth/vendor/CurvePoolEmaPriceOracleWithMinMax.sol new file mode 100644 index 0000000000..f0fe9c3595 --- /dev/null +++ b/contracts/plugins/assets/frax-eth/vendor/CurvePoolEmaPriceOracleWithMinMax.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: ISC +pragma solidity ^0.8.19; + +// Inspired by Frax Finance: https://github.com/FraxFinance + +// Original Author +// Drake Evans: https://github.com/DrakeEvans + +// Original Reviewers +// Dennis: https://github.com/denett + +// ==================================================================== + +import { ICurvePoolEmaPriceOracleWithMinMax } from "./ICurvePoolEmaPriceOracleWithMinMax.sol"; + +interface IEmaPriceOracleStableSwap { + function price_oracle() external view returns (uint256); +} + +struct ConstructorParams { + address curvePoolEmaPriceOracleAddress; + uint256 minimumCurvePoolEma; + uint256 maximumCurvePoolEma; +} + +/// @title CurvePoolEmaPriceOracleWithMinMax +/// @author Drake Evans (Frax Finance) https://github.com/drakeevans +/// @notice An oracle for getting EMA prices from Curve +contract CurvePoolEmaPriceOracleWithMinMax is ICurvePoolEmaPriceOracleWithMinMax { + /// @notice Curve pool, source of EMA + address public immutable CURVE_POOL_EMA_PRICE_ORACLE; + + /// @notice Precision of Curve pool price_oracle() + uint256 public constant CURVE_POOL_EMA_PRICE_ORACLE_PRECISION = 1e18; + + /// @notice Maximum price of token1 in token0 units of the EMA + /// @dev Must match precision of EMA + uint256 public minimumCurvePoolEma; + + /// @notice Maximum price of token1 in token0 units of the EMA + /// @dev Must match precision of EMA + uint256 public maximumCurvePoolEma; + + constructor( + address curvePoolEmaPriceOracleAddress, + uint256 _minimumCurvePoolEma, + uint256 _maximumCurvePoolEma + ) { + CURVE_POOL_EMA_PRICE_ORACLE = curvePoolEmaPriceOracleAddress; + minimumCurvePoolEma = _minimumCurvePoolEma; + maximumCurvePoolEma = _maximumCurvePoolEma; + } + + function _getCurvePoolToken1EmaPrice() internal view returns (uint256 _token1Price) { + uint256 _priceRaw = IEmaPriceOracleStableSwap(CURVE_POOL_EMA_PRICE_ORACLE).price_oracle(); + uint256 _price = _priceRaw > maximumCurvePoolEma ? maximumCurvePoolEma : _priceRaw; + + _token1Price = _price < minimumCurvePoolEma ? minimumCurvePoolEma : _price; + } + + /// @notice The ```getCurvePoolToken1EmaPrice``` function gets the price of the second token in the Curve pool (token1) + /// @dev Returned in units of the first token (token0) + /// @return _emaPrice The price of the second token in the Curve pool + function getCurvePoolToken1EmaPrice() external view returns (uint256 _emaPrice) { + return _getCurvePoolToken1EmaPrice(); + } +} \ No newline at end of file diff --git a/contracts/plugins/assets/frax-eth/vendor/ICurvePoolEmaPriceOracleWithMinMax.sol b/contracts/plugins/assets/frax-eth/vendor/ICurvePoolEmaPriceOracleWithMinMax.sol new file mode 100644 index 0000000000..f738375e3a --- /dev/null +++ b/contracts/plugins/assets/frax-eth/vendor/ICurvePoolEmaPriceOracleWithMinMax.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +interface ICurvePoolEmaPriceOracleWithMinMax { + function CURVE_POOL_EMA_PRICE_ORACLE() external view returns (address); + + function CURVE_POOL_EMA_PRICE_ORACLE_PRECISION() external view returns (uint256); + + function getCurvePoolToken1EmaPrice() external view returns (uint256 _emaPrice); + + function maximumCurvePoolEma() external view returns (uint256); + + function minimumCurvePoolEma() external view returns (uint256); +} diff --git a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts index 0c558284b0..44a00283aa 100644 --- a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts +++ b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts @@ -11,8 +11,9 @@ import { SfraxEthMock, TestICollateral, IsfrxEth, + SFraxEthCollateral, } from '../../../../typechain' -import { pushFraxOracleForward, pushOracleForward } from '../../../utils/oracles' +import { pushOracleForward } from '../../../utils/oracles' import { bn, fp } from '../../../../common/numbers' import { CollateralStatus } from '../../../../common/constants' import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers' @@ -27,7 +28,7 @@ import { FRX_ETH, SFRX_ETH, ETH_USD_PRICE_FEED, - FRXETH_ETH_PRICE_FEED, + CURVE_POOL_EMA_PRICE_ORACLE_ADDRESS, } from './constants' import { advanceTime, @@ -43,7 +44,6 @@ import { interface SFrxEthCollateralFixtureContext extends CollateralFixtureContext { frxEth: ERC20Mock sfrxEth: IsfrxEth - targetPerRefChainlinkFeed: MockV3Aggregator } /* @@ -51,8 +51,9 @@ interface SFrxEthCollateralFixtureContext extends CollateralFixtureContext { */ interface SfrxEthCollateralOpts extends CollateralOpts { - targetPerRefChainlinkFeed?: string - targetPerRefChainlinkTimeout?: BigNumberish + curvePoolEmaPriceOracleAddress?: string + _minimumCurvePoolEma?: BigNumberish + _maximumCurvePoolEma?: BigNumberish } export const defaultRethCollateralOpts: SfrxEthCollateralOpts = { @@ -67,8 +68,9 @@ export const defaultRethCollateralOpts: SfrxEthCollateralOpts = { defaultThreshold: DEFAULT_THRESHOLD, delayUntilDefault: DELAY_UNTIL_DEFAULT, revenueHiding: fp('0'), - targetPerRefChainlinkFeed: FRXETH_ETH_PRICE_FEED, - targetPerRefChainlinkTimeout: ORACLE_TIMEOUT, + curvePoolEmaPriceOracleAddress: CURVE_POOL_EMA_PRICE_ORACLE_ADDRESS, + _minimumCurvePoolEma: 0, + _maximumCurvePoolEma: fp(1), } export const deployCollateral = async ( @@ -93,17 +95,15 @@ export const deployCollateral = async ( delayUntilDefault: opts.delayUntilDefault, }, opts.revenueHiding, - opts.targetPerRefChainlinkFeed ?? FRXETH_ETH_PRICE_FEED, - opts.targetPerRefChainlinkTimeout ?? ORACLE_TIMEOUT, + opts.curvePoolEmaPriceOracleAddress ?? CURVE_POOL_EMA_PRICE_ORACLE_ADDRESS, + opts._minimumCurvePoolEma ?? 0, + opts._maximumCurvePoolEma ?? fp(1), { gasLimit: 2000000000 } ) await collateral.deployed() // Push forward chainlink feed await pushOracleForward(opts.chainlinkFeed!) - opts.targetPerRefChainlinkFeed?.toLocaleLowerCase() == FRXETH_ETH_PRICE_FEED.toLocaleLowerCase() - ? await pushFraxOracleForward(opts.targetPerRefChainlinkFeed ?? FRXETH_ETH_PRICE_FEED) - : await pushOracleForward(opts.targetPerRefChainlinkFeed!) // sometimes we are trying to test a negative test case and we want this to fail silently // fortunately this syntax fails silently because our tools are terrible await expect(collateral.refresh()) @@ -136,9 +136,6 @@ const makeCollateralFixtureContext = ( await MockV3AggregatorFactory.deploy(18, targetPerRefChainlinkDefaultAnswer) ) - collateralOpts.targetPerRefChainlinkFeed = targetPerRefChainlinkFeed.address - collateralOpts.targetPerRefChainlinkTimeout = ORACLE_TIMEOUT - const frxEth = (await ethers.getContractAt('ERC20Mock', FRX_ETH)) as ERC20Mock const sfrxEth = (await ethers.getContractAt('IsfrxEth', SFRX_ETH)) as IsfrxEth const collateral = await deployCollateral(collateralOpts) @@ -167,26 +164,14 @@ const mintCollateralTo: MintCollateralFunc = as user: SignerWithAddress, recipient: string ) => { - await mintSfrxETH( - ctx.sfrxEth, - user, - amount, - recipient, - ctx.chainlinkFeed, - ctx.targetPerRefChainlinkFeed - ) + await mintSfrxETH(ctx.sfrxEth, user, amount, recipient, ctx.chainlinkFeed) } const changeTargetPerRef = async ( ctx: SFrxEthCollateralFixtureContext, percentChange: BigNumber ) => { - // We leave the actual refPerTok exchange where it is and just change {target/tok} - { - const lastRound = await ctx.targetPerRefChainlinkFeed.latestRoundData() - const nextAnswer = lastRound.answer.add(lastRound.answer.mul(percentChange).div(100)) - await ctx.targetPerRefChainlinkFeed.updateAnswer(nextAnswer) - } + // TODO } const reduceTargetPerRef = async ( @@ -228,8 +213,6 @@ const increaseRefPerTok = async ( // push chainlink oracle forward so that tryPrice() still works const latestRoundData = await ctx.chainlinkFeed.latestRoundData() await ctx.chainlinkFeed.updateAnswer(latestRoundData.answer) - const latestRoundDataTpR = await ctx.targetPerRefChainlinkFeed.latestRoundData() - await ctx.targetPerRefChainlinkFeed.updateAnswer(latestRoundDataTpR.answer) } const getExpectedPrice = async (ctx: SFrxEthCollateralFixtureContext): Promise => { @@ -237,14 +220,15 @@ const getExpectedPrice = async (ctx: SFrxEthCollateralFixtureContext): Promise { erc20: erc20.address, revenueHiding: fp('0.01'), chainlinkFeed: chainlinkFeed.address, - targetPerRefChainlinkFeed: targetPerRefenChainlinkFeed.address, }) // Should remain SOUND after a 1% decrease diff --git a/test/plugins/individual-collateral/frax-eth/constants.ts b/test/plugins/individual-collateral/frax-eth/constants.ts index f9a1c72752..aa6cda39c6 100644 --- a/test/plugins/individual-collateral/frax-eth/constants.ts +++ b/test/plugins/individual-collateral/frax-eth/constants.ts @@ -8,6 +8,8 @@ export const SFRX_ETH = networkConfig['31337'].tokens.sfrxETH as string export const WETH = networkConfig['31337'].tokens.WETH as string export const FRX_ETH_MINTER = '0xbAFA44EFE7901E04E39Dad13167D089C559c1138' export const FRXETH_ETH_PRICE_FEED = networkConfig['31337'].chainlinkFeeds.frxETH as string +export const CURVE_POOL_EMA_PRICE_ORACLE_ADDRESS = networkConfig['31337'] + .CURVE_POOL_ETH_FRXETH as string export const PRICE_TIMEOUT = bn('604800') // 1 week export const ORACLE_TIMEOUT = bn(86400) // 24 hours in seconds diff --git a/test/plugins/individual-collateral/frax-eth/helpers.ts b/test/plugins/individual-collateral/frax-eth/helpers.ts index 3323c07939..99ce493da5 100644 --- a/test/plugins/individual-collateral/frax-eth/helpers.ts +++ b/test/plugins/individual-collateral/frax-eth/helpers.ts @@ -13,8 +13,7 @@ export const mintSfrxETH = async ( account: SignerWithAddress, amount: BigNumberish, recipient: string, - chainlinkFeed: MockV3Aggregator, - targetPerTokChainlinkFeed: MockV3Aggregator + chainlinkFeed: MockV3Aggregator ) => { await setBalance(account.address, fp(100000)) const frxEthMinter: IfrxEthMinter = ( @@ -34,9 +33,6 @@ export const mintSfrxETH = async ( // push chainlink oracle forward so that tryPrice() still works const lastAnswer = await chainlinkFeed.latestRoundData() await chainlinkFeed.updateAnswer(lastAnswer.answer) - - const lastAnswerTpR = await targetPerTokChainlinkFeed.latestRoundData() - await targetPerTokChainlinkFeed.updateAnswer(lastAnswerTpR.answer) } export const mintFrxETH = async ( From bfeb22d6a27354507ab34c84f22f665a7076c762 Mon Sep 17 00:00:00 2001 From: Patrick McKelvy Date: Mon, 15 Jan 2024 19:55:40 -0500 Subject: [PATCH 09/11] need to fix oracle manipulation in tests. --- .../frax-eth/vendor/CurvePoolEmaPriceOracleWithMinMax.sol | 2 +- .../frax-eth/vendor/ICurvePoolEmaPriceOracleWithMinMax.sol | 2 +- .../individual-collateral/frax-eth/SFrxEthTestSuite.test.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/contracts/plugins/assets/frax-eth/vendor/CurvePoolEmaPriceOracleWithMinMax.sol b/contracts/plugins/assets/frax-eth/vendor/CurvePoolEmaPriceOracleWithMinMax.sol index f0fe9c3595..c39f943cf9 100644 --- a/contracts/plugins/assets/frax-eth/vendor/CurvePoolEmaPriceOracleWithMinMax.sol +++ b/contracts/plugins/assets/frax-eth/vendor/CurvePoolEmaPriceOracleWithMinMax.sol @@ -31,7 +31,7 @@ contract CurvePoolEmaPriceOracleWithMinMax is ICurvePoolEmaPriceOracleWithMinMax address public immutable CURVE_POOL_EMA_PRICE_ORACLE; /// @notice Precision of Curve pool price_oracle() - uint256 public constant CURVE_POOL_EMA_PRICE_ORACLE_PRECISION = 1e18; + uint256 public constant CURVE_POOL_EMA_PRICE_ORACLE_DECIMALS = 18; /// @notice Maximum price of token1 in token0 units of the EMA /// @dev Must match precision of EMA diff --git a/contracts/plugins/assets/frax-eth/vendor/ICurvePoolEmaPriceOracleWithMinMax.sol b/contracts/plugins/assets/frax-eth/vendor/ICurvePoolEmaPriceOracleWithMinMax.sol index f738375e3a..b947e1a9d8 100644 --- a/contracts/plugins/assets/frax-eth/vendor/ICurvePoolEmaPriceOracleWithMinMax.sol +++ b/contracts/plugins/assets/frax-eth/vendor/ICurvePoolEmaPriceOracleWithMinMax.sol @@ -4,7 +4,7 @@ pragma solidity 0.8.19; interface ICurvePoolEmaPriceOracleWithMinMax { function CURVE_POOL_EMA_PRICE_ORACLE() external view returns (address); - function CURVE_POOL_EMA_PRICE_ORACLE_PRECISION() external view returns (uint256); + function CURVE_POOL_EMA_PRICE_ORACLE_DECIMALS() external view returns (uint256); function getCurvePoolToken1EmaPrice() external view returns (uint256 _emaPrice); diff --git a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts index 44a00283aa..874e7c16a3 100644 --- a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts +++ b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts @@ -222,7 +222,7 @@ const getExpectedPrice = async (ctx: SFrxEthCollateralFixtureContext): Promise Date: Tue, 16 Jan 2024 10:41:47 -0500 Subject: [PATCH 10/11] noisy new collateral test param. --- .../mocks/EmaPriceOracleStableSwapMock.sol | 33 +++++++++++++++++++ .../aave-v3/AaveV3FiatCollateral.test.ts | 1 + .../ankr/AnkrEthCollateralTestSuite.test.ts | 1 + .../cbeth/CBETHCollateral.test.ts | 1 + .../cbeth/CBETHCollateralL2.test.ts | 1 + .../individual-collateral/collateralTests.ts | 3 +- .../compoundv3/CometTestSuite.test.ts | 1 + .../curve/crv/CrvStableMetapoolSuite.test.ts | 1 + .../CrvStableRTokenMetapoolTestSuite.test.ts | 1 + .../curve/crv/CrvStableTestSuite.test.ts | 1 + .../curve/cvx/CvxStableMetapoolSuite.test.ts | 1 + .../CvxStableRTokenMetapoolTestSuite.test.ts | 1 + .../curve/cvx/CvxStableTestSuite.test.ts | 1 + .../dsr/SDaiCollateralTestSuite.test.ts | 1 + .../flux-finance/FTokenFiatCollateral.test.ts | 1 + .../frax-eth/SFrxEthTestSuite.test.ts | 30 ++++++++++------- .../frax/SFraxCollateralTestSuite.test.ts | 1 + .../lido/LidoStakedEthTestSuite.test.ts | 1 + .../MorphoAAVEFiatCollateral.test.ts | 1 + .../MorphoAAVENonFiatCollateral.test.ts | 1 + ...orphoAAVESelfReferentialCollateral.test.ts | 1 + .../individual-collateral/pluginTestTypes.ts | 3 ++ .../RethCollateralTestSuite.test.ts | 1 + .../stargate/StargateUSDCTestSuite.test.ts | 1 + .../YearnV2CurveFiatCollateral.test.ts | 1 + 25 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 contracts/plugins/mocks/EmaPriceOracleStableSwapMock.sol diff --git a/contracts/plugins/mocks/EmaPriceOracleStableSwapMock.sol b/contracts/plugins/mocks/EmaPriceOracleStableSwapMock.sol new file mode 100644 index 0000000000..6d94dc396f --- /dev/null +++ b/contracts/plugins/mocks/EmaPriceOracleStableSwapMock.sol @@ -0,0 +1,33 @@ +// SPDX-License-Identifier: ISC +pragma solidity ^0.8.19; + +interface IEmaPriceOracleStableSwap { + function price_oracle() external view returns (uint256); +} + +/// @title CurvePoolEmaPriceOracleWithMinMax +/// @author Drake Evans (Frax Finance) https://github.com/drakeevans +/// @notice An oracle for getting EMA prices from Curve +contract EmaPriceOracleStableSwapMock is IEmaPriceOracleStableSwap { + uint256 public initPrice; + uint256 internal _price; + + constructor( + uint256 _initPrice + ) { + initPrice = _initPrice; + _price = _initPrice; + } + + function resetPrice() external { + _price = initPrice; + } + + function setPrice(uint256 newPrice) external { + _price = newPrice; + } + + function price_oracle() external view returns (uint256) { + return _price; + } +} \ No newline at end of file diff --git a/test/plugins/individual-collateral/aave-v3/AaveV3FiatCollateral.test.ts b/test/plugins/individual-collateral/aave-v3/AaveV3FiatCollateral.test.ts index a15ac37a23..b2628df278 100644 --- a/test/plugins/individual-collateral/aave-v3/AaveV3FiatCollateral.test.ts +++ b/test/plugins/individual-collateral/aave-v3/AaveV3FiatCollateral.test.ts @@ -213,6 +213,7 @@ export const stableOpts = { itClaimsRewards: it.skip, // untested: very complicated to get Aave to handout rewards, and none are live currently. // The StaticATokenV3LM contract is formally verified and the function we added for claimRewards() is pretty obviously correct. itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itHasRevenueHiding: it, itChecksNonZeroDefaultThreshold: it, diff --git a/test/plugins/individual-collateral/ankr/AnkrEthCollateralTestSuite.test.ts b/test/plugins/individual-collateral/ankr/AnkrEthCollateralTestSuite.test.ts index e21a0f66a0..8a3a07a83f 100644 --- a/test/plugins/individual-collateral/ankr/AnkrEthCollateralTestSuite.test.ts +++ b/test/plugins/individual-collateral/ankr/AnkrEthCollateralTestSuite.test.ts @@ -286,6 +286,7 @@ const opts = { getExpectedPrice, itClaimsRewards: it.skip, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itChecksPriceChanges: it, itHasRevenueHiding: it, diff --git a/test/plugins/individual-collateral/cbeth/CBETHCollateral.test.ts b/test/plugins/individual-collateral/cbeth/CBETHCollateral.test.ts index 8f5bb5efe1..cd35ee5c0c 100644 --- a/test/plugins/individual-collateral/cbeth/CBETHCollateral.test.ts +++ b/test/plugins/individual-collateral/cbeth/CBETHCollateral.test.ts @@ -243,6 +243,7 @@ const opts = { getExpectedPrice, itClaimsRewards: it.skip, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itChecksPriceChanges: it, itHasRevenueHiding: it, diff --git a/test/plugins/individual-collateral/cbeth/CBETHCollateralL2.test.ts b/test/plugins/individual-collateral/cbeth/CBETHCollateralL2.test.ts index fbc3f6874b..a4a9c32425 100644 --- a/test/plugins/individual-collateral/cbeth/CBETHCollateralL2.test.ts +++ b/test/plugins/individual-collateral/cbeth/CBETHCollateralL2.test.ts @@ -274,6 +274,7 @@ const opts = { getExpectedPrice, itClaimsRewards: it.skip, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itChecksPriceChanges: it, itHasRevenueHiding: it, diff --git a/test/plugins/individual-collateral/collateralTests.ts b/test/plugins/individual-collateral/collateralTests.ts index 1ecfb0d657..f2eec97cb5 100644 --- a/test/plugins/individual-collateral/collateralTests.ts +++ b/test/plugins/individual-collateral/collateralTests.ts @@ -78,6 +78,7 @@ export default function fn( getExpectedPrice, itClaimsRewards, itChecksTargetPerRefDefault, + itChecksTargetPerRefDefaultUp, itChecksRefPerTokDefault, itChecksPriceChanges, itChecksNonZeroDefaultThreshold, @@ -491,7 +492,7 @@ export default function fn( } ) - itChecksTargetPerRefDefault( + itChecksTargetPerRefDefaultUp( 'enters IFFY state when target-per-ref depegs above high threshold', async () => { const delayUntilDefault = await collateral.delayUntilDefault() diff --git a/test/plugins/individual-collateral/compoundv3/CometTestSuite.test.ts b/test/plugins/individual-collateral/compoundv3/CometTestSuite.test.ts index 7c91bd2064..0aa72a386e 100644 --- a/test/plugins/individual-collateral/compoundv3/CometTestSuite.test.ts +++ b/test/plugins/individual-collateral/compoundv3/CometTestSuite.test.ts @@ -407,6 +407,7 @@ const opts = { getExpectedPrice, itClaimsRewards: it, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itChecksPriceChanges: it, itChecksNonZeroDefaultThreshold: it, diff --git a/test/plugins/individual-collateral/curve/crv/CrvStableMetapoolSuite.test.ts b/test/plugins/individual-collateral/curve/crv/CrvStableMetapoolSuite.test.ts index 12964d9bbc..2bf70ca7cd 100644 --- a/test/plugins/individual-collateral/curve/crv/CrvStableMetapoolSuite.test.ts +++ b/test/plugins/individual-collateral/curve/crv/CrvStableMetapoolSuite.test.ts @@ -219,6 +219,7 @@ const opts = { makeCollateralFixtureContext, mintCollateralTo, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itHasRevenueHiding: it, itClaimsRewards: it, diff --git a/test/plugins/individual-collateral/curve/crv/CrvStableRTokenMetapoolTestSuite.test.ts b/test/plugins/individual-collateral/curve/crv/CrvStableRTokenMetapoolTestSuite.test.ts index bbabc4c8aa..0ab94cd268 100644 --- a/test/plugins/individual-collateral/curve/crv/CrvStableRTokenMetapoolTestSuite.test.ts +++ b/test/plugins/individual-collateral/curve/crv/CrvStableRTokenMetapoolTestSuite.test.ts @@ -289,6 +289,7 @@ const opts = { makeCollateralFixtureContext, mintCollateralTo, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itHasRevenueHiding: it, itClaimsRewards: it, diff --git a/test/plugins/individual-collateral/curve/crv/CrvStableTestSuite.test.ts b/test/plugins/individual-collateral/curve/crv/CrvStableTestSuite.test.ts index 21260f9906..27a7a5c213 100644 --- a/test/plugins/individual-collateral/curve/crv/CrvStableTestSuite.test.ts +++ b/test/plugins/individual-collateral/curve/crv/CrvStableTestSuite.test.ts @@ -228,6 +228,7 @@ const opts = { makeCollateralFixtureContext, mintCollateralTo, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itHasRevenueHiding: it, itClaimsRewards: it, diff --git a/test/plugins/individual-collateral/curve/cvx/CvxStableMetapoolSuite.test.ts b/test/plugins/individual-collateral/curve/cvx/CvxStableMetapoolSuite.test.ts index ce3a93e9e0..e259ef05be 100644 --- a/test/plugins/individual-collateral/curve/cvx/CvxStableMetapoolSuite.test.ts +++ b/test/plugins/individual-collateral/curve/cvx/CvxStableMetapoolSuite.test.ts @@ -227,6 +227,7 @@ const opts = { makeCollateralFixtureContext, mintCollateralTo, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itHasRevenueHiding: it, itClaimsRewards: it, diff --git a/test/plugins/individual-collateral/curve/cvx/CvxStableRTokenMetapoolTestSuite.test.ts b/test/plugins/individual-collateral/curve/cvx/CvxStableRTokenMetapoolTestSuite.test.ts index 9000295bb8..11d8fcb5a9 100644 --- a/test/plugins/individual-collateral/curve/cvx/CvxStableRTokenMetapoolTestSuite.test.ts +++ b/test/plugins/individual-collateral/curve/cvx/CvxStableRTokenMetapoolTestSuite.test.ts @@ -291,6 +291,7 @@ const opts = { makeCollateralFixtureContext, mintCollateralTo, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itHasRevenueHiding: it, itClaimsRewards: it, diff --git a/test/plugins/individual-collateral/curve/cvx/CvxStableTestSuite.test.ts b/test/plugins/individual-collateral/curve/cvx/CvxStableTestSuite.test.ts index 8cbdd58345..859b762b3f 100644 --- a/test/plugins/individual-collateral/curve/cvx/CvxStableTestSuite.test.ts +++ b/test/plugins/individual-collateral/curve/cvx/CvxStableTestSuite.test.ts @@ -424,6 +424,7 @@ const opts = { makeCollateralFixtureContext, mintCollateralTo, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itHasRevenueHiding: it, itClaimsRewards: it, diff --git a/test/plugins/individual-collateral/dsr/SDaiCollateralTestSuite.test.ts b/test/plugins/individual-collateral/dsr/SDaiCollateralTestSuite.test.ts index 4d539a4a25..2919f0ebc4 100644 --- a/test/plugins/individual-collateral/dsr/SDaiCollateralTestSuite.test.ts +++ b/test/plugins/individual-collateral/dsr/SDaiCollateralTestSuite.test.ts @@ -211,6 +211,7 @@ const opts = { getExpectedPrice, itClaimsRewards: it.skip, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itChecksPriceChanges: it, itChecksNonZeroDefaultThreshold: it, diff --git a/test/plugins/individual-collateral/flux-finance/FTokenFiatCollateral.test.ts b/test/plugins/individual-collateral/flux-finance/FTokenFiatCollateral.test.ts index ea7c5554f2..180a889352 100644 --- a/test/plugins/individual-collateral/flux-finance/FTokenFiatCollateral.test.ts +++ b/test/plugins/individual-collateral/flux-finance/FTokenFiatCollateral.test.ts @@ -254,6 +254,7 @@ all.forEach((curr: FTokenEnumeration) => { getExpectedPrice, itClaimsRewards: it.skip, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itChecksPriceChanges: it, itChecksNonZeroDefaultThreshold: it, diff --git a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts index 874e7c16a3..5b01678b55 100644 --- a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts +++ b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts @@ -12,6 +12,8 @@ import { TestICollateral, IsfrxEth, SFraxEthCollateral, + EmaPriceOracleStableSwapMock__factory, + EmaPriceOracleStableSwapMock, } from '../../../../typechain' import { pushOracleForward } from '../../../utils/oracles' import { bn, fp } from '../../../../common/numbers' @@ -44,6 +46,7 @@ import { interface SFrxEthCollateralFixtureContext extends CollateralFixtureContext { frxEth: ERC20Mock sfrxEth: IsfrxEth + curveEmaOracle: EmaPriceOracleStableSwapMock } /* @@ -112,7 +115,6 @@ export const deployCollateral = async ( } const chainlinkDefaultAnswer = bn('1600e8') -const targetPerRefChainlinkDefaultAnswer = fp('1.026349814867976366') type Fixture = () => Promise @@ -132,21 +134,28 @@ const makeCollateralFixtureContext = ( ) collateralOpts.chainlinkFeed = chainlinkFeed.address - const targetPerRefChainlinkFeed = ( - await MockV3AggregatorFactory.deploy(18, targetPerRefChainlinkDefaultAnswer) + const EmaPriceOracleStableSwapMockFactory = ( + await ethers.getContractFactory('EmaPriceOracleStableSwapMock') ) + const curveEmaOracle = ( + await EmaPriceOracleStableSwapMockFactory.deploy(fp('0.997646')) + ) + collateralOpts.curvePoolEmaPriceOracleAddress = curveEmaOracle.address + const frxEth = (await ethers.getContractAt('ERC20Mock', FRX_ETH)) as ERC20Mock const sfrxEth = (await ethers.getContractAt('IsfrxEth', SFRX_ETH)) as IsfrxEth const collateral = await deployCollateral(collateralOpts) + const p = await collateral.price() + return { alice, collateral, chainlinkFeed, frxEth, sfrxEth, - targetPerRefChainlinkFeed, + curveEmaOracle, tok: sfrxEth, } } @@ -171,7 +180,8 @@ const changeTargetPerRef = async ( ctx: SFrxEthCollateralFixtureContext, percentChange: BigNumber ) => { - // TODO + const initPrice = await ctx.curveEmaOracle.price_oracle() + await ctx.curveEmaOracle.setPrice(initPrice.add(initPrice.mul(percentChange).div(100))) } const reduceTargetPerRef = async ( @@ -183,9 +193,9 @@ const reduceTargetPerRef = async ( const increaseTargetPerRef = async ( ctx: SFrxEthCollateralFixtureContext, - pctDecrease: BigNumberish + pctIncrease: BigNumberish ) => { - await changeTargetPerRef(ctx, bn(pctDecrease)) + await changeTargetPerRef(ctx, bn(pctIncrease)) } // prettier-ignore @@ -253,11 +263,6 @@ const collateralSpecificStatusTests = () => { const chainlinkFeed = ( await (await ethers.getContractFactory('MockV3Aggregator')).deploy(8, chainlinkDefaultAnswer) ) - const targetPerRefenChainlinkFeed = ( - await ( - await ethers.getContractFactory('MockV3Aggregator') - ).deploy(18, targetPerRefChainlinkDefaultAnswer) - ) const collateral = await deployCollateral({ erc20: erc20.address, @@ -308,6 +313,7 @@ const opts = { getExpectedPrice, itClaimsRewards: it.skip, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it.skip, itChecksRefPerTokDefault: it.skip, itChecksPriceChanges: it, itHasRevenueHiding: it.skip, // implemnted in this file diff --git a/test/plugins/individual-collateral/frax/SFraxCollateralTestSuite.test.ts b/test/plugins/individual-collateral/frax/SFraxCollateralTestSuite.test.ts index 43d09c95be..28d0f1bcd9 100644 --- a/test/plugins/individual-collateral/frax/SFraxCollateralTestSuite.test.ts +++ b/test/plugins/individual-collateral/frax/SFraxCollateralTestSuite.test.ts @@ -193,6 +193,7 @@ const opts = { getExpectedPrice, itClaimsRewards: it.skip, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksNonZeroDefaultThreshold: it.skip, itChecksRefPerTokDefault: it, itChecksPriceChanges: it, diff --git a/test/plugins/individual-collateral/lido/LidoStakedEthTestSuite.test.ts b/test/plugins/individual-collateral/lido/LidoStakedEthTestSuite.test.ts index 1f4213ac61..366c8c81c2 100644 --- a/test/plugins/individual-collateral/lido/LidoStakedEthTestSuite.test.ts +++ b/test/plugins/individual-collateral/lido/LidoStakedEthTestSuite.test.ts @@ -265,6 +265,7 @@ const opts = { getExpectedPrice, itClaimsRewards: it.skip, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itChecksPriceChanges: it, itChecksNonZeroDefaultThreshold: it, diff --git a/test/plugins/individual-collateral/morpho-aave/MorphoAAVEFiatCollateral.test.ts b/test/plugins/individual-collateral/morpho-aave/MorphoAAVEFiatCollateral.test.ts index a573f48887..afb9e7da0d 100644 --- a/test/plugins/individual-collateral/morpho-aave/MorphoAAVEFiatCollateral.test.ts +++ b/test/plugins/individual-collateral/morpho-aave/MorphoAAVEFiatCollateral.test.ts @@ -360,6 +360,7 @@ const makeAaveFiatCollateralTestSuite = ( getExpectedPrice, itClaimsRewards: it.skip, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itChecksPriceChanges: it, itChecksNonZeroDefaultThreshold: it, diff --git a/test/plugins/individual-collateral/morpho-aave/MorphoAAVENonFiatCollateral.test.ts b/test/plugins/individual-collateral/morpho-aave/MorphoAAVENonFiatCollateral.test.ts index f9a0339f9a..5428212d2c 100644 --- a/test/plugins/individual-collateral/morpho-aave/MorphoAAVENonFiatCollateral.test.ts +++ b/test/plugins/individual-collateral/morpho-aave/MorphoAAVENonFiatCollateral.test.ts @@ -233,6 +233,7 @@ const makeAaveNonFiatCollateralTestSuite = ( getExpectedPrice, itClaimsRewards: it.skip, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itChecksPriceChanges: it, itChecksNonZeroDefaultThreshold: it, diff --git a/test/plugins/individual-collateral/morpho-aave/MorphoAAVESelfReferentialCollateral.test.ts b/test/plugins/individual-collateral/morpho-aave/MorphoAAVESelfReferentialCollateral.test.ts index 8e934cedca..6c1b80f2c6 100644 --- a/test/plugins/individual-collateral/morpho-aave/MorphoAAVESelfReferentialCollateral.test.ts +++ b/test/plugins/individual-collateral/morpho-aave/MorphoAAVESelfReferentialCollateral.test.ts @@ -227,6 +227,7 @@ const opts = { getExpectedPrice, itClaimsRewards: it.skip, itChecksTargetPerRefDefault: it.skip, + itChecksTargetPerRefDefaultUp: it.skip, itChecksRefPerTokDefault: it, itChecksPriceChanges: it, itChecksNonZeroDefaultThreshold: it.skip, diff --git a/test/plugins/individual-collateral/pluginTestTypes.ts b/test/plugins/individual-collateral/pluginTestTypes.ts index 34bfefbb20..2dca2653da 100644 --- a/test/plugins/individual-collateral/pluginTestTypes.ts +++ b/test/plugins/individual-collateral/pluginTestTypes.ts @@ -91,6 +91,9 @@ export interface CollateralTestSuiteFixtures // toggle on or off: tests that focus on a targetPerRef default itChecksTargetPerRefDefault: Mocha.TestFunction | Mocha.PendingTestFunction + // toggle on or off: tests that focus on a targetPerRef defaulting upwards + itChecksTargetPerRefDefaultUp: Mocha.TestFunction | Mocha.PendingTestFunction + // toggle on or off: tests that focus on a refPerTok default itChecksRefPerTokDefault: Mocha.TestFunction | Mocha.PendingTestFunction diff --git a/test/plugins/individual-collateral/rocket-eth/RethCollateralTestSuite.test.ts b/test/plugins/individual-collateral/rocket-eth/RethCollateralTestSuite.test.ts index d10488770e..f766a3bc08 100644 --- a/test/plugins/individual-collateral/rocket-eth/RethCollateralTestSuite.test.ts +++ b/test/plugins/individual-collateral/rocket-eth/RethCollateralTestSuite.test.ts @@ -272,6 +272,7 @@ const opts = { getExpectedPrice, itClaimsRewards: it.skip, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itChecksPriceChanges: it, itChecksNonZeroDefaultThreshold: it, diff --git a/test/plugins/individual-collateral/stargate/StargateUSDCTestSuite.test.ts b/test/plugins/individual-collateral/stargate/StargateUSDCTestSuite.test.ts index f3f81e978d..1968edfe85 100644 --- a/test/plugins/individual-collateral/stargate/StargateUSDCTestSuite.test.ts +++ b/test/plugins/individual-collateral/stargate/StargateUSDCTestSuite.test.ts @@ -312,6 +312,7 @@ export const stableOpts = { increaseTargetPerRef, itClaimsRewards: it, // reward growth not supported in mock itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itChecksNonZeroDefaultThreshold: it, itHasRevenueHiding: it, diff --git a/test/plugins/individual-collateral/yearnv2/YearnV2CurveFiatCollateral.test.ts b/test/plugins/individual-collateral/yearnv2/YearnV2CurveFiatCollateral.test.ts index 9242e0fb03..835b90db4a 100644 --- a/test/plugins/individual-collateral/yearnv2/YearnV2CurveFiatCollateral.test.ts +++ b/test/plugins/individual-collateral/yearnv2/YearnV2CurveFiatCollateral.test.ts @@ -237,6 +237,7 @@ tests.forEach((test: CurveFiatTest) => { makeCollateralFixtureContext, mintCollateralTo, itChecksTargetPerRefDefault: it, + itChecksTargetPerRefDefaultUp: it, itChecksRefPerTokDefault: it, itHasRevenueHiding: it, itClaimsRewards: it.skip, From 33ba8ab6bea28a46894e21ffd61c5b958792865d Mon Sep 17 00:00:00 2001 From: Patrick McKelvy Date: Tue, 16 Jan 2024 10:47:44 -0500 Subject: [PATCH 11/11] fix linting. --- .../frax-eth/vendor/CurvePoolEmaPriceOracleWithMinMax.sol | 7 +++++-- .../frax-eth/vendor/ICurvePoolEmaPriceOracleWithMinMax.sol | 2 ++ .../frax-eth/SFrxEthTestSuite.test.ts | 2 -- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/contracts/plugins/assets/frax-eth/vendor/CurvePoolEmaPriceOracleWithMinMax.sol b/contracts/plugins/assets/frax-eth/vendor/CurvePoolEmaPriceOracleWithMinMax.sol index c39f943cf9..75f829a0dc 100644 --- a/contracts/plugins/assets/frax-eth/vendor/CurvePoolEmaPriceOracleWithMinMax.sol +++ b/contracts/plugins/assets/frax-eth/vendor/CurvePoolEmaPriceOracleWithMinMax.sol @@ -14,6 +14,7 @@ pragma solidity ^0.8.19; import { ICurvePoolEmaPriceOracleWithMinMax } from "./ICurvePoolEmaPriceOracleWithMinMax.sol"; interface IEmaPriceOracleStableSwap { + // solhint-disable-next-line func-name-mixedcase function price_oracle() external view returns (uint256); } @@ -28,6 +29,7 @@ struct ConstructorParams { /// @notice An oracle for getting EMA prices from Curve contract CurvePoolEmaPriceOracleWithMinMax is ICurvePoolEmaPriceOracleWithMinMax { /// @notice Curve pool, source of EMA + // solhint-disable-next-line var-name-mixedcase address public immutable CURVE_POOL_EMA_PRICE_ORACLE; /// @notice Precision of Curve pool price_oracle() @@ -58,10 +60,11 @@ contract CurvePoolEmaPriceOracleWithMinMax is ICurvePoolEmaPriceOracleWithMinMax _token1Price = _price < minimumCurvePoolEma ? minimumCurvePoolEma : _price; } - /// @notice The ```getCurvePoolToken1EmaPrice``` function gets the price of the second token in the Curve pool (token1) + /// @notice The ```getCurvePoolToken1EmaPrice``` function gets the price of the second token + /// in the Curve pool (token1) /// @dev Returned in units of the first token (token0) /// @return _emaPrice The price of the second token in the Curve pool function getCurvePoolToken1EmaPrice() external view returns (uint256 _emaPrice) { return _getCurvePoolToken1EmaPrice(); } -} \ No newline at end of file +} diff --git a/contracts/plugins/assets/frax-eth/vendor/ICurvePoolEmaPriceOracleWithMinMax.sol b/contracts/plugins/assets/frax-eth/vendor/ICurvePoolEmaPriceOracleWithMinMax.sol index b947e1a9d8..94a8b2dbde 100644 --- a/contracts/plugins/assets/frax-eth/vendor/ICurvePoolEmaPriceOracleWithMinMax.sol +++ b/contracts/plugins/assets/frax-eth/vendor/ICurvePoolEmaPriceOracleWithMinMax.sol @@ -2,8 +2,10 @@ pragma solidity 0.8.19; interface ICurvePoolEmaPriceOracleWithMinMax { + // solhint-disable-next-line func-name-mixedcase function CURVE_POOL_EMA_PRICE_ORACLE() external view returns (address); + // solhint-disable-next-line func-name-mixedcase function CURVE_POOL_EMA_PRICE_ORACLE_DECIMALS() external view returns (uint256); function getCurvePoolToken1EmaPrice() external view returns (uint256 _emaPrice); diff --git a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts index 5b01678b55..fb9f69b97d 100644 --- a/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts +++ b/test/plugins/individual-collateral/frax-eth/SFrxEthTestSuite.test.ts @@ -147,8 +147,6 @@ const makeCollateralFixtureContext = ( const sfrxEth = (await ethers.getContractAt('IsfrxEth', SFRX_ETH)) as IsfrxEth const collateral = await deployCollateral(collateralOpts) - const p = await collateral.price() - return { alice, collateral,