Audit

Everything we release is reviewed extensively by the Byte Masons, and we strive to get as many high-quality opinions from reputable audit firms as possible. Security is our primary focus when building new systems, so expect the best audits we can afford before we ship any new contracts.

YearnV1

Reaper v1 was built on Yearn.Finance's battle-tested architecture which allowed us to start up quickly. This means the contracts underlying Reaper are exactly the same as Yearn's.

Audit

We have implemented a few additions to Yearn's contracts. We have added state for profit/loss tracking, support for tokens and deposits that require tax, and deposit caps. These additions have all been audited by Solidity.Finance.
They found no vulnerabilities. You can find their report here.

Side-by-Side Comparison

In this section we will take a look at the core business logic of both Reaper's vault contract and Yearnv1's vault contract. Feel free to grab the codes yourself and pop them into DiffChecker to see the differences yourself!
The core business logic of a contract refers to all the functions which receive and send funds to external sources. These are the key security points an audit will focus on.

Yearnv1's Deposit Functions

function depositAll() external { deposit(token.balanceOf(msg.sender)); }
function deposit(uint256 _amount) public {
uint256 _pool = balance();
uint256 _before = token.balanceOf(address(this));
token.safeTransferFrom(msg.sender, address(this), _amount);
uint256 _after = token.balanceOf(address(this));
_amount = _after.sub(_before); // Additional check for deflationary tokens
uint256 shares = 0;
if (totalSupply() == 0) {
shares = _amount;
} else {
shares = (_amount.mul(totalSupply())).div(_pool);
}
_mint(msg.sender, shares);
}

Reaper's Deposit Functions

function depositAll() external {
deposit(token.balanceOf(msg.sender));
}
function deposit(uint _amount) public nonReentrant {
require(_amount > 0, "please provide amount"); //Diff 1 QOL
uint256 _pool = balance();
uint256 _before = token.balanceOf(address(this));
token.safeTransferFrom(msg.sender, address(this), _amount);
uint256 _after = token.balanceOf(address(this));
_amount = _after.sub(_before);
uint256 shares = 0;
if (totalSupply() == 0) {
shares = _amount;
} else {
shares = (_amount.mul(totalSupply())).div(_pool);
}
_mint(msg.sender, shares);
earn(); //Diff 2 automating the harvest function
incrementDeposits(_amount); //Diff 3 tracking for analytics
}
function incrementDeposits(uint _amount) internal returns (bool) {
uint initial = cumulativeDeposits[msg.sender];
uint newTotal = initial.add(_amount);
cumulativeDeposits[msg.sender] = newTotal;
emit DepositsIncremented(msg.sender, _amount, newTotal);
return true;
}
Differences in Reaper Contract:
  1. 1.
    Line 6 -- A require statement has been added to ensure users can't deposit 0 tokens and waste gas.
  2. 2.
    Line 19 -- The earn function essentially harvests rewards from the strategy contract and deposits them into the vault. Yearn has the same function but they call it elsewhere. It has been moved into the deposit function in order to automate the process.
  3. 3.
    Line 20 -- The increment deposit function is new to the Reaper contract and is used for analytics purposes. It merely tracks a user's cumulative deposits and emits the data for display on the front end.

Yearnv1's Withdrawal Functions

function withdrawAll() external {
withdraw(balanceOf(msg.sender));
}
// No rebalance implementation for lower fees and faster swaps
function withdraw(uint256 _shares) public {
uint256 r = (balance().mul(_shares)).div(totalSupply()); //amount of money the shares are worth
_burn(msg.sender, _shares);
// Check balance
uint256 b = token.balanceOf(address(this));
if (b < r) {
uint256 _withdraw = r.sub(b);
IController(controller).withdraw(address(token), _withdraw); //Way to make the vault generalized. they are withdrawing from the strategy the funds that are owed that are not in the vault.
uint256 _after = token.balanceOf(address(this));
uint256 _diff = _after.sub(b);
if (_diff < _withdraw) {
r = b.add(_diff);
}
}
token.safeTransfer(msg.sender, r);
}

Reaper's Withdrawal Functions

function withdrawAll() external {
withdraw(balanceOf(msg.sender));
}
/**
* @dev Function to exit the system. The vault will withdraw the required tokens
* from the strategy and pay up the token holder. A proportional number of IOU
* tokens are burned in the process.
*/
function withdraw(uint256 _shares) public nonReentrant {
require(_shares > 0, "please provide amount"); //Diff 1 Quality of Life
uint256 r = (balance().mul(_shares)).div(totalSupply());
_burn(msg.sender, _shares);
uint b = token.balanceOf(address(this));
if (b < r) {
uint _withdraw = r.sub(b);
IStrategy(strategy).withdraw(_withdraw); //Diff 2 Reaper Vaults are specific
uint _after = token.balanceOf(address(this));
uint _diff = _after.sub(b);
if (_diff < _withdraw) {
r = b.add(_diff);
}
}
token.safeTransfer(msg.sender, r);
incrementWithdrawals(r); // Diff 3
}
function incrementWithdrawals(uint _amount) internal returns (bool) {
uint initial = cumulativeWithdrawals[msg.sender];
uint newTotal = initial.add(_amount);
cumulativeWithdrawals[msg.sender] = newTotal;
emit WithdrawalsIncremented(msg.sender, _amount, newTotal);
return true;
}
Differences in Reaper Contract:
  1. 1.
    Line 11 -- This require statement ensures users can not call the withdrawal function on 0 tokens. This prevents users from wasting gas.
  2. 2.
    Line 18 -- This line pulls tokens from the strategy contract if there are not enough tokens in the vault to cover a user's withdrawal. Yearn's function includes an extra parameter 'token'. This is so the contract knows which kind of tokens it is looking to pull. Because Yearn's vaults are 'general' -- the contract can handle all sorts of tokens -- this extra parameter is necessary. Reaper's vaults are specific. Each vault only talks to one type of strategy. So, the extra parameter is unnecessary.
  3. 3.
    Line 26 -- The increment withdrawal function is new to the Reaper contract and is used for analytics purposes. It merely tracks a user's cumulative withdrawals and emits the data for display on the front end.