import { ethers , upgrades , waffle } from "hardhat" ; import { BigNumber , Signer , Wallet } from "ethers" ; import chai from "chai" ; import { MockProvider , solidity } from "ethereum-waffle" ; import "@openzeppelin/test-helpers" ; import { StrictAlpacaOraclePriceFeed , StrictAlpacaOraclePriceFeed__factory , MockAlpacaOracle__factory , MockAlpacaOracle , AccessControlConfig , AccessControlConfig__factory , } from "../../../typechain" ; import { smockit , MockContract } from "@eth-optimism/smock" ; import { AddressOne , AddressTwo } from "../../helper/address" ; import { WeiPerWad } from "../../helper/unit" ; import { DateTime } from "luxon" ; import { duration , increase , latest } from "../../helper/time" ; chai . use ( solidity ) ; const { expect } = chai ; type fixture = { strictAlpacaOraclePriceFeed : StrictAlpacaOraclePriceFeed ; mockedAlpacaOracleA : MockContract ; mockedAlpacaOracleB : MockContract ; mockedAccessControlConfig : MockContract ; } ; const token0Address = AddressOne ; const token1Address = AddressTwo ; const nHoursAgoInSec = ( now , n ) : BigNumber => { return now . sub ( n * 60 * 60 ) ; } ; const loadFixtureHandler = async ( maybeWallets ? , maybeProvider ? ) : Promise < fixture > => { const [ deployer ] = await ethers . getSigners ( ) ; const MockAlpacaOracle = ( await ethers . getContractFactory ( "MockAlpacaOracle" , deployer ) ) as MockAlpacaOracle__factory ; const mockAlpacaOracleA = ( await upgrades . deployProxy ( MockAlpacaOracle , [ ] ) ) as MockAlpacaOracle ; await mockAlpacaOracleA . deployed ( ) ; const mockedAlpacaOracleA = await smockit ( mockAlpacaOracleA ) ; const mockAlpacaOracleB = ( await upgrades . deployProxy ( MockAlpacaOracle , [ ] ) ) as MockAlpacaOracle ; await mockAlpacaOracleB . deployed ( ) ; const mockedAlpacaOracleB = await smockit ( mockAlpacaOracleA ) ; const AccessControlConfig = ( await ethers . getContractFactory ( "AccessControlConfig" , deployer ) ) as AccessControlConfig__factory ; const mockAccessControlConfig = ( await upgrades . deployProxy ( AccessControlConfig , [ ] ) ) as AccessControlConfig ; await mockAccessControlConfig . deployed ( ) ; const mockedAccessControlConfig = await smockit ( mockAccessControlConfig ) ; const StrictAlpacaOraclePriceFeed = ( await ethers . getContractFactory ( "StrictAlpacaOraclePriceFeed" , deployer ) ) as StrictAlpacaOraclePriceFeed__factory ; const strictAlpacaOraclePriceFeed = ( await upgrades . deployProxy ( StrictAlpacaOraclePriceFeed , [ mockedAlpacaOracleA . address , token0Address , token1Address , mockedAlpacaOracleB . address , token0Address , token1Address , mockedAccessControlConfig . address , ] ) ) as StrictAlpacaOraclePriceFeed ; await strictAlpacaOraclePriceFeed . deployed ( ) ; return { strictAlpacaOraclePriceFeed , mockedAlpacaOracleA , mockedAlpacaOracleB , mockedAccessControlConfig } ; } ; describe ( "StrictAlpacaOraclePriceFeed" , ( ) => { let deployer ; let alice ; let bob ; let dev ; let deployerAddress ; let aliceAddress ; let bobAddress ; let devAddress ; let strictAlpacaOraclePriceFeed ; let mockedAlpacaOracleA ; let mockedAlpacaOracleB ; let mockedAccessControlConfig ; let alpacaOraclePriceFeedAsAlice ; let alpacaOraclePriceFeedAsBob ; beforeEach ( async ( ) => { ; ( { strictAlpacaOraclePriceFeed , mockedAlpacaOracleA , mockedAlpacaOracleB , mockedAccessControlConfig } = await waffle . loadFixture ( loadFixtureHandler ) ) ; [ deployer , alice , bob , dev ] = await ethers . getSigners ( ) ; [ deployerAddress , aliceAddress , bobAddress , devAddress ] = await Promise . all ( [ deployer . getAddress ( ) , alice . getAddress ( ) , bob . getAddress ( ) , dev . getAddress ( ) , ] ) ; alpacaOraclePriceFeedAsAlice = StrictAlpacaOraclePriceFeed__factory . connect ( strictAlpacaOraclePriceFeed . address , alice ) as StrictAlpacaOraclePriceFeed ; alpacaOraclePriceFeedAsBob = StrictAlpacaOraclePriceFeed__factory . connect ( strictAlpacaOraclePriceFeed . address , bob ) as StrictAlpacaOraclePriceFeed ; } ) ; describe ( "#peekPrice()" , ( ) => { const assertGetPriceCall = ( calls ) => { expect ( calls . length ) . to . be . equal ( 1 ) ; expect ( calls [ 0 ] [ 0 ] ) . to . be . equal ( token0Address ) ; expect ( calls [ 0 ] [ 1 ] ) . to . be . equal ( token1Address ) ; } ; context ( "when priceLife is 24 hours" , ( ) => { context ( "when primary alpacaOracle returns 25 hours old price" , ( ) => { it ( "should be able to get price with okFlag = false, with price from primary" , async ( ) => { const now = await latest ( ) ; mockedAlpacaOracleA . smocked . getPrice . will . return . with ( [ WeiPerWad . mul ( 100 ) , nHoursAgoInSec ( now , 25 ) ] ) ; mockedAlpacaOracleB . smocked . getPrice . will . return . with ( [ WeiPerWad . mul ( 99 ) , nHoursAgoInSec ( now , 1 ) ] ) ; const [ price , ok ] = await strictAlpacaOraclePriceFeed . peekPrice ( ) ; expect ( price ) . to . be . equal ( WeiPerWad . mul ( 100 ) ) ; expect ( ok ) . to . be . false ; assertGetPriceCall ( mockedAlpacaOracleA . smocked . getPrice . calls ) ; assertGetPriceCall ( mockedAlpacaOracleB . smocked . getPrice . calls ) ; } ) ; } ) ; context ( "when secondary alpacaOracle returns 25 hours old price" , ( ) => { it ( "should be able to get price with okFlag = false, with price from primary" , async ( ) => { const now = await latest ( ) ; mockedAlpacaOracleA . smocked . getPrice . will . return . with ( [ WeiPerWad . mul ( 100 ) , nHoursAgoInSec ( now , 1 ) ] ) ; mockedAlpacaOracleB . smocked . getPrice . will . return . with ( [ WeiPerWad . mul ( 99 ) , nHoursAgoInSec ( now , 25 ) ] ) ; const [ price , ok ] = await strictAlpacaOraclePriceFeed . peekPrice ( ) ; expect ( price ) . to . be . equal ( WeiPerWad . mul ( 100 ) ) ; expect ( ok ) . to . be . false ; assertGetPriceCall ( mockedAlpacaOracleA . smocked . getPrice . calls ) ; assertGetPriceCall ( mockedAlpacaOracleB . smocked . getPrice . calls ) ; } ) ; } ) ; context ( "when both alpacaOracle returns 1 hours old price" , ( ) => { it ( "should be able to get price with okFlag = true, with price from primary" , async ( ) => { const now = await latest ( ) ; mockedAlpacaOracleA . smocked . getPrice . will . return . with ( [ WeiPerWad . mul ( 100 ) , nHoursAgoInSec ( now , 1 ) ] ) ; mockedAlpacaOracleB . smocked . getPrice . will . return . with ( [ WeiPerWad . mul ( 99 ) , nHoursAgoInSec ( now , 1 ) ] ) ; const [ price , ok ] = await strictAlpacaOraclePriceFeed . peekPrice ( ) ; expect ( price ) . to . be . equal ( WeiPerWad . mul ( 100 ) ) ; expect ( ok ) . to . be . true ; assertGetPriceCall ( mockedAlpacaOracleA . smocked . getPrice . calls ) ; assertGetPriceCall ( mockedAlpacaOracleB . smocked . getPrice . calls ) ; } ) ; } ) ; } ) ; context ( "when maxPriceDiff is 10500 (5%)" , ( ) => { context ( "when primary returns price = 100 WAD, secondary returns price = 106 WAD (diff -6%)" , ( ) => { it ( "should be able to get price with okFlag = false (primary price too low)" , async ( ) => { const now = await latest ( ) ; mockedAlpacaOracleA . smocked . getPrice . will . return . with ( [ WeiPerWad . mul ( 100 ) , nHoursAgoInSec ( now , 1 ) ] ) ; mockedAlpacaOracleB . smocked . getPrice . will . return . with ( [ WeiPerWad . mul ( 106 ) , nHoursAgoInSec ( now , 1 ) ] ) ; const [ price , ok ] = await strictAlpacaOraclePriceFeed . peekPrice ( ) ; expect ( price ) . to . be . equal ( WeiPerWad . mul ( 100 ) ) ; expect ( ok ) . to . be . false ; assertGetPriceCall ( mockedAlpacaOracleA . smocked . getPrice . calls ) ; assertGetPriceCall ( mockedAlpacaOracleB . smocked . getPrice . calls ) ; } ) ; } ) ; context ( "when primary returns price = 106 WAD, secondary returns price = 100 WAD (diff +6%)" , ( ) => { it ( "should be able to get price with okFlag = false (primary price too high)" , async ( ) => { const now = await latest ( ) ; mockedAlpacaOracleA . smocked . getPrice . will . return . with ( [ WeiPerWad . mul ( 106 ) , nHoursAgoInSec ( now , 1 ) ] ) ; mockedAlpacaOracleB . smocked . getPrice . will . return . with ( [ WeiPerWad . mul ( 100 ) , nHoursAgoInSec ( now , 1 ) ] ) ; const [ price , ok ] = await strictAlpacaOraclePriceFeed . peekPrice ( ) ; expect ( price ) . to . be . equal ( WeiPerWad . mul ( 106 ) ) ; expect ( ok ) . to . be . false ; assertGetPriceCall ( mockedAlpacaOracleA . smocked . getPrice . calls ) ; assertGetPriceCall ( mockedAlpacaOracleB . smocked . getPrice . calls ) ; } ) ; } ) ; context ( "when primary returns price = 100 WAD, secondary returns price = 105 WAD (diff -5%)" , ( ) => { it ( "should be able to get price with okFlag = true" , async ( ) => { const now = await latest ( ) ; mockedAlpacaOracleA . smocked . getPrice . will . return . with ( [ WeiPerWad . mul ( 100 ) , nHoursAgoInSec ( now , 1 ) ] ) ; mockedAlpacaOracleB . smocked . getPrice . will . return . with ( [ WeiPerWad . mul ( 105 ) , nHoursAgoInSec ( now , 1 ) ] ) ; const [ price , ok ] = await strictAlpacaOraclePriceFeed . peekPrice ( ) ; expect ( price ) . to . be . equal ( WeiPerWad . mul ( 100 ) ) ; expect ( ok ) . to . be . true ; assertGetPriceCall ( mockedAlpacaOracleA . smocked . getPrice . calls ) ; assertGetPriceCall ( mockedAlpacaOracleB . smocked . getPrice . calls ) ; } ) ; } ) ; context ( "when primary returns price = 105 WAD, secondary returns price = 100 WAD (diff +5%)" , ( ) => { it ( "should be able to get price with okFlag = true" , async ( ) => { const now = await latest ( ) ; mockedAlpacaOracleA . smocked . getPrice . will . return . with ( [ WeiPerWad . mul ( 105 ) , nHoursAgoInSec ( now , 1 ) ] ) ; mockedAlpacaOracleB . smocked . getPrice . will . return . with ( [ WeiPerWad . mul ( 100 ) , nHoursAgoInSec ( now , 1 ) ] ) ; const [ price , ok ] = await strictAlpacaOraclePriceFeed . peekPrice ( ) ; expect ( price ) . to . be . equal ( WeiPerWad . mul ( 105 ) ) ; expect ( ok ) . to . be . true ; assertGetPriceCall ( mockedAlpacaOracleA . smocked . getPrice . calls ) ; assertGetPriceCall ( mockedAlpacaOracleB . smocked . getPrice . calls ) ; } ) ; } ) ; } ) ; context ( "when in paused state" , ( ) => { it ( "should be able to get price with okFlag = false" , async ( ) => { mockedAccessControlConfig . smocked . hasRole . will . return . with ( true ) ; await strictAlpacaOraclePriceFeed . pause ( ) ; const now = await latest ( ) ; mockedAlpacaOracleA . smocked . getPrice . will . return . with ( [ WeiPerWad . mul ( 100 ) , nHoursAgoInSec ( now , 1 ) ] ) ; mockedAlpacaOracleB . smocked . getPrice . will . return . with ( [ WeiPerWad . mul ( 99 ) , nHoursAgoInSec ( now , 1 ) ] ) ; const [ price , ok ] = await strictAlpacaOraclePriceFeed . peekPrice ( ) ; expect ( price ) . to . be . equal ( WeiPerWad . mul ( 100 ) ) ; expect ( ok ) . to . be . false ; assertGetPriceCall ( mockedAlpacaOracleA . smocked . getPrice . calls ) ; assertGetPriceCall ( mockedAlpacaOracleB . smocked . getPrice . calls ) ; } ) ; } ) ; } ) ; } ) ;