import { Wallet } from "@ethersproject/wallet" ; import { DEAD_ADDRESS , ONE_HOUR , ONE_WEEK , ZERO_ADDRESS } from "@utils/constants" ; import { StandardAccounts } from "@utils/machines" ; import { expect } from "chai" ; import { ethers } from "hardhat" ; import { BN , simpleToExactAmount , sum } from "@utils/math" ; import { AssetProxy__factory , EmissionsController , EmissionsController__factory , MockERC20 , MockERC20__factory , MockNexus , MockNexus__factory , MockRewardsDistributionRecipient , MockRewardsDistributionRecipient__factory , MockStakingContract , MockStakingContract__factory , } from "types/generated" ; import { currentWeekEpoch , increaseTime , getTimestamp , increaseTimeTo , startWeek , weekEpoch } from "@utils/time" ; import { Account } from "types/common" ; import { keccak256 , toUtf8Bytes } from "ethers/lib/utils" ; const defaultConfig = { A : - 166000000000000 , B : 168479942061125 , C : - 168479942061125 , D : 166000000000000 , EPOCHS : 312 , } ; const INITIAL_DIALS_NO = 3 ; interface VoteHistoryExpectation { dialId : number ; votesNo : number ; lastVote : number | BN ; lastEpoch : number ; } interface DialData { disabled : boolean ; notify : boolean ; cap : number ; balance : BN ; recipient : string ; voteHistory : { votes : BN ; epoch : number ; } [ ] ; } param { VoteHistoryExpectation } return { votesHistory } const expectDialVotesHistoryForDial = async ( emissionsController , { dialId , votesNo , lastVote , lastEpoch } ) => { const votesHistory = await emissionsController . getDialVoteHistory ( dialId ) ; expect ( votesHistory , "voteHistory" ) . length ( votesNo ) ; expect ( votesHistory [ votesHistory . length - 1 ] [ 0 ] , "vote" ) . to . eq ( lastVote ) ; expect ( votesHistory [ votesHistory . length - 1 ] [ 1 ] , "epoch" ) . to . eq ( lastEpoch ) ; return votesHistory [ votesHistory . length - 1 ] ; } ; const expectDialVotesHistoryForDials = async ( emissionsController , votesHistoryExpectations = [ ] ) => { const expectations = Promise . all ( votesHistoryExpectations . map ( ( voteHistory ) => expectDialVotesHistoryForDial ( emissionsController , { ... voteHistory , lastVote : voteHistory . lastVote > 0 ? simpleToExactAmount ( voteHistory . lastVote ) : voteHistory . lastVote , } ) ) ) ; return await expectations ; } ; const expectDialVotesHistoryWithoutChangeOnWeights = async ( emissionsController , votesHistoryExpectations = [ ] ) => expectDialVotesHistoryForDials ( emissionsController , votesHistoryExpectations . map ( ( voteHistory ) => ( { ... voteHistory , votesNo : voteHistory . votesNo + 1 , lastEpoch : voteHistory . lastEpoch + 1 , } ) ) ) ; param { number } epochDelta return { emissionForEpoch } const calcWeeklyReward = ( epochDelta ) : BN => { const { A , B , C , D , EPOCHS } = defaultConfig ; const inputScale = simpleToExactAmount ( 1 , 3 ) ; const calculationScale = 12 ; const x = BN . from ( epochDelta ) . mul ( simpleToExactAmount ( 1 , calculationScale ) ) . div ( BN . from ( EPOCHS ) ) ; const a = BN . from ( A ) . mul ( inputScale ) . mul ( x . pow ( 3 ) ) . div ( simpleToExactAmount ( 1 , calculationScale * 3 ) ) ; const b = BN . from ( B ) . mul ( inputScale ) . mul ( x . pow ( 2 ) ) . div ( simpleToExactAmount ( 1 , calculationScale * 2 ) ) ; const c = BN . from ( C ) . mul ( inputScale ) . mul ( x ) . div ( simpleToExactAmount ( 1 , calculationScale ) ) ; const d = BN . from ( D ) . mul ( inputScale ) ; return a . add ( b ) . add ( c ) . add ( d ) . mul ( simpleToExactAmount ( 1 , 6 ) ) ; } ; param { EmissionsController } emissionsController param { number } epoch return { emissionForEpoch } const nextRewardAmount = async ( emissionsController , epoch = 1 ) : Promise < BN > => { const [ startEpoch , lastEpoch ] = await emissionsController . epochs ( ) ; return calcWeeklyReward ( lastEpoch - startEpoch + epoch ) ; } ; param { EmissionsController } emissionsController param { number } startEpoch param { number } deltaEpoch- const expectTopLineEmissionForEpoch = ( emissionsController , startEpoch ) => async ( deltaEpoch ) => { const emissionForEpoch = await emissionsController . topLineEmission ( startEpoch + deltaEpoch ) ; const expectedEmissionAmount = await nextRewardAmount ( emissionsController , deltaEpoch ) ; expect ( emissionForEpoch ) . eq ( expectedEmissionAmount ) ; } ; const snapDial = async ( emissionsController , dialId ) : Promise < DialData > => { const dialData = await emissionsController . dials ( dialId ) ; const voteHistory = await emissionsController . getDialVoteHistory ( dialId ) ; return { ... dialData , voteHistory , } ; } ; describe ( "EmissionsController" , async ( ) => { let sa ; let nexus ; let staking1 ; let staking2 ; let rewardToken ; let dials ; let emissionsController ; let currentEpoch ; let nextEpoch ; let voter1 ; let voter2 ; let voter3 ; const totalRewardsSupply = simpleToExactAmount ( 100000000 ) ; const totalRewards = simpleToExactAmount ( 29400963 ) ; return { Promise } const deployEmissionsController = async ( ) : Promise < void > => { staking1 = await new MockStakingContract__factory ( sa . default . signer ) . deploy ( ) ; staking2 = await new MockStakingContract__factory ( sa . default . signer ) . deploy ( ) ; nexus = await new MockNexus__factory ( sa . default . signer ) . deploy ( sa . governor . address , DEAD_ADDRESS , DEAD_ADDRESS ) ; rewardToken = await new MockERC20__factory ( sa . default . signer ) . deploy ( "Reward" , "RWD" , 18 , sa . default . address , totalRewardsSupply ) ; const deployDial = ( ) => new MockRewardsDistributionRecipient__factory ( sa . default . signer ) . deploy ( rewardToken . address , DEAD_ADDRESS ) ; dials = await Promise . all ( [ ... Array ( INITIAL_DIALS_NO ) . keys ( ) ] . map ( deployDial ) ) ; const dialAddresses = dials . map ( ( dial ) => dial . address ) ; const emissionsControllerImpl = await new EmissionsController__factory ( sa . default . signer ) . deploy ( nexus . address , rewardToken . address , defaultConfig ) ; const initializeData = emissionsControllerImpl . interface . encodeFunctionData ( "initialize" , [ dialAddresses , [ 0 , 0 , 0 ] , [ true , true , false ] , [ staking1 . address , staking2 . address ] , ] ) ; const proxy = await new AssetProxy__factory ( sa . default . signer ) . deploy ( emissionsControllerImpl . address , DEAD_ADDRESS , initializeData ) ; emissionsController = new EmissionsController__factory ( sa . default . signer ) . attach ( proxy . address ) ; await rewardToken . transfer ( emissionsController . address , totalRewards ) ; await staking1 . setGovernanceHook ( emissionsController . address ) ; await staking2 . setGovernanceHook ( emissionsController . address ) ; const currentTime = await getTimestamp ( ) ; currentEpoch = weekEpoch ( currentTime ) ; nextEpoch = currentEpoch . add ( 1 ) ; } ; before ( async ( ) => { const accounts = await ethers . getSigners ( ) ; sa = await new StandardAccounts ( ) . initAccounts ( accounts ) ; console . log ( `Keeper ${ keccak256 ( toUtf8Bytes ( "Keeper" ) ) }` ) ; voter1 = sa . dummy1 ; voter2 = sa . dummy2 ; voter3 = sa . dummy3 ; console . log ( `Voter 1 ${ voter1 . address }` ) ; console . log ( `Voter 2 ${ voter2 . address }` ) ; console . log ( `Voter 3 ${ voter3 . address }` ) ; const currentTime = await getTimestamp ( ) ; const startCurrentPeriod = startWeek ( currentTime ) ; const earlyNextPeriod = startCurrentPeriod . add ( ONE_WEEK ) . add ( ONE_HOUR ) ; await increaseTimeTo ( earlyNextPeriod ) ; } ) ; describe ( "deploy and initialize" , ( ) => { before ( async ( ) => { await deployEmissionsController ( ) ; console . log ( `Emissions Controller contract size ${ EmissionsController__factory . bytecode . length }` ) ; } ) ; it ( "Immutable variables set on deployment" , async ( ) => { expect ( await emissionsController . nexus ( ) , "nexus" ) . to . eq ( nexus . address ) ; expect ( await emissionsController . REWARD_TOKEN ( ) , "rewardToken" ) . to . eq ( rewardToken . address ) ; } ) ; it ( "Dials set on initialization" , async ( ) => { const dial1 = await snapDial ( emissionsController , 0 ) ; expect ( dial1 . recipient , "dial 1 recipient" ) . to . eq ( dials [ 0 ] . address ) ; expect ( dial1 . notify , "dial 1 notify" ) . to . eq ( true ) ; expect ( dial1 . cap , "dial 1 cap" ) . to . eq ( 0 ) ; expect ( dial1 . balance , "dial 1 balance" ) . to . eq ( 0 ) ; expect ( dial1 . disabled , "dial 1 disabled" ) . to . eq ( false ) ; expect ( dial1 . voteHistory , "dial 1 vote len" ) . to . lengthOf ( 1 ) ; expect ( dial1 . voteHistory [ 0 ] . votes , "dial 1 votes" ) . to . eq ( 0 ) ; expect ( dial1 . voteHistory [ 0 ] . epoch , "dial 1 votes epoch" ) . to . eq ( nextEpoch ) ; const dial3 = await emissionsController . dials ( 2 ) ; expect ( dial3 . recipient , "dial 3 recipient" ) . to . eq ( dials [ 2 ] . address ) ; expect ( dial3 . notify , "dial 3 notify" ) . to . eq ( false ) ; } ) ; it ( "epoch set on initialization" , async ( ) => { const [ startEpoch , lastEpoch ] = await emissionsController . epochs ( ) ; expect ( startEpoch , "start epoch" ) . to . eq ( nextEpoch ) ; expect ( lastEpoch , "last epoch" ) . to . eq ( nextEpoch ) ; } ) ; it ( "transfer MTA on initialization" , async ( ) => { expect ( await rewardToken . balanceOf ( emissionsController . address ) , "ec rewards bal" ) . to . eq ( totalRewards ) ; } ) ; it ( "Staking contracts set on initialization" , async ( ) => { expect ( await emissionsController . stakingContracts ( 0 ) , "staking contract 1" ) . to . eq ( staking1 . address ) ; expect ( await emissionsController . stakingContracts ( 1 ) , "staking contract 2" ) . to . eq ( staking2 . address ) ; } ) ; context ( "should fail when" , ( ) => { it ( "nexus is zero" , async ( ) => { const tx = new EmissionsController__factory ( sa . default . signer ) . deploy ( ZERO_ADDRESS , rewardToken . address , defaultConfig ) ; await expect ( tx ) . to . revertedWith ( "Nexus address is zero" ) ; } ) ; it ( "rewards token is zero" , async ( ) => { const tx = new EmissionsController__factory ( sa . default . signer ) . deploy ( nexus . address , ZERO_ADDRESS , defaultConfig ) ; await expect ( tx ) . to . revertedWith ( "Reward token address is zero" ) ; } ) ; } ) ; context ( "should fail to initialize when" , ( ) => { before ( async ( ) => { emissionsController = await new EmissionsController__factory ( sa . default . signer ) . deploy ( nexus . address , rewardToken . address , defaultConfig ) ; } ) ; const stakingContract1 = Wallet . createRandom ( ) ; const stakingContract2 = Wallet . createRandom ( ) ; const tests = [ { desc : "recipients empty" , dialIndexes : [ ] , caps : [ ] , notifies : [ true , false ] , fixedDistributionAmounts : [ 0 , 0 ] , stakingContracts : [ stakingContract1 . address , stakingContract2 . address ] , } , { desc : "notifies empty" , dialIndexes : [ 0 , 1 ] , caps : [ 0 , 0 ] , notifies : [ ] , fixedDistributionAmounts : [ 0 , 0 ] , stakingContracts : [ stakingContract1 . address , stakingContract2 . address ] , } , { desc : "different recipient and notify lengths" , dialIndexes : [ 0 ] , caps : [ 0 , 0 ] , notifies : [ true , false ] , fixedDistributionAmounts : [ 0 , 0 ] , stakingContracts : [ stakingContract1 . address , stakingContract2 . address ] , } , ] ; tests . forEach ( ( test ) => { it ( test . desc , async ( ) => { const recipients = test . dialIndexes . map ( ( i ) => dials [ i ] . address ) ; const tx = emissionsController . initialize ( recipients , test . caps , test . notifies , test . stakingContracts ) ; await expect ( tx ) . to . revertedWith ( "Initialize args mismatch" ) ; } ) ; } ) ; it ( "first staking contract is zero" , async ( ) => { const recipients = dials . map ( ( d ) => d . address ) ; const tx = emissionsController . initialize ( recipients , [ 0 , 0 , 0 ] , [ true , true , false ] , [ ZERO_ADDRESS , staking2 . address ] ) ; await expect ( tx ) . to . revertedWith ( "Staking contract address is zero" ) ; } ) ; it ( "second staking contract is zero" , async ( ) => { const recipients = dials . map ( ( d ) => d . address ) ; const tx = emissionsController . initialize ( recipients , [ 0 , 0 , 0 ] , [ true , true , false ] , [ staking1 . address , ZERO_ADDRESS ] ) ; await expect ( tx ) . to . revertedWith ( "Staking contract address is zero" ) ; } ) ; } ) ; } ) ; describe ( "calling view functions" , ( ) => { describe ( "fetch weekly emissions" , ( ) => { let startEpoch ; let expectTopLineEmissions ; before ( async ( ) => { await deployEmissionsController ( ) ; [ startEpoch ] = await emissionsController . epochs ( ) ; expectTopLineEmissions = expectTopLineEmissionForEpoch ( emissionsController , startEpoch ) ; } ) ; it ( "fails fetching an smaller epoch than deployed time" , async ( ) => { const tx = emissionsController . topLineEmission ( startEpoch ) ; await expect ( tx ) . to . revertedWith ( "Wrong epoch number" ) ; } ) ; it ( "fails fetching same epoch as deployed time" , async ( ) => { const tx = emissionsController . topLineEmission ( startEpoch ) ; await expect ( tx ) . to . revertedWith ( "Wrong epoch number" ) ; } ) ; it ( "fetches week 1" , async ( ) => { await expectTopLineEmissions ( 1 ) ; } ) ; it ( "fetches week 8 - Two months" , async ( ) => { await expectTopLineEmissions ( 8 ) ; } ) ; it ( "fetches week 100 - one year eleven months" , async ( ) => { await expectTopLineEmissions ( 100 ) ; } ) ; it ( "fetches week 311 - six years, pre-last epoch" , async ( ) => { await expectTopLineEmissions ( 311 ) ; } ) ; it ( "fetches week 312 - six years, last epoch" , async ( ) => { await expectTopLineEmissions ( 312 ) ; } ) ; it ( "fails fetching week 313 - six years + one week" , async ( ) => { const tx = emissionsController . topLineEmission ( startEpoch + 313 ) ; await expect ( tx ) . to . revertedWith ( "Wrong epoch number" ) ; } ) ; it ( "fails fetching week 5200 - Ten years" , async ( ) => { const tx = emissionsController . topLineEmission ( startEpoch + 5200 ) ; await expect ( tx ) . to . revertedWith ( "Wrong epoch number" ) ; } ) ; } ) ; describe ( "gets a dials weighted votes  " , ( ) => { let startEpoch ; before ( async ( ) => { await deployEmissionsController ( ) ; [ startEpoch ] = await emissionsController . epochs ( ) ; } ) ; it ( "gets initial dials vote history " , async ( ) => { ; [ ... Array ( INITIAL_DIALS_NO ) . keys ( ) ] . forEach ( async ( dialId ) => { const voteHistory = await emissionsController . getDialVoteHistory ( dialId ) ; const [ [ votes , epoch ] ] = voteHistory ; expect ( voteHistory , "voteHistory" ) . length ( 1 ) ; expect ( votes , "votes" ) . to . eq ( 0 ) ; expect ( epoch , "epoch" ) . to . eq ( startEpoch ) ; } ) ; } ) ; } ) ; } ) ; describe ( "using admin functions" , ( ) => { describe ( "add dial" , ( ) => { let newDial ; beforeEach ( async ( ) => { await deployEmissionsController ( ) ; newDial = await new MockRewardsDistributionRecipient__factory ( sa . default . signer ) . deploy ( rewardToken . address , DEAD_ADDRESS ) ; dials . push ( newDial ) ; } ) ; it ( "governor adds new dial in the first launch week" , async ( ) => { const tx = await emissionsController . connect ( sa . governor . signer ) . addDial ( newDial . address , 0 , true ) ; await expect ( tx ) . to . emit ( emissionsController , "AddedDial" ) . withArgs ( 3 , newDial . address ) ; const savedDial = await snapDial ( emissionsController , 3 ) ; expect ( savedDial . recipient , "recipient" ) . to . eq ( newDial . address ) ; expect ( savedDial . notify , "notify" ) . to . eq ( true ) ; expect ( savedDial . cap , "staking" ) . to . eq ( 0 ) ; expect ( savedDial . balance , "balance" ) . to . eq ( 0 ) ; expect ( savedDial . disabled , "disabled" ) . to . eq ( false ) ; expect ( savedDial . voteHistory , "number votes" ) . to . lengthOf ( 1 ) ; expect ( savedDial . voteHistory [ 0 ] . epoch , "epoch" ) . to . eq ( nextEpoch ) ; expect ( savedDial . voteHistory [ 0 ] . votes , "votes" ) . to . eq ( 0 ) ; } ) ; it ( "governor adds new dial in the second launch week" , async ( ) => { await increaseTime ( ONE_WEEK ) ; const tx = await emissionsController . connect ( sa . governor . signer ) . addDial ( newDial . address , 0 , true ) ; await expect ( tx ) . to . emit ( emissionsController , "AddedDial" ) . withArgs ( 3 , newDial . address ) ; const savedDial = await snapDial ( emissionsController , 3 ) ; expect ( savedDial . recipient , "recipient" ) . to . eq ( newDial . address ) ; expect ( savedDial . notify , "notify" ) . to . eq ( true ) ; expect ( savedDial . cap , "staking" ) . to . eq ( 0 ) ; expect ( savedDial . balance , "balance" ) . to . eq ( 0 ) ; expect ( savedDial . disabled , "disabled" ) . to . eq ( false ) ; expect ( savedDial . voteHistory , "number votes" ) . to . lengthOf ( 1 ) ; const epochExpected = await currentWeekEpoch ( ) ; expect ( savedDial . voteHistory [ 0 ] . epoch , "epoch" ) . to . eq ( epochExpected ) ; expect ( savedDial . voteHistory [ 0 ] . votes , "votes" ) . to . eq ( 0 ) ; } ) ; context ( "should fail when" , ( ) => { it ( "recipient is zero" , async ( ) => { const tx = emissionsController . connect ( sa . governor . signer ) . addDial ( ZERO_ADDRESS , 0 , true ) ; await expect ( tx ) . to . revertedWith ( "Dial address is zero" ) ; } ) ; it ( "cap > 100" , async ( ) => { const tx = emissionsController . connect ( sa . governor . signer ) . addDial ( dials [ 0 ] . address , 101 , true ) ; await expect ( tx ) . to . revertedWith ( "Invalid cap" ) ; } ) ; it ( "existing dial" , async ( ) => { const tx = emissionsController . connect ( sa . governor . signer ) . addDial ( dials [ 0 ] . address , 0 , true ) ; await expect ( tx ) . to . revertedWith ( "Dial already exists" ) ; } ) ; it ( "not governor" , async ( ) => { const tx = emissionsController . addDial ( newDial . address , 0 , true ) ; await expect ( tx ) . to . revertedWith ( "Only governor can execute" ) ; } ) ; } ) ; } ) ; describe ( "update dial" , ( ) => { const voter1Staking1Votes = simpleToExactAmount ( 100 ) ; const voter2Staking1Votes = simpleToExactAmount ( 200 ) ; const voter3Staking1Votes = simpleToExactAmount ( 300 ) ; let dial1 ; let dial2 ; let dial3 ; beforeEach ( async ( ) => { await deployEmissionsController ( ) ; await increaseTime ( ONE_WEEK ) ; await staking1 . setVotes ( voter1 . address , voter1Staking1Votes ) ; await staking1 . setVotes ( voter2 . address , voter2Staking1Votes ) ; await staking1 . setVotes ( voter3 . address , voter3Staking1Votes ) ; await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 200 } ] ) ; await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ { dialId : 1 , weight : 200 } ] ) ; await emissionsController . connect ( voter3 . signer ) . setVoterDialWeights ( [ { dialId : 2 , weight : 200 } ] ) ; } ) ; it ( "Governor disables dial 1 with votes" , async ( ) => { const dialBefore = await snapDial ( emissionsController , 0 ) ; expect ( dialBefore . disabled , "dial 1 disabled before" ) . to . eq ( false ) ; const tx = await emissionsController . connect ( sa . governor . signer ) . updateDial ( 0 , true , true ) ; await expect ( tx ) . to . emit ( emissionsController , "UpdatedDial" ) . withArgs ( 0 , true , true ) ; const dialAfter = await snapDial ( emissionsController , 0 ) ; expect ( dialAfter . disabled , "dial 1 disabled after" ) . to . eq ( true ) ; await increaseTime ( ONE_WEEK ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx2 = await emissionsController . calculateRewards ( ) ; const adjustedDial2 = nextEpochEmission . mul ( 200 ) . div ( 500 ) ; const adjustedDial3 = nextEpochEmission . mul ( 300 ) . div ( 500 ) ; await expect ( tx2 ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ 0 , adjustedDial2 , adjustedDial3 ] ) ; } ) ; it ( "Governor reenables dial" , async ( ) => { const dialId = 0 ; await emissionsController . connect ( sa . governor . signer ) . updateDial ( dialId , true , true ) ; await increaseTime ( ONE_WEEK ) ; await emissionsController . calculateRewards ( ) ; await increaseTime ( ONE_WEEK . add ( 60 ) ) ; const tx = await emissionsController . connect ( sa . governor . signer ) . updateDial ( 0 , false , true ) ; await expect ( tx ) . to . emit ( emissionsController , "UpdatedDial" ) . withArgs ( 0 , false , true ) ; expect ( ( await emissionsController . dials ( 0 ) ) . disabled , "dial 1 reenabled after" ) . to . eq ( false ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx2 = await emissionsController . calculateRewards ( ) ; dial1 = nextEpochEmission . mul ( 100 ) . div ( 600 ) ; dial2 = nextEpochEmission . mul ( 200 ) . div ( 600 ) ; dial3 = nextEpochEmission . mul ( 300 ) . div ( 600 ) ; await expect ( tx2 ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ dial1 , dial2 , dial3 ] ) ; } ) ; it ( "Governor fails to disable invalid 4th dial" , async ( ) => { const tx = emissionsController . connect ( sa . governor . signer ) . updateDial ( 3 , true , true ) ; await expect ( tx ) . to . revertedWith ( "Invalid dial id" ) ; } ) ; it ( "Default user fails to update dial" , async ( ) => { const tx = emissionsController . updateDial ( 1 , true , true ) ; await expect ( tx ) . to . revertedWith ( "Only governor can execute" ) ; } ) ; } ) ; describe ( "adding staking contract" , ( ) => { let newStakingContract ; before ( async ( ) => { await deployEmissionsController ( ) ; newStakingContract = await new MockStakingContract__factory ( sa . default . signer ) . deploy ( ) ; } ) ; context ( "should fail when" , ( ) => { it ( "Only governor" , async ( ) => { const tx = emissionsController . addStakingContract ( newStakingContract . address ) ; await expect ( tx ) . to . revertedWith ( "Only governor can execute" ) ; } ) ; it ( "staking contract already exists" , async ( ) => { const tx = emissionsController . connect ( sa . governor . signer ) . addStakingContract ( staking1 . address ) ; await expect ( tx ) . to . revertedWith ( "StakingContract already exists" ) ; } ) ; it ( "staking contract is zero" , async ( ) => { const tx = emissionsController . connect ( sa . governor . signer ) . addStakingContract ( ZERO_ADDRESS ) ; await expect ( tx ) . to . revertedWith ( "Staking contract address is zero" ) ; } ) ; } ) ; it ( "should add staking contract" , async ( ) => { const tx = await emissionsController . connect ( sa . governor . signer ) . addStakingContract ( newStakingContract . address ) ; await expect ( tx ) . to . emit ( emissionsController , "AddStakingContract" ) . withArgs ( newStakingContract . address ) ; const currentTime = await getTimestamp ( ) ; expect ( await emissionsController . stakingContractAddTime ( newStakingContract . address ) , "add timestamp" ) . to . gte ( currentTime ) ; expect ( await emissionsController . stakingContracts ( 2 ) , "add to staking contract array" ) . to . eq ( newStakingContract . address ) ; } ) ; } ) ; } ) ; describe ( "donating" , ( ) => { const voter1Staking1Votes = simpleToExactAmount ( 100 ) ; const voter1Staking2Votes = simpleToExactAmount ( 200 ) ; const voter2Staking1Votes = simpleToExactAmount ( 600 ) ; const voter3Staking1Votes = simpleToExactAmount ( 300 ) ; beforeEach ( async ( ) => { await deployEmissionsController ( ) ; await rewardToken . approve ( emissionsController . address , totalRewardsSupply ) ; await staking1 . setVotes ( voter1 . address , voter1Staking1Votes ) ; await staking2 . setVotes ( voter1 . address , voter1Staking2Votes ) ; await staking1 . setVotes ( voter2 . address , voter2Staking1Votes ) ; await staking1 . setVotes ( voter3 . address , voter3Staking1Votes ) ; await increaseTime ( ONE_WEEK ) ; } ) ; context ( "fail to donate when" , ( ) => { it ( "No dial ids or amounts" , async ( ) => { const tx = emissionsController . donate ( [ ] , [ ] ) ; await expect ( tx ) . to . revertedWith ( "Invalid inputs" ) ; } ) ; it ( "No dial ids but amounts" , async ( ) => { const tx = emissionsController . donate ( [ ] , [ 100 ] ) ; await expect ( tx ) . to . revertedWith ( "Invalid inputs" ) ; } ) ; it ( "No amounts but dials" , async ( ) => { const tx = emissionsController . donate ( [ 0 ] , [ ] ) ; await expect ( tx ) . to . revertedWith ( "Invalid inputs" ) ; } ) ; it ( "Less dial ids than amounts" , async ( ) => { const tx = emissionsController . donate ( [ 0 ] , [ 100 , 200 ] ) ; await expect ( tx ) . to . revertedWith ( "Invalid inputs" ) ; } ) ; it ( "Less amounts than dials" , async ( ) => { const tx = emissionsController . donate ( [ 0 , 1 , 2 ] , [ 100 , 200 ] ) ; await expect ( tx ) . to . revertedWith ( "Invalid inputs" ) ; } ) ; it ( "first dial is invalid" , async ( ) => { const tx = emissionsController . donate ( [ 3 ] , [ 100 ] ) ; await expect ( tx ) . to . revertedWith ( "Invalid dial id" ) ; } ) ; } ) ; context ( "Voter 1 80/20 votes to dial 1 & 2, Voter 2 50/50 votes to dial 2 & 3" , ( ) => { let dial1 ; let dial2 ; let dial3 ; beforeEach ( async ( ) => { await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 160 } , { dialId : 1 , weight : 40 } , ] ) ; await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ { dialId : 1 , weight : 100 } , { dialId : 2 , weight : 100 } , ] ) ; await increaseTime ( ONE_WEEK ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; dial1 = nextEpochEmission . mul ( ( 300 * 4 ) / 5 ) . div ( 900 ) ; dial2 = nextEpochEmission . mul ( 300 / 5 + 600 / 2 ) . div ( 900 ) ; dial3 = nextEpochEmission . mul ( 600 / 2 ) . div ( 900 ) ; } ) ; it ( "donation to dial 1 before rewards calculated" , async ( ) => { const donationAmount = simpleToExactAmount ( 100 ) ; const tx = await emissionsController . donate ( [ 0 ] , [ donationAmount ] ) ; await expect ( tx ) . to . emit ( emissionsController , "DonatedRewards" ) . withArgs ( 0 , donationAmount ) ; await expect ( tx ) . to . emit ( rewardToken , "Transfer" ) . withArgs ( sa . default . address , emissionsController . address , donationAmount ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( donationAmount ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; it ( "donation to dial 1 after rewards calculated" , async ( ) => { await emissionsController . calculateRewards ( ) ; const donationAmount = simpleToExactAmount ( 100 ) ; const tx = await emissionsController . donate ( [ 0 ] , [ donationAmount ] ) ; await expect ( tx ) . to . emit ( emissionsController , "DonatedRewards" ) . withArgs ( 0 , donationAmount ) ; await expect ( tx ) . to . emit ( rewardToken , "Transfer" ) . withArgs ( sa . default . address , emissionsController . address , donationAmount ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( donationAmount . add ( dial1 ) ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( dial2 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( dial3 ) ; } ) ; it ( "donation to dials 1, 2 and 3 after rewards calculated" , async ( ) => { await emissionsController . calculateRewards ( ) ; const donationAmounts = [ simpleToExactAmount ( 100 ) , simpleToExactAmount ( 200 ) , simpleToExactAmount ( 300 ) ] ; const tx = await emissionsController . donate ( [ 0 , 1 , 2 ] , donationAmounts ) ; await expect ( tx ) . to . emit ( emissionsController , "DonatedRewards" ) . withArgs ( 0 , donationAmounts [ 0 ] ) ; await expect ( tx ) . to . emit ( emissionsController , "DonatedRewards" ) . withArgs ( 1 , donationAmounts [ 1 ] ) ; await expect ( tx ) . to . emit ( emissionsController , "DonatedRewards" ) . withArgs ( 2 , donationAmounts [ 2 ] ) ; await expect ( tx ) . to . emit ( rewardToken , "Transfer" ) . withArgs ( sa . default . address , emissionsController . address , simpleToExactAmount ( 600 ) ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( dial1 . add ( donationAmounts [ 0 ] ) ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( dial2 . add ( donationAmounts [ 1 ] ) ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( dial3 . add ( donationAmounts [ 2 ] ) ) ; } ) ; } ) ; } ) ; describe ( "calculating rewards" , ( ) => { const VOTERS = { "1" : { votes : 300 } , "2" : { votes : 600 } , "3" : { votes : 300 } } ; const voter1Staking1Votes = simpleToExactAmount ( VOTERS [ "1" ] . votes / 3 ) ; const voter1Staking2Votes = simpleToExactAmount ( ( VOTERS [ "1" ] . votes / 3 ) * 2 ) ; const voter2Staking1Votes = simpleToExactAmount ( VOTERS [ "2" ] . votes ) ; const voter3Staking1Votes = simpleToExactAmount ( VOTERS [ "3" ] . votes ) ; beforeEach ( async ( ) => { await deployEmissionsController ( ) ; await staking1 . setVotes ( voter1 . address , voter1Staking1Votes ) ; await staking2 . setVotes ( voter1 . address , voter1Staking2Votes ) ; await staking1 . setVotes ( voter2 . address , voter2Staking1Votes ) ; await staking1 . setVotes ( voter3 . address , voter3Staking1Votes ) ; await increaseTime ( ONE_WEEK ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance before" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance before" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance before" ) . to . eq ( 0 ) ; expect ( await emissionsController . callStatic . getVotes ( voter1 . address ) , "Voter 1 votes before" ) . to . eq ( simpleToExactAmount ( VOTERS [ "1" ] . votes ) ) ; expect ( await emissionsController . callStatic . getVotes ( voter2 . address ) , "Voter 2 votes before" ) . to . eq ( simpleToExactAmount ( VOTERS [ "2" ] . votes ) ) ; expect ( await emissionsController . callStatic . getVotes ( voter3 . address ) , "Voter 3 votes before" ) . to . eq ( simpleToExactAmount ( VOTERS [ "3" ] . votes ) ) ; } ) ; it ( "with no weights" , async ( ) => { const [ startEpoch , lastEpochBefore ] = await emissionsController . epochs ( ) ; await increaseTime ( ONE_WEEK ) ; const dialsVoteHistory = [ { dialId : 0 , votesNo : 1 , lastVote : 0 , lastEpoch : startEpoch } ] ; await expectDialVotesHistoryForDials ( emissionsController , dialsVoteHistory ) ; const tx = await emissionsController . calculateRewards ( ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ 0 , 0 , 0 ] ) ; const [ , lastEpochMid ] = await emissionsController . epochs ( ) ; expect ( lastEpochMid , "last epoch after" ) . to . eq ( lastEpochBefore + 1 ) ; await expectDialVotesHistoryWithoutChangeOnWeights ( emissionsController , dialsVoteHistory ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; it ( "fails after not waiting a week" , async ( ) => { await increaseTime ( ONE_WEEK ) ; await emissionsController . calculateRewards ( ) ; await increaseTime ( 60 ) ; const tx = emissionsController . calculateRewards ( ) ; await expect ( tx ) . to . revertedWith ( "Must wait for new period" ) ; } ) ; context ( "after change to voting weights" , ( ) => { context ( "in first emissions period" , ( ) => { let lastEpochBefore ; let startEpoch ; beforeEach ( async ( ) => { ; [ startEpoch , lastEpochBefore ] = await emissionsController . epochs ( ) ; [ ... Array ( INITIAL_DIALS_NO ) . keys ( ) ] . forEach ( async ( dialId ) => expectDialVotesHistoryForDial ( emissionsController , { dialId , votesNo : 1 , lastVote : 0 , lastEpoch : startEpoch } ) ) ; } ) ; afterEach ( async ( ) => { const [ , lastEpochAfter ] = await emissionsController . epochs ( ) ; expect ( lastEpochAfter , "last epoch after" ) . to . eq ( lastEpochBefore + 1 ) ; } ) ; it ( "Voter 1 all votes to dial 1" , async ( ) => { await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 200 } ] ) ; const dialsVoteHistory = [ { dialId : 0 , votesNo : 1 , lastVote : VOTERS [ "1" ] . votes , lastEpoch : startEpoch , } , ] ; await expectDialVotesHistoryForDials ( emissionsController , dialsVoteHistory ) ; await increaseTime ( ONE_WEEK ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; await expectDialVotesHistoryWithoutChangeOnWeights ( emissionsController , dialsVoteHistory ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ nextEpochEmission , 0 , 0 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( nextEpochEmission ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; it ( "Voter 1 all votes to dial 1, Voter 2 all votes to dial 2" , async ( ) => { await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 200 } ] ) ; await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ { dialId : 1 , weight : 200 } ] ) ; await increaseTime ( ONE_WEEK ) ; const dialsVoteHistory = [ { dialId : 0 , votesNo : 1 , lastVote : VOTERS [ "1" ] . votes , lastEpoch : startEpoch } , { dialId : 1 , votesNo : 1 , lastVote : VOTERS [ "2" ] . votes , lastEpoch : startEpoch } , { dialId : 2 , votesNo : 1 , lastVote : 0 , lastEpoch : startEpoch } , ] ; await expectDialVotesHistoryForDials ( emissionsController , dialsVoteHistory ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; const dial1 = nextEpochEmission . div ( 3 ) ; const dial2 = nextEpochEmission . mul ( 2 ) . div ( 3 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ dial1 , dial2 , 0 ] ) ; await expectDialVotesHistoryWithoutChangeOnWeights ( emissionsController , dialsVoteHistory ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( dial1 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( dial2 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; it ( "Voter 1 50/50 votes to dial 1 & 2, Voter 2 50/50 votes to dial 1 & 2" , async ( ) => { await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 100 } , { dialId : 1 , weight : 100 } , ] ) ; await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 100 } , { dialId : 1 , weight : 100 } , ] ) ; await increaseTime ( ONE_WEEK ) ; const dial1Votes = VOTERS [ "1" ] . votes * 0.5 + VOTERS [ "2" ] . votes * 0.5 ; const dial2Votes = VOTERS [ "1" ] . votes * 0.5 + VOTERS [ "2" ] . votes * 0.5 ; const dialsVoteHistory = [ { dialId : 0 , votesNo : 1 , lastVote : dial1Votes , lastEpoch : startEpoch } , { dialId : 1 , votesNo : 1 , lastVote : dial2Votes , lastEpoch : startEpoch } , { dialId : 2 , votesNo : 1 , lastVote : 0 , lastEpoch : startEpoch } , ] ; await expectDialVotesHistoryForDials ( emissionsController , dialsVoteHistory ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; await expectDialVotesHistoryWithoutChangeOnWeights ( emissionsController , dialsVoteHistory ) ; const dial1 = nextEpochEmission . div ( 2 ) ; const dial2 = nextEpochEmission . div ( 2 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ dial1 , dial2 , 0 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( dial1 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( dial2 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; it ( "Voter 1 20/80 votes to dial 1 & 2, Voter 2 all votes to dial 3" , async ( ) => { await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 40 } , { dialId : 1 , weight : 160 } , ] ) ; await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ { dialId : 2 , weight : 200 } ] ) ; await increaseTime ( ONE_WEEK ) ; const dial1Votes = VOTERS [ "1" ] . votes * 0.2 ; const dial2Votes = VOTERS [ "1" ] . votes * 0.8 ; const dial3Votes = VOTERS [ "2" ] . votes ; const dialsVoteHistory = [ { dialId : 0 , votesNo : 1 , lastVote : dial1Votes , lastEpoch : startEpoch } , { dialId : 1 , votesNo : 1 , lastVote : dial2Votes , lastEpoch : startEpoch } , { dialId : 2 , votesNo : 1 , lastVote : dial3Votes , lastEpoch : startEpoch } , ] ; await expectDialVotesHistoryForDials ( emissionsController , dialsVoteHistory ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; await expectDialVotesHistoryWithoutChangeOnWeights ( emissionsController , dialsVoteHistory ) ; const dial1 = nextEpochEmission . mul ( VOTERS [ "1" ] . votes ) . div ( 5 ) . div ( 900 ) ; const dial2 = nextEpochEmission . mul ( VOTERS [ "1" ] . votes ) . mul ( 4 ) . div ( 5 ) . div ( 900 ) ; const dial3 = nextEpochEmission . mul ( VOTERS [ "2" ] . votes ) . div ( 900 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ dial1 , dial2 , dial3 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( dial1 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( dial2 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( dial3 ) ; } ) ; } ) ; context ( "in second emissions period" , ( ) => { let balDial1Before ; let balDial2Before ; let balDial3Before ; let startEpoch ; beforeEach ( async ( ) => { ; [ startEpoch ] = await emissionsController . epochs ( ) ; await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 40 } , { dialId : 1 , weight : 160 } , ] ) ; await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ { dialId : 2 , weight : 200 } ] ) ; await increaseTime ( ONE_WEEK ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; balDial1Before = nextEpochEmission . mul ( 300 ) . div ( 5 ) . div ( 900 ) ; balDial2Before = nextEpochEmission . mul ( 300 ) . mul ( 4 ) . div ( 5 ) . div ( 900 ) ; balDial3Before = nextEpochEmission . mul ( 600 ) . div ( 900 ) ; await emissionsController . calculateRewards ( ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( balDial1Before ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( balDial2Before ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( balDial3Before ) ; } ) ; it ( "Voter 1 changes weights to 80/20 dial 1 & 2" , async ( ) => { await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 160 } , { dialId : 1 , weight : 40 } , ] ) ; await increaseTime ( ONE_WEEK ) ; const dialsVoteHistory = [ { dialId : 0 , votesNo : 2 , lastVote : VOTERS [ "1" ] . votes * 0.8 , lastEpoch : startEpoch + 1 } , { dialId : 1 , votesNo : 2 , lastVote : VOTERS [ "1" ] . votes * 0.2 , lastEpoch : startEpoch + 1 } , { dialId : 2 , votesNo : 2 , lastVote : VOTERS [ "2" ] . votes , lastEpoch : startEpoch + 1 } , ] ; await expectDialVotesHistoryForDials ( emissionsController , dialsVoteHistory ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; await expectDialVotesHistoryWithoutChangeOnWeights ( emissionsController , dialsVoteHistory ) ; const dial1 = nextEpochEmission . mul ( VOTERS [ "1" ] . votes * 0.8 ) . div ( 900 ) ; const dial2 = nextEpochEmission . mul ( VOTERS [ "1" ] . votes * 0.2 ) . div ( 900 ) ; const dial3 = nextEpochEmission . mul ( VOTERS [ "2" ] . votes ) . div ( 900 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ dial1 , dial2 , dial3 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( balDial1Before . add ( dial1 ) ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( balDial2Before . add ( dial2 ) ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( balDial3Before . add ( dial3 ) ) ; } ) ; it ( "Voter 1 removes 20% to dial 1" , async ( ) => { await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 1 , weight : 160 } ] ) ; await increaseTime ( ONE_WEEK ) ; const dialsVoteHistory = [ { dialId : 0 , votesNo : 2 , lastVote : 0 , lastEpoch : startEpoch + 1 } , { dialId : 1 , votesNo : 2 , lastVote : VOTERS [ "1" ] . votes * 0.8 , lastEpoch : startEpoch + 1 } , { dialId : 2 , votesNo : 2 , lastVote : VOTERS [ "2" ] . votes , lastEpoch : startEpoch + 1 } , ] ; await expectDialVotesHistoryForDials ( emissionsController , dialsVoteHistory ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; await expectDialVotesHistoryWithoutChangeOnWeights ( emissionsController , dialsVoteHistory ) ; const dial2 = nextEpochEmission . mul ( ( VOTERS [ "1" ] . votes * 4 ) / 5 ) . div ( 840 ) ; const dial3 = nextEpochEmission . mul ( VOTERS [ "2" ] . votes ) . div ( 840 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ 0 , dial2 , dial3 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( balDial1Before ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( balDial2Before . add ( dial2 ) ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( balDial3Before . add ( dial3 ) ) ; } ) ; it ( "Voter 1 changes all to dial 3" , async ( ) => { await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 2 , weight : 200 } ] ) ; await increaseTime ( ONE_WEEK ) ; const dialsVoteHistory = [ { dialId : 0 , votesNo : 2 , lastVote : 0 , lastEpoch : startEpoch + 1 } , { dialId : 1 , votesNo : 2 , lastVote : 0 , lastEpoch : startEpoch + 1 } , { dialId : 2 , votesNo : 2 , lastVote : VOTERS [ "1" ] . votes + VOTERS [ "2" ] . votes , lastEpoch : startEpoch + 1 } , ] ; await expectDialVotesHistoryForDials ( emissionsController , dialsVoteHistory ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; await expectDialVotesHistoryWithoutChangeOnWeights ( emissionsController , dialsVoteHistory ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ 0 , 0 , nextEpochEmission ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( balDial1Before ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( balDial2Before ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( balDial3Before . add ( nextEpochEmission ) ) ; } ) ; it ( "Voter 3 all weight on dial 1" , async ( ) => { await emissionsController . connect ( voter3 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 200 } ] ) ; await increaseTime ( ONE_WEEK ) ; const dialsVoteHistory = [ { dialId : 0 , votesNo : 2 , lastVote : VOTERS [ "1" ] . votes * 0.2 + VOTERS [ "3" ] . votes , lastEpoch : startEpoch + 1 } , { dialId : 1 , votesNo : 2 , lastVote : VOTERS [ "1" ] . votes * 0.8 , lastEpoch : startEpoch + 1 } , { dialId : 2 , votesNo : 2 , lastVote : VOTERS [ "2" ] . votes , lastEpoch : startEpoch + 1 } , ] ; await expectDialVotesHistoryForDials ( emissionsController , dialsVoteHistory ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; await expectDialVotesHistoryWithoutChangeOnWeights ( emissionsController , dialsVoteHistory ) ; const dial1 = nextEpochEmission . mul ( 300 + 300 / 5 ) . div ( 1200 ) ; const dial2 = nextEpochEmission . mul ( ( 300 * 4 ) / 5 ) . div ( 1200 ) ; const dial3 = nextEpochEmission . mul ( VOTERS [ "2" ] . votes ) . div ( 1200 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ dial1 , dial2 , dial3 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( balDial1Before . add ( dial1 ) ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( balDial2Before . add ( dial2 ) ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( balDial3Before . add ( dial3 ) ) ; } ) ; it ( "Voter 3 all weight on dial 2" , async ( ) => { await emissionsController . connect ( voter3 . signer ) . setVoterDialWeights ( [ { dialId : 1 , weight : 200 } ] ) ; await increaseTime ( ONE_WEEK ) ; const dialsVoteHistory = [ { dialId : 0 , votesNo : 2 , lastVote : VOTERS [ "1" ] . votes * 0.2 , lastEpoch : startEpoch + 1 } , { dialId : 1 , votesNo : 2 , lastVote : VOTERS [ "1" ] . votes * 0.8 + VOTERS [ "3" ] . votes , lastEpoch : startEpoch + 1 } , { dialId : 2 , votesNo : 2 , lastVote : VOTERS [ "2" ] . votes , lastEpoch : startEpoch + 1 } , ] ; await expectDialVotesHistoryForDials ( emissionsController , dialsVoteHistory ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; await expectDialVotesHistoryWithoutChangeOnWeights ( emissionsController , dialsVoteHistory ) ; const dial1 = nextEpochEmission . mul ( 300 / 5 ) . div ( 1200 ) ; const dial2 = nextEpochEmission . mul ( 300 + ( 300 * 4 ) / 5 ) . div ( 1200 ) ; const dial3 = nextEpochEmission . mul ( 600 ) . div ( 1200 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ dial1 , dial2 , dial3 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( balDial1Before . add ( dial1 ) ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( balDial2Before . add ( dial2 ) ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( balDial3Before . add ( dial3 ) ) ; } ) ; it ( "Voter 2 removes all votes to dial 3" , async ( ) => { await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ ] ) ; await increaseTime ( ONE_WEEK ) ; const dialsVoteHistory = [ { dialId : 0 , votesNo : 2 , lastVote : VOTERS [ "1" ] . votes * 0.2 , lastEpoch : startEpoch + 1 } , { dialId : 1 , votesNo : 2 , lastVote : VOTERS [ "1" ] . votes * 0.8 , lastEpoch : startEpoch + 1 } , { dialId : 2 , votesNo : 2 , lastVote : 0 , lastEpoch : startEpoch + 1 } , ] ; await expectDialVotesHistoryForDials ( emissionsController , dialsVoteHistory ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; await expectDialVotesHistoryWithoutChangeOnWeights ( emissionsController , dialsVoteHistory ) ; const dial1 = nextEpochEmission . mul ( 300 / 5 ) . div ( 300 ) ; const dial2 = nextEpochEmission . mul ( ( 300 * 4 ) / 5 ) . div ( 300 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ dial1 , dial2 , 0 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( balDial1Before . add ( dial1 ) ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( balDial2Before . add ( dial2 ) ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( balDial3Before ) ; } ) ; } ) ; context ( "after first emissions period" , ( ) => { beforeEach ( async ( ) => { await increaseTime ( ONE_WEEK ) ; } ) ; it ( "Voter 1 changes weights to 80/20 dial 1 & 2" , async ( ) => { await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 160 } , { dialId : 1 , weight : 40 } , ] ) ; const tx = await emissionsController . calculateRewards ( ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ 0 , 0 , 0 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; } ) ; } ) ; context ( "change voting power" , ( ) => { context ( "first voting period" , ( ) => { it ( "Voter 1 does not change their voting power" , async ( ) => { expect ( await emissionsController . callStatic . getVotes ( voter1 . address ) , "Voter 1 votes before" ) . to . eq ( simpleToExactAmount ( 300 ) ) ; await staking1 . setVotes ( voter1 . address , voter1Staking1Votes ) ; expect ( await emissionsController . callStatic . getVotes ( voter1 . address ) , "Voter 1 votes after" ) . to . eq ( simpleToExactAmount ( 300 ) ) ; } ) ; it ( "Voter 3 increases voting power before setting weights" , async ( ) => { expect ( await emissionsController . callStatic . getVotes ( voter3 . address ) , "Voter 3 votes before" ) . to . eq ( voter3Staking1Votes ) ; await staking1 . setVotes ( voter3 . address , simpleToExactAmount ( 400 ) ) ; expect ( await emissionsController . callStatic . getVotes ( voter3 . address ) , "Voter 3 votes after" ) . to . eq ( simpleToExactAmount ( 400 ) ) ; } ) ; it ( "Voter 1 increases voting power to dial 1" , async ( ) => { await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 200 } ] ) ; await increaseTime ( ONE_WEEK ) ; await staking2 . setVotes ( voter1 . address , simpleToExactAmount ( 300 ) ) ; expect ( await emissionsController . callStatic . getVotes ( voter1 . address ) , "Voter 1 votes after" ) . to . eq ( simpleToExactAmount ( 400 ) ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ nextEpochEmission , 0 , 0 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( nextEpochEmission ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; context ( "Voter 1 votes to dial 1, Voter 2 votes to dial 2" , ( ) => { beforeEach ( async ( ) => { await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 200 } ] ) ; await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ { dialId : 1 , weight : 200 } ] ) ; } ) ; it ( "Voter 2 doubled voting power" , async ( ) => { await staking1 . setVotes ( voter2 . address , simpleToExactAmount ( 1200 ) ) ; expect ( await emissionsController . callStatic . getVotes ( voter1 . address ) , "Voter 1 votes after" ) . to . eq ( simpleToExactAmount ( 300 ) ) ; expect ( await emissionsController . callStatic . getVotes ( voter2 . address ) , "Voter 2 votes after" ) . to . eq ( simpleToExactAmount ( 1200 ) ) ; await increaseTime ( ONE_WEEK ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; const dial1 = nextEpochEmission . div ( 5 ) ; const dial2 = nextEpochEmission . mul ( 4 ) . div ( 5 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ dial1 , dial2 , 0 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( dial1 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( dial2 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; it ( "Voter 2 halves voting power" , async ( ) => { await staking1 . setVotes ( voter2 . address , simpleToExactAmount ( 300 ) ) ; expect ( await emissionsController . callStatic . getVotes ( voter1 . address ) , "Voter 1 votes after" ) . to . eq ( simpleToExactAmount ( 300 ) ) ; expect ( await emissionsController . callStatic . getVotes ( voter2 . address ) , "Voter 2 votes after" ) . to . eq ( simpleToExactAmount ( 300 ) ) ; await increaseTime ( ONE_WEEK ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; const dial1 = nextEpochEmission . div ( 2 ) ; const dial2 = nextEpochEmission . div ( 2 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ dial1 , dial2 , 0 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( dial1 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( dial2 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; it ( "Voter 2 removes all voting power" , async ( ) => { await staking1 . setVotes ( voter2 . address , simpleToExactAmount ( 0 ) ) ; expect ( await emissionsController . callStatic . getVotes ( voter1 . address ) , "Voter 1 votes after" ) . to . eq ( simpleToExactAmount ( 300 ) ) ; expect ( await emissionsController . callStatic . getVotes ( voter2 . address ) , "Voter 2 votes after" ) . to . eq ( simpleToExactAmount ( 0 ) ) ; await increaseTime ( ONE_WEEK ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; const dial1 = nextEpochEmission ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ dial1 , 0 , 0 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( dial1 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; it ( "Voter 2 delegates to Voter 1 who has set weights" , async ( ) => { await staking1 . transferVotes ( voter2 . address , voter1 . address , simpleToExactAmount ( 600 ) ) ; expect ( await emissionsController . callStatic . getVotes ( voter1 . address ) , "Voter 1 votes after" ) . to . eq ( simpleToExactAmount ( 900 ) ) ; expect ( await emissionsController . callStatic . getVotes ( voter2 . address ) , "Voter 2 votes after" ) . to . eq ( simpleToExactAmount ( 0 ) ) ; await increaseTime ( ONE_WEEK ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; const dial1 = nextEpochEmission ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ dial1 , 0 , 0 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( dial1 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; it ( "Voter 2 delegates to Voter 3 who has not set weights" , async ( ) => { await staking1 . transferVotes ( voter2 . address , voter1 . address , simpleToExactAmount ( 600 ) ) ; expect ( await emissionsController . callStatic . getVotes ( voter1 . address ) , "Voter 1 votes after" ) . to . eq ( simpleToExactAmount ( 900 ) ) ; expect ( await emissionsController . callStatic . getVotes ( voter2 . address ) , "Voter 2 votes after" ) . to . eq ( simpleToExactAmount ( 0 ) ) ; await increaseTime ( ONE_WEEK ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; const dial1 = nextEpochEmission ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ dial1 , 0 , 0 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( dial1 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; } ) ; } ) ; } ) ; context ( "with fixed distribution dial" , ( ) => { let fixedDistributionAmount ; let nextEpochEmission ; let weightedDistributionAmount ; beforeEach ( async ( ) => { await emissionsController . connect ( sa . governor . signer ) . addDial ( staking1 . address , 10 , true ) ; nextEpochEmission = await nextRewardAmount ( emissionsController ) ; fixedDistributionAmount = nextEpochEmission . div ( 10 ) ; weightedDistributionAmount = nextEpochEmission . sub ( fixedDistributionAmount ) ; } ) ; it ( "Only Voter 1 allocates 1% to dial 1" , async ( ) => { await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 2 } , { dialId : 3 , weight : 10 } , ] ) ; await increaseTime ( ONE_WEEK ) ; const tx = await emissionsController . calculateRewards ( ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ weightedDistributionAmount , 0 , 0 , fixedDistributionAmount ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( weightedDistributionAmount ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 3 ) ) . balance , "dial 4 balance after" ) . to . eq ( fixedDistributionAmount ) ; } ) ; it ( "Voter 1 20/80 votes to dial 1 & 2, Voter 2 all votes to dial 4" , async ( ) => { await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 40 } , { dialId : 1 , weight : 160 } , ] ) ; await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ { dialId : 3 , weight : 200 } ] ) ; await increaseTime ( ONE_WEEK ) ; const tx = await emissionsController . calculateRewards ( ) ; const dial1 = weightedDistributionAmount . mul ( 20 ) . div ( 100 ) ; const dial2 = weightedDistributionAmount . mul ( 80 ) . div ( 100 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ dial1 , dial2 , 0 , fixedDistributionAmount ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( dial1 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( dial2 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 3 ) ) . balance , "dial 4 balance after" ) . to . eq ( fixedDistributionAmount ) ; } ) ; it ( "Voter 1 and 2 all to dial 4 which is fixed" , async ( ) => { await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 3 , weight : 200 } ] ) ; await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ { dialId : 3 , weight : 200 } ] ) ; await increaseTime ( ONE_WEEK ) ; const tx = await emissionsController . calculateRewards ( ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ 0 , 0 , 0 , fixedDistributionAmount ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 3 ) ) . balance , "dial 4 balance after" ) . to . eq ( fixedDistributionAmount ) ; } ) ; it . skip ( "Fixed distributions > weekly emissions" , async ( ) => { const newDial = await new MockRewardsDistributionRecipient__factory ( sa . default . signer ) . deploy ( rewardToken . address , DEAD_ADDRESS ) ; await emissionsController . connect ( sa . governor . signer ) . addDial ( newDial . address , 0 , true ) ; await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 200 } ] ) ; await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ { dialId : 1 , weight : 200 } ] ) ; await increaseTime ( ONE_WEEK ) ; const tx = emissionsController . calculateRewards ( ) ; await expect ( tx ) . to . revertedWith ( "staking amounts > weekly emission" ) ; } ) ; } ) ; context ( "dial is" , ( ) => { let startEpoch ; beforeEach ( async ( ) => { ; [ startEpoch ] = await emissionsController . epochs ( ) ; await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 200 } ] ) ; await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ { dialId : 1 , weight : 200 } ] ) ; } ) ; context ( "in first emissions period" , ( ) => { it ( "is disable then it should not receive any distribution" , async ( ) => { const dialBefore = [ ] ; let tx = await emissionsController . connect ( sa . governor . signer ) . updateDial ( 0 , true , true ) ; dialBefore [ 0 ] = await snapDial ( emissionsController , 0 ) ; dialBefore [ 1 ] = await snapDial ( emissionsController , 1 ) ; expect ( dialBefore [ 0 ] . disabled , "dial 1 disabled before" ) . to . eq ( true ) ; expect ( dialBefore [ 1 ] . disabled , "dial 2 disabled before" ) . to . eq ( false ) ; const dialsVoteHistory = [ { dialId : 0 , votesNo : 1 , lastVote : VOTERS [ "1" ] . votes , lastEpoch : startEpoch } , { dialId : 1 , votesNo : 1 , lastVote : VOTERS [ "2" ] . votes , lastEpoch : startEpoch } , ] ; await expectDialVotesHistoryForDials ( emissionsController , dialsVoteHistory ) ; await increaseTime ( ONE_WEEK ) ; const nextEpochEmission = await nextRewardAmount ( emissionsController ) ; tx = await emissionsController . calculateRewards ( ) ; const dialAfter = [ ] ; dialAfter [ 0 ] = await snapDial ( emissionsController , 0 ) ; dialAfter [ 1 ] = await snapDial ( emissionsController , 1 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ 0 , nextEpochEmission , 0 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( nextEpochEmission ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; expect ( dialBefore [ 0 ] . voteHistory . length , "dial 1 vote history should not change" ) . to . eq ( dialAfter [ 0 ] . voteHistory . length ) ; expectDialVotesHistoryWithoutChangeOnWeights ( emissionsController , [ dialsVoteHistory [ 1 ] ] ) ; } ) ; } ) ; context ( "in second emissions period" , ( ) => { const dialBefore = [ ] ; const dialAfter = [ ] ; let nextEpochEmission ; beforeEach ( async ( ) => { await emissionsController . connect ( sa . governor . signer ) . updateDial ( 0 , true , true ) ; dialBefore [ 0 ] = await snapDial ( emissionsController , 0 ) ; dialBefore [ 1 ] = await snapDial ( emissionsController , 1 ) ; expect ( dialBefore [ 0 ] . disabled , "dial 1 disabled before" ) . to . eq ( true ) ; expect ( dialBefore [ 1 ] . disabled , "dial 2 disabled before" ) . to . eq ( false ) ; const dialsVoteHistory = [ { dialId : 0 , votesNo : 1 , lastVote : VOTERS [ "1" ] . votes , lastEpoch : startEpoch } , { dialId : 1 , votesNo : 1 , lastVote : VOTERS [ "2" ] . votes , lastEpoch : startEpoch } , ] ; await expectDialVotesHistoryForDials ( emissionsController , dialsVoteHistory ) ; await increaseTime ( ONE_WEEK ) ; nextEpochEmission = [ await nextRewardAmount ( emissionsController ) ] ; await emissionsController . calculateRewards ( ) ; dialAfter [ 0 ] = await snapDial ( emissionsController , 0 ) ; dialAfter [ 1 ] = await snapDial ( emissionsController , 1 ) ; expect ( dialBefore [ 0 ] . voteHistory . length , "dial 1 vote history should not change" ) . to . eq ( dialAfter [ 0 ] . voteHistory . length ) ; } ) ; it ( "is disabled then re-enabled it should receive distribution" , async ( ) => { let tx = await emissionsController . connect ( sa . governor . signer ) . updateDial ( 0 , false , true ) ; await expect ( tx ) . to . emit ( emissionsController , "UpdatedDial" ) . withArgs ( 0 , false , true ) ; await increaseTime ( ONE_WEEK ) ; nextEpochEmission [ 1 ] = await nextRewardAmount ( emissionsController ) ; tx = await emissionsController . calculateRewards ( ) ; dialAfter [ 0 ] = await snapDial ( emissionsController , 0 ) ; dialAfter [ 1 ] = await snapDial ( emissionsController , 1 ) ; const adjustedDial1 = nextEpochEmission [ 1 ] . div ( 3 ) ; const adjustedDial2 = nextEpochEmission [ 1 ] . mul ( 2 ) . div ( 3 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ adjustedDial1 , adjustedDial2 , 0 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( adjustedDial1 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( nextEpochEmission [ 0 ] . add ( adjustedDial2 ) ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; expect ( dialBefore [ 0 ] . voteHistory . length + 1 , "dial 1 vote history should increase" ) . to . eq ( dialAfter [ 0 ] . voteHistory . length ) ; expect ( dialBefore [ 0 ] . voteHistory . slice ( - 1 ) [ 0 ] . votes , "dial 1 vote weight should not change" ) . to . eq ( dialAfter [ 0 ] . voteHistory . slice ( - 1 ) [ 0 ] . votes ) ; expect ( dialBefore [ 1 ] . voteHistory . length + 2 , "dial 2 vote history should increase" ) . to . eq ( dialAfter [ 1 ] . voteHistory . length ) ; expect ( dialBefore [ 1 ] . voteHistory . slice ( - 1 ) [ 0 ] . votes , "dial 2 vote weight should not change" ) . to . eq ( dialAfter [ 1 ] . voteHistory . slice ( - 1 ) [ 0 ] . votes ) ; } ) ; } ) ; it ( "added that was not in previous distributions" , async ( ) => { const dialBefore = [ ] ; const dialAfter = [ ] ; const adjustedDials = [ [ ] ] ; dialBefore [ 0 ] = await snapDial ( emissionsController , 0 ) ; dialBefore [ 1 ] = await snapDial ( emissionsController , 1 ) ; expect ( dialBefore [ 0 ] . disabled , "dial 1 disabled before" ) . to . eq ( false ) ; expect ( dialBefore [ 1 ] . disabled , "dial 2 disabled before" ) . to . eq ( false ) ; const dialsVoteHistory = [ { dialId : 0 , votesNo : 1 , lastVote : VOTERS [ "1" ] . votes , lastEpoch : startEpoch } , { dialId : 1 , votesNo : 1 , lastVote : VOTERS [ "2" ] . votes , lastEpoch : startEpoch } , { dialId : 2 , votesNo : 1 , lastVote : 0 , lastEpoch : startEpoch } , ] ; await expectDialVotesHistoryForDials ( emissionsController , dialsVoteHistory ) ; await increaseTime ( ONE_WEEK ) ; let nextEpochEmission = await nextRewardAmount ( emissionsController ) ; let tx = await emissionsController . calculateRewards ( ) ; dialAfter [ 0 ] = await snapDial ( emissionsController , 0 ) ; dialAfter [ 1 ] = await snapDial ( emissionsController , 1 ) ; adjustedDials [ 0 ] = [ ] ; adjustedDials [ 1 ] = [ ] ; adjustedDials [ 3 ] = [ ] ; adjustedDials [ 0 ] [ 0 ] = nextEpochEmission . div ( 3 ) ; adjustedDials [ 1 ] [ 0 ] = nextEpochEmission . mul ( 2 ) . div ( 3 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ adjustedDials [ 0 ] [ 0 ] , adjustedDials [ 1 ] [ 0 ] , 0 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( adjustedDials [ 0 ] [ 0 ] ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( adjustedDials [ 1 ] [ 0 ] ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; expectDialVotesHistoryWithoutChangeOnWeights ( emissionsController , dialsVoteHistory ) ; const newDial = await new MockRewardsDistributionRecipient__factory ( sa . default . signer ) . deploy ( rewardToken . address , DEAD_ADDRESS ) ; tx = await emissionsController . connect ( sa . governor . signer ) . addDial ( newDial . address , 0 , true ) ; await expect ( tx ) . to . emit ( emissionsController , "AddedDial" ) . withArgs ( 3 , newDial . address ) ; await emissionsController . connect ( voter3 . signer ) . setVoterDialWeights ( [ { dialId : 3 , weight : 200 } ] ) ; dialBefore [ 3 ] = await snapDial ( emissionsController , 3 ) ; await increaseTime ( ONE_WEEK ) ; nextEpochEmission = await nextRewardAmount ( emissionsController ) ; tx = await emissionsController . calculateRewards ( ) ; dialAfter [ 0 ] = await snapDial ( emissionsController , 0 ) ; dialAfter [ 1 ] = await snapDial ( emissionsController , 1 ) ; dialAfter [ 3 ] = await snapDial ( emissionsController , 3 ) ; adjustedDials [ 0 ] [ 1 ] = nextEpochEmission . div ( 4 ) ; adjustedDials [ 1 ] [ 1 ] = nextEpochEmission . div ( 2 ) ; adjustedDials [ 3 ] [ 0 ] = BN . from ( 0 ) ; adjustedDials [ 3 ] [ 1 ] = nextEpochEmission . div ( 4 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ adjustedDials [ 0 ] [ 1 ] , adjustedDials [ 1 ] [ 1 ] , 0 , adjustedDials [ 3 ] [ 1 ] ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( adjustedDials [ 0 ] . reduce ( sum ) ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( adjustedDials [ 1 ] . reduce ( sum ) ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 3 ) ) . balance , "dial new balance after" ) . to . eq ( adjustedDials [ 3 ] . reduce ( sum ) ) ; expect ( dialBefore [ 3 ] . voteHistory . length + 1 , "dial new vote history should increase" ) . to . eq ( dialAfter [ 3 ] . voteHistory . length ) ; expect ( dialBefore [ 3 ] . voteHistory . slice ( - 1 ) [ 0 ] . votes , "dial 1 vote weight should not change" ) . to . eq ( dialAfter [ 3 ] . voteHistory . slice ( - 1 ) [ 0 ] . votes ) ; } ) ; } ) ; context ( "change the number of contracts" , async ( ) => { const dialBefore = [ ] ; const dialAfter = [ ] ; const nextEpochEmission = [ ] ; const adjustedDials = [ [ ] ] ; let startEpoch ; let staking3 ; const voter1Staking3Votes = simpleToExactAmount ( 300 ) ; beforeEach ( async ( ) => { ; [ startEpoch ] = await emissionsController . epochs ( ) ; await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 200 } ] ) ; await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ { dialId : 1 , weight : 200 } ] ) ; dialBefore [ 0 ] = await snapDial ( emissionsController , 0 ) ; dialBefore [ 1 ] = await snapDial ( emissionsController , 1 ) ; expect ( dialBefore [ 0 ] . disabled , "dial 1 disabled before" ) . to . eq ( false ) ; expect ( dialBefore [ 1 ] . disabled , "dial 2 disabled before" ) . to . eq ( false ) ; const dialsVoteHistory = [ { dialId : 0 , votesNo : 1 , lastVote : VOTERS [ "1" ] . votes , lastEpoch : startEpoch } , { dialId : 1 , votesNo : 1 , lastVote : VOTERS [ "2" ] . votes , lastEpoch : startEpoch } , ] ; await expectDialVotesHistoryForDials ( emissionsController , dialsVoteHistory ) ; await increaseTime ( ONE_WEEK ) ; nextEpochEmission [ 0 ] = await nextRewardAmount ( emissionsController ) ; const tx = await emissionsController . calculateRewards ( ) ; adjustedDials [ 0 ] = [ ] ; adjustedDials [ 0 ] [ 0 ] = nextEpochEmission [ 0 ] . div ( 3 ) ; adjustedDials [ 1 ] = [ ] ; adjustedDials [ 1 ] [ 0 ] = nextEpochEmission [ 0 ] . mul ( 2 ) . div ( 3 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ adjustedDials [ 0 ] [ 0 ] , adjustedDials [ 1 ] [ 0 ] , 0 ] ) ; await expectDialVotesHistoryWithoutChangeOnWeights ( emissionsController , dialsVoteHistory ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( adjustedDials [ 0 ] . reduce ( sum ) ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( adjustedDials [ 1 ] . reduce ( sum ) ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; staking3 = await new MockStakingContract__factory ( sa . default . signer ) . deploy ( ) ; } ) ; it ( "adds a new staking contract and sets voter dial weights" , async ( ) => { let tx = await emissionsController . connect ( sa . governor . signer ) . addStakingContract ( staking3 . address ) ; await expect ( tx ) . to . emit ( emissionsController , "AddStakingContract" ) . withArgs ( staking3 . address ) ; await staking3 . setVotes ( voter1 . address , voter1Staking3Votes ) ; const voter1VotingPower = simpleToExactAmount ( VOTERS [ "1" ] . votes ) . add ( voter1Staking3Votes ) ; expect ( await emissionsController . getVotes ( voter1 . address ) , "Voter 1 voting power" ) . to . eq ( voter1VotingPower ) ; const voter1PreferenceBefore = await emissionsController . voterPreferences ( voter1 . address ) ; await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 200 } ] ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; const voter1PreferenceAfter = await emissionsController . voterPreferences ( voter1 . address ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( voter1VotingPower ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( simpleToExactAmount ( VOTERS [ "2" ] . votes ) ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( BN . from ( 0 ) ) ; expect ( voter1PreferenceAfter . votesCast , "voter 1 votesCast" ) . to . eq ( voter1VotingPower ) ; expect ( voter1PreferenceBefore . lastSourcePoke , "voter 1 lastSourcePoke updated" ) . to . lessThan ( voter1PreferenceAfter . lastSourcePoke ) ; await increaseTime ( ONE_WEEK ) ; nextEpochEmission [ 1 ] = await nextRewardAmount ( emissionsController ) ; tx = await emissionsController . calculateRewards ( ) ; adjustedDials [ 0 ] [ 1 ] = nextEpochEmission [ 1 ] . div ( 2 ) ; adjustedDials [ 1 ] [ 1 ] = nextEpochEmission [ 1 ] . div ( 2 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ adjustedDials [ 0 ] [ 1 ] , adjustedDials [ 1 ] [ 1 ] , 0 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( adjustedDials [ 0 ] . reduce ( sum ) ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( adjustedDials [ 1 ] . reduce ( sum ) ) ; dialAfter [ 0 ] = await snapDial ( emissionsController , 0 ) ; dialAfter [ 1 ] = await snapDial ( emissionsController , 1 ) ; expect ( dialBefore [ 0 ] . voteHistory . length + 2 , "dial 1 vote history should increase" ) . to . eq ( dialAfter [ 0 ] . voteHistory . length ) ; expect ( dialBefore [ 1 ] . voteHistory . length + 2 , "dial 2 vote history should increase" ) . to . eq ( dialAfter [ 1 ] . voteHistory . length ) ; expect ( dialAfter [ 0 ] . voteHistory . slice ( - 1 ) [ 0 ] . votes , "dial 1 last vote" ) . to . eq ( voter1VotingPower ) ; expect ( dialBefore [ 1 ] . voteHistory . slice ( - 1 ) [ 0 ] . votes , "dial 2 vote weight should not change" ) . to . eq ( dialAfter [ 1 ] . voteHistory . slice ( - 1 ) [ 0 ] . votes ) ; } ) ; it ( "adds a new staking contract and pokes voter 1" , async ( ) => { let tx = await emissionsController . connect ( sa . governor . signer ) . addStakingContract ( staking3 . address ) ; await expect ( tx ) . to . emit ( emissionsController , "AddStakingContract" ) . withArgs ( staking3 . address ) ; await staking3 . setVotes ( voter1 . address , voter1Staking3Votes ) ; const voter1VotingPower = simpleToExactAmount ( VOTERS [ "1" ] . votes ) . add ( voter1Staking3Votes ) ; expect ( await emissionsController . getVotes ( voter1 . address ) , "Voter 1 voting power" ) . to . eq ( voter1VotingPower ) ; const voter1PreferenceBefore = await emissionsController . voterPreferences ( voter1 . address ) ; tx = await emissionsController . pokeSources ( voter1 . address ) ; await expect ( tx ) . to . emit ( emissionsController , "SourcesPoked" ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; const voter1PreferenceAfter = await emissionsController . voterPreferences ( voter1 . address ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( voter1VotingPower ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( simpleToExactAmount ( VOTERS [ "2" ] . votes ) ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( BN . from ( 0 ) ) ; expect ( voter1PreferenceAfter . votesCast , "voter 1 votesCast" ) . to . eq ( voter1VotingPower ) ; expect ( voter1PreferenceBefore . lastSourcePoke , "voter 1 lastSourcePoke updated" ) . to . lessThan ( voter1PreferenceAfter . lastSourcePoke ) ; await increaseTime ( ONE_WEEK ) ; nextEpochEmission [ 1 ] = await nextRewardAmount ( emissionsController ) ; tx = await emissionsController . calculateRewards ( ) ; adjustedDials [ 0 ] [ 1 ] = nextEpochEmission [ 1 ] . div ( 2 ) ; adjustedDials [ 1 ] [ 1 ] = nextEpochEmission [ 1 ] . div ( 2 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ adjustedDials [ 0 ] [ 1 ] , adjustedDials [ 1 ] [ 1 ] , 0 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( adjustedDials [ 0 ] . reduce ( sum ) ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( adjustedDials [ 1 ] . reduce ( sum ) ) ; dialAfter [ 0 ] = await snapDial ( emissionsController , 0 ) ; dialAfter [ 1 ] = await snapDial ( emissionsController , 1 ) ; expect ( dialBefore [ 0 ] . voteHistory . length + 2 , "dial 1 vote history should increase" ) . to . eq ( dialAfter [ 0 ] . voteHistory . length ) ; expect ( dialBefore [ 1 ] . voteHistory . length + 2 , "dial 2 vote history should increase" ) . to . eq ( dialAfter [ 1 ] . voteHistory . length ) ; expect ( dialAfter [ 0 ] . voteHistory . slice ( - 1 ) [ 0 ] . votes , "dial 1 last vote" ) . to . eq ( voter1VotingPower ) ; expect ( dialBefore [ 1 ] . voteHistory . slice ( - 1 ) [ 0 ] . votes , "dial 2 vote weight should not change" ) . to . eq ( dialAfter [ 1 ] . voteHistory . slice ( - 1 ) [ 0 ] . votes ) ; } ) ; it ( "adds a new staking contract and without updating voter dial weights" , async ( ) => { let tx = await emissionsController . connect ( sa . governor . signer ) . addStakingContract ( staking3 . address ) ; await expect ( tx ) . to . emit ( emissionsController , "AddStakingContract" ) . withArgs ( staking3 . address ) ; await staking3 . setVotes ( voter1 . address , voter1Staking3Votes ) ; const voter1VotingPower = simpleToExactAmount ( VOTERS [ "1" ] . votes ) . add ( voter1Staking3Votes ) ; expect ( await emissionsController . getVotes ( voter1 . address ) , "Voter 1 voting power" ) . to . eq ( voter1VotingPower ) ; await increaseTime ( ONE_WEEK ) ; nextEpochEmission [ 1 ] = await nextRewardAmount ( emissionsController ) ; tx = await emissionsController . calculateRewards ( ) ; adjustedDials [ 0 ] [ 1 ] = nextEpochEmission [ 1 ] . div ( 3 ) ; adjustedDials [ 1 ] [ 1 ] = nextEpochEmission [ 1 ] . mul ( 2 ) . div ( 3 ) ; await expect ( tx ) . to . emit ( emissionsController , "PeriodRewards" ) . withArgs ( [ adjustedDials [ 0 ] [ 1 ] , adjustedDials [ 1 ] [ 1 ] , 0 ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( adjustedDials [ 0 ] . reduce ( sum ) ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( adjustedDials [ 1 ] . reduce ( sum ) ) ; dialAfter [ 0 ] = await snapDial ( emissionsController , 0 ) ; dialAfter [ 1 ] = await snapDial ( emissionsController , 1 ) ; expect ( dialBefore [ 0 ] . voteHistory . length + 2 , "dial 1 vote history should increase" ) . to . eq ( dialAfter [ 0 ] . voteHistory . length ) ; expect ( dialBefore [ 1 ] . voteHistory . length + 2 , "dial 2 vote history should increase" ) . to . eq ( dialAfter [ 1 ] . voteHistory . length ) ; expect ( dialBefore [ 0 ] . voteHistory . slice ( - 1 ) [ 0 ] . votes , "dial 1 vote weight should not change" ) . to . eq ( dialAfter [ 0 ] . voteHistory . slice ( - 1 ) [ 0 ] . votes ) ; expect ( dialBefore [ 1 ] . voteHistory . slice ( - 1 ) [ 0 ] . votes , "dial 2 vote weight should not change" ) . to . eq ( dialAfter [ 1 ] . voteHistory . slice ( - 1 ) [ 0 ] . votes ) ; } ) ; } ) ; } ) ; describe ( "distributing rewards" , ( ) => { const voter1Staking1Votes = simpleToExactAmount ( 100 ) ; const voter1Staking2Votes = simpleToExactAmount ( 200 ) ; const voter2Staking1Votes = simpleToExactAmount ( 600 ) ; const voter3Staking1Votes = simpleToExactAmount ( 300 ) ; beforeEach ( async ( ) => { await deployEmissionsController ( ) ; await staking1 . setVotes ( voter1 . address , voter1Staking1Votes ) ; await staking2 . setVotes ( voter1 . address , voter1Staking2Votes ) ; await staking1 . setVotes ( voter2 . address , voter2Staking1Votes ) ; await staking1 . setVotes ( voter3 . address , voter3Staking1Votes ) ; await increaseTime ( ONE_WEEK . mul ( 2 ) ) ; } ) ; context ( "Fail to distribute rewards when" , ( ) => { it ( "when first dial id is invalid" , async ( ) => { const tx = emissionsController . distributeRewards ( [ 4 ] ) ; await expect ( tx ) . to . revertedWith ( "Invalid dial id" ) ; } ) ; it ( "when middle dial id is invalid" , async ( ) => { const tx = emissionsController . distributeRewards ( [ 1 , 4 , 2 ] ) ; await expect ( tx ) . to . revertedWith ( "Invalid dial id" ) ; } ) ; it ( "when last dial id is invalid" , async ( ) => { const tx = emissionsController . distributeRewards ( [ 0 , 1 , 2 , 4 ] ) ; await expect ( tx ) . to . revertedWith ( "Invalid dial id" ) ; } ) ; } ) ; context ( "No rewards" , ( ) => { beforeEach ( async ( ) => { expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance before" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance before" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance before" ) . to . eq ( 0 ) ; } ) ; it ( "first dial only" , async ( ) => { const tx = await emissionsController . distributeRewards ( [ 0 ] ) ; await expect ( tx ) . to . not . emit ( emissionsController , "DistributedReward" ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; it ( "all dials" , async ( ) => { const tx = await emissionsController . distributeRewards ( [ 0 , 1 , 2 ] ) ; await expect ( tx ) . to . not . emit ( emissionsController , "DistributedReward" ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; } ) ; context ( "Rewards in each dial" , ( ) => { beforeEach ( async ( ) => { await rewardToken . approve ( emissionsController . address , simpleToExactAmount ( 600 ) ) ; await emissionsController . donate ( [ 0 , 1 , 2 ] , [ simpleToExactAmount ( 100 ) , simpleToExactAmount ( 200 ) , simpleToExactAmount ( 300 ) ] ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance before" ) . to . eq ( simpleToExactAmount ( 100 ) ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance before" ) . to . eq ( simpleToExactAmount ( 200 ) ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance before" ) . to . eq ( simpleToExactAmount ( 300 ) ) ; } ) ; it ( "no dials" , async ( ) => { const tx = await emissionsController . distributeRewards ( [ ] ) ; await expect ( tx ) . to . not . emit ( emissionsController , "DistributedReward" ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( simpleToExactAmount ( 100 ) ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( simpleToExactAmount ( 200 ) ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( simpleToExactAmount ( 300 ) ) ; } ) ; it ( "first dial only" , async ( ) => { const tx = await emissionsController . distributeRewards ( [ 0 ] ) ; await expect ( tx ) . to . emit ( emissionsController , "DistributedReward" ) . withArgs ( 0 , simpleToExactAmount ( 100 ) ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( simpleToExactAmount ( 200 ) ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( simpleToExactAmount ( 300 ) ) ; } ) ; it ( "all dials " , async ( ) => { const tx = await emissionsController . distributeRewards ( [ 0 , 1 , 2 ] ) ; await expect ( tx ) . to . emit ( emissionsController , "DistributedReward" ) . withArgs ( 0 , simpleToExactAmount ( 100 ) ) ; await expect ( tx ) . to . emit ( emissionsController , "DistributedReward" ) . withArgs ( 1 , simpleToExactAmount ( 200 ) ) ; await expect ( tx ) . to . emit ( emissionsController , "DistributedReward" ) . withArgs ( 2 , simpleToExactAmount ( 300 ) ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; it ( "all dials in reverse order" , async ( ) => { const tx = await emissionsController . distributeRewards ( [ 2 , 1 , 0 ] ) ; await expect ( tx ) . to . emit ( emissionsController , "DistributedReward" ) . withArgs ( 0 , simpleToExactAmount ( 100 ) ) ; await expect ( tx ) . to . emit ( emissionsController , "DistributedReward" ) . withArgs ( 1 , simpleToExactAmount ( 200 ) ) ; await expect ( tx ) . to . emit ( emissionsController , "DistributedReward" ) . withArgs ( 2 , simpleToExactAmount ( 300 ) ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; it ( "first and last dials" , async ( ) => { const tx = await emissionsController . distributeRewards ( [ 0 , 2 ] ) ; await expect ( tx ) . to . emit ( emissionsController , "DistributedReward" ) . withArgs ( 0 , simpleToExactAmount ( 100 ) ) ; await expect ( tx ) . to . emit ( emissionsController , "DistributedReward" ) . withArgs ( 2 , simpleToExactAmount ( 300 ) ) ; expect ( ( await emissionsController . dials ( 0 ) ) . balance , "dial 1 balance after" ) . to . eq ( 0 ) ; expect ( ( await emissionsController . dials ( 1 ) ) . balance , "dial 2 balance after" ) . to . eq ( simpleToExactAmount ( 200 ) ) ; expect ( ( await emissionsController . dials ( 2 ) ) . balance , "dial 3 balance after" ) . to . eq ( 0 ) ; } ) ; } ) ; } ) ; describe ( "Poke sources" , ( ) => { let currentTime ; beforeEach ( async ( ) => { await deployEmissionsController ( ) ; await emissionsController . connect ( sa . governor . signer ) . addDial ( staking1 . address , 10 , true ) ; await emissionsController . connect ( sa . governor . signer ) . addDial ( staking2 . address , 10 , true ) ; await increaseTime ( ONE_WEEK ) ; currentTime = await getTimestamp ( ) ; } ) ; it ( "should poke voter 1 with no voting power" , async ( ) => { const tx = await emissionsController . pokeSources ( voter1 . address ) ; await expect ( tx ) . to . not . emit ( emissionsController , "SourcesPoked" ) ; } ) ; it ( "should poke voter 1 with voting power but no preferences set" , async ( ) => { const voterPreferencesBefore = await emissionsController . voterPreferences ( voter1 . address ) ; expect ( voterPreferencesBefore . lastSourcePoke , "last poke time before" ) . to . eq ( 0 ) ; expect ( voterPreferencesBefore . votesCast , "votes cast before" ) . to . eq ( 0 ) ; await staking1 . setVotes ( voter1 . address , simpleToExactAmount ( 50 ) ) ; await staking2 . setVotes ( voter1 . address , simpleToExactAmount ( 70 ) ) ; const tx = await emissionsController . pokeSources ( voter1 . address ) ; await expect ( tx ) . to . not . emit ( emissionsController , "SourcesPoked" ) ; const voterPreferencesAfter = await emissionsController . voterPreferences ( voter1 . address ) ; expect ( voterPreferencesAfter . lastSourcePoke , "last poke time after" ) . to . eq ( 0 ) ; expect ( voterPreferencesAfter . votesCast , "votes cast after" ) . to . eq ( 0 ) ; } ) ; it ( "should poke voter 1 with voting power and weights set" , async ( ) => { const voterPreferencesBefore = await emissionsController . voterPreferences ( voter1 . address ) ; expect ( voterPreferencesBefore . lastSourcePoke , "last poke time before" ) . to . eq ( 0 ) ; await staking1 . setVotes ( voter1 . address , simpleToExactAmount ( 30 ) ) ; await staking2 . setVotes ( voter1 . address , simpleToExactAmount ( 70 ) ) ; await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 120 } , { dialId : 3 , weight : 80 } , ] ) ; const currentEpochWeek = await currentWeekEpoch ( ) ; const epochs = await emissionsController . epochs ( ) ; expect ( epochs . lastEpoch , "last epoch" ) . to . eq ( currentEpochWeek ) ; expect ( epochs . startEpoch , "start epoch" ) . to . eq ( currentEpochWeek ) ; const tx = await emissionsController . pokeSources ( voter1 . address ) ; await expect ( tx ) . to . emit ( emissionsController , "SourcesPoked" ) . withArgs ( voter1 . address , 0 ) ; const voterPreferencesAfter = await emissionsController . voterPreferences ( voter1 . address ) ; expect ( voterPreferencesAfter . lastSourcePoke , "last poke time after" ) . to . gt ( currentTime ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; expect ( dialVotes , "number of dials" ) . to . lengthOf ( 5 ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( simpleToExactAmount ( 60 ) ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( 0 ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( 0 ) ; expect ( dialVotes [ 3 ] , "dial 4 votes" ) . to . eq ( simpleToExactAmount ( 40 ) ) ; expect ( dialVotes [ 4 ] , "dial 5 votes" ) . to . eq ( 0 ) ; } ) ; context ( "after a new staking contract added with voter 1 voting power" , ( ) => { const voter1Staking1VotingPower = simpleToExactAmount ( 1000 ) ; const voter1Staking2VotingPower = simpleToExactAmount ( 2000 ) ; const voter1Staking3VotingPower = simpleToExactAmount ( 6000 ) ; const voter2Staking1VotingPower = simpleToExactAmount ( 2222 ) ; let staking3 ; beforeEach ( async ( ) => { await staking1 . setVotes ( voter1 . address , voter1Staking1VotingPower ) ; await staking2 . setVotes ( voter1 . address , voter1Staking2VotingPower ) ; await staking1 . setVotes ( voter2 . address , voter2Staking1VotingPower ) ; await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 120 } , { dialId : 3 , weight : 80 } , ] ) ; staking3 = await new MockStakingContract__factory ( sa . default . signer ) . deploy ( ) ; await staking3 . setVotes ( voter1 . address , voter1Staking3VotingPower ) ; await emissionsController . connect ( sa . governor . signer ) . addStakingContract ( staking3 . address ) ; await staking3 . setGovernanceHook ( emissionsController . address ) ; } ) ; it ( "should poke voter 1" , async ( ) => { const tx = await emissionsController . pokeSources ( voter1 . address ) ; await expect ( tx ) . to . emit ( emissionsController , "SourcesPoked" ) . withArgs ( voter1 . address , voter1Staking3VotingPower ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; expect ( dialVotes , "number of dials" ) . to . lengthOf ( 5 ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( simpleToExactAmount ( 9000 ) . mul ( 6 ) . div ( 10 ) ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( 0 ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( 0 ) ; expect ( dialVotes [ 3 ] , "dial 4 votes" ) . to . eq ( simpleToExactAmount ( 9000 ) . mul ( 4 ) . div ( 10 ) ) ; expect ( dialVotes [ 4 ] , "dial 5 votes" ) . to . eq ( 0 ) ; const preferencesAfter = await emissionsController . voterPreferences ( voter1 . address ) ; expect ( preferencesAfter . lastSourcePoke , "lastSourcePoke after" ) . to . gte ( currentTime ) ; expect ( preferencesAfter . votesCast , "votes cast after" ) . to . eq ( simpleToExactAmount ( 9000 ) ) ; } ) ; it ( "should poke voter 2 with no preferences set" , async ( ) => { const tx = await emissionsController . pokeSources ( voter2 . address ) ; await expect ( tx ) . to . not . emit ( emissionsController , "SourcesPoked" ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; expect ( dialVotes , "number of dials" ) . to . lengthOf ( 5 ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( simpleToExactAmount ( 3000 ) . mul ( 6 ) . div ( 10 ) ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( 0 ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( 0 ) ; expect ( dialVotes [ 3 ] , "dial 4 votes" ) . to . eq ( simpleToExactAmount ( 3000 ) . mul ( 4 ) . div ( 10 ) ) ; expect ( dialVotes [ 4 ] , "dial 5 votes" ) . to . eq ( 0 ) ; const preferencesAfter = await emissionsController . voterPreferences ( voter2 . address ) ; expect ( preferencesAfter . lastSourcePoke , "lastSourcePoke after" ) . to . eq ( 0 ) ; expect ( preferencesAfter . votesCast , "votes cast after" ) . to . eq ( 0 ) ; } ) ; } ) ; } ) ; describe ( "setting preferences" , ( ) => { before ( async ( ) => { await deployEmissionsController ( ) ; await emissionsController . connect ( sa . governor . signer ) . addDial ( staking1 . address , 10 , true ) ; await emissionsController . connect ( sa . governor . signer ) . addDial ( staking2 . address , 10 , true ) ; for ( let i = 0 ; i < 15 ; i += 1 ) { await emissionsController . connect ( sa . governor . signer ) . addDial ( Wallet . createRandom ( ) . address , 0 , true ) ; } } ) ; it ( "should set 15 preferences" , async ( ) => { const preferences = [ { dialId : 0 , weight : 1 } , { dialId : 1 , weight : 2 } , { dialId : 2 , weight : 3 } , { dialId : 3 , weight : 4 } , { dialId : 4 , weight : 5 } , { dialId : 5 , weight : 6 } , { dialId : 6 , weight : 7 } , { dialId : 7 , weight : 8 } , { dialId : 8 , weight : 9 } , { dialId : 9 , weight : 10 } , { dialId : 10 , weight : 11 } , { dialId : 11 , weight : 12 } , { dialId : 12 , weight : 13 } , { dialId : 13 , weight : 14 } , { dialId : 14 , weight : 15 } , ] ; const tx = await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( preferences ) ; await expect ( tx ) . to . emit ( emissionsController , "PreferencesChanged" ) ; const receipt = await tx . wait ( ) ; expect ( receipt . events [ 0 ] . args [ 0 ] , "sender" ) . to . eq ( voter1 . address ) ; expect ( receipt . events [ 0 ] . args [ 1 ] , "preferences length" ) . to . lengthOf ( 15 ) ; expect ( receipt . events [ 0 ] . args [ 1 ] [ 0 ] . dialId , "first preference dial id" ) . to . eq ( preferences [ 0 ] . dialId ) ; expect ( receipt . events [ 0 ] . args [ 1 ] [ 0 ] . weight , "first preference weight" ) . to . eq ( preferences [ 0 ] . weight ) ; expect ( receipt . events [ 0 ] . args [ 1 ] [ 14 ] . dialId , "last preference dial id" ) . to . eq ( preferences [ 14 ] . dialId ) ; expect ( receipt . events [ 0 ] . args [ 1 ] [ 14 ] . weight , "last preference weight" ) . to . eq ( preferences [ 14 ] . weight ) ; const voterPreferencesAfter = await emissionsController . getVoterPreferences ( voter1 . address ) ; expect ( voterPreferencesAfter [ 0 ] . dialId , "pos 1 dial id after" ) . to . eq ( preferences [ 0 ] . dialId ) ; expect ( voterPreferencesAfter [ 0 ] . weight , "pos 1 weight after" ) . to . eq ( preferences [ 0 ] . weight ) ; expect ( voterPreferencesAfter [ 1 ] . dialId , "pos 2 dial id after" ) . to . eq ( preferences [ 1 ] . dialId ) ; expect ( voterPreferencesAfter [ 1 ] . weight , "pos 2 weight after" ) . to . eq ( preferences [ 1 ] . weight ) ; expect ( voterPreferencesAfter [ 14 ] . dialId , "pos 15 dial id after" ) . to . eq ( preferences [ 14 ] . dialId ) ; expect ( voterPreferencesAfter [ 14 ] . weight , "pos 15 weight after" ) . to . eq ( preferences [ 14 ] . weight ) ; expect ( voterPreferencesAfter [ 15 ] . dialId , "pos 16 dial id after" ) . to . eq ( 255 ) ; expect ( voterPreferencesAfter [ 15 ] . weight , "pos 16 weight after" ) . to . eq ( 0 ) ; } ) ; it ( "should set 16 preferences" , async ( ) => { const preferences = [ { dialId : 0 , weight : 1 } , { dialId : 1 , weight : 2 } , { dialId : 2 , weight : 3 } , { dialId : 3 , weight : 4 } , { dialId : 4 , weight : 5 } , { dialId : 5 , weight : 6 } , { dialId : 6 , weight : 7 } , { dialId : 7 , weight : 8 } , { dialId : 8 , weight : 9 } , { dialId : 9 , weight : 10 } , { dialId : 10 , weight : 11 } , { dialId : 11 , weight : 12 } , { dialId : 12 , weight : 13 } , { dialId : 13 , weight : 14 } , { dialId : 14 , weight : 15 } , { dialId : 15 , weight : 16 } , ] ; const tx = await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( preferences ) ; await expect ( tx ) . to . emit ( emissionsController , "PreferencesChanged" ) ; const receipt = await tx . wait ( ) ; expect ( receipt . events [ 0 ] . args [ 0 ] , "sender" ) . to . eq ( voter1 . address ) ; expect ( receipt . events [ 0 ] . args [ 1 ] , "preferences length" ) . to . lengthOf ( 16 ) ; expect ( receipt . events [ 0 ] . args [ 1 ] [ 0 ] . dialId , "first preference dial id" ) . to . eq ( preferences [ 0 ] . dialId ) ; expect ( receipt . events [ 0 ] . args [ 1 ] [ 0 ] . weight , "first preference weight" ) . to . eq ( preferences [ 0 ] . weight ) ; expect ( receipt . events [ 0 ] . args [ 1 ] [ 15 ] . dialId , "last preference dial id" ) . to . eq ( preferences [ 15 ] . dialId ) ; expect ( receipt . events [ 0 ] . args [ 1 ] [ 15 ] . weight , "last preference weight" ) . to . eq ( preferences [ 15 ] . weight ) ; const voterPreferencesAfter = await emissionsController . getVoterPreferences ( voter1 . address ) ; expect ( voterPreferencesAfter [ 0 ] . dialId , "pos 1 dial id after" ) . to . eq ( preferences [ 0 ] . dialId ) ; expect ( voterPreferencesAfter [ 0 ] . weight , "pos 1 weight after" ) . to . eq ( preferences [ 0 ] . weight ) ; expect ( voterPreferencesAfter [ 1 ] . dialId , "pos 2 dial id after" ) . to . eq ( preferences [ 1 ] . dialId ) ; expect ( voterPreferencesAfter [ 1 ] . weight , "pos 2 weight after" ) . to . eq ( preferences [ 1 ] . weight ) ; expect ( voterPreferencesAfter [ 14 ] . dialId , "pos 15 dial id after" ) . to . eq ( preferences [ 14 ] . dialId ) ; expect ( voterPreferencesAfter [ 14 ] . weight , "pos 15 weight after" ) . to . eq ( preferences [ 14 ] . weight ) ; expect ( voterPreferencesAfter [ 15 ] . dialId , "pos 16 dial id after" ) . to . eq ( preferences [ 15 ] . dialId ) ; expect ( voterPreferencesAfter [ 15 ] . weight , "pos 16 weight after" ) . to . eq ( preferences [ 15 ] . weight ) ; } ) ; it ( "should set 100% on dial 20" , async ( ) => { const voterPreferencesBefore = await emissionsController . voterPreferences ( voter2 . address ) ; expect ( voterPreferencesBefore . lastSourcePoke , "lastSourcePoke after" ) . to . eq ( 0 ) ; const preferences = [ { dialId : 19 , weight : 200 } ] ; const tx = await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( preferences ) ; await expect ( tx ) . to . emit ( emissionsController , "PreferencesChanged" ) ; const receipt = await tx . wait ( ) ; expect ( receipt . events [ 0 ] . args [ 0 ] , "sender" ) . to . eq ( voter2 . address ) ; expect ( receipt . events [ 0 ] . args [ 1 ] , "preferences length" ) . to . lengthOf ( 1 ) ; expect ( receipt . events [ 0 ] . args [ 1 ] [ 0 ] . dialId , "first preference dial id" ) . to . eq ( preferences [ 0 ] . dialId ) ; expect ( receipt . events [ 0 ] . args [ 1 ] [ 0 ] . weight , "first preference weight" ) . to . eq ( preferences [ 0 ] . weight ) ; const voterWeightsAfter = await emissionsController . getVoterPreferences ( voter2 . address ) ; expect ( voterWeightsAfter [ 0 ] . dialId , "pos 1 dial id after" ) . to . eq ( preferences [ 0 ] . dialId ) ; expect ( voterWeightsAfter [ 0 ] . weight , "pos 1 weight after" ) . to . eq ( preferences [ 0 ] . weight ) ; expect ( voterWeightsAfter [ 1 ] . dialId , "pos 2 dial id after" ) . to . eq ( 255 ) ; expect ( voterWeightsAfter [ 1 ] . weight , "pos 2 weight after" ) . to . eq ( 0 ) ; expect ( voterWeightsAfter [ 2 ] . dialId , "pos 3 dial id after" ) . to . eq ( 0 ) ; expect ( voterWeightsAfter [ 2 ] . weight , "pos 3 weight after" ) . to . eq ( 0 ) ; const voterPreferencesAfter = await emissionsController . voterPreferences ( voter2 . address ) ; expect ( voterPreferencesAfter . lastSourcePoke , "lastSourcePoke after" ) . to . gt ( 0 ) ; } ) ; it ( "should override previous dial weights" , async ( ) => { const previousPreferences = [ { dialId : 0 , weight : 120 } , { dialId : 1 , weight : 60 } , { dialId : 2 , weight : 20 } , ] ; await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( previousPreferences ) ; const voterPreferencesBefore = await emissionsController . getVoterPreferences ( voter1 . address ) ; expect ( voterPreferencesBefore [ 0 ] . dialId , "pos 1 dial id before" ) . to . eq ( 0 ) ; expect ( voterPreferencesBefore [ 0 ] . weight , "pos 1 weight before" ) . to . eq ( 120 ) ; expect ( voterPreferencesBefore [ 1 ] . dialId , "pos 2 dial id before" ) . to . eq ( 1 ) ; expect ( voterPreferencesBefore [ 1 ] . weight , "pos 2 weight before" ) . to . eq ( 60 ) ; expect ( voterPreferencesBefore [ 2 ] . dialId , "pos 3 dial id before" ) . to . eq ( 2 ) ; expect ( voterPreferencesBefore [ 2 ] . weight , "pos 3 weight before" ) . to . eq ( 20 ) ; expect ( voterPreferencesBefore [ 3 ] . dialId , "pos 4 dial id before" ) . to . eq ( 255 ) ; expect ( voterPreferencesBefore [ 3 ] . weight , "pos 4 weight before" ) . to . eq ( 0 ) ; expect ( voterPreferencesBefore [ 4 ] . dialId , "pos 5 dial id before" ) . to . eq ( 0 ) ; expect ( voterPreferencesBefore [ 4 ] . weight , "pos 5 weight before" ) . to . eq ( 0 ) ; expect ( voterPreferencesBefore [ 15 ] . dialId , "pos 16 dial id before" ) . to . eq ( 0 ) ; expect ( voterPreferencesBefore [ 15 ] . weight , "pos 16 weight before" ) . to . eq ( 0 ) ; const newPreferences = [ { dialId : 1 , weight : 60 } , { dialId : 2 , weight : 20 } , { dialId : 5 , weight : 30 } , { dialId : 19 , weight : 70 } , ] ; await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( newPreferences ) ; const voterPreferencesAfter = await emissionsController . getVoterPreferences ( voter1 . address ) ; expect ( voterPreferencesAfter [ 0 ] . dialId , "pos 1 dial id after" ) . to . eq ( 1 ) ; expect ( voterPreferencesAfter [ 0 ] . weight , "pos 1 weight after" ) . to . eq ( 60 ) ; expect ( voterPreferencesAfter [ 1 ] . dialId , "pos 2 dial id after" ) . to . eq ( 2 ) ; expect ( voterPreferencesAfter [ 1 ] . weight , "pos 2 weight after" ) . to . eq ( 20 ) ; expect ( voterPreferencesAfter [ 2 ] . dialId , "pos 3 dial id after" ) . to . eq ( 5 ) ; expect ( voterPreferencesAfter [ 2 ] . weight , "pos 3 weight after" ) . to . eq ( 30 ) ; expect ( voterPreferencesAfter [ 3 ] . dialId , "pos 4 dial id after" ) . to . eq ( 19 ) ; expect ( voterPreferencesAfter [ 3 ] . weight , "pos 4 weight after" ) . to . eq ( 70 ) ; expect ( voterPreferencesAfter [ 4 ] . dialId , "pos 5 dial id after" ) . to . eq ( 255 ) ; expect ( voterPreferencesAfter [ 4 ] . weight , "pos 5 weight after" ) . to . eq ( 0 ) ; expect ( voterPreferencesBefore [ 5 ] . dialId , "pos 6 dial id after" ) . to . eq ( 0 ) ; expect ( voterPreferencesBefore [ 5 ] . weight , "pos 6 weight after" ) . to . eq ( 0 ) ; expect ( voterPreferencesBefore [ 15 ] . dialId , "pos 16 dial id after" ) . to . eq ( 0 ) ; expect ( voterPreferencesBefore [ 15 ] . weight , "pos 16 weight after" ) . to . eq ( 0 ) ; } ) ; describe ( "should fail when" , ( ) => { it ( "weights > 100% to a single dial" , async ( ) => { const tx = emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 201 } ] ) ; await expect ( tx ) . to . revertedWith ( "Imbalanced weights" ) ; } ) ; it ( "weights > 100% across multiple dials" , async ( ) => { const tx = emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 180 } , { dialId : 1 , weight : 21 } , ] ) ; await expect ( tx ) . to . revertedWith ( "Imbalanced weights" ) ; } ) ; it ( "invalid dial id" , async ( ) => { const tx = emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 20 , weight : 200 } ] ) ; await expect ( tx ) . to . revertedWith ( "Invalid dial id" ) ; } ) ; it ( "0% weight" , async ( ) => { const tx = emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 1 , weight : 100 } , { dialId : 18 , weight : 0 } , ] ) ; await expect ( tx ) . to . revertedWith ( "Must give a dial some weight" ) ; } ) ; it ( "setting 17 preferences" , async ( ) => { const tx = emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 10 } , { dialId : 1 , weight : 10 } , { dialId : 2 , weight : 10 } , { dialId : 3 , weight : 10 } , { dialId : 4 , weight : 10 } , { dialId : 5 , weight : 10 } , { dialId : 6 , weight : 10 } , { dialId : 7 , weight : 10 } , { dialId : 8 , weight : 10 } , { dialId : 9 , weight : 10 } , { dialId : 10 , weight : 10 } , { dialId : 11 , weight : 10 } , { dialId : 12 , weight : 10 } , { dialId : 13 , weight : 10 } , { dialId : 14 , weight : 10 } , { dialId : 15 , weight : 10 } , { dialId : 16 , weight : 10 } , ] ) ; await expect ( tx ) . to . revertedWith ( "Max of 16 preferences" ) ; } ) ; } ) ; } ) ; describe ( "staking contract hook" , ( ) => { const amount = simpleToExactAmount ( 100 ) ; let currentTime ; let voter1PreferencesBefore ; beforeEach ( async ( ) => { await deployEmissionsController ( ) ; await staking1 . setVotes ( voter1 . address , simpleToExactAmount ( 100 ) ) ; await staking1 . setVotes ( voter2 . address , simpleToExactAmount ( 200 ) ) ; const preferencesBefore = await emissionsController . voterPreferences ( voter1 . address ) ; expect ( preferencesBefore . lastSourcePoke , "lastSourcePoke before" ) . to . eq ( 0 ) ; await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 2 , weight : 120 } , { dialId : 1 , weight : 60 } , { dialId : 0 , weight : 20 } , ] ) ; voter1PreferencesBefore = await emissionsController . voterPreferences ( voter1 . address ) ; } ) ; context ( "in first launch week" , ( ) => { beforeEach ( async ( ) => { currentTime = await getTimestamp ( ) ; } ) ; it ( "should increase voter 1's voting power" , async ( ) => { const preferencesBefore = await emissionsController . voterPreferences ( voter1 . address ) ; expect ( preferencesBefore . lastSourcePoke , "lastSourcePoke before" ) . to . gt ( 0 ) ; const tx = await staking1 . setVotes ( voter1 . address , simpleToExactAmount ( 300 ) ) ; await expect ( tx ) . to . emit ( emissionsController , "VotesCast" ) . withArgs ( staking1 . address , ZERO_ADDRESS , voter1 . address , simpleToExactAmount ( 200 ) ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; expect ( dialVotes , "number of dials" ) . to . lengthOf ( 3 ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( simpleToExactAmount ( 300 ) . mul ( 1 ) . div ( 10 ) ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( simpleToExactAmount ( 300 ) . mul ( 3 ) . div ( 10 ) ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( simpleToExactAmount ( 300 ) . mul ( 6 ) . div ( 10 ) ) ; const preferencesAfter = await emissionsController . voterPreferences ( voter1 . address ) ; expect ( preferencesAfter . lastSourcePoke , "lastSourcePoke after" ) . to . eq ( voter1PreferencesBefore . lastSourcePoke ) ; expect ( preferencesAfter . votesCast , "votes cast after" ) . to . eq ( simpleToExactAmount ( 300 ) ) ; } ) ; it ( "should set voter 2's preferences" , async ( ) => { const tx = await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ { dialId : 1 , weight : 180 } , ] ) ; await expect ( tx ) . to . emit ( emissionsController , "PreferencesChanged" ) ; const receipt = await tx . wait ( ) ; expect ( receipt . events [ 0 ] . args [ 0 ] , "sender" ) . to . eq ( voter2 . address ) ; expect ( receipt . events [ 0 ] . args [ 1 ] , "preferences length" ) . to . lengthOf ( 1 ) ; expect ( receipt . events [ 0 ] . args [ 1 ] [ 0 ] . dialId , "first preference dial id" ) . to . eq ( 1 ) ; expect ( receipt . events [ 0 ] . args [ 1 ] [ 0 ] . weight , "first preference weight" ) . to . eq ( 180 ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; expect ( dialVotes , "number of dials" ) . to . lengthOf ( 3 ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( simpleToExactAmount ( 100 ) . mul ( 1 ) . div ( 10 ) ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( simpleToExactAmount ( 100 ) . mul ( 3 ) . div ( 10 ) . add ( simpleToExactAmount ( 200 ) . mul ( 9 ) . div ( 10 ) ) ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( simpleToExactAmount ( 100 ) . mul ( 6 ) . div ( 10 ) ) ; const preferences = await emissionsController . voterPreferences ( voter2 . address ) ; expect ( preferences . lastSourcePoke , "lastSourcePoke after" ) . to . gte ( currentTime ) ; expect ( preferences . votesCast , "votes cast after" ) . to . eq ( simpleToExactAmount ( 200 ) ) ; } ) ; it ( "should increase voter 3's voting power who doesn't have preferences" , async ( ) => { const tx = await staking1 . setVotes ( voter3 . address , simpleToExactAmount ( 1000 ) ) ; await expect ( tx ) . to . not . emit ( emissionsController , "VotesCast" ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; expect ( dialVotes , "number of dials" ) . to . lengthOf ( 3 ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( simpleToExactAmount ( 100 ) . mul ( 1 ) . div ( 10 ) ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( simpleToExactAmount ( 100 ) . mul ( 3 ) . div ( 10 ) ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( simpleToExactAmount ( 100 ) . mul ( 6 ) . div ( 10 ) ) ; const preferences = await emissionsController . voterPreferences ( voter3 . address ) ; expect ( preferences . lastSourcePoke , "lastSourcePoke after" ) . to . eq ( 0 ) ; expect ( preferences . votesCast , "votes cast after" ) . to . eq ( 0 ) ; } ) ; } ) ; context ( "in second launch week" , ( ) => { beforeEach ( async ( ) => { await increaseTime ( ONE_WEEK ) ; currentTime = await getTimestamp ( ) ; } ) ; it ( "should increase voter 1's voting power" , async ( ) => { const tx = await staking1 . setVotes ( voter1 . address , simpleToExactAmount ( 500 ) ) ; await expect ( tx ) . to . emit ( emissionsController , "VotesCast" ) . withArgs ( staking1 . address , ZERO_ADDRESS , voter1 . address , simpleToExactAmount ( 400 ) ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; expect ( dialVotes , "number of dials" ) . to . lengthOf ( 3 ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( simpleToExactAmount ( 500 ) . mul ( 1 ) . div ( 10 ) ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( simpleToExactAmount ( 500 ) . mul ( 3 ) . div ( 10 ) ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( simpleToExactAmount ( 500 ) . mul ( 6 ) . div ( 10 ) ) ; const preferencesAfter = await emissionsController . voterPreferences ( voter1 . address ) ; expect ( preferencesAfter . lastSourcePoke , "lastSourcePoke after" ) . to . gte ( voter1PreferencesBefore . lastSourcePoke ) ; expect ( preferencesAfter . votesCast , "votes cast after" ) . to . eq ( simpleToExactAmount ( 500 ) ) ; } ) ; it ( "should decrease voter 1's voting power" , async ( ) => { const tx = await staking1 . setVotes ( voter1 . address , simpleToExactAmount ( 10 ) ) ; await expect ( tx ) . to . emit ( emissionsController , "VotesCast" ) . withArgs ( staking1 . address , voter1 . address , ZERO_ADDRESS , simpleToExactAmount ( 90 ) ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; expect ( dialVotes , "number of dials" ) . to . lengthOf ( 3 ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( simpleToExactAmount ( 10 ) . mul ( 1 ) . div ( 10 ) ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( simpleToExactAmount ( 10 ) . mul ( 3 ) . div ( 10 ) ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( simpleToExactAmount ( 10 ) . mul ( 6 ) . div ( 10 ) ) ; const preferencesAfter = await emissionsController . voterPreferences ( voter1 . address ) ; expect ( preferencesAfter . lastSourcePoke , "lastSourcePoke after" ) . to . eq ( voter1PreferencesBefore . lastSourcePoke ) ; expect ( preferencesAfter . votesCast , "votes cast after" ) . to . eq ( simpleToExactAmount ( 10 ) ) ; } ) ; it ( "should set voter 2's preferences" , async ( ) => { const tx = await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ { dialId : 1 , weight : 180 } , ] ) ; await expect ( tx ) . to . emit ( emissionsController , "PreferencesChanged" ) ; const receipt = await tx . wait ( ) ; expect ( receipt . events [ 0 ] . args [ 0 ] , "sender" ) . to . eq ( voter2 . address ) ; expect ( receipt . events [ 0 ] . args [ 1 ] , "preferences length" ) . to . lengthOf ( 1 ) ; expect ( receipt . events [ 0 ] . args [ 1 ] [ 0 ] . dialId , "first preference dial id" ) . to . eq ( 1 ) ; expect ( receipt . events [ 0 ] . args [ 1 ] [ 0 ] . weight , "first preference weight" ) . to . eq ( 180 ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; expect ( dialVotes , "number of dials" ) . to . lengthOf ( 3 ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( simpleToExactAmount ( 100 ) . mul ( 1 ) . div ( 10 ) ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( simpleToExactAmount ( 100 ) . mul ( 3 ) . div ( 10 ) . add ( simpleToExactAmount ( 200 ) . mul ( 9 ) . div ( 10 ) ) ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( simpleToExactAmount ( 100 ) . mul ( 6 ) . div ( 10 ) ) ; const preferencesAfter = await emissionsController . voterPreferences ( voter2 . address ) ; expect ( preferencesAfter . lastSourcePoke , "lastSourcePoke after" ) . to . gte ( currentTime ) ; expect ( preferencesAfter . votesCast , "votes cast after" ) . to . eq ( simpleToExactAmount ( 200 ) ) ; } ) ; it ( "should increase voter 2's voting power after setting preferences" , async ( ) => { await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ { dialId : 1 , weight : 180 } , ] ) ; const tx = await staking1 . setVotes ( voter2 . address , simpleToExactAmount ( 300 ) ) ; await expect ( tx ) . to . emit ( emissionsController , "VotesCast" ) . withArgs ( staking1 . address , ZERO_ADDRESS , voter2 . address , simpleToExactAmount ( 100 ) ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; expect ( dialVotes , "number of dials" ) . to . lengthOf ( 3 ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( simpleToExactAmount ( 100 ) . mul ( 1 ) . div ( 10 ) ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( simpleToExactAmount ( 100 ) . mul ( 3 ) . div ( 10 ) . add ( simpleToExactAmount ( 300 ) . mul ( 9 ) . div ( 10 ) ) ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( simpleToExactAmount ( 100 ) . mul ( 6 ) . div ( 10 ) ) ; const preferencesAfter = await emissionsController . voterPreferences ( voter2 . address ) ; expect ( preferencesAfter . lastSourcePoke , "lastSourcePoke after" ) . to . gte ( currentTime ) ; expect ( preferencesAfter . votesCast , "votes cast after" ) . to . eq ( simpleToExactAmount ( 300 ) ) ; } ) ; it ( "should increase voter 3's voting power who doesn't have preferences" , async ( ) => { const tx = await staking1 . setVotes ( voter3 . address , simpleToExactAmount ( 1000 ) ) ; await expect ( tx ) . to . not . emit ( emissionsController , "VotesCast" ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; expect ( dialVotes , "number of dials" ) . to . lengthOf ( 3 ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( simpleToExactAmount ( 100 ) . mul ( 1 ) . div ( 10 ) ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( simpleToExactAmount ( 100 ) . mul ( 3 ) . div ( 10 ) ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( simpleToExactAmount ( 100 ) . mul ( 6 ) . div ( 10 ) ) ; const preferencesAfter = await emissionsController . voterPreferences ( voter3 . address ) ; expect ( preferencesAfter . lastSourcePoke , "lastSourcePoke after" ) . to . eq ( 0 ) ; expect ( preferencesAfter . votesCast , "votes cast after" ) . to . eq ( 0 ) ; } ) ; } ) ; context ( "should fail when" , ( ) => { it ( "called by default account" , async ( ) => { const tx = emissionsController . moveVotingPowerHook ( voter1 . address , voter2 . address , amount ) ; await expect ( tx ) . to . revertedWith ( "Caller must be staking contract" ) ; } ) ; it ( "called by governor" , async ( ) => { const tx = emissionsController . connect ( sa . governor . signer ) . moveVotingPowerHook ( voter1 . address , voter2 . address , amount ) ; await expect ( tx ) . to . revertedWith ( "Caller must be staking contract" ) ; } ) ; } ) ; context ( "after a new staking contract added with voter 1's voting power" , ( ) => { const voter1Staking1VotingPower = simpleToExactAmount ( 1000 ) ; const voter1Staking2VotingPower = simpleToExactAmount ( 2000 ) ; const voter1Staking3VotingPower = simpleToExactAmount ( 6000 ) ; const voter2Staking3VotingPower = simpleToExactAmount ( 2300 ) ; let staking3 ; beforeEach ( async ( ) => { await staking1 . setVotes ( voter1 . address , voter1Staking1VotingPower ) ; await staking2 . setVotes ( voter1 . address , voter1Staking2VotingPower ) ; await emissionsController . connect ( voter1 . signer ) . setVoterDialWeights ( [ { dialId : 0 , weight : 120 } , { dialId : 2 , weight : 80 } , ] ) ; staking3 = await new MockStakingContract__factory ( sa . default . signer ) . deploy ( ) ; await staking3 . setVotes ( voter1 . address , voter1Staking3VotingPower ) ; await staking3 . setVotes ( voter2 . address , voter2Staking3VotingPower ) ; await emissionsController . connect ( sa . governor . signer ) . addStakingContract ( staking3 . address ) ; await staking3 . setGovernanceHook ( emissionsController . address ) ; currentTime = await getTimestamp ( ) ; } ) ; it ( "should poke when voter 1 increases voting power in new staking contract" , async ( ) => { const tx = staking3 . setVotes ( voter1 . address , voter1Staking3VotingPower . mul ( 3 ) ) ; await expect ( tx ) . to . emit ( emissionsController , "SourcesPoked" ) . withArgs ( voter1 . address , voter1Staking3VotingPower . mul ( 3 ) ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; expect ( dialVotes , "number of dials" ) . to . lengthOf ( 3 ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( simpleToExactAmount ( 21000 ) . mul ( 6 ) . div ( 10 ) ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( 0 ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( simpleToExactAmount ( 21000 ) . mul ( 4 ) . div ( 10 ) ) ; const preferences = await emissionsController . voterPreferences ( voter1 . address ) ; expect ( preferences . lastSourcePoke , "lastSourcePoke after" ) . to . gte ( currentTime ) ; expect ( preferences . votesCast , "votes cast after" ) . to . eq ( simpleToExactAmount ( 21000 ) ) ; } ) ; it ( "should poke when voter 1 removes voting power from new staking contract" , async ( ) => { const tx = staking3 . setVotes ( voter1 . address , 0 ) ; await expect ( tx ) . to . emit ( emissionsController , "SourcesPoked" ) . withArgs ( voter1 . address , 0 ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; expect ( dialVotes , "number of dials" ) . to . lengthOf ( 3 ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( simpleToExactAmount ( 3000 ) . mul ( 6 ) . div ( 10 ) ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( 0 ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( simpleToExactAmount ( 3000 ) . mul ( 4 ) . div ( 10 ) ) ; const preferences = await emissionsController . voterPreferences ( voter1 . address ) ; expect ( preferences . lastSourcePoke , "lastSourcePoke after" ) . to . gte ( currentTime ) ; expect ( preferences . votesCast , "votes cast after" ) . to . eq ( simpleToExactAmount ( 3000 ) ) ; } ) ; it ( "should add voter 2's preferences" , async ( ) => { const tx = await emissionsController . connect ( voter2 . signer ) . setVoterDialWeights ( [ { dialId : 1 , weight : 200 } , ] ) ; await expect ( tx ) . to . emit ( emissionsController , "PreferencesChanged" ) ; const receipt = await tx . wait ( ) ; expect ( receipt . events [ 0 ] . args [ 0 ] , "sender" ) . to . eq ( voter2 . address ) ; expect ( receipt . events [ 0 ] . args [ 1 ] , "preferences length" ) . to . lengthOf ( 1 ) ; expect ( receipt . events [ 0 ] . args [ 1 ] [ 0 ] . dialId , "first preference dial id" ) . to . eq ( 1 ) ; expect ( receipt . events [ 0 ] . args [ 1 ] [ 0 ] . weight , "first preference weight" ) . to . eq ( 200 ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; expect ( dialVotes , "number of dials" ) . to . lengthOf ( 3 ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( simpleToExactAmount ( 3000 ) . mul ( 6 ) . div ( 10 ) ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( simpleToExactAmount ( 2300 + 200 ) ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( simpleToExactAmount ( 3000 ) . mul ( 4 ) . div ( 10 ) ) ; const preferences = await emissionsController . voterPreferences ( voter2 . address ) ; expect ( preferences . lastSourcePoke , "lastSourcePoke after" ) . to . gte ( currentTime ) ; expect ( preferences . votesCast , "votes cast after" ) . to . eq ( simpleToExactAmount ( 2300 + 200 ) ) ; } ) ; it ( "should do nothing when voter 3 adds voting power to new staking contract" , async ( ) => { const tx = staking3 . setVotes ( voter3 . address , simpleToExactAmount ( 4000 ) ) ; await expect ( tx ) . to . not . emit ( emissionsController , "SourcesPoked" ) ; await expect ( tx ) . to . not . emit ( emissionsController , "VotesCast" ) ; const dialVotes = await emissionsController . getDialVotes ( ) ; expect ( dialVotes , "number of dials" ) . to . lengthOf ( 3 ) ; expect ( dialVotes [ 0 ] , "dial 1 votes" ) . to . eq ( simpleToExactAmount ( 3000 ) . mul ( 6 ) . div ( 10 ) ) ; expect ( dialVotes [ 1 ] , "dial 2 votes" ) . to . eq ( 0 ) ; expect ( dialVotes [ 2 ] , "dial 3 votes" ) . to . eq ( simpleToExactAmount ( 3000 ) . mul ( 4 ) . div ( 10 ) ) ; const preferences = await emissionsController . voterPreferences ( voter3 . address ) ; expect ( preferences . lastSourcePoke , "lastSourcePoke after" ) . to . eq ( 0 ) ; expect ( preferences . votesCast , "votes cast after" ) . to . eq ( 0 ) ; } ) ; } ) ; } ) ; } ) ;