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

Test deployment of RewardsAggregator contract #157

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
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: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
ETHERSCAN_TOKEN=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
MAINNET_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
FORKING_URL=https://eth-mainnet.g.alchemy.com/v2/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
44 changes: 44 additions & 0 deletions contracts/test/TACoRewardsDispenserMock.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// SPDX-License-Identifier: AGPL-3.0-or-later

pragma solidity ^0.8.0;

// TODO: This is the original contract, we need to mock it.
contract TACoRewardsDispenser {
uint256 public constant REWARDS_CALCULATION_BASE = 10000;
uint256 public constant ONE_YEAR = 365 * 1 days;

IERC20 public token;
IProxy public claimableRewards; // TODO: what interface to use for claimabeRewards contract?
IApplication public tacoApplication;
// Rewards APY expressed wrt REWARDS_CALCULATION_BASE (e.g. 5% = 500)
uint256 public rewardsAPY;

constructor(IERC20 _token, IProxy _claimableRewards, IApplication _tacoApplication, uint256 _rewardsAPY) {
// TODO: we need some checks here using "require"
token = _token;
claimableRewards = _claimableRewards;
tacoApplication = _tacoApplication;
rewardsAPY = _rewardsAPY;
}

function dispenseRewardsForCycle() external {
// This function can only be called once per rewards cycle, so it can be permissionless.
uint256 periodFinish = tacoApplication.periodFinish();
require(block.timestamp >= periodFinish);

// 1. Calculate rewards for this cycle
uint256 rewardCycleDuration = tacoApplication.rewardDuration();
uint256 authorizedOverall = tacoApplication.authorizedOverall();

uint256 rewardsForCycle = authorizedOverall * rewardsAPY * rewardCycleDuration / ONE_YEAR / REWARDS_CALCULATION_BASE;

// 2. Get rewards from ClaimableRewards (or FutureRewards?)
token.safeTransferFrom(claimableRewards, address(this), rewardsForCycle);

// 3. Approve (invariant: before and after this TX this approval is always 0)
token.approve(tacoApplication, rewardsForCycle);

// 4. Push rewards for cycle
tacoApplication.pushReward(rewardsForCycle);
}
}
35 changes: 0 additions & 35 deletions deploy_test/deploy.js

This file was deleted.

