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

High severity #3

Merged
merged 4 commits into from Jun 11, 2023
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
43 changes: 34 additions & 9 deletions contracts/GaugeExtraRewarder.sol
Expand Up @@ -54,7 +54,7 @@ contract GaugeExtraRewarder is Ownable {
uint public ACC_TOKEN_PRECISION = 1e12;


address private immutable GAUGE;
address private GAUGE;

event LogOnReward(address indexed user, uint256 indexed pid, uint256 amount, address indexed to);

Expand All @@ -69,31 +69,43 @@ contract GaugeExtraRewarder is Ownable {


function onReward(uint256 /*pid*/, address _user, address to, uint256 /*extraData*/, uint256 lpToken) onlyGauge external {
if(stop) return;
if(stop){
return;
}
PoolInfo memory pool = updatePool();
UserInfo storage user = userInfo[_user];
uint256 pending;
uint256 accRewardPerShare = pool.accRewardPerShare;
if (user.amount > 0) {
pending = int256( user.amount.mul(accRewardPerShare) / ACC_TOKEN_PRECISION ).sub(user.rewardDebt).toUInt256();
pending = _pendingReward(_user);
rewardToken.safeTransfer(to, pending);
}
user.amount = lpToken;
user.rewardDebt = int256(lpToken.mul(pool.accRewardPerShare) / ACC_TOKEN_PRECISION);
}


/// @notice View function to see pending WBNB on frontend.
/// @notice View function to see pending Rewards on frontend.
/// @param _user Address of user.
/// @return pending rewardToken reward for a given user.
function pendingReward(address _user) external view returns (uint256 pending){
function pendingReward(address _user) public view returns (uint256 pending){
pending = _pendingReward(_user);
}
function _pendingReward(address _user) internal view returns(uint256 pending){
PoolInfo memory pool = poolInfo;
UserInfo storage user = userInfo[_user];
uint256 accRewardPerShare = pool.accRewardPerShare;
uint256 lpSupply = IERC20(IGauge(GAUGE).TOKEN()).balanceOf(GAUGE);

if (block.timestamp > pool.lastRewardTime && lpSupply != 0) {
uint256 time = block.timestamp.sub(pool.lastRewardTime);
// if we reach the end, look for the missing seconds up to LastDistributedTime ; else use block.timestamp
uint _tempTimestamp;
if( block.timestamp >= lastDistributedTime){
// if lastRewardTime is > than LastDistributedTime then set tempTimestamp to 0 to avoid underflow
_tempTimestamp = pool.lastRewardTime > lastDistributedTime ? 0 : lastDistributedTime.sub(pool.lastRewardTime);
} else {
_tempTimestamp = block.timestamp.sub(pool.lastRewardTime);
}
uint256 time = _tempTimestamp;
uint256 reward = time.mul(rewardPerSecond);
accRewardPerShare = accRewardPerShare.add( reward.mul(ACC_TOKEN_PRECISION) / lpSupply );
}
Expand Down Expand Up @@ -137,11 +149,19 @@ contract GaugeExtraRewarder is Ownable {
/// @return pool Returns the pool that was updated.
function updatePool() public returns (PoolInfo memory pool) {
pool = poolInfo;

if (block.timestamp > pool.lastRewardTime) {
uint256 lpSupply = IERC20(IGauge(GAUGE).TOKEN()).balanceOf(GAUGE);
if (lpSupply > 0) {
uint256 time = block.timestamp.sub(pool.lastRewardTime);
// if we reach the end, look for the missing seconds up to LastDistributedTime ; else use block.timestamp
uint _tempTimestamp;
if( block.timestamp >= lastDistributedTime){
// if lastRewardTime is > than LastDistributedTime then set tempTimestamp to 0 to avoid underflow
_tempTimestamp = pool.lastRewardTime > lastDistributedTime ? 0 : lastDistributedTime.sub(pool.lastRewardTime);
} else {
_tempTimestamp = block.timestamp.sub(pool.lastRewardTime);
}

uint256 time = _tempTimestamp;
uint256 reward = time.mul(rewardPerSecond);
pool.accRewardPerShare = pool.accRewardPerShare.add( reward.mul(ACC_TOKEN_PRECISION).div(lpSupply) );
}
Expand All @@ -166,5 +186,10 @@ contract GaugeExtraRewarder is Ownable {
stop = false;
}

function _gauge() external view returns(address){
return GAUGE;
}



}
2 changes: 1 addition & 1 deletion contracts/GaugeV2.sol
Expand Up @@ -294,7 +294,7 @@ contract GaugeV2 is ReentrancyGuard, Ownable {
}
}

///@notice User harvest function
///@notice User harvest function
function getReward() public nonReentrant updateReward(msg.sender) {
uint256 reward = rewards[msg.sender];
if (reward > 0) {
Expand Down
31 changes: 8 additions & 23 deletions contracts/VoterV3.sol
Expand Up @@ -299,6 +299,9 @@ contract VoterV3 is IVoter, OwnableUpgradeable, ReentrancyGuardUpgradeable {
require(isAlive[_gauge], "gauge already dead");
isAlive[_gauge] = false;
claimable[_gauge] = 0;
uint _time = _epochTimestamp();
totWeightsPerEpoch[_time] -= weightsPerEpoch[_time][poolForGauge[_gauge]];

emit GaugeKilled(_gauge);
}

Expand All @@ -310,27 +313,7 @@ contract VoterV3 is IVoter, OwnableUpgradeable, ReentrancyGuardUpgradeable {
isAlive[_gauge] = true;
emit GaugeRevived(_gauge);
}

/// @notice Kill a malicious gauge completly
/// @param _gauge gauge to kill
function killGaugeTotally(address _gauge) external Governance {
require(isAlive[_gauge], "gauge already dead");

delete isAlive[_gauge];
delete internal_bribes[_gauge];
delete external_bribes[_gauge];
delete poolForGauge[_gauge];
delete isGauge[_gauge];
delete claimable[_gauge];
delete supplyIndex[_gauge];

address _pool = poolForGauge[_gauge];
gauges[_pool] = address(0);


emit GaugeKilled(_gauge);
}


/* -----------------------------------------------------------------------------
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------
Expand Down Expand Up @@ -370,7 +353,9 @@ contract VoterV3 is IVoter, OwnableUpgradeable, ReentrancyGuardUpgradeable {
if (_votes > 0) {
IBribe(internal_bribes[gauges[_pool]])._withdraw(uint256(_votes), _tokenId);
IBribe(external_bribes[gauges[_pool]])._withdraw(uint256(_votes), _tokenId);
_totalWeight += _votes;

// if is alive remove _votes, else don't because we already done it in killGauge()
if(isAlive[gauges[_pool]]) _totalWeight += _votes;
}

emit Abstained(_tokenId, _votes);
Expand Down Expand Up @@ -432,7 +417,7 @@ contract VoterV3 is IVoter, OwnableUpgradeable, ReentrancyGuardUpgradeable {
address _pool = _poolVote[i];
address _gauge = gauges[_pool];

if (isGauge[_gauge]) {
if (isGauge[_gauge] && isAlive[_gauge]) {
uint256 _poolWeight = _weights[i] * _weight / _totalVoteWeight;
require(votes[_tokenId][_pool] == 0);
require(_poolWeight != 0);
Expand Down
128 changes: 128 additions & 0 deletions test/TestAudit/m01_gaugeExtraReward.js
@@ -0,0 +1,128 @@


const { expect } = require("chai");
const { erc20Abi } = require("../Abi.js")
const { ethers } = require("hardhat");


describe("test rewarder", function () {

beforeEach(async () => {
//await ethers.provider.send('evm_increaseTime', [5]);
//await ethers.provider.send('evm_mine');

const blockNumBefore = await ethers.provider.getBlockNumber();
const blockBefore = await ethers.provider.getBlock(blockNumBefore);
timestampBefore = blockBefore.timestamp;
});

it("Deploy contract", async function () {
accounts = await ethers.getSigners();
owner = accounts[0]


usdc = await ethers.getContractAt(erc20Abi, "0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d")
usdt = await ethers.getContractAt(erc20Abi, "0x55d398326f99059fF775485246999027B3197955")

user = ethers.utils.getAddress("0x993Ae2b514677c7AC52bAeCd8871d2b362A9D693")
depositor = ethers.utils.getAddress("0x8894E0a0c962CB723c1976a4421c95949bE2D4E3")


// deploy gauge
data = await ethers.getContractFactory("GaugeV2");
// we care only of _token variable to let the ExtraRewarder read the total balance
GaugeV2 = await data.deploy(usdc.address, usdc.address, usdt.address, owner.address, owner.address, owner.address, true);
txDeployed = await GaugeV2.deployed();
console.log("GaugeV2: ", GaugeV2.address)


// deploy contract
data = await ethers.getContractFactory("GaugeExtraRewarder");
GaugeExtraRewarder = await data.deploy(usdc.address, GaugeV2.address);
txDeployed = await GaugeExtraRewarder.deployed();
console.log("GaugeExtraRewarder: ", GaugeExtraRewarder.address)
});

it("Set contracts ", async function () {
// transfer ownership to depositor and set gaugerewarder to gauge
await GaugeV2.setGaugeRewarder(GaugeExtraRewarder.address)
await GaugeExtraRewarder.transferOwnership(depositor)


// send 1k usdc as reward
const rewardAmount = ethers.utils.parseEther("1000")
await hre.network.provider.request({method: "hardhat_impersonateAccount",params: [depositor]});
signer = await ethers.getSigner(depositor)
await usdc.connect(signer).transfer(GaugeExtraRewarder.address, rewardAmount)
await usdt.connect(signer).transfer(user, rewardAmount)
await GaugeExtraRewarder.connect(signer).setDistributionRate(rewardAmount)
await hre.network.provider.request({method: "hardhat_stopImpersonatingAccount",params: [depositor]});
console.log("Reward Per Second: ", await GaugeExtraRewarder.rewardPerSecond())
console.log("lastDistributedTime: ", await GaugeExtraRewarder.lastDistributedTime())

});

it("User interaction", async function () {

await hre.network.provider.request({method: "hardhat_impersonateAccount",params: [user]});
signer = await ethers.getSigner(user)
const amountToDeposit = ethers.utils.parseEther("1000")
await usdt.connect(signer).approve(GaugeV2.address, amountToDeposit)

// deposit for user
const bal_before = await usdc.balanceOf(user)
await GaugeV2.connect(signer).depositAll()
console.log("Pending Reward after first deposit: ", await GaugeExtraRewarder.pendingReward(user) /1e18)


// user recall onReward after 3days
await ethers.provider.send('evm_increaseTime', [3 * 86400]);
await ethers.provider.send('evm_mine');
console.log("Pending Reward after 3days: ", await GaugeExtraRewarder.pendingReward(user) /1e18)


// user recall onReward after 6days
await ethers.provider.send('evm_increaseTime', [3 * 86400]);
await ethers.provider.send('evm_mine');
console.log("Pending Reward after 6days: ", await GaugeExtraRewarder.pendingReward(user) /1e18)

// user recall onReward after 7days
await ethers.provider.send('evm_increaseTime', [86400]);
await ethers.provider.send('evm_mine');
console.log("Pending Reward after 7days: ", await GaugeExtraRewarder.pendingReward(user) /1e18)


// user recall onReward after 14days
await ethers.provider.send('evm_increaseTime', [7 * 86400]);
await ethers.provider.send('evm_mine');
console.log("Pending Reward after 14days: ", await GaugeExtraRewarder.pendingReward(user) /1e18)

// user recall onReward after 21days
await ethers.provider.send('evm_increaseTime', [7 * 86400]);
await ethers.provider.send('evm_mine');
console.log("Pending Reward after 21days: ", await GaugeExtraRewarder.pendingReward(user) /1e18)
expect( await GaugeExtraRewarder.pendingReward(user)).to.above(ethers.utils.parseEther("999.99"))


// withdraw to trigger the claim, check amounts and pending
await GaugeV2.connect(signer).withdrawAll()
expect( await GaugeExtraRewarder.pendingReward(user)).to.equal(0)
await ethers.provider.send('evm_increaseTime', [7 * 86400]);
await ethers.provider.send('evm_mine');
expect( await GaugeExtraRewarder.pendingReward(user)).to.equal(0)

const bal_after = await usdc.balanceOf(user)
console.log("Start bal: ", bal_before /1e18)
console.log("End Bal: ", bal_after /1e18)
expect(bal_after.sub(bal_before)).to.be.above(ethers.utils.parseEther("999.99"))


await hre.network.provider.request({method: "hardhat_stopImpersonatingAccount",params: [user]});


});



});