Skip to content

Commit

Permalink
code4rena (#84)
Browse files Browse the repository at this point in the history
* feat: code4rena fixes

* test: increase test coverage (#86)

Co-authored-by: 0xahtle7 <[email protected]>
  • Loading branch information
0xMaharishi and 0xahtle7 authored Jun 2, 2022
1 parent 03255b9 commit 023077d
Show file tree
Hide file tree
Showing 40 changed files with 711 additions and 244 deletions.
24 changes: 9 additions & 15 deletions contracts/Aura.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
pragma solidity 0.8.11;

import { IERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
import { ERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/ERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/utils/SafeERC20.sol";
import { Address } from "@openzeppelin/contracts-0.8/utils/Address.sol";
import { AuraMath } from "./AuraMath.sol";

interface IStaker {
Expand All @@ -18,14 +15,13 @@ interface IStaker {
* distirbuted along a supply curve (cliffs etc). Fork of ConvexToken.
*/
contract AuraToken is ERC20 {
using SafeERC20 for IERC20;
using Address for address;
using AuraMath for uint256;

address public operator;
address public immutable vecrvProxy;

uint256 public constant EMISSIONS_MAX_SUPPLY = 5e25; // 50m
uint256 public constant INIT_MINT_AMOUNT = 5e25; // 50m
uint256 public constant totalCliffs = 500;
uint256 public immutable reductionPerCliff;

Expand Down Expand Up @@ -55,20 +51,14 @@ contract AuraToken is ERC20 {
/**
* @dev Initialise and mints initial supply of tokens.
* @param _to Target address to mint.
* @param _amount Amount of tokens to mint.
* @param _minter The minter address.
*/
function init(
address _to,
uint256 _amount,
address _minter
) external {
function init(address _to, address _minter) external {
require(msg.sender == operator, "Only operator");
require(totalSupply() == 0, "Only once");
require(_amount > 0, "Must mint something");
require(_minter != address(0), "Invalid minter");

_mint(_to, _amount);
_mint(_to, INIT_MINT_AMOUNT);
updateOperator();
minter = _minter;
minterMinted = 0;
Expand All @@ -80,7 +70,11 @@ contract AuraToken is ERC20 {
* @dev This can be called if the operator of the voterProxy somehow changes.
*/
function updateOperator() public {
require(totalSupply() != 0, "!init");

address newOperator = IStaker(vecrvProxy).operator();
require(newOperator != operator && newOperator != address(0), "!operator");

emit OperatorChanged(operator, newOperator);
operator = newOperator;
}
Expand All @@ -98,7 +92,7 @@ contract AuraToken is ERC20 {
}

// e.g. emissionsMinted = 6e25 - 5e25 - 0 = 1e25;
uint256 emissionsMinted = totalSupply() - EMISSIONS_MAX_SUPPLY - minterMinted;
uint256 emissionsMinted = totalSupply() - INIT_MINT_AMOUNT - minterMinted;
// e.g. reductionPerCliff = 5e25 / 500 = 1e23
// e.g. cliff = 1e25 / 1e23 = 100
uint256 cliff = emissionsMinted.div(reductionPerCliff);
Expand Down
37 changes: 31 additions & 6 deletions contracts/AuraBalRewardPool.sol
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
pragma solidity 0.8.11;

import { AuraMath } from "./AuraMath.sol";
import { SafeMath } from "@openzeppelin/contracts-0.8/utils/math/SafeMath.sol";
import { IERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/utils/SafeERC20.sol";

Expand All @@ -18,7 +17,7 @@ import { IAuraLocker } from "./Interfaces.sol";
* - Penalty on claim at 20%
*/
contract AuraBalRewardPool {
using SafeMath for uint256;
using AuraMath for uint256;
using SafeERC20 for IERC20;

IERC20 public immutable rewardToken;
Expand All @@ -27,7 +26,7 @@ contract AuraBalRewardPool {

address public immutable rewardManager;

IAuraLocker public immutable auraLocker;
IAuraLocker public auraLocker;
address public immutable penaltyForwarder;
uint256 public pendingPenalty = 0;
uint256 public immutable startTime;
Expand All @@ -47,6 +46,7 @@ contract AuraBalRewardPool {
event Withdrawn(address indexed user, uint256 amount);
event RewardPaid(address indexed user, uint256 reward, bool locked);
event PenaltyForwarded(uint256 amount);
event Rescued();

/**
* @dev Simple constructoor
Expand All @@ -64,14 +64,17 @@ contract AuraBalRewardPool {
address _penaltyForwarder,
uint256 _startDelay
) {
require(_stakingToken != _rewardToken && _stakingToken != address(0), "!tokens");
stakingToken = IERC20(_stakingToken);
rewardToken = IERC20(_rewardToken);
require(_rewardManager != address(0), "!manager");
rewardManager = _rewardManager;
require(_auraLocker != address(0), "!locker");
auraLocker = IAuraLocker(_auraLocker);
require(_penaltyForwarder != address(0), "!forwarder");
penaltyForwarder = _penaltyForwarder;
rewardToken.safeApprove(_auraLocker, type(uint256).max);

require(_startDelay < 2 weeks, "!delay");
require(_startDelay > 4 days && _startDelay < 2 weeks, "!delay");
startTime = block.timestamp + _startDelay;
}

Expand Down Expand Up @@ -175,6 +178,7 @@ contract AuraBalRewardPool {
if (reward > 0) {
rewards[msg.sender] = 0;
if (_lock) {
rewardToken.safeIncreaseAllowance(address(auraLocker), reward);
auraLocker.lock(msg.sender, reward);
} else {
uint256 penalty = (reward * 2) / 10;
Expand All @@ -196,6 +200,27 @@ contract AuraBalRewardPool {
emit PenaltyForwarded(toForward);
}

/**
* @dev Rescues the reward token provided it hasn't been initiated yet
*/
function rescueReward() public {
require(msg.sender == rewardManager, "!rescuer");
require(block.timestamp < startTime && rewardRate == 0, "Already started");

uint256 balance = rewardToken.balanceOf(address(this));
rewardToken.transfer(rewardManager, balance);

emit Rescued();
}

/**
* @dev Updates the locker address
*/
function setLocker(address _newLocker) external {
require(msg.sender == rewardManager, "!auth");
auraLocker = IAuraLocker(_newLocker);
}

/**
* @dev Called once to initialise the rewards based on balance of stakeToken
*/
Expand Down
8 changes: 3 additions & 5 deletions contracts/AuraClaimZap.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
pragma solidity 0.8.11;

import { AuraMath } from "./AuraMath.sol";
import { IAuraLocker, ICrvDepositorWrapper } from "./Interfaces.sol";
Expand Down Expand Up @@ -206,15 +206,13 @@ contract AuraClaimZap {
}

//stake up to given amount of cvx
if (depositCvxMaxAmount > 0) {
if (depositCvxMaxAmount > 0 && _checkOption(options, uint256(Options.LockCvx))) {
uint256 cvxBalance = IERC20(cvx).balanceOf(msg.sender).sub(removeCvxBalance);
cvxBalance = AuraMath.min(cvxBalance, depositCvxMaxAmount);
if (cvxBalance > 0) {
//pull cvx
IERC20(cvx).safeTransferFrom(msg.sender, address(this), cvxBalance);
if (_checkOption(options, uint256(Options.LockCvx))) {
IAuraLocker(locker).lock(msg.sender, cvxBalance);
}
IAuraLocker(locker).lock(msg.sender, cvxBalance);
}
}
}
Expand Down
98 changes: 66 additions & 32 deletions contracts/AuraLocker.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
pragma experimental ABIEncoderV2;
pragma solidity 0.8.11;

import { IERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/IERC20.sol";
import { SafeERC20 } from "@openzeppelin/contracts-0.8/token/ERC20/utils/SafeERC20.sol";
Expand Down Expand Up @@ -69,7 +68,7 @@ contract AuraLocker is ReentrancyGuard, Ownable, IAuraLocker {

// Rewards
address[] public rewardTokens;
uint256 public queuedCvxCrvRewards = 0;
mapping(address => uint256) public queuedRewards;
uint256 public constant newRewardRatio = 830;
// Core reward data
mapping(address => RewardData) public rewardData;
Expand Down Expand Up @@ -100,6 +99,8 @@ contract AuraLocker is ReentrancyGuard, Ownable, IAuraLocker {
mapping(address => mapping(uint256 => uint256)) public delegateeUnlocks;

// Config
// Blacklisted smart contract interactions
mapping(address => bool) public blacklist;
// Tokens
IERC20 public immutable stakingToken;
address public immutable cvxCrv;
Expand Down Expand Up @@ -130,6 +131,7 @@ contract AuraLocker is ReentrancyGuard, Ownable, IAuraLocker {
event KickReward(address indexed _user, address indexed _kicked, uint256 _reward);
event RewardAdded(address indexed _token, uint256 _reward);

event BlacklistModified(address account, bool blacklisted);
event KickIncentiveSet(uint256 rate, uint256 delay);
event Shutdown();

Expand Down Expand Up @@ -187,14 +189,38 @@ contract AuraLocker is ReentrancyGuard, Ownable, IAuraLocker {
_;
}

modifier notBlacklisted(address _sender, address _receiver) {
require(!blacklist[_sender], "blacklisted");

if (_sender != _receiver) {
require(!blacklist[_receiver], "blacklisted");
}

_;
}

/***************************************
ADMIN
****************************************/

function modifyBlacklist(address _account, bool _blacklisted) external onlyOwner {
uint256 cs;
// solhint-disable-next-line no-inline-assembly
assembly {
cs := extcodesize(_account)
}
require(cs != 0, "Must be contract");

blacklist[_account] = _blacklisted;
emit BlacklistModified(_account, _blacklisted);
}

// Add a new reward token to be distributed to stakers
function addReward(address _rewardsToken, address _distributor) external onlyOwner {
require(rewardData[_rewardsToken].lastUpdateTime == 0, "Reward already exists");
require(_rewardsToken != address(stakingToken), "Cannot add StakingToken as reward");
require(rewardTokens.length < 5, "Max rewards length");

rewardTokens.push(_rewardsToken);
rewardData[_rewardsToken].lastUpdateTime = uint32(block.timestamp);
rewardData[_rewardsToken].periodFinish = uint32(block.timestamp);
Expand Down Expand Up @@ -255,7 +281,7 @@ contract AuraLocker is ReentrancyGuard, Ownable, IAuraLocker {
}

//lock tokens
function _lock(address _account, uint256 _amount) internal {
function _lock(address _account, uint256 _amount) internal notBlacklisted(msg.sender, _account) {
require(_amount > 0, "Cannot stake 0");
require(!isShutdown, "shutdown");

Expand Down Expand Up @@ -318,21 +344,35 @@ contract AuraLocker is ReentrancyGuard, Ownable, IAuraLocker {
}
}

function getReward(address _account, bool[] calldata _skipIdx) external nonReentrant updateReward(_account) {
uint256 rewardTokensLength = rewardTokens.length;
require(_skipIdx.length == rewardTokensLength, "!arr");
for (uint256 i; i < rewardTokensLength; i++) {
if (_skipIdx[i]) continue;
address _rewardsToken = rewardTokens[i];
uint256 reward = userData[_account][_rewardsToken].rewards;
if (reward > 0) {
userData[_account][_rewardsToken].rewards = 0;
IERC20(_rewardsToken).safeTransfer(_account, reward);
emit RewardPaid(_account, _rewardsToken, reward);
}
}
}

function checkpointEpoch() external {
_checkpointEpoch();
}

//insert a new epoch if needed. fill in any gaps
function _checkpointEpoch() internal {
uint256 currentEpoch = block.timestamp.div(rewardsDuration).mul(rewardsDuration);
uint256 epochindex = epochs.length;

//first epoch add in constructor, no need to check 0 length
//check to add
if (epochs[epochindex - 1].date < currentEpoch) {
//fill any epoch gaps until the next epoch date.
while (epochs[epochs.length - 1].date != currentEpoch) {
uint256 nextEpochDate = uint256(epochs[epochs.length - 1].date).add(rewardsDuration);
uint256 nextEpochDate = uint256(epochs[epochs.length - 1].date);
if (nextEpochDate < currentEpoch) {
while (nextEpochDate != currentEpoch) {
nextEpochDate = nextEpochDate.add(rewardsDuration);
epochs.push(Epoch({ supply: 0, date: uint32(nextEpochDate) }));
}
}
Expand Down Expand Up @@ -401,7 +441,7 @@ contract AuraLocker is ReentrancyGuard, Ownable, IAuraLocker {
uint256 currentEpoch = block.timestamp.sub(_checkDelay).div(rewardsDuration).mul(rewardsDuration);
uint256 epochsover = currentEpoch.sub(uint256(locks[length - 1].unlockTime)).div(rewardsDuration);
uint256 rRate = AuraMath.min(kickRewardPerEpoch.mul(epochsover + 1), denominator);
reward = uint256(locks[length - 1].amount).mul(rRate).div(denominator);
reward = uint256(locked).mul(rRate).div(denominator);
}
} else {
//use a processed index(nextUnlockIndex) to not loop as much
Expand Down Expand Up @@ -817,18 +857,20 @@ contract AuraLocker is ReentrancyGuard, Ownable, IAuraLocker {
REWARD FUNDING
****************************************/

function queueNewRewards(uint256 _rewards) external nonReentrant {
require(rewardDistributors[cvxCrv][msg.sender], "!authorized");
function queueNewRewards(address _rewardsToken, uint256 _rewards) external nonReentrant {
require(rewardDistributors[_rewardsToken][msg.sender], "!authorized");
require(_rewards > 0, "No reward");

RewardData storage rdata = rewardData[cvxCrv];
RewardData storage rdata = rewardData[_rewardsToken];

IERC20(_rewardsToken).safeTransferFrom(msg.sender, address(this), _rewards);

IERC20(cvxCrv).safeTransferFrom(msg.sender, address(this), _rewards);
_rewards = _rewards.add(queuedRewards[_rewardsToken]);
require(_rewards < 1e25, "!rewards");

_rewards = _rewards.add(queuedCvxCrvRewards);
if (block.timestamp >= rdata.periodFinish) {
_notifyReward(cvxCrv, _rewards);
queuedCvxCrvRewards = 0;
_notifyReward(_rewardsToken, _rewards);
queuedRewards[_rewardsToken] = 0;
return;
}

Expand All @@ -838,25 +880,13 @@ contract AuraLocker is ReentrancyGuard, Ownable, IAuraLocker {
uint256 currentAtNow = rdata.rewardRate * elapsedTime;
uint256 queuedRatio = currentAtNow.mul(1000).div(_rewards);
if (queuedRatio < newRewardRatio) {
_notifyReward(cvxCrv, _rewards);
queuedCvxCrvRewards = 0;
_notifyReward(_rewardsToken, _rewards);
queuedRewards[_rewardsToken] = 0;
} else {
queuedCvxCrvRewards = _rewards;
queuedRewards[_rewardsToken] = _rewards;
}
}

function notifyRewardAmount(address _rewardsToken, uint256 _reward) external {
require(_rewardsToken != cvxCrv, "Use queueNewRewards");
require(rewardDistributors[_rewardsToken][msg.sender], "Must be rewardsDistributor");
require(_reward > 0, "No reward");

_notifyReward(_rewardsToken, _reward);

// handle the transfer of reward tokens via `transferFrom` to reduce the number
// of transactions required and ensure correctness of the _reward amount
IERC20(_rewardsToken).safeTransferFrom(msg.sender, address(this), _reward);
}

function _notifyReward(address _rewardsToken, uint256 _reward) internal updateReward(address(0)) {
RewardData storage rdata = rewardData[_rewardsToken];

Expand All @@ -868,6 +898,10 @@ contract AuraLocker is ReentrancyGuard, Ownable, IAuraLocker {
rdata.rewardRate = _reward.add(leftover).div(rewardsDuration).to96();
}

// Equivalent to 10 million tokens over a weeks duration
require(rdata.rewardRate < 1e20, "!rewardRate");
require(lockedSupply >= 1e20, "!balance");

rdata.lastUpdateTime = block.timestamp.to32();
rdata.periodFinish = block.timestamp.add(rewardsDuration).to32();

Expand Down
2 changes: 1 addition & 1 deletion contracts/AuraMath.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.11;
pragma solidity 0.8.11;

/// @notice A library for performing overflow-/underflow-safe math,
/// updated with awesomeness from of DappHub (https://github.com/dapphub/ds-math).
Expand Down
Loading

0 comments on commit 023077d

Please sign in to comment.