18 changes: 12 additions & 6 deletions hardhat.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,28 +20,34 @@ module.exports = {
},
namedAccounts: {
deployer: {
hardhat: 0,
sepolia: 0,
mainnet: 0,
mainnet_test: 0,
},
rewardsHolder: {
sepolia: "0xCe692F6fA86319Af43050fB7F09FDC43319F7612",
mainnet: "0x9F6e831c8F8939DC0C830C6e492e7cEf4f9C2F5f",
mainnet_test: 0,
merkleRewardsHolder: {
hardhat: "0xec8183891331a845E2DCf5CD616Ee32736E2BAA4",
mainnet: "0xec8183891331a845E2DCf5CD616Ee32736E2BAA4",
},
tokenContract: {
hardhat: "0xCdF7028ceAB81fA0C6971208e83fa7872994beE5",
sepolia: "0x46abDF5aD1726ba700794539C3dB8fE591854729",
mainnet: "0xCdF7028ceAB81fA0C6971208e83fa7872994beE5",
},
// TODO: check if council is the right owner
owner: {
hardhat: "0x9F6e831c8F8939DC0C830C6e492e7cEf4f9C2F5f",
sepolia: 0,
mainnet: "0x9F6e831c8F8939DC0C830C6e492e7cEf4f9C2F5f",
mainnet_test: 0,
},
tacoApp: {
hardhat: "0x347CC7ede7e5517bD47D20620B2CF1b406edcF07",
sepolia: "0x329bc9Df0e45f360583374726ccaFF003264a136",
mainnet: "0x347CC7ede7e5517bD47D20620B2CF1b406edcF07",
},
oldCumulativeMerkleDrop: {
hardhat: "0xeA7CA290c7811d1cC2e79f8d706bD05d8280BD37",
mainnet: "0xeA7CA290c7811d1cC2e79f8d706bD05d8280BD37",
},
},
networks,
}
10 changes: 1 addition & 9 deletions hardhat.networks.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,6 @@ register(
"mainnet",
process.env.ETHERSCAN_TOKEN
)
register(
"mainnet_test",
["deploy_test"],
1,
process.env.MAINNET_RPC_URL,
process.env.MAINNET_PRIVATE_KEY,
"mainnet",
process.env.ETHERSCAN_TOKEN
)
register(
"sepolia",
["deploy"],
Expand All @@ -56,6 +47,7 @@ networks["hardhat"] = {
forking: {
enabled: !!process.env.FORKING_URL,
url: process.env.FORKING_URL || "",
blockNumber: 20775560,
},
}

Expand Down
14 changes: 12 additions & 2 deletions test/CumulativeMerkle.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const { ethers } = require("hardhat")
const { ethers, network } = require("hardhat")
const { expect } = require("chai")
const { describe, it, before, beforeEach } = require("mocha")
const { after, describe, it, before, beforeEach } = require("mocha")
const { MerkleTree } = require("merkletreejs")
const fc = require("fast-check")
const keccak256 = require("keccak256")
Expand All @@ -10,14 +10,24 @@ const { dist } = require("./constants")
const { cumDist } = require("./constants")

describe("Cumulative Merkle Distribution", function () {
const forking = network.config.forking

let token

before(function () {
// Disabling mainnet fork in case it is enabled to make tests faster
network.config.forking = undefined

// numRuns must be less or equal to the number of accounts in `dist`
const numRuns = Object.keys(dist.claims).length
fc.configureGlobal({ numRuns: numRuns, skipEqualValues: true })
})

after(function() {
// Restoring mainnet fork configuration for subsequent tests
network.config.forking = forking
})

beforeEach(async function () {
const Token = await ethers.getContractFactory("TokenMock")
token = await Token.deploy()
Expand Down
121 changes: 121 additions & 0 deletions test/ForkDeployment.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
const { getNamedAccounts, network, ethers } = require("hardhat")
const helpers = require("@nomicfoundation/hardhat-network-helpers")
const { before, describe, it } = require("mocha")
const { expect } = require("chai")

const { getLastMerkleClaim } = require("./utils")

describe("Deployment of RewardsAggregator using mainnet network fork", function () {
before(function (done) {
if ((network.config.forking && network.config.forking.enabled) !== true) {
done(
new Error("Ethereum mainnet not being forked. FORKING_URL env var set?")
)
} else {
done()
}
})

it("should be deployed RewardsAggregator", async function () {
const {
tokenContract: tokenAddr,
tacoApp: tacoAppAddr,
oldCumulativeMerkleDrop: oldCumulativeMerkleDropAddr,
merkleRewardsHolder: merkleRewardsHolderAddr,
owner: ownerAddr,
} = await getNamedAccounts()

// This staking prov is going to be used for tests. It has merkle-claimed
// in the past and also it has remaining tokens to merkle-claim
const stakingProv = "0x0154C52ec5b6a3010758dDe78079589E67526767"
const claim = getLastMerkleClaim(stakingProv)

const tokenContract = await ethers.getContractAt("TokenMock", tokenAddr)

const RewardsAggregator = await ethers.getContractFactory(
"RewardsAggregator"
)
// Step1: deployment of the contract
const rewardsAggregator = await RewardsAggregator.deploy(
tokenAddr,
tacoAppAddr,
oldCumulativeMerkleDropAddr,
merkleRewardsHolderAddr,
ownerAddr
)

const ownerSigner = await ethers.getSigner(ownerAddr)
const merkleRewardsHolderSigner = await ethers.getSigner(
merkleRewardsHolderAddr
)

await helpers.setBalance(ownerAddr, 10n ** 18n)
await helpers.setBalance(merkleRewardsHolderAddr, 10n ** 18n)
// Step2: set the Merkle root
await rewardsAggregator.connect(ownerSigner).setMerkleRoot(claim.merkleRoot)

// Step3: set ClaimableRewards contract allowance so RewardsAggregator can
// pull tokens and to push them to the stake beneficiary. The old Merkle
// contract allowance must be set to 0 and the same amount must be set in
// the new RewardsAggregator contract.
// await tokenContract.connect(merkleRewardsHolder)
await tokenContract
.connect(merkleRewardsHolderSigner)
.approve(rewardsAggregator.address, 1000000n * 10n ** 18n)

// Check if the contract was successfully deployed
expect(await rewardsAggregator.owner()).to.equal(ownerAddr)
expect(await rewardsAggregator.token()).to.equal(tokenAddr)
expect(await rewardsAggregator.application()).to.equal(tacoAppAddr)
expect(await rewardsAggregator.merkleRoot()).to.equal(claim.merkleRoot)
expect(await rewardsAggregator.merkleRewardsHolder()).to.equal(
merkleRewardsHolderAddr
)
expect(await rewardsAggregator.oldCumulativeMerkleDrop()).to.equal(
oldCumulativeMerkleDropAddr
)

const claimed = await rewardsAggregator.cumulativeMerkleClaimed(stakingProv)
expect(claimed).to.be.greaterThan(ethers.BigNumber.from(0))

// Let's take a snapshot of the current state of the blockchain
const snapshot = await helpers.takeSnapshot()

// Check if it is possible to claim Merkle rewards
const prevBalance = await tokenContract.balanceOf(claim.beneficiary)
const toBeClaimed = ethers.BigNumber.from(claim.amount).sub(claimed)
const expectedBalance = prevBalance.add(toBeClaimed)

await rewardsAggregator.claimMerkle(
stakingProv,
claim.beneficiary,
claim.amount,
claim.merkleRoot,
claim.proof
)

expect(expectedBalance).to.equal(
await tokenContract.balanceOf(claim.beneficiary)
)

// should not be possible to get the same Merkle claim twice
await expect(
rewardsAggregator.claimMerkle(
stakingProv,
claim.beneficiary,
claim.amount,
claim.merkleRoot,
claim.proof
)
).to.be.revertedWith("Nothing to claim")

await snapshot.restore()

// TODO: test a TACoApp claim. It is necessary to create a mock for
// TACoRewardsDispenser. This mock only has to send tokens to
// TACoApplication (it is supossed to be the computed amount of rewards
// earned by the stakes)

// TODO: test a total claim (Merkle + TACoApp)
})
})
Loading