Skip to content

Commit

Permalink
chore: update to solidity 0.8 (#244)
Browse files Browse the repository at this point in the history
* feat: solidity 0.8

* chore: openzeppelin 4.0.0

* fix: update README.md

Co-authored-by: Just some guy <3859395+fubuloubu@users.noreply.github.com>

* fix: suggestions
Co-authored-by: Just some guy <3859395+fubuloubu@users.noreply.github.com>

fix: missed

Co-authored-by: Just some guy <3859395+fubuloubu@users.noreply.github.com>

* fix: fix unwanted replace

* fix: missed suggestions

Co-authored-by: Just some guy <3859395+fubuloubu@users.noreply.github.com>
  • Loading branch information
Steffel and fubuloubu committed Apr 5, 2021
1 parent 9056254 commit 52cec16
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 99 deletions.
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,11 @@ Any command `in code blocks` is meant to be executed from a Mac/Linux terminal o
- If you're in Windows using WSL, type `code .` to launch VSCode
- At this point install [Solidity Compiler](https://marketplace.visualstudio.com/items?itemName=JuanBlanco.solidity) - be sure to _Install in WSL_
- Install [Vyper](https://marketplace.visualstudio.com/items?itemName=tintinweb.vscode-vyper) as well on WSL
- Open one of the .sol files, right click the code and click _Soldity: Change Workspace compiler version (Remote)_, Change to 0.6.12
- Open one of the .sol files, right click the code and click _Solidity: Change Workspace compiler version (Remote)_, Change to 0.8.3
- Alternatively, go to File -> Preferences -> Settings
- If you’re using WSL, go to the Remote [WSL] tab
- Otherwise choose the Workspace tab
- Search for _Solidity_ and copy and paste _v0.6.12+commit.27d51765_ into the _Solidity: Compile Using Remote Version_ textbox
- Search for _Solidity_ and copy and paste _v0.8.3_ into the _Solidity: Compile Using Remote Version_ textbox
- Set Black as the linter.
- You'll see a toast notification the bottom right asking about linting, choose _black_
- If you don't see this, just go to _File_ -> _Preferences_ -> _Settings_
Expand Down
7 changes: 3 additions & 4 deletions brownie-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,19 @@ autofetch_sources: true

# require OpenZepplin Contracts
dependencies:
- OpenZeppelin/openzeppelin-contracts@3.1.0
- OpenZeppelin/openzeppelin-contracts@4.0.0

# path remapping to support OpenZepplin imports with NPM-style path
compiler:
solc:
version: 0.6.12
version: 0.8.3
remappings:
- "@openzeppelin=OpenZeppelin/openzeppelin-contracts@3.1.0"
- "@openzeppelin=OpenZeppelin/openzeppelin-contracts@4.0.0"

reports:
exclude_paths:
- contracts/test/Token.sol
- contracts/test/TestGuestList.sol
exclude_contracts:
- SafeMath
- SafeERC20
- Address
37 changes: 17 additions & 20 deletions contracts/BaseStrategy.sol
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.7.0;
pragma experimental ABIEncoderV2;
pragma solidity >=0.8.0 <0.9.0;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

struct StrategyParams {
uint256 performanceFee;
Expand Down Expand Up @@ -176,7 +175,6 @@ interface StrategyAPI {
* `harvest()`, and `harvestTrigger()` for further details.
*/
abstract contract BaseStrategy {
using SafeMath for uint256;
using SafeERC20 for IERC20;
string public metadataURI;

Expand Down Expand Up @@ -295,7 +293,7 @@ abstract contract BaseStrategy {
_;
}

constructor(address _vault) public {
constructor(address _vault) {
_initialize(_vault, msg.sender, msg.sender, msg.sender);
}

Expand All @@ -316,18 +314,17 @@ abstract contract BaseStrategy {

vault = VaultAPI(_vault);
want = IERC20(vault.token());
want.safeApprove(_vault, uint256(-1)); // Give Vault unlimited access (might save gas)
want.safeApprove(_vault, type(uint256).max); // Give Vault unlimited access (might save gas)
strategist = _strategist;
rewards = _rewards;
keeper = _keeper;

// initialize variables
minReportDelay = 0;
maxReportDelay = 86400;
profitFactor = 100;
debtThreshold = 0;

vault.approve(rewards, uint256(-1)); // Allow rewards to be pulled
vault.approve(rewards, type(uint256).max); // Allow rewards to be pulled
}

/**
Expand Down Expand Up @@ -374,7 +371,7 @@ abstract contract BaseStrategy {
require(_rewards != address(0));
vault.approve(rewards, 0);
rewards = _rewards;
vault.approve(rewards, uint256(-1));
vault.approve(rewards, type(uint256).max);
emit UpdatedRewards(_rewards);
}

Expand Down Expand Up @@ -638,10 +635,10 @@ abstract contract BaseStrategy {
if (params.activation == 0) return false;

// Should not trigger if we haven't waited long enough since previous harvest
if (block.timestamp.sub(params.lastReport) < minReportDelay) return false;
if ((block.timestamp - params.lastReport) < minReportDelay) return false;

// Should trigger if hasn't been called in a while
if (block.timestamp.sub(params.lastReport) >= maxReportDelay) return true;
if ((block.timestamp - params.lastReport) >= maxReportDelay) return true;

// If some amount is owed, pay it back
// NOTE: Since debt is based on deposits, it makes sense to guard against large
Expand All @@ -654,15 +651,15 @@ abstract contract BaseStrategy {
// Check for profits and losses
uint256 total = estimatedTotalAssets();
// Trigger if we have a loss to report
if (total.add(debtThreshold) < params.totalDebt) return true;
if ((total + debtThreshold) < params.totalDebt) return true;

uint256 profit = 0;
if (total > params.totalDebt) profit = total.sub(params.totalDebt); // We've earned a profit!
if (total > params.totalDebt) profit = total - params.totalDebt; // We've earned a profit!

// Otherwise, only trigger if it "makes sense" economically (gas cost
// is <N% of value moved)
uint256 credit = vault.creditAvailable();
return (profitFactor.mul(callCost) < credit.add(profit));
return ((profitFactor * callCost) < (credit + profit));
}

/**
Expand Down Expand Up @@ -694,7 +691,7 @@ abstract contract BaseStrategy {
(debtPayment, loss) = liquidatePosition(totalAssets > debtOutstanding ? totalAssets : debtOutstanding);
// NOTE: take up any remainder here as profit
if (debtPayment > debtOutstanding) {
profit = debtPayment.sub(debtOutstanding);
profit = debtPayment - debtOutstanding;
debtPayment = debtOutstanding;
}
} else {
Expand Down Expand Up @@ -727,7 +724,7 @@ abstract contract BaseStrategy {
uint256 amountFreed;
(amountFreed, _loss) = liquidatePosition(_amountNeeded);
// Send it directly back (NOTE: Using `msg.sender` saves some gas here)
want.safeTransfer(msg.sender, amountFreed);
SafeERC20.safeTransfer(want, msg.sender, amountFreed);
// NOTE: Reinvest anything leftover on next `tend`/`harvest`
}

Expand All @@ -751,7 +748,7 @@ abstract contract BaseStrategy {
require(msg.sender == address(vault) || msg.sender == governance());
require(BaseStrategy(_newStrategy).vault() == vault);
prepareMigration(_newStrategy);
want.safeTransfer(_newStrategy, want.balanceOf(address(this)));
SafeERC20.safeTransfer(want, _newStrategy, want.balanceOf(address(this)));
}

/**
Expand Down Expand Up @@ -814,14 +811,14 @@ abstract contract BaseStrategy {
address[] memory _protectedTokens = protectedTokens();
for (uint256 i; i < _protectedTokens.length; i++) require(_token != _protectedTokens[i], "!protected");

IERC20(_token).safeTransfer(governance(), IERC20(_token).balanceOf(address(this)));
SafeERC20.safeTransfer(IERC20(_token), governance(), IERC20(_token).balanceOf(address(this)));
}
}

abstract contract BaseStrategyInitializable is BaseStrategy {
event Cloned(address indexed clone);

constructor(address _vault) public BaseStrategy(_vault) {}
constructor(address _vault) BaseStrategy(_vault) {}

function initialize(
address _vault,
Expand Down
40 changes: 17 additions & 23 deletions contracts/BaseWrapper.sol
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.6.12;
pragma experimental ABIEncoderV2;
pragma solidity >=0.8.0 <0.9.0;

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

import {VaultAPI} from "./BaseStrategy.sol";

Expand All @@ -21,7 +19,6 @@ interface RegistryAPI {

abstract contract BaseWrapper {
using Math for uint256;
using SafeMath for uint256;
using SafeERC20 for IERC20;

IERC20 public token;
Expand All @@ -41,7 +38,7 @@ abstract contract BaseWrapper {
// VaultsAPI.depositLimit is unlimited
uint256 constant UNCAPPED_DEPOSITS = type(uint256).max;

constructor(address _token, address _registry) public {
constructor(address _token, address _registry) {
// Recommended to use a token with a `Registry.latestVault(_token) != address(0)`
token = IERC20(_token);
// Recommended to use `v2.registry.ychad.eth`
Expand Down Expand Up @@ -96,15 +93,15 @@ abstract contract BaseWrapper {
VaultAPI[] memory vaults = allVaults();

for (uint256 id = 0; id < vaults.length; id++) {
balance = balance.add(vaults[id].balanceOf(account).mul(vaults[id].pricePerShare()).div(10**uint256(vaults[id].decimals())));
balance += (vaults[id].balanceOf(account) * vaults[id].pricePerShare()) / 10**uint256(vaults[id].decimals());
}
}

function totalAssets() public view returns (uint256 assets) {
VaultAPI[] memory vaults = allVaults();

for (uint256 id = 0; id < vaults.length; id++) {
assets = assets.add(vaults[id].totalAssets());
assets += vaults[id].totalAssets();
}
}

Expand All @@ -118,9 +115,9 @@ abstract contract BaseWrapper {

if (pullFunds) {
if (amount != DEPOSIT_EVERYTHING) {
token.safeTransferFrom(depositor, address(this), amount);
SafeERC20.safeTransferFrom(token, depositor, address(this), amount);
} else {
token.safeTransferFrom(depositor, address(this), token.balanceOf(depositor));
SafeERC20.safeTransferFrom(token, depositor, address(this), token.balanceOf(depositor));
}
}

Expand All @@ -143,7 +140,7 @@ abstract contract BaseWrapper {
}

uint256 afterBal = token.balanceOf(address(this));
deposited = beforeBal.sub(afterBal);
deposited = beforeBal - afterBal;
// `receiver` now has shares of `_bestVault` as balance, converted to `token` here
// Issue a refund if not everything was deposited
if (depositor != address(this) && afterBal > 0) token.safeTransfer(depositor, afterBal);
Expand Down Expand Up @@ -191,21 +188,18 @@ abstract contract BaseWrapper {

if (amount != WITHDRAW_EVERYTHING) {
// Compute amount to withdraw fully to satisfy the request
uint256 estimatedShares = amount
.sub(withdrawn) // NOTE: Changes every iteration
.mul(10**uint256(vaults[id].decimals()))
.div(vaults[id].pricePerShare()); // NOTE: Every Vault is different
uint256 estimatedShares = ((amount - withdrawn) * 10**uint256(vaults[id].decimals())) / vaults[id].pricePerShare();

// Limit amount to withdraw to the maximum made available to this contract
// NOTE: Avoid corner case where `estimatedShares` isn't precise enough
// NOTE: If `0 < estimatedShares < 1` but `availableShares > 1`, this will withdraw more than necessary
if (estimatedShares > 0 && estimatedShares < availableShares) {
withdrawn = withdrawn.add(vaults[id].withdraw(estimatedShares));
withdrawn = withdrawn + (vaults[id].withdraw(estimatedShares));
} else {
withdrawn = withdrawn.add(vaults[id].withdraw(availableShares));
withdrawn = withdrawn + (vaults[id].withdraw(availableShares));
}
} else {
withdrawn = withdrawn.add(vaults[id].withdraw());
withdrawn += vaults[id].withdraw();
}

// Check if we have fully satisfied the request
Expand All @@ -218,11 +212,11 @@ abstract contract BaseWrapper {
// NOTE: Invariant is `withdrawn <= amount`
if (withdrawn > amount) {
// Don't forget to approve the deposit
if (token.allowance(address(this), address(_bestVault)) < withdrawn.sub(amount)) {
if (token.allowance(address(this), address(_bestVault)) < (withdrawn - amount)) {
token.safeApprove(address(_bestVault), UNLIMITED_APPROVAL); // Vaults are trusted
}

_bestVault.deposit(withdrawn.sub(amount), sender);
_bestVault.deposit(withdrawn - amount, sender);
withdrawn = amount;
}

Expand Down Expand Up @@ -254,7 +248,7 @@ abstract contract BaseWrapper {
uint256 _amount = amount;
if (_depositLimit < UNCAPPED_DEPOSITS && _amount < WITHDRAW_EVERYTHING) {
// Can only deposit up to this amount
uint256 _depositLeft = _depositLimit.sub(_totalAssets);
uint256 _depositLeft = _depositLimit - _totalAssets;
if (_amount > _depositLeft) _amount = _depositLeft;
}

Expand All @@ -268,7 +262,7 @@ abstract contract BaseWrapper {
// NOTE: Due to the precision loss of certain calculations, there is a small inefficency
// on how migrations are calculated, and this could lead to a DoS issue. Hence, this
// value is made to be configurable to allow the user to specify how much is acceptable
require(withdrawn.sub(migrated) <= maxMigrationLoss);
require((withdrawn - migrated) <= maxMigrationLoss);
} // else: nothing to migrate! (not a failure)
}
}
23 changes: 14 additions & 9 deletions contracts/test/AffiliateToken.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.12;
pragma solidity 0.8.3;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

Expand All @@ -19,6 +19,7 @@ contract AffiliateToken is ERC20, BaseWrapper {
address public affiliate;

address public pendingAffiliate;
uint8 private immutable _decimals;

modifier onlyAffiliate() {
require(msg.sender == affiliate);
Expand All @@ -30,10 +31,10 @@ contract AffiliateToken is ERC20, BaseWrapper {
address _registry,
string memory name,
string memory symbol
) public BaseWrapper(_token, _registry) ERC20(name, symbol) {
) BaseWrapper(_token, _registry) ERC20(name, symbol) {
DOMAIN_SEPARATOR = keccak256(abi.encode(DOMAIN_TYPEHASH, keccak256(bytes(name)), keccak256(bytes("1")), _getChainId(), address(this)));
affiliate = msg.sender;
_setupDecimals(uint8(ERC20(address(token)).decimals()));
_decimals = uint8(ERC20(address(token)).decimals());
}

function _getChainId() internal view returns (uint256) {
Expand All @@ -57,34 +58,34 @@ contract AffiliateToken is ERC20, BaseWrapper {
uint256 totalShares = totalSupply();

if (totalShares > 0) {
return totalVaultBalance(address(this)).mul(numShares).div(totalShares);
return (totalVaultBalance(address(this)) * numShares) / totalShares;
} else {
return numShares;
}
}

function pricePerShare() external view returns (uint256) {
return totalVaultBalance(address(this)).mul(10**uint256(decimals())).div(totalSupply());
return (totalVaultBalance(address(this)) * (10**uint256(decimals()))) / totalSupply();
}

function _sharesForValue(uint256 amount) internal view returns (uint256) {
// total wrapper assets before deposit (assumes deposit already occured)
uint256 totalWrapperAssets = totalVaultBalance(address(this)).sub(amount);
uint256 totalWrapperAssets = totalVaultBalance(address(this)) - amount;

if (totalWrapperAssets > 0) {
return totalSupply().mul(amount).div(totalWrapperAssets);
return (totalSupply() * amount) / totalWrapperAssets;
} else {
return amount;
}
}

function deposit() external returns (uint256) {
return deposit(uint256(-1)); // Deposit everything
return deposit(type(uint256).max); // Deposit everything
}

function deposit(uint256 amount) public returns (uint256 deposited) {
deposited = _deposit(msg.sender, address(this), amount, true); // `true` = pull from `msg.sender`
uint256 shares = _sharesForValue(deposited); // NOTE: Must be calculated after deposit is handled
uint256 shares = _sharesForValue(deposited); // NOTE: Must be calculated after deposit is handled
_mint(msg.sender, shares);
}

Expand Down Expand Up @@ -139,4 +140,8 @@ contract AffiliateToken is ERC20, BaseWrapper {

_approve(owner, spender, amount);
}

function decimals() public virtual override view returns (uint8) {
return _decimals;
}
}
5 changes: 2 additions & 3 deletions contracts/test/TestGuestList.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.6.0 <0.7.0;
pragma experimental ABIEncoderV2;
pragma solidity 0.8.3;

/**
* @notice A basic guest list contract for testing.
Expand All @@ -18,7 +17,7 @@ contract TestGuestList {
* @dev Note that since this is just for testing, you're unable to change
* `bouncer`.
*/
constructor() public {
constructor() {
bouncer = msg.sender;
}

Expand Down
Loading

0 comments on commit 52cec16

Please sign in to comment.