Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DAO-386: Add test coverage to veRIF contract #3

Merged
merged 9 commits into from
Jun 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .prettierrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"files": "*.sol",
"options": {
"singleQuote": false,
"explicitTypes": "always"
"explicitTypes": "always",
"bracketSpacing": false
}
}
]
Expand Down
1 change: 0 additions & 1 deletion .solhint.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"extends": "solhint:recommended",
"rules": {
"no-global-import": "off",
"func-visibility": ["warn", { "ignoreConstructors": true }]
}
}
15 changes: 8 additions & 7 deletions contracts/VeRIFToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@
// Compatible with OpenZeppelin Contracts ^5.0.0
pragma solidity ^0.8.20;

import "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20WrapperUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {ERC20Upgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol";
import {ERC20PermitUpgradeable, NoncesUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol";
import {ERC20VotesUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20VotesUpgradeable.sol";
import {ERC20WrapperUpgradeable} from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20WrapperUpgradeable.sol";
import {OwnableUpgradeable} from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import {UUPSUpgradeable} from "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";
import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";

contract VeRIFToken is
Initializable,
Expand Down
5 changes: 3 additions & 2 deletions scripts/deploy-verif.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { ethers, upgrades } from 'hardhat'
import { VeRIFToken } from '../typechain-types'

export const deployVeRif = async (rifTokenAddress: string, deployerAddress: string) => {
const VeRIFTokenFactory = await ethers.getContractFactory('VeRIFToken')
const veRIFToken = await upgrades.deployProxy(VeRIFTokenFactory, [rifTokenAddress, deployerAddress], {
const veRIFToken = (await upgrades.deployProxy(VeRIFTokenFactory, [rifTokenAddress, deployerAddress], {
initializer: 'initialize',
kind: 'uups',
timeout: 0, // wait indefinitely
unsafeAllow: ['internal-function-storage'],
})
}) as unknown as VeRIFToken)

return await veRIFToken.waitForDeployment()
}
198 changes: 198 additions & 0 deletions test/VeRIFToken.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,198 @@
import { SignerWithAddress } from '@nomicfoundation/hardhat-ethers/signers'
import { loadFixture } from '@nomicfoundation/hardhat-toolbox/network-helpers'
import { expect } from 'chai'
import { ethers } from 'hardhat'
import { deployVeRif } from '../scripts/deploy-verif'
import { RIFToken, VeRIFToken } from '../typechain-types'

describe('VeRIFToken', () => {
let owner: SignerWithAddress, holder: SignerWithAddress, voter: SignerWithAddress
let rif: RIFToken
let veRIF: VeRIFToken
const votingPower = 10n * 10n ** 18n

const deployRif = () => ethers.deployContract('RIFToken')
const deployWrappedRif = async () => deployVeRif(await rif.getAddress(), owner.address)

const deploy = async () => {
rif = await loadFixture(deployRif)
veRIF = await loadFixture(deployWrappedRif)
}

before(async () => {
;[owner, holder, voter] = await ethers.getSigners()
await deploy()
})

it('Should assign the initial balance to the contract itself', async () => {
const contractBalance = await rif.balanceOf(rif)
expect(contractBalance).to.equal(ethers.parseUnits('1000000000', 18))
})

describe('Wrapping RIF tokens to veRIF', () => {
it('holder should NOT initially own RIF tokens', async () => {
expect(await rif.balanceOf(holder.address)).to.equal(0)
})

it('should transfer all RIF tokens to deployer and close distribution', async () => {
await rif.setAuthorizedManagerContract(owner)
expect(await rif.balanceOf(owner.address)).to.equal(ethers.parseUnits('1000000000', 18))

const latestBlock = await ethers.provider.getBlock('latest')
if (!latestBlock) throw new Error('latest block not found')
await rif.closeTokenDistribution(latestBlock.timestamp)
expect(await rif.distributionTime()).to.not.be.equal(0)
})

it("owner should send some RIFs to holder's address", async () => {
const tx = await rif.transfer(holder.address, votingPower)
await tx.wait()
expect(tx)
.to.emit(rif, 'Transfer')
.withArgs(await rif.getAddress(), holder.address, votingPower)
})

it('holder should approve allowance for veRIF', async () => {
const tx = await rif.connect(holder).approve(veRIF.getAddress(), votingPower)
await tx.wait()
expect(tx).to.emit(rif, 'Approval').withArgs(holder.address, veRIF.getAddress(), votingPower)
})

it('allowance for veRIF should be set on the RIF token', async () => {
expect(await rif.allowance(holder.address, veRIF.getAddress())).to.equal(votingPower)
})

it('veRIF should NOT have any RIF tokens on its balance', async () => {
expect(await rif.balanceOf(veRIF.getAddress())).to.equal(0)
})

it('holder should NOT have any veRIF tokens on his balance', async () => {
expect(await veRIF.balanceOf(holder.address)).to.equal(0)
})

/** depositFor is a method for minting veRIF tokens */
it('holder should deposit underlying tokens and mint the corresponding amount of veRIF tokens', async () => {
await expect(veRIF.connect(holder).depositFor(holder.address, votingPower))
.to.emit(veRIF, 'Transfer')
.withArgs(ethers.ZeroAddress, holder.address, votingPower)
})

it('holder should NOT have RIF tokens anymore', async () => {
expect(await rif.balanceOf(holder.address)).to.equal(0)
})

it('veRIF now should own RIFs belonged to the holder', async () => {
expect(await rif.balanceOf(veRIF.getAddress())).to.equal(votingPower)
})

it('holder should have the same amount of veRIF tokens as the deposited RIF tokens', async () => {
expect(await veRIF.balanceOf(holder.address)).to.equal(votingPower)
})

it('holder should NOT be able to deposit more RIF tokens than he has', async () => {
await expect(veRIF.connect(holder).depositFor(holder.address, votingPower)).to.be.reverted
})

/** delegate */
it('holder should NOT have vote power yet', async () => {
expect(await veRIF.getVotes(holder.address)).to.equal(0)
})

it('holder should delegate vote power to himself', async () => {
const tx = await veRIF.connect(holder).delegate(holder.address)
await expect(tx)
.to.emit(veRIF, 'DelegateChanged')
.withArgs(holder.address, ethers.ZeroAddress, holder.address)
})

it('holder should now have delegate set', async () => {
expect(await veRIF.delegates(holder.address)).to.equal(holder.address)
})

it('holder should have vote power', async () => {
expect(await veRIF.getVotes(holder.address)).to.equal(votingPower)
})
})

describe('Unwrapping RIF tokens from veRIF tokens', () => {
/** withdrawTo is a method for burning veRIF tokens */
it('holder should burn veRIF tokens', async () => {
const tx = veRIF.connect(holder).withdrawTo(holder.address, votingPower)
await expect(tx).to.emit(veRIF, 'Transfer').withArgs(holder.address, ethers.ZeroAddress, votingPower)
})

it('holder should no longer own veRIF tokens', async () => {
expect(await veRIF.balanceOf(holder.address)).to.equal(0)
})

it('holder should return his RIFs back', async () => {
expect(await rif.balanceOf(holder.address)).to.equal(votingPower)
})

it('veRIF should no longer own RIFs', async () => {
expect(await rif.balanceOf(await veRIF.getAddress())).to.equal(0)
})

it('veRIF should no longer have allowance for RIFs from the holder', async () => {
expect(await rif.allowance(holder.address, await veRIF.getAddress())).to.equal(0)
})

it('holder should still have the delegate set', async () => {
expect(await veRIF.delegates(holder.address)).to.equal(holder.address)
})

it('holder should no longer have voting power', async () => {
const vp = await veRIF.getVotes(holder.address)
expect(vp).to.equal(0)
})
})

describe('Delegating voting power to a voter address', () => {
it('should already have 2 checkpoints because of delegation and burning operations', async () => {
const numCheckpoints = await veRIF.numCheckpoints(holder.address)
expect(numCheckpoints).to.equal(2)
})

it('holder should mint veRIF again', async () => {
;(await rif.connect(holder).approve(await veRIF.getAddress(), votingPower)).wait()
await expect(veRIF.connect(holder).depositFor(holder.address, votingPower))
.to.emit(veRIF, 'Transfer')
.withArgs(ethers.ZeroAddress, holder.address, votingPower)
const checkPoint2 = await veRIF.checkpoints(holder.address, 2)
expect(checkPoint2._value).to.equal(votingPower)
})

it('should have 3 checkpoints now', async () => {
const numCheckpoints = await veRIF.numCheckpoints(holder.address)
expect(numCheckpoints).to.equal(3)
})

it('holder should still be delegated to vote (from the previous time)', async () => {
expect(await veRIF.delegates(holder.address)).to.equal(holder.address)
})

it('holder should already have vote power', async () => {
expect(await veRIF.getVotes(holder.address)).to.equal(votingPower)
})

it('holder should delegate his voting power to the voter (another address)', async () => {
const tx = veRIF.connect(holder).delegate(voter.address)
await expect(tx)
.to.emit(veRIF, 'DelegateChanged')
.withArgs(holder.address, holder.address, voter.address)
})

it('should have 4 checkpoints now', async () => {
const numCheckpoints = await veRIF.numCheckpoints(holder.address)
expect(numCheckpoints).to.equal(4)
})

it('holder should NOT have vote power any more', async () => {
expect(await veRIF.getVotes(holder.address)).to.equal(0)
})

it("voter should now have holder's voting power", async () => {
expect(await veRIF.getVotes(voter.address)).to.equal(votingPower)
})
})
})
Loading