import { ethers , upgrades , waffle } from "hardhat" ; import { Signer } from "ethers" ; import chai from "chai" ; import { solidity } from "ethereum-waffle" ; import "@openzeppelin/test-helpers" ; import { IbTokenPriceFeed , IbTokenPriceFeed__factory , MockPriceFeed__factory , MockPriceFeed } from "../../../typechain" ; import { smockit , MockContract } from "@eth-optimism/smock" ; import { formatBytes32BigNumber } from "../../helper/format" ; import { parseEther } from "ethers/lib/utils" ; import { AccessControlConfig } from "../../../typechain/AccessControlConfig" ; import { AccessControlConfig__factory } from "../../../typechain/factories/AccessControlConfig__factory" ; import * as TimeHelpers from "../../helper/time" ; import { BigNumber } from "@ethersproject/bignumber" ; chai . use ( solidity ) ; const { expect } = chai ; type fixture = { ibTokenPriceFeed : IbTokenPriceFeed ; mockedIbBasePriceFeed : MockContract ; mockedBaseUsdPriceFeed : MockContract ; mockedAccessControlConfig : MockContract ; } ; const ORACLE_TIME_DELAY = 900 ; const loadFixtureHandler = async ( ) : Promise < fixture > => { const [ deployer ] = await ethers . getSigners ( ) ; const MockPriceFeed = ( await ethers . getContractFactory ( "MockPriceFeed" , deployer ) ) as MockPriceFeed__factory ; const mockPriceFeedA = ( await upgrades . deployProxy ( MockPriceFeed , [ ] ) ) as MockPriceFeed ; await mockPriceFeedA . deployed ( ) ; const mockedIbBasePriceFeed = await smockit ( mockPriceFeedA ) ; const mockPriceFeedB = ( await upgrades . deployProxy ( MockPriceFeed , [ ] ) ) as MockPriceFeed ; await mockPriceFeedB . deployed ( ) ; const mockedBaseUsdPriceFeed = await smockit ( mockPriceFeedB ) ; 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 IbTokenPriceFeed = ( await ethers . getContractFactory ( "IbTokenPriceFeed" , deployer ) ) as IbTokenPriceFeed__factory ; const ibTokenPriceFeed = ( await upgrades . deployProxy ( IbTokenPriceFeed , [ mockedIbBasePriceFeed . address , mockedBaseUsdPriceFeed . address , mockedAccessControlConfig . address , ORACLE_TIME_DELAY , ] ) ) as IbTokenPriceFeed ; await ibTokenPriceFeed . deployed ( ) ; return { ibTokenPriceFeed , mockedIbBasePriceFeed , mockedBaseUsdPriceFeed , mockedAccessControlConfig } ; } ; describe ( "IbTokenPriceFeed" , ( ) => { let deployer ; let alice ; let bob ; let dev ; let deployerAddress ; let aliceAddress ; let bobAddress ; let devAddress ; let ibTokenPriceFeed ; let mockedIbBasePriceFeed ; let mockedBaseUsdPriceFeed ; let mockedAccessControlConfig ; let ibTokenPriceFeedAsAlice ; let ibTokenPriceFeedAsBob ; beforeEach ( async ( ) => { ; ( { ibTokenPriceFeed , mockedIbBasePriceFeed , mockedBaseUsdPriceFeed , 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 ( ) , ] ) ; ibTokenPriceFeedAsAlice = IbTokenPriceFeed__factory . connect ( ibTokenPriceFeed . address , alice ) as IbTokenPriceFeed ; ibTokenPriceFeedAsBob = IbTokenPriceFeed__factory . connect ( ibTokenPriceFeed . address , bob ) as IbTokenPriceFeed ; } ) ; describe ( "#peekPrice()" , ( ) => { const assertPeekPriceCall = ( calls ) => { expect ( calls . length ) . to . be . equal ( 1 ) ; } ; context ( "when ibInBasePriceFeed returns ok=false" , ( ) => { it ( "should be able to get price with okFlag = false" , async ( ) => { mockedIbBasePriceFeed . smocked . peekPrice . will . return . with ( [ formatBytes32BigNumber ( parseEther ( "1.1" ) ) , false ] ) ; mockedBaseUsdPriceFeed . smocked . peekPrice . will . return . with ( [ formatBytes32BigNumber ( parseEther ( "400" ) ) , true ] ) ; await expect ( ibTokenPriceFeed . setPrice ( ) ) . to . be . revertedWith ( "IbTokenPriceFeed/not-ok" ) ; assertPeekPriceCall ( mockedIbBasePriceFeed . smocked . peekPrice . calls ) ; assertPeekPriceCall ( mockedBaseUsdPriceFeed . smocked . peekPrice . calls ) ; const [ price , ok ] = await ibTokenPriceFeed . peekPrice ( ) ; expect ( BigNumber . from ( price ) ) . to . be . equal ( 0 ) ; expect ( ok ) . to . be . false ; } ) ; } ) ; context ( "when baseInUsdPriceFeed returns ok=false" , ( ) => { it ( "should be able to get price with okFlag = false" , async ( ) => { mockedIbBasePriceFeed . smocked . peekPrice . will . return . with ( [ formatBytes32BigNumber ( parseEther ( "1.1" ) ) , true ] ) ; mockedBaseUsdPriceFeed . smocked . peekPrice . will . return . with ( [ formatBytes32BigNumber ( parseEther ( "400" ) ) , false ] ) ; await expect ( ibTokenPriceFeed . setPrice ( ) ) . to . be . revertedWith ( "IbTokenPriceFeed/not-ok" ) ; assertPeekPriceCall ( mockedIbBasePriceFeed . smocked . peekPrice . calls ) ; assertPeekPriceCall ( mockedBaseUsdPriceFeed . smocked . peekPrice . calls ) ; const [ price , ok ] = await ibTokenPriceFeed . peekPrice ( ) ; expect ( BigNumber . from ( price ) ) . to . be . equal ( 0 ) ; expect ( ok ) . to . be . false ; } ) ; } ) ; context ( "when both returns ok=true" , ( ) => { it ( "should be able to get price with okFlag = true" , async ( ) => { mockedIbBasePriceFeed . smocked . peekPrice . will . return . with ( [ formatBytes32BigNumber ( parseEther ( "1.1" ) ) , true ] ) ; mockedBaseUsdPriceFeed . smocked . peekPrice . will . return . with ( [ formatBytes32BigNumber ( parseEther ( "400" ) ) , true ] ) ; await ibTokenPriceFeed . setPrice ( ) ; assertPeekPriceCall ( mockedIbBasePriceFeed . smocked . peekPrice . calls ) ; assertPeekPriceCall ( mockedBaseUsdPriceFeed . smocked . peekPrice . calls ) ; TimeHelpers . increase ( BigNumber . from ( 900 ) ) ; await ibTokenPriceFeed . setPrice ( ) ; const [ price , ok ] = await ibTokenPriceFeed . peekPrice ( ) ; expect ( BigNumber . from ( price ) ) . to . be . equal ( parseEther ( "440" ) ) ; expect ( ok ) . to . be . true ; } ) ; } ) ; context ( "when contract is in paused state" , ( ) => { it ( "should be able to get price with okFlag = false" , async ( ) => { mockedAccessControlConfig . smocked . hasRole . will . return . with ( true ) ; await ibTokenPriceFeed . pause ( ) ; mockedIbBasePriceFeed . smocked . peekPrice . will . return . with ( [ formatBytes32BigNumber ( parseEther ( "1.1" ) ) , true ] ) ; mockedBaseUsdPriceFeed . smocked . peekPrice . will . return . with ( [ formatBytes32BigNumber ( parseEther ( "400" ) ) , true ] ) ; await expect ( ibTokenPriceFeed . setPrice ( ) ) . to . reverted ; } ) ; } ) ; } ) ; } ) ;