Skip to content

Commit

Permalink
test(fork): add integration test for rewards manager
Browse files Browse the repository at this point in the history
  • Loading branch information
oliviera9 committed Feb 22, 2024
1 parent 5ac665d commit abae978
Show file tree
Hide file tree
Showing 3 changed files with 121 additions and 16 deletions.
7 changes: 7 additions & 0 deletions contracts/core/RewardsManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,13 @@ contract RewardsManager is IRewardsManager, Initializable, UUPSUpgradeable, Acce
maxTotalSupply = _maxTotalSupply;
}

/// @inheritdoc IRewardsManager
function changeToken(address token_) external onlyRole(Roles.CHANGE_TOKEN_ROLE) {
address previousToken = token;
token = token_;
emit TokenChanged(previousToken, token);
}

/// @inheritdoc IRewardsManager
function claimRewardByEpoch(uint16 epoch) external {
address sender = _msgSender();
Expand Down
15 changes: 15 additions & 0 deletions contracts/interfaces/IRewardsManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,21 @@ pragma solidity ^0.8.17;
* @notice
*/
interface IRewardsManager {
/**
* @dev Emitted when the token changes
*
* @param previousToken the previous token
* @param newToken the new token
*/
event TokenChanged(address previousToken, address newToken);

/* @notice Change token
*
* @param token
*
*/
function changeToken(address token) external;

/*
* Allows a staker to claim their rewards for a specific epoch.
* @param {uint16} epoch - The epoch number for which rewards are being claimed.
Expand Down
115 changes: 99 additions & 16 deletions test/fork/dao.test.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const { mineUpTo, time } = require('@nomicfoundation/hardhat-network-helpers')
const { time, mineUpTo } = require('@nomicfoundation/hardhat-network-helpers')
const { expect } = require('chai')
const hre = require('hardhat')

Expand All @@ -17,7 +17,8 @@ const {
ERC20_VAULT,
PNT_ON_ETH_ADDRESS,
DANDELION_VOTING_V1_ADDRESS,
ETHPNT_ADDRESS
ETHPNT_ADDRESS,
REWARDS_MANAGER
} = require('../../tasks/config')
const AclAbi = require('../abi/ACL.json')
const DandelionVotingAbi = require('../abi/DandelionVoting.json')
Expand All @@ -26,8 +27,14 @@ const ERC20VaultAbi = require('../abi/ERC20Vault.json')
const EthPntAbi = require('../abi/ethPNT.json')
const FinanceAbi = require('../abi/Finance.json')
const VaultAbi = require('../abi/Vault.json')
const { PNETWORK_NETWORK_IDS, ZERO_ADDRESS, PNETWORK_ADDRESS } = require('../constants')
const { CHANGE_TOKEN_ROLE, CREATE_VOTES_ROLE, CREATE_PAYMENTS_ROLE, UPGRADE_ROLE } = require('../roles')
const { PNETWORK_NETWORK_IDS, ZERO_ADDRESS, PNETWORK_ADDRESS, ONE_DAY } = require('../constants')
const {
CHANGE_TOKEN_ROLE,
CREATE_VOTES_ROLE,
CREATE_PAYMENTS_ROLE,
UPGRADE_ROLE,
DEPOSIT_REWARD_ROLE
} = require('../roles')
const { encode } = require('../utils')
const { hardhatReset } = require('../utils/hardhat-reset')
const { sendEth } = require('../utils/send-eth')
Expand Down Expand Up @@ -58,18 +65,25 @@ const encodeFunctionCall = (_to, _calldata) => ({

const hasPermission = (acl, who, where, what) => acl['hasPermission(address,address,bytes32)'](who, where, what)

const setPermission = (acl, permissionManager, entity, app, role) =>
const grantPermission = (acl, permissionManager, entity, app, role) =>
acl.connect(permissionManager).grantPermission(entity, app, role)

const grantCreateVotesPermission = async (_acl, _permissionManager, _who) => {
let hasPerm = await hasPermission(_acl, _who, DANDELION_VOTING_ADDRESS, CREATE_VOTES_ROLE)
expect(hasPerm).to.be.false
await setPermission(_acl, _permissionManager, _who, DANDELION_VOTING_ADDRESS, CREATE_VOTES_ROLE)
await grantPermission(_acl, _permissionManager, _who, DANDELION_VOTING_ADDRESS, CREATE_VOTES_ROLE)
hasPerm = await hasPermission(_acl, _who, DANDELION_VOTING_ADDRESS, CREATE_VOTES_ROLE)
expect(hasPerm).to.be.true
}

const openNewVoteAndReachQuorum = async (_votingContract, _voteCreator, _voters, _executionScript, _metadata) => {
const openNewVoteAndReachQuorum = async (
_votingContract,
_voteCreator,
_voters,
_executionScript,
_metadata,
_durationBlocks = false
) => {
const supports = true
const executionScriptBytes = getBytes(_executionScript)

Expand All @@ -87,10 +101,10 @@ const openNewVoteAndReachQuorum = async (_votingContract, _voteCreator, _voters,
await expect(_votingContract.connect(voter).vote(voteId, supports)).to.emit(_votingContract, 'CastVote')
}
}

const vote = await _votingContract.getVote(voteId)
const executionBlock = vote[3]
await mineUpTo(executionBlock + 1n)
const executionTs = vote[3]
if (_durationBlocks) await mineUpTo(executionTs + 1n)
else await time.increaseTo(executionTs + 1n)
return voteId
}

Expand All @@ -114,7 +128,9 @@ describe('Integration tests on Gnosis deployment', () => {
registrationManager,
RegistrationManager,
daoTreasury,
finance
finance,
rewardsManager,
RewardsManager

const TOKEN_HOLDERS_ADDRESSES = [
'0xc4442915B1FB44972eE4D8404cE05a8D2A1248dA',
Expand All @@ -137,11 +153,14 @@ describe('Integration tests on Gnosis deployment', () => {
await stakingManagerRm.connect(daoOwner).grantRole(CHANGE_TOKEN_ROLE, SAFE_ADDRESS)
await lendingManager.connect(daoOwner).grantRole(CHANGE_TOKEN_ROLE, SAFE_ADDRESS)
await registrationManager.connect(daoOwner).grantRole(CHANGE_TOKEN_ROLE, SAFE_ADDRESS)
await rewardsManager.connect(daoOwner).grantRole(CHANGE_TOKEN_ROLE, SAFE_ADDRESS)
await stakingManager.connect(daoOwner).changeToken(await pntOnGnosis.getAddress())
await stakingManagerLm.connect(daoOwner).changeToken(await pntOnGnosis.getAddress())
await stakingManagerRm.connect(daoOwner).changeToken(await pntOnGnosis.getAddress())
await lendingManager.connect(daoOwner).changeToken(await pntOnGnosis.getAddress())
await registrationManager.connect(daoOwner).changeToken(await pntOnGnosis.getAddress())
await rewardsManager.connect(daoOwner).changeToken(await pntOnGnosis.getAddress())
await rewardsManager.connect(daoOwner).grantRole(DEPOSIT_REWARD_ROLE, DANDELION_VOTING_ADDRESS)
}

const upgradeContracts = async () => {
Expand All @@ -150,11 +169,13 @@ describe('Integration tests on Gnosis deployment', () => {
await stakingManagerRm.connect(daoOwner).grantRole(UPGRADE_ROLE, faucet.address)
await lendingManager.connect(daoOwner).grantRole(UPGRADE_ROLE, faucet.address)
await registrationManager.connect(daoOwner).grantRole(UPGRADE_ROLE, faucet.address)
await rewardsManager.connect(daoOwner).grantRole(UPGRADE_ROLE, faucet.address)
await hre.upgrades.upgradeProxy(stakingManager, StakingManager)
await hre.upgrades.upgradeProxy(stakingManagerLm, StakingManagerPermissioned)
await hre.upgrades.upgradeProxy(stakingManagerRm, StakingManagerPermissioned)
await hre.upgrades.upgradeProxy(lendingManager, LendingManager)
await hre.upgrades.upgradeProxy(registrationManager, RegistrationManager)
await hre.upgrades.upgradeProxy(rewardsManager, RewardsManager)
}

beforeEach(async () => {
Expand All @@ -172,6 +193,7 @@ describe('Integration tests on Gnosis deployment', () => {
StakingManagerPermissioned = await hre.ethers.getContractFactory('StakingManagerPermissioned')
RegistrationManager = await hre.ethers.getContractFactory('RegistrationManager')
LendingManager = await hre.ethers.getContractFactory('LendingManager')
RewardsManager = await hre.ethers.getContractFactory('RewardsManager')

acl = await hre.ethers.getContractAt(AclAbi, ACL_ADDRESS)
daoVoting = await hre.ethers.getContractAt(DandelionVotingAbi, DANDELION_VOTING_ADDRESS)
Expand All @@ -183,6 +205,7 @@ describe('Integration tests on Gnosis deployment', () => {
stakingManagerRm = StakingManagerPermissioned.attach(STAKING_MANAGER_RM)
registrationManager = RegistrationManager.attach(REGISTRATION_MANAGER)
lendingManager = LendingManager.attach(LENDING_MANAGER)
rewardsManager = RewardsManager.attach(REWARDS_MANAGER)

await missingSteps()

Expand Down Expand Up @@ -338,7 +361,7 @@ describe('Integration tests on Gnosis deployment', () => {
})

it('should create an immediate payment via finance app', async () => {
await setPermission(acl, daoOwner, faucet.address, await finance.getAddress(), CREATE_PAYMENTS_ROLE)
await grantPermission(acl, daoOwner, faucet.address, await finance.getAddress(), CREATE_PAYMENTS_ROLE)
const amount = parseEther('1.5')
await mintPntOnGnosis(await daoTreasury.getAddress(), parseEther('200000'))
expect(await pntOnGnosis.balanceOf(await daoTreasury.getAddress())).to.be.eq(parseEther('200000'))
Expand All @@ -355,7 +378,7 @@ describe('Integration tests on Gnosis deployment', () => {
})

it('should open a vote (1)', async () => {
await setPermission(acl, daoOwner, user.address, await daoVoting.getAddress(), CREATE_VOTES_ROLE)
await grantPermission(acl, daoOwner, user.address, await daoVoting.getAddress(), CREATE_VOTES_ROLE)
await expect(
user.sendTransaction({
to: '0x0cf759bcCfEf5f322af58ADaE2D28885658B5e02',
Expand All @@ -373,7 +396,6 @@ describe('Integration tests on Gnosis deployment', () => {
const RECEIVER = FORWARDER_ETH
const ETH_PTN_ADDRESS = ETHPNT_ADDRESS

const voteId = 1
const metadata = 'Should we inflate more?'

const userData = encode(
Expand Down Expand Up @@ -414,7 +436,7 @@ describe('Integration tests on Gnosis deployment', () => {
currentBlock = await hre.ethers.provider.getBlockNumber()
expect(await daoPNT.totalSupplyAt(currentBlock)).to.be.eq(30000)
await grantCreateVotesPermission(acl, daoOwner, tokenHolders[0].address)
await openNewVoteAndReachQuorum(daoVoting, tokenHolders[0], tokenHolders, executionScript, metadata)
const voteId = await openNewVoteAndReachQuorum(daoVoting, tokenHolders[0], tokenHolders, executionScript, metadata)
await expect(daoVoting.executeVote(voteId))
.to.emit(daoVoting, 'ExecuteVote')
.withArgs(voteId)
Expand All @@ -433,6 +455,66 @@ describe('Integration tests on Gnosis deployment', () => {
it('should be possible to pegin to finance vault', async () => {
await mintPntOnGnosis(FINANCE_VAULT, '10', '0xc0ffee')
})

it('should be possible to deposit rewards from a vote', async () => {
const metadata = 'Should we deposit rewards?'
await mintPntOnGnosis(await daoTreasury.getAddress(), parseEther('200000'))
const executionScript = encodeCallScript(
[
[FINANCE_VAULT, encodeVaultTransfer(await pntOnGnosis.getAddress(), DANDELION_VOTING_ADDRESS, 100)],
[await pntOnGnosis.getAddress(), pntOnGnosis.interface.encodeFunctionData('approve', [REWARDS_MANAGER, 100])],
[REWARDS_MANAGER, rewardsManager.interface.encodeFunctionData('depositForEpoch', [2, 100])]
].map((_args) => encodeFunctionCall(..._args))
)
await grantCreateVotesPermission(acl, daoOwner, tokenHolders[0].address)
const voteId = await openNewVoteAndReachQuorum(daoVoting, tokenHolders[0], tokenHolders, executionScript, metadata)
await expect(daoVoting.executeVote(voteId)).to.emit(daoVoting, 'ExecuteVote').withArgs(voteId)
await expect(rewardsManager.registerRewardsForEpoch(2, [tokenHolders[1].address])).to.be.reverted
await time.increase(35 * ONE_DAY)
await expect(rewardsManager.registerRewardsForEpoch(2, [tokenHolders[1].address]))
.to.emit(rewardsManager, 'RewardRegistered')
.withArgs(2, tokenHolders[1].address, 25)
await time.increase(130 * ONE_DAY)
await expect(rewardsManager.registerRewardsForEpoch(2, [tokenHolders[0].address, tokenHolders[3].address]))
.to.emit(rewardsManager, 'RewardRegistered')
.withArgs(2, tokenHolders[0].address, 25)
.and.to.emit(rewardsManager, 'RewardRegistered')
.withArgs(2, tokenHolders[3].address, 25)
await expect(rewardsManager.connect(tokenHolders[0]).claimRewardByEpoch(2)).to.be.revertedWithCustomError(
rewardsManager,
'TooEarly'
)
await expect(rewardsManager.connect(tokenHolders[1]).claimRewardByEpoch(2)).to.be.revertedWithCustomError(
rewardsManager,
'TooEarly'
)
await expect(rewardsManager.connect(tokenHolders[3]).claimRewardByEpoch(2)).to.be.revertedWithCustomError(
rewardsManager,
'TooEarly'
)

await time.increase(200 * ONE_DAY)

const claimRewardsAndAssertTransfer = (_holder, _epoch) =>
expect(rewardsManager.connect(_holder).claimRewardByEpoch(_epoch))
.to.emit(pntOnGnosis, 'Transfer')
.withArgs(REWARDS_MANAGER, _holder.address, 25)
.and.to.emit(daoPNT, 'Transfer')
.withArgs(_holder, ZERO_ADDRESS, 25)

await claimRewardsAndAssertTransfer(tokenHolders[0], 2)
await claimRewardsAndAssertTransfer(tokenHolders[1], 2)
await claimRewardsAndAssertTransfer(tokenHolders[3], 2)

await expect(rewardsManager.connect(tokenHolders[2]).claimRewardByEpoch(2)).to.be.revertedWithCustomError(
rewardsManager,
'NothingToClaim'
)
await expect(rewardsManager.connect(tokenHolders[2]).registerRewardsForEpoch(2, [tokenHolders[2]]))
.to.emit(rewardsManager, 'RewardRegistered')
.withArgs(2, tokenHolders[2].address, 25)
await claimRewardsAndAssertTransfer(tokenHolders[2], 2)
})
})

describe('Integration tests on Ethereum deployment', () => {
Expand Down Expand Up @@ -465,7 +547,8 @@ describe('Integration tests on Ethereum deployment', () => {
association,
tokenHolders,
executionScript,
'change inflation owner?'
'change inflation owner?',
true
)
expect(await ethPnt.inflationRecipientsWhitelist(await crossExecutor.getAddress())).to.be.false
await expect(daoVotingV1.executeVote(voteId))
Expand Down

0 comments on commit abae978

Please sign in to comment.