import { ethers , upgrades , waffle } from "hardhat" ; import { Signer , BigNumber , Wallet } from "ethers" ; import chai from "chai" ; import { MockProvider , solidity } from "ethereum-waffle" ; import "@openzeppelin/test-helpers" ; import { BookKeeper__factory , PositionManager__factory , PositionManager , BookKeeper , BEP20__factory , IbTokenAdapter__factory , IbTokenAdapter , AlpacaToken__factory , FairLaunch__factory , Shield__factory , BEP20 , Shield , AlpacaToken , FairLaunch , CollateralPoolConfig__factory , CollateralPoolConfig , AccessControlConfig__factory , AccessControlConfig , } from "../../../../../typechain" ; import { smockit , MockContract } from "@eth-optimism/smock" ; import { WeiPerRad , WeiPerRay , WeiPerWad , weiToRay } from "../../../../helper/unit" ; import { advanceBlock } from "../../../../helper/time" ; chai . use ( solidity ) ; const { expect } = chai ; const { AddressZero } = ethers . constants ; const { parseEther , formatBytes32String } = ethers . utils ; type fixture = { ibTokenAdapter : IbTokenAdapter ; bookKeeper : BookKeeper ; ibDUMMY : BEP20 ; shield : Shield ; alpacaToken : AlpacaToken ; fairLaunch : FairLaunch ; } ; const ALPACA_PER_BLOCK = ethers . utils . parseEther ( "100" ) ; const COLLATERAL_POOL_ID = formatBytes32String ( "ibDUMMY" ) ; const loadFixtureHandler = async ( maybeWallets ? , maybeProvider ? ) : Promise < fixture > => { const [ deployer , alice , bob , dev ] = await ethers . getSigners ( ) ; const AccessControlConfig = ( await ethers . getContractFactory ( "AccessControlConfig" , deployer ) ) as AccessControlConfig__factory ; const accessControlConfig = ( await upgrades . deployProxy ( AccessControlConfig , [ ] ) ) as AccessControlConfig ; const CollateralPoolConfig = ( await ethers . getContractFactory ( "CollateralPoolConfig" , deployer ) ) as CollateralPoolConfig__factory ; const collateralPoolConfig = ( await upgrades . deployProxy ( CollateralPoolConfig , [ accessControlConfig . address , ] ) ) as CollateralPoolConfig ; const BookKeeper = ( await ethers . getContractFactory ( "BookKeeper" , deployer ) ) as BookKeeper__factory ; const bookKeeper = ( await upgrades . deployProxy ( BookKeeper , [ collateralPoolConfig . address , accessControlConfig . address , ] ) ) as BookKeeper ; await bookKeeper . deployed ( ) ; const BEP20 = ( await ethers . getContractFactory ( "BEP20" , deployer ) ) as BEP20__factory ; const ibDUMMY = await BEP20 . deploy ( "ibDUMMY" , "ibDUMMY" ) ; await ibDUMMY . deployed ( ) ; await ibDUMMY . mint ( await alice . getAddress ( ) , ethers . utils . parseEther ( "100" ) ) ; await ibDUMMY . mint ( await bob . getAddress ( ) , ethers . utils . parseEther ( "100" ) ) ; const AlpacaToken = ( await ethers . getContractFactory ( "AlpacaToken" , deployer ) ) as AlpacaToken__factory ; const alpacaToken = await AlpacaToken . deploy ( 88 , 89 ) ; await alpacaToken . mint ( await deployer . getAddress ( ) , ethers . utils . parseEther ( "150" ) ) ; await alpacaToken . deployed ( ) ; const FairLaunch = ( await ethers . getContractFactory ( "FairLaunch" , deployer ) ) as FairLaunch__factory ; const fairLaunch = await FairLaunch . deploy ( alpacaToken . address , await deployer . getAddress ( ) , ALPACA_PER_BLOCK , 0 , 0 , 0 ) ; await fairLaunch . deployed ( ) ; const Shield = ( await ethers . getContractFactory ( "Shield" , deployer ) ) as Shield__factory ; const shield = await Shield . deploy ( deployer . address , fairLaunch . address ) ; await shield . deployed ( ) ; await fairLaunch . addPool ( 1 , ibDUMMY . address , true ) ; await fairLaunch . transferOwnership ( shield . address ) ; await shield . transferOwnership ( await deployer . getAddress ( ) ) ; await alpacaToken . transferOwnership ( fairLaunch . address ) ; const PositionManager = ( await ethers . getContractFactory ( "PositionManager" , deployer ) ) as PositionManager__factory ; const positionManager = ( await upgrades . deployProxy ( PositionManager , [ bookKeeper . address , bookKeeper . address , ] ) ) as PositionManager ; await positionManager . deployed ( ) ; await accessControlConfig . grantRole ( await accessControlConfig . POSITION_MANAGER_ROLE ( ) , positionManager . address ) ; await accessControlConfig . grantRole ( await accessControlConfig . COLLATERAL_MANAGER_ROLE ( ) , positionManager . address ) ; const IbTokenAdapter = ( await ethers . getContractFactory ( "IbTokenAdapter" , deployer ) ) as IbTokenAdapter__factory ; const ibTokenAdapter = ( await upgrades . deployProxy ( IbTokenAdapter , [ bookKeeper . address , COLLATERAL_POOL_ID , ibDUMMY . address , alpacaToken . address , fairLaunch . address , 0 , shield . address , await deployer . getAddress ( ) , BigNumber . from ( 1000 ) , await dev . getAddress ( ) , positionManager . address , ] ) ) as IbTokenAdapter ; await ibTokenAdapter . deployed ( ) ; await accessControlConfig . grantRole ( ethers . utils . solidityKeccak256 ( [ "string" ] , [ "ADAPTER_ROLE" ] ) , ibTokenAdapter . address ) ; return { ibTokenAdapter , bookKeeper , ibDUMMY , shield , alpacaToken , fairLaunch , } ; } ; describe ( "IbTokenAdapter" , ( ) => { let deployer ; let alice ; let bob ; let dev ; let deployerAddress ; let aliceAddress ; let bobAddress ; let devAddress ; let ibTokenAdapter ; let bookKeeper ; let ibDUMMY ; let shield ; let alpacaToken ; let fairLaunch ; let ibTokenAdapterAsAlice ; let ibTokenAdapterAsBob ; let ibDUMMYasAlice ; let ibDUMMYasBob ; beforeEach ( async ( ) => { ; ( { ibTokenAdapter , bookKeeper , ibDUMMY , shield , alpacaToken , fairLaunch } = 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 ( ) , ] ) ; ibTokenAdapterAsAlice = IbTokenAdapter__factory . connect ( ibTokenAdapter . address , alice ) ; ibTokenAdapterAsBob = IbTokenAdapter__factory . connect ( ibTokenAdapter . address , bob ) ; ibDUMMYasAlice = BEP20__factory . connect ( ibDUMMY . address , alice ) ; ibDUMMYasBob = BEP20__factory . connect ( ibDUMMY . address , bob ) ; } ) ; describe ( "#initialize" , async ( ) => { context ( "when collateralToken not match with FairLaunch" , async ( ) => { it ( "should revert" , async ( ) => { const IbTokenAdapter = ( await ethers . getContractFactory ( "IbTokenAdapter" , deployer ) ) as IbTokenAdapter__factory ; await expect ( upgrades . deployProxy ( IbTokenAdapter , [ bookKeeper . address , COLLATERAL_POOL_ID , alpacaToken . address , alpacaToken . address , fairLaunch . address , 0 , shield . address , deployerAddress , BigNumber . from ( 1000 ) , deployerAddress , deployerAddress , ] ) ) . to . be . revertedWith ( "IbTokenAdapter/collateralToken-not-match" ) ; } ) ; } ) ; context ( "when rewardToken not match with FairLaunch" , async ( ) => { it ( "should revert" , async ( ) => { const IbTokenAdapter = ( await ethers . getContractFactory ( "IbTokenAdapter" , deployer ) ) as IbTokenAdapter__factory ; await expect ( upgrades . deployProxy ( IbTokenAdapter , [ bookKeeper . address , COLLATERAL_POOL_ID , ibDUMMY . address , ibDUMMY . address , fairLaunch . address , 0 , shield . address , deployerAddress , BigNumber . from ( 1000 ) , deployerAddress , deployerAddress , ] ) ) . to . be . revertedWith ( "IbTokenAdapter/reward-token-not-match" ) ; } ) ; } ) ; context ( "when shield not match with FairLaunch" , async ( ) => { it ( "should revert" , async ( ) => { const IbTokenAdapter = ( await ethers . getContractFactory ( "IbTokenAdapter" , deployer ) ) as IbTokenAdapter__factory ; await expect ( upgrades . deployProxy ( IbTokenAdapter , [ bookKeeper . address , COLLATERAL_POOL_ID , ibDUMMY . address , alpacaToken . address , fairLaunch . address , 0 , deployerAddress , deployerAddress , BigNumber . from ( 1000 ) , deployerAddress , deployerAddress , ] ) ) . to . be . revertedWith ( "IbTokenAdapter/shield-not-match" ) ; } ) ; } ) ; context ( "when timelock not match with FairLaunch" , async ( ) => { it ( "should revert" , async ( ) => { const IbTokenAdapter = ( await ethers . getContractFactory ( "IbTokenAdapter" , deployer ) ) as IbTokenAdapter__factory ; await expect ( upgrades . deployProxy ( IbTokenAdapter , [ bookKeeper . address , COLLATERAL_POOL_ID , ibDUMMY . address , alpacaToken . address , fairLaunch . address , 0 , shield . address , shield . address , BigNumber . from ( 1000 ) , deployerAddress , deployerAddress , ] ) ) . to . be . revertedWith ( "IbTokenAdapter/timelock-not-match" ) ; } ) ; } ) ; context ( "when all assumptions are correct" , async ( ) => { it ( "should initalized correctly" , async ( ) => { expect ( await ibTokenAdapter . bookKeeper ( ) ) . to . be . eq ( bookKeeper . address ) ; expect ( await ibTokenAdapter . collateralPoolId ( ) ) . to . be . eq ( COLLATERAL_POOL_ID ) ; expect ( await ibTokenAdapter . collateralToken ( ) ) . to . be . eq ( ibDUMMY . address ) ; expect ( await ibTokenAdapter . fairlaunch ( ) ) . to . be . eq ( fairLaunch . address ) ; expect ( await ibTokenAdapter . pid ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . shield ( ) ) . to . be . eq ( shield . address ) ; expect ( await ibTokenAdapter . timelock ( ) ) . to . be . eq ( deployerAddress ) ; expect ( await ibTokenAdapter . decimals ( ) ) . to . be . eq ( 18 ) ; } ) ; } ) ; } ) ; describe ( "#netAssetValuation" , async ( ) => { context ( "when all collateral tokens are deposited by deposit function" , async ( ) => { it ( "should return the correct net asset valuation" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await ibTokenAdapter . netAssetValuation ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . withdraw ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await ibTokenAdapter . netAssetValuation ( ) ) . to . be . eq ( 0 ) ; } ) ; } ) ; context ( "when some one directly transfer collateral tokens to IbTokenAdapter" , async ( ) => { it ( "should only recognized collateral tokens from deposit function" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; await ibDUMMYasBob . transfer ( ibTokenAdapter . address , ethers . utils . parseEther ( "88" ) ) ; expect ( await ibDUMMY . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "88" ) ) ; expect ( await ibTokenAdapter . netAssetValuation ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . withdraw ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await ibDUMMY . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "88" ) ) ; expect ( await ibTokenAdapter . netAssetValuation ( ) ) . to . be . eq ( 0 ) ; } ) ; } ) ; } ) ; describe ( "#netAssetPerShare" , async ( ) => { context ( "when all collateral tokens are deposited by deposit function" , async ( ) => { it ( "should return the correct net asset per share" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await ibTokenAdapter . netAssetPerShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . withdraw ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await ibTokenAdapter . netAssetPerShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; } ) ; } ) ; context ( "when some one directly transfer collateral tokens to IbTokenAdapter" , async ( ) => { it ( "should only recognized collateral tokens from deposit function" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; await ibDUMMYasBob . transfer ( ibTokenAdapter . address , ethers . utils . parseEther ( "88" ) ) ; expect ( await ibDUMMY . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "88" ) ) ; expect ( await ibTokenAdapter . netAssetPerShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . withdraw ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await ibDUMMY . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "88" ) ) ; expect ( await ibTokenAdapter . netAssetPerShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; } ) ; } ) ; } ) ; describe ( "#deposit" , async ( ) => { context ( "when IbTokenAdapter is not live" , async ( ) => { it ( "should revert" , async ( ) => { await ibTokenAdapter . cage ( ) ; await expect ( ibTokenAdapter . deposit ( deployerAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ deployerAddress ] ) ) ) . to . be . revertedWith ( "IbTokenAdapter/not live" ) ; } ) ; } ) ; context ( "when all parameters are valid" , async ( ) => { it ( "should work" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , 0 , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "90" ) ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "100" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( ethers . utils . parseEther ( "0" ) ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "100" ) ) ; expect ( await alpacaToken . balanceOf ( devAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "10" ) ) ; await ibDUMMYasBob . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "4" ) ) ; await ibTokenAdapterAsBob . deposit ( bobAddress , ethers . utils . parseEther ( "4" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ bobAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "90" ) ) ; expect ( await alpacaToken . balanceOf ( bobAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "5" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "300" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "100" ) ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1200" ) ) ; expect ( await alpacaToken . balanceOf ( devAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "10" ) ) ; await ibTokenAdapterAsBob . deposit ( bobAddress , 0 , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ bobAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "220" ) ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "90" ) ) ; expect ( await alpacaToken . balanceOf ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "72" ) ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "5" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "320" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( ethers . utils . parseEther ( "220" ) ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "100" ) ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1280" ) ) ; expect ( await alpacaToken . balanceOf ( devAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "18" ) ) ; } ) ; } ) ; } ) ; describe ( "#withdraw" , async ( ) => { context ( "when withdraw more than what IbTokenAdapter staked" , async ( ) => { it ( "should revert" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; await expect ( ibTokenAdapterAsAlice . withdraw ( aliceAddress , ethers . utils . parseEther ( "100" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ) . to . be . revertedWith ( "withdraw: not good" ) ; } ) ; } ) ; context ( "when withdraw more than what he staked" , async ( ) => { it ( "should revert" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; await ibDUMMYasBob . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "4" ) ) ; await ibTokenAdapterAsBob . deposit ( bobAddress , ethers . utils . parseEther ( "4" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ bobAddress ] ) ) ; await expect ( ibTokenAdapterAsAlice . withdraw ( aliceAddress , ethers . utils . parseEther ( "2" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ) . to . be . revertedWith ( "IbTokenAdapter/insufficient staked amount" ) ; } ) ; } ) ; context ( "when IbTokenAdapter is not live" , async ( ) => { it ( "should still allow user to withdraw" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; await ibTokenAdapter . cage ( ) ; expect ( await ibTokenAdapter . live ( ) ) . to . be . eq ( 0 ) ; let aliceIbDUMMYbefore = await ibDUMMY . balanceOf ( aliceAddress ) ; await ibTokenAdapterAsAlice . withdraw ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; let aliceIbDUMMYafter = await ibDUMMY . balanceOf ( aliceAddress ) ; expect ( aliceIbDUMMYafter . sub ( aliceIbDUMMYbefore ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await fairLaunch . pendingAlpaca ( 0 , ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; } ) ; it ( "should still allow user to withdraw with pending rewards (if any)" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; await ibDUMMYasBob . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "4" ) ) ; await ibTokenAdapterAsBob . deposit ( bobAddress , ethers . utils . parseEther ( "4" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ bobAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( bobAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "5" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "200" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "800" ) ) ; await advanceBlock ( ) ; await ibTokenAdapter . cage ( ) ; expect ( await ibTokenAdapter . live ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . pendingRewards ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . pendingRewards ( bobAddress ) ) . to . be . eq ( 0 ) ; let aliceIbDUMMYbefore = await ibDUMMY . balanceOf ( aliceAddress ) ; await ibTokenAdapterAsAlice . withdraw ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; let aliceIbDUMMYafter = await ibDUMMY . balanceOf ( aliceAddress ) ; expect ( aliceIbDUMMYafter . sub ( aliceIbDUMMYbefore ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await fairLaunch . pendingAlpaca ( 0 , ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "180" ) ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "200" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "800" ) ) ; expect ( await alpacaToken . balanceOf ( devAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "20" ) ) ; let bobIbDUMMYbefore = await ibDUMMY . balanceOf ( bobAddress ) ; await ibTokenAdapterAsBob . withdraw ( bobAddress , ethers . utils . parseEther ( "4" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ bobAddress ] ) ) ; let bobIbDUMMYafter = await ibDUMMY . balanceOf ( bobAddress ) ; expect ( bobIbDUMMYafter . sub ( bobIbDUMMYbefore ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await fairLaunch . pendingAlpaca ( 0 , ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "180" ) ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "200" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( 0 ) ; } ) ; } ) ; context ( "when all parameters are valid" , async ( ) => { it ( "should work" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; let aliceIbDUMMYbefore = await ibDUMMY . balanceOf ( aliceAddress ) ; await ibTokenAdapterAsAlice . withdraw ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; let aliceIbDUMMYafter = await ibDUMMY . balanceOf ( aliceAddress ) ; expect ( aliceIbDUMMYafter . sub ( aliceIbDUMMYbefore ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "90" ) ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "100" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( devAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "10" ) ) ; } ) ; } ) ; context ( "when bob withdraw collateral to alice" , async ( ) => { context ( "when bob doesn't has collateral" , ( ) => { it ( "should be revert" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; await expect ( ibTokenAdapterAsBob . withdraw ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ bobAddress ] ) ) ) . to . be . revertedWith ( "IbTokenAdapter/insufficient staked amount" ) ; } ) ; } ) ; context ( "when bob has collateral" , async ( ) => { it ( "should be able to call withdraw" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; await ibDUMMYasBob . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsBob . deposit ( bobAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ bobAddress ] ) ) ; let aliceIbDUMMYbefore = await ibDUMMY . balanceOf ( aliceAddress ) ; let bobIbDUMMYbefore = await ibDUMMY . balanceOf ( bobAddress ) ; await ibTokenAdapterAsBob . withdraw ( aliceAddress , ethers . utils . parseEther ( "1" ) , "0x" ) ; let aliceIbDUMMYafter = await ibDUMMY . balanceOf ( aliceAddress ) ; let bobIbDUMMYafter = await ibDUMMY . balanceOf ( bobAddress ) ; expect ( aliceIbDUMMYafter . sub ( aliceIbDUMMYbefore ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( bobIbDUMMYafter . sub ( bobIbDUMMYbefore ) ) . to . be . eq ( ethers . utils . parseEther ( "0" ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "250" ) ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( "0" ) ; expect ( await alpacaToken . balanceOf ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "45" ) ) ; } ) ; } ) ; } ) ; } ) ; describe ( "#emergencyWithdraw" , async ( ) => { context ( "when IbTokenAdapter is not live" , async ( ) => { it ( "should allow users to exit with emergencyWithdraw and normal withdraw" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; await ibDUMMYasBob . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "4" ) ) ; await ibTokenAdapterAsBob . deposit ( bobAddress , ethers . utils . parseEther ( "4" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ bobAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( bobAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "5" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "200" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "800" ) ) ; await advanceBlock ( ) ; await ibTokenAdapter . cage ( ) ; expect ( await ibTokenAdapter . live ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . pendingRewards ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . pendingRewards ( bobAddress ) ) . to . be . eq ( 0 ) ; let aliceIbDUMMYbefore = await ibDUMMY . balanceOf ( aliceAddress ) ; await ibTokenAdapterAsAlice . emergencyWithdraw ( aliceAddress ) ; let aliceIbDUMMYafter = await ibDUMMY . balanceOf ( aliceAddress ) ; expect ( aliceIbDUMMYafter . sub ( aliceIbDUMMYbefore ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await fairLaunch . pendingAlpaca ( 0 , ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "200" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "800" ) ) ; let bobIbDUMMYbefore = await ibDUMMY . balanceOf ( bobAddress ) ; await ibTokenAdapterAsBob . withdraw ( bobAddress , ethers . utils . parseEther ( "4" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ bobAddress ] ) ) ; let bobIbDUMMYafter = await ibDUMMY . balanceOf ( bobAddress ) ; expect ( bobIbDUMMYafter . sub ( bobIbDUMMYbefore ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await fairLaunch . pendingAlpaca ( 0 , ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( bobAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "200" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( 0 ) ; } ) ; } ) ; context ( "when all states are normal" , async ( ) => { it ( "should work" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; let aliceIbDUMMYbefore = await ibDUMMY . balanceOf ( aliceAddress ) ; await ibTokenAdapterAsAlice . emergencyWithdraw ( aliceAddress ) ; let aliceIbDUMMYafter = await ibDUMMY . balanceOf ( aliceAddress ) ; expect ( aliceIbDUMMYafter . sub ( aliceIbDUMMYbefore ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "100" ) ) ; expect ( await fairLaunch . pendingAlpaca ( 0 , ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; } ) ; } ) ; } ) ; describe ( "#pendingRewards" , async ( ) => { context ( "when IbTokenAdapter doesn't has any collateralTokens" , async ( ) => { it ( "should returns 0 pending ALPACA" , async ( ) => { expect ( await ibTokenAdapter . pendingRewards ( deployerAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . pendingRewards ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . pendingRewards ( bobAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . pendingRewards ( devAddress ) ) . to . be . eq ( 0 ) ; } ) ; } ) ; context ( "when IbTokenAdapter is not live" , async ( ) => { it ( "should return correct pending ALPACA for each user" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; await ibDUMMYasBob . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "4" ) ) ; await ibTokenAdapterAsBob . deposit ( bobAddress , ethers . utils . parseEther ( "4" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ bobAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "5" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "200" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "800" ) ) ; expect ( await ibTokenAdapter . pendingRewards ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . pendingRewards ( bobAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . pendingRewards ( deployerAddress ) ) . to . be . eq ( 0 ) ; await ibTokenAdapter . cage ( ) ; expect ( await ibTokenAdapter . live ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . pendingRewards ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . pendingRewards ( bobAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . pendingRewards ( deployerAddress ) ) . to . be . eq ( 0 ) ; } ) ; } ) ; context ( "when multiple users use IbTokenAdapter" , async ( ) => { it ( "should returns correct pending ALPACA for each user" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; await ibDUMMYasBob . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "4" ) ) ; await ibTokenAdapterAsBob . deposit ( bobAddress , ethers . utils . parseEther ( "4" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ bobAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "5" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "200" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "800" ) ) ; expect ( await ibTokenAdapter . pendingRewards ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . pendingRewards ( bobAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . pendingRewards ( deployerAddress ) ) . to . be . eq ( 0 ) ; await advanceBlock ( ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "5" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "200" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "800" ) ) ; expect ( await ibTokenAdapter . pendingRewards ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "220" ) ) ; expect ( await ibTokenAdapter . pendingRewards ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "80" ) ) ; expect ( await ibTokenAdapter . pendingRewards ( deployerAddress ) ) . to . be . eq ( 0 ) ; } ) ; } ) ; } ) ; describe ( "#cage/#uncage" , async ( ) => { context ( "when whitelist cage" , async ( ) => { it ( "should put IbTokenAdapter live = 0" , async ( ) => { await ibTokenAdapter . cage ( ) ; expect ( await ibTokenAdapter . live ( ) ) . to . be . eq ( 0 ) ; } ) ; } ) ; context ( "when caller not owner role cage" , async ( ) => { context ( "when assumptions still valid" , async ( ) => { it ( "should revert" , async ( ) => { await expect ( ibTokenAdapterAsAlice . cage ( ) ) . to . be . revertedWith ( "IbTokenAdapter/not-authorized" ) ; } ) ; } ) ; context ( "when shield's owner is changed" , async ( ) => { it ( "should put IbTokenAdapter live = 0" , async ( ) => { await shield . transferOwnership ( aliceAddress ) ; await ibTokenAdapterAsAlice . cage ( ) ; expect ( await ibTokenAdapter . live ( ) ) . to . be . eq ( 0 ) ; } ) ; } ) ; } ) ; context ( "when uncage live IbTokenAdapter" , async ( ) => { it ( "should revert" , async ( ) => { await expect ( ibTokenAdapter . uncage ( ) ) . to . be . revertedWith ( "IbTokenAdapter/not-caged" ) ; } ) ; } ) ; context ( "when cage and uncage" , async ( ) => { it ( "should resume operations perfectly" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; await ibDUMMYasBob . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "4" ) ) ; await ibTokenAdapterAsBob . deposit ( bobAddress , ethers . utils . parseEther ( "4" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ bobAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( bobAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "5" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "200" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "800" ) ) ; await advanceBlock ( ) ; await ibTokenAdapter . cage ( ) ; expect ( await ibTokenAdapter . live ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . pendingRewards ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . pendingRewards ( bobAddress ) ) . to . be . eq ( 0 ) ; let aliceIbDUMMYbefore = await ibDUMMY . balanceOf ( aliceAddress ) ; await ibTokenAdapterAsAlice . withdraw ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; let aliceIbDUMMYafter = await ibDUMMY . balanceOf ( aliceAddress ) ; expect ( aliceIbDUMMYafter . sub ( aliceIbDUMMYbefore ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await fairLaunch . pendingAlpaca ( 0 , ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "180" ) ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "200" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "800" ) ) ; expect ( await alpacaToken . balanceOf ( devAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "20" ) ) ; await ibTokenAdapter . uncage ( ) ; expect ( await ibTokenAdapter . live ( ) ) . to . be . eq ( 1 ) ; await advanceBlock ( ) ; expect ( await fairLaunch . pendingAlpaca ( 0 , ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "100" ) ) ; expect ( await ibTokenAdapter . pendingRewards ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . pendingRewards ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "100" ) ) ; let bobIbDUMMYbefore = await ibDUMMY . balanceOf ( bobAddress ) ; await ibTokenAdapterAsBob . withdraw ( bobAddress , ethers . utils . parseEther ( "4" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ bobAddress ] ) ) ; let bobIbDUMMYafter = await ibDUMMY . balanceOf ( bobAddress ) ; expect ( bobIbDUMMYafter . sub ( bobIbDUMMYbefore ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await fairLaunch . pendingAlpaca ( 0 , ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "180" ) ) ; expect ( await alpacaToken . balanceOf ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "180" ) ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "250" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( devAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "40" ) ) ; } ) ; } ) ; } ) ; describe ( "#complex" , async ( ) => { context ( "when someone sends reward token to IbTokenAdapter" , async ( ) => { it ( "should take them as rewards earned" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; await alpacaToken . transfer ( ibTokenAdapter . address , ethers . utils . parseEther ( "150" ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "150" ) ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . pendingRewards ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "250" ) ) ; expect ( await fairLaunch . pendingAlpaca ( 0 , ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "100" ) ) ; await ibTokenAdapterAsAlice . withdraw ( aliceAddress , 0 , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "315" ) ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "350" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "350" ) ) ; expect ( await ibTokenAdapter . pendingRewards ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await fairLaunch . pendingAlpaca ( 0 , ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( devAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "35" ) ) ; } ) ; } ) ; context ( "when Alice exit with emergency withdraw, but Bob wait for uncage and withdraw" , async ( ) => { it ( "should only give Bob his rewards" , async ( ) => { await ibDUMMYasAlice . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "1" ) ) ; await ibTokenAdapterAsAlice . deposit ( aliceAddress , ethers . utils . parseEther ( "1" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ aliceAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; await ibDUMMYasBob . approve ( ibTokenAdapter . address , ethers . utils . parseEther ( "4" ) ) ; await ibTokenAdapterAsBob . deposit ( bobAddress , ethers . utils . parseEther ( "4" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ bobAddress ] ) ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( bobAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "5" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "200" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "800" ) ) ; await advanceBlock ( ) ; await ibTokenAdapter . cage ( ) ; expect ( await ibTokenAdapter . live ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . pendingRewards ( aliceAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . pendingRewards ( bobAddress ) ) . to . be . eq ( 0 ) ; let aliceIbDUMMYbefore = await ibDUMMY . balanceOf ( aliceAddress ) ; await ibTokenAdapterAsAlice . emergencyWithdraw ( aliceAddress ) ; let aliceIbDUMMYafter = await ibDUMMY . balanceOf ( aliceAddress ) ; expect ( aliceIbDUMMYafter . sub ( aliceIbDUMMYbefore ) ) . to . be . eq ( ethers . utils . parseEther ( "1" ) ) ; expect ( await fairLaunch . pendingAlpaca ( 0 , ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "200" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "800" ) ) ; await ibTokenAdapter . uncage ( ) ; expect ( await ibTokenAdapter . live ( ) ) . to . be . eq ( 1 ) ; expect ( await ibTokenAdapter . pendingRewards ( bobAddress ) ) . to . be . eq ( 0 ) ; let bobIbDUMMYbefore = await ibDUMMY . balanceOf ( bobAddress ) ; await ibTokenAdapterAsBob . withdraw ( bobAddress , ethers . utils . parseEther ( "4" ) , ethers . utils . defaultAbiCoder . encode ( [ "address" ] , [ bobAddress ] ) ) ; let bobIbDUMMYafter = await ibDUMMY . balanceOf ( bobAddress ) ; expect ( bobIbDUMMYafter . sub ( bobIbDUMMYbefore ) ) . to . be . eq ( ethers . utils . parseEther ( "4" ) ) ; expect ( await fairLaunch . pendingAlpaca ( 0 , ibTokenAdapter . address ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( ibTokenAdapter . address ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await alpacaToken . balanceOf ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( bobAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "90" ) ) ; expect ( await ibTokenAdapter . totalShare ( ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . accRewardPerShare ( ) ) . to . be . eq ( weiToRay ( ethers . utils . parseEther ( "225" ) ) ) ; expect ( await ibTokenAdapter . accRewardBalance ( ) ) . to . be . eq ( ethers . utils . parseEther ( "200" ) ) ; expect ( await ibTokenAdapter . stake ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . rewardDebts ( aliceAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . stake ( bobAddress ) ) . to . be . eq ( 0 ) ; expect ( await ibTokenAdapter . rewardDebts ( bobAddress ) ) . to . be . eq ( 0 ) ; expect ( await alpacaToken . balanceOf ( devAddress ) ) . to . be . eq ( ethers . utils . parseEther ( "10" ) ) ; } ) ; } ) ; } ) ; } ) ;