Skip to content

Commit

Permalink
feat: add tests for OETHL2
Browse files Browse the repository at this point in the history
  • Loading branch information
clement-ux committed Oct 29, 2024
1 parent 9aa77c7 commit 920b82b
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 0 deletions.
3 changes: 3 additions & 0 deletions common/configuration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ export interface ITokens {
wsgUSDbC?: string
yvCurveUSDPcrvUSD?: string
yvCurveUSDCcrvUSD?: string
wsuperOETHb?: string

pyUSD?: string
aEthPyUSD?: string
Expand Down Expand Up @@ -513,6 +514,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
sUSDbC: '0x4c80e24119cfb836cdf0a6b53dc23f04f7e652ca',
wstETH: '0xc1CBa3fCea344f92D9239c08C0568f6F2F0ee452',
STG: '0xE3B53AF74a4BF62Ae5511055290838050bf764Df',
wsuperOETHb: '0x7FcD174E80f264448ebeE8c88a7C4476AAF58Ea6'
},
chainlinkFeeds: {
DAI: '0x591e79239a7d679378ec8c847e5038150364c78f', // 0.3%, 24hr
Expand All @@ -529,6 +531,7 @@ export const networkConfig: { [key: string]: INetworkConfig } = {
stETHETH: '0xf586d0728a47229e747d824a939000Cf21dEF5A0', // 0.5%, 24h
ETHUSD: '0x71041dddad3595F9CEd3DcCFBe3D1F4b0a16Bb70', // 0.15%, 20min
wstETHstETH: '0xB88BAc61a4Ca37C43a3725912B1f472c9A5bc061', // 0.5%, 24h
wsuperOETHb: '0x28C964c985fe84736fAdc7Cf0bBd58B54bc7CF93'
},
GNOSIS_EASY_AUCTION: '0xb1875Feaeea32Bbb02DE83D81772e07E37A40f02', // mock
COMET_REWARDS: '0x123964802e6ABabBE1Bc9547D72Ef1B69B00A6b1',
Expand Down
258 changes: 258 additions & 0 deletions test/plugins/individual-collateral/origin/OETHCollateralL2Base.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
import collateralTests from '../collateralTests'
import { setStorageAt, getStorageAt } from '@nomicfoundation/hardhat-network-helpers'
import { CollateralFixtureContext, CollateralOpts, MintCollateralFunc } from '../pluginTestTypes'
import { mintWSUPEROETHB } from './helpers'
import { expect } from 'chai'
import { ethers } from 'hardhat'
import { ContractFactory, BigNumber, BigNumberish } from 'ethers'
import {
ERC20Mock,
MockV3Aggregator,
MockV3Aggregator__factory,
TestICollateral,
IWSuperOETHb,
} from '../../../../typechain'
import { pushOracleForward } from '../../../utils/oracles'
import { bn, fp } from '../../../../common/numbers'
import { ZERO_ADDRESS } from '../../../../common/constants'
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
import {
PRICE_TIMEOUT,
MAX_TRADE_VOL,
DEFAULT_THRESHOLD,
DELAY_UNTIL_DEFAULT,
FORK_BLOCK_BASE,
BASE_PRICE_FEEDS,
BASE_FEEDS_TIMEOUT,
BASE_ORACLE_ERROR,
BASE_WSUPEROETHB,
BASE_WSUPEROETHB_WHALE,
} from './constants'
import { getResetFork } from '../helpers'

/*
Define interfaces
*/
interface WSUPEROETHBCollateralFixtureContext extends CollateralFixtureContext {
wsuperoethb: IWSuperOETHb
targetPerRefChainlinkFeed: MockV3Aggregator
uoaPerTargetChainlinkFeed: MockV3Aggregator
}

/*
Define deployment functions
*/

interface WSUPEROETHBCollateralOpts extends CollateralOpts {
targetPerTokChainlinkFeed?: string
uoaPerTargetChainlinkFeed?: string
uoaPerTargetChainlinkTimeout?: BigNumberish
}

export const defaultWSUPEROETHBCollateralOpts: WSUPEROETHBCollateralOpts = {
erc20: BASE_WSUPEROETHB,
targetName: ethers.utils.formatBytes32String('ETH'),
rewardERC20: ZERO_ADDRESS,
priceTimeout: PRICE_TIMEOUT,
chainlinkFeed: BASE_PRICE_FEEDS.wsuperOETHb_ETH, // ignored
oracleTimeout: '1000', // ignored
oracleError: BASE_ORACLE_ERROR,
maxTradeVolume: MAX_TRADE_VOL,
defaultThreshold: DEFAULT_THRESHOLD,
delayUntilDefault: DELAY_UNTIL_DEFAULT,
targetPerTokChainlinkFeed: BASE_PRICE_FEEDS.wsuperOETHb_ETH,
uoaPerTargetChainlinkFeed: BASE_PRICE_FEEDS.ETH_USD,
uoaPerTargetChainlinkTimeout: BASE_FEEDS_TIMEOUT.ETH_USD,
revenueHiding: fp('1e-4'),
}

export const deployCollateral = async (
opts: WSUPEROETHBCollateralOpts = {}
): Promise<TestICollateral> => {
opts = { ...defaultWSUPEROETHBCollateralOpts, ...opts }

const WSuperOETHbCollateralFactory: ContractFactory = await ethers.getContractFactory(
'OETHCollateralL2Base'
)

const collateral = <TestICollateral>await WSuperOETHbCollateralFactory.deploy(
{
erc20: opts.erc20,
targetName: opts.targetName,
rewardERC20: opts.rewardERC20,
priceTimeout: opts.priceTimeout,
chainlinkFeed: opts.chainlinkFeed,
oracleError: opts.oracleError,
oracleTimeout: opts.oracleTimeout,
maxTradeVolume: opts.maxTradeVolume,
defaultThreshold: opts.defaultThreshold,
delayUntilDefault: opts.delayUntilDefault,
},
opts.revenueHiding,
opts.targetPerTokChainlinkFeed,
opts.chainlinkFeed ?? opts.uoaPerTargetChainlinkFeed,
opts.uoaPerTargetChainlinkTimeout,
{ gasLimit: 2000000000 }
)

// Push forward chainlink feed
await pushOracleForward(opts.uoaPerTargetChainlinkFeed!)

await collateral.deployed()
// 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())

return collateral
}

const defaultAnswers = {
targetPerRefChainlinkFeed: bn('1e18'),
uoaPerTargetChainlinkFeed: bn('2000e8'),
refPerTokenChainlinkFeed: bn('1.1e18'),
}

type Fixture<T> = () => Promise<T>

const makeCollateralFixtureContext = (
alice: SignerWithAddress,
opts: CollateralOpts = {}
): Fixture<WSUPEROETHBCollateralFixtureContext> => {
const collateralOpts = { ...defaultWSUPEROETHBCollateralOpts, ...opts }

const makeCollateralFixtureContext = async () => {
const MockV3AggregatorFactory = <MockV3Aggregator__factory>(
await ethers.getContractFactory('MockV3Aggregator')
)

const targetPerRefChainlinkFeed = await MockV3AggregatorFactory.deploy(
18,
defaultAnswers.targetPerRefChainlinkFeed
)
const uoaPerTargetChainlinkFeed = await MockV3AggregatorFactory.deploy(8, defaultAnswers.uoaPerTargetChainlinkFeed)

collateralOpts.chainlinkFeed = uoaPerTargetChainlinkFeed.address
collateralOpts.uoaPerTargetChainlinkFeed = uoaPerTargetChainlinkFeed.address

const wsuperOETHb = (await ethers.getContractAt('IWSuperOETHb', BASE_WSUPEROETHB)) as IWSuperOETHb
const rewardToken = (await ethers.getContractAt('ERC20Mock', ZERO_ADDRESS)) as ERC20Mock
const collateral = await deployCollateral(collateralOpts)

return {
alice,
collateral,
wsuperoethb: wsuperOETHb,
tok: wsuperOETHb,
rewardToken,
chainlinkFeed: uoaPerTargetChainlinkFeed,
targetPerRefChainlinkFeed: targetPerRefChainlinkFeed,
uoaPerTargetChainlinkFeed,
}
}

return makeCollateralFixtureContext
}

/*
Define helper functions
*/

const mintCollateralTo: MintCollateralFunc<WSUPEROETHBCollateralFixtureContext> = async (
ctx: WSUPEROETHBCollateralFixtureContext,
amount: BigNumberish,
user: SignerWithAddress,
recipient: string
) => {
await mintWSUPEROETHB(ctx.wsuperoethb, user, amount, recipient, BASE_WSUPEROETHB_WHALE)
}

const reduceTargetPerRef = async (
ctx: WSUPEROETHBCollateralFixtureContext,
pctDecrease: BigNumberish
) => {
const lastRound = await ctx.chainlinkFeed.latestRoundData()
const nextAnswer = lastRound.answer.sub(lastRound.answer.mul(pctDecrease).div(100))
await ctx.chainlinkFeed.updateAnswer(nextAnswer)
}

const increaseTargetPerRef = async (
ctx: WSUPEROETHBCollateralFixtureContext,
pctIncrease: BigNumberish
) => {
const lastRound = await ctx.chainlinkFeed.latestRoundData()
const nextAnswer = lastRound.answer.add(lastRound.answer.mul(pctIncrease).div(100))
await ctx.chainlinkFeed.updateAnswer(nextAnswer)
}

const reduceRefPerTok = async (ctx: WSUPEROETHBCollateralFixtureContext, pctDecrease: BigNumberish) => {
const slot = 2
const storedTotalSupply = BigNumber.from(await getStorageAt(ctx.tok.address, slot))
const newStoredTotalAssets = storedTotalSupply.add(storedTotalSupply.mul(pctDecrease).div(100))
await setStorageAt(ctx.tok.address, slot, newStoredTotalAssets)
}

const increaseRefPerTok = async (
ctx: WSUPEROETHBCollateralFixtureContext,
pctIncrease: BigNumberish
) => {
const slot = 2
const storedTotalSupply = BigNumber.from(await getStorageAt(ctx.tok.address, slot))
const newStoredTotalAssets = storedTotalSupply.sub(storedTotalSupply.mul(pctIncrease).div(100))
await setStorageAt(ctx.tok.address, slot, newStoredTotalAssets)
}

const getExpectedPrice = async (ctx: WSUPEROETHBCollateralFixtureContext): Promise<BigNumber> => {
const uoaPerTargetChainlinkFeedAnswer = await ctx.uoaPerTargetChainlinkFeed.latestAnswer()
const uoaPerTargetChainlinkFeedDecimals = await ctx.uoaPerTargetChainlinkFeed.decimals()

const refPerTok = await ctx.collateral.refPerTok()

const result = uoaPerTargetChainlinkFeedAnswer
.mul(bn(10).pow(18 - uoaPerTargetChainlinkFeedDecimals))
.mul(refPerTok)
.div(fp('1'))

return result
}

const collateralSpecificConstructorTests = () => { }

// eslint-disable-next-line @typescript-eslint/no-empty-function
const collateralSpecificStatusTests = () => { }

// eslint-disable-next-line @typescript-eslint/no-empty-function
const beforeEachRewardsTest = async () => { }

/*
Run the test suite
*/

const opts = {
deployCollateral,
collateralSpecificConstructorTests,
collateralSpecificStatusTests,
beforeEachRewardsTest,
makeCollateralFixtureContext,
mintCollateralTo,
reduceTargetPerRef,
increaseTargetPerRef,
reduceRefPerTok,
increaseRefPerTok,
getExpectedPrice,
itClaimsRewards: it.skip,
itChecksTargetPerRefDefault: it.skip,
itChecksTargetPerRefDefaultUp: it.skip,
itChecksRefPerTokDefault: it,
itChecksPriceChanges: it,
itChecksNonZeroDefaultThreshold: it,
itHasRevenueHiding: it,
resetFork: getResetFork(FORK_BLOCK_BASE),
collateralName: 'OETHCollateralL2Base',
chainlinkDefaultAnswer: defaultAnswers.uoaPerTargetChainlinkFeed,
itIsPricedByPeg: true,
itHasOracleRefPerTok: true,
targetNetwork: 'base',
toleranceDivisor: bn('1e2'),
}

collateralTests(opts)
31 changes: 31 additions & 0 deletions test/plugins/individual-collateral/origin/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { bn, fp } from '../../../../common/numbers'
import { networkConfig } from '../../../../common/configuration'
import { combinedError } from '../../../../scripts/deployment/utils'

// Mainnet Addresses

// Base Addresses
export const BASE_WSUPEROETHB = networkConfig['8453'].tokens.wsuperOETHb as string
export const BASE_WSUPEROETHB_WHALE = '0x190e5C6AabB2BeC4eB0B9b2274e9b62cdaEDF356' // Silo
export const FORK_BLOCK_BASE = 21698000
export const BASE_PRICE_FEEDS = {
// traditional finance notation, opposite of our unit system
wsuperOETHb_ETH: networkConfig['8453'].chainlinkFeeds.wsuperOETHb, // {ETH/wsuperOETHb}
ETH_USD: networkConfig['8453'].chainlinkFeeds.ETHUSD, // {USD/ETH}
}
export const BASE_FEEDS_TIMEOUT = {
wsuperOETHb_ETH: bn(86400),
ETH_USD: bn(1200),
}
export const BASE_ORACLE_ERROR = combinedError(
fp('0.0015'),
combinedError(fp('0.005'), fp('0.005'))
)

// Data
export const PRICE_TIMEOUT = bn('604800') // 1 week
export const ORACLE_TIMEOUT = bn(86400) // 24 hours in seconds
export const ORACLE_ERROR = fp('0.005')
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)
20 changes: 20 additions & 0 deletions test/plugins/individual-collateral/origin/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { SignerWithAddress } from '@nomiclabs/hardhat-ethers/signers'
import { IERC20 } from '../../../../typechain'
import { whileImpersonating } from '../../../utils/impersonation'
import { BigNumberish } from 'ethers'
import { FORK_BLOCK_BASE, BASE_WSUPEROETHB_WHALE } from './constants'
import { getResetFork } from '../helpers'

export const mintWSUPEROETHB = async (
wsuperoethb: IERC20,
account: SignerWithAddress,
amount: BigNumberish,
recipient: string,
whale: string = BASE_WSUPEROETHB_WHALE
) => {
await whileImpersonating(whale, async (wsuperoethbWhale) => {
await wsuperoethb.connect(wsuperoethbWhale).transfer(recipient, amount)
})
}

export const resetFork = getResetFork(FORK_BLOCK_BASE)

0 comments on commit 920b82b

Please sign in to comment.