Contract Source Code:
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import {ExponentialNoError} from '@contracts/utils/ExponentialNoError.sol';
import {Roles} from '@contracts/utils/Roles.sol';
import {UFragments} from '@contracts/utils/UFragments.sol';
import {IUSDA} from '@interfaces/core/IUSDA.sol';
import {IVaultController} from '@interfaces/core/IVaultController.sol';
import {IERC20Metadata, IERC20} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';
import {Pausable} from '@openzeppelin/contracts/security/Pausable.sol';
import {Context} from '@openzeppelin/contracts/utils/Context.sol';
import {EnumerableSet} from '@openzeppelin/contracts/utils/structs/EnumerableSet.sol';
import {SafeERC20} from '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol';
/// @notice USDA token contract, handles all minting/burning of usda
/// @dev extends UFragments
contract USDA is Pausable, UFragments, IUSDA, ExponentialNoError, Roles {
using EnumerableSet for EnumerableSet.AddressSet;
using SafeERC20 for IERC20;
bytes32 public constant VAULT_CONTROLLER_ROLE = keccak256('VAULT_CONTROLLER');
EnumerableSet.AddressSet internal _vaultControllers;
/// @dev The reserve token
IERC20 public sUSD;
/// @dev The address of the pauser
address public pauser;
/// @dev The reserve amount
uint256 public reserveAmount;
/// @notice Checks if _msgSender() is a valid VaultController
modifier onlyVaultController() {
_checkRole(VAULT_CONTROLLER_ROLE, _msgSender());
_;
}
/// @notice Checks if _msgSender() is pauser
modifier onlyPauser() {
if (_msgSender() != address(pauser)) revert USDA_OnlyPauser();
_;
}
/// @notice Any function with this modifier will call the pay_interest() function before any function logic is called
modifier paysInterest() {
for (uint256 _i; _i < _vaultControllers.length();) {
IVaultController(_vaultControllers.at(_i)).calculateInterest();
unchecked {
_i++;
}
}
_;
}
constructor(IERC20 _sUSDAddr) UFragments('USDA Token', 'USDA') {
sUSD = _sUSDAddr;
}
/// @notice Sets the pauser for both USDA and VaultController
/// @dev The pauser is a separate role from the owner
function setPauser(address _pauser) external override onlyOwner {
pauser = _pauser;
emit PauserSet(_pauser);
}
/// @notice Pause contract
/// @dev Can only be called by the pauser
function pause() external override onlyPauser {
_pause();
}
/// @notice Unpause contract, pauser only
/// @dev Can only be called by the pauser
function unpause() external override onlyPauser {
_unpause();
}
/// @notice Deposit sUSD to mint USDA
/// @dev Caller should obtain 1 USDA for each sUSD
/// the calculations for deposit mimic the calculations done by mint in the ampleforth contract, simply with the susd transfer
/// 'fragments' are the units that we see, so 1000 fragments == 1000 USDA
/// 'gons' are the internal accounting unit, used to keep scale.
/// We use the variable _gonsPerFragment in order to convert between the two
/// try dimensional analysis when doing the math in order to verify units are correct
/// @param _susdAmount The amount of sUSD to deposit
function deposit(uint256 _susdAmount) external override {
_deposit(_susdAmount, _msgSender());
}
/// @notice Deposits sUSD to mint USDA and transfer to a different address
/// @param _susdAmount The amount of sUSD to deposit
/// @param _target The address to receive the USDA tokens
function depositTo(uint256 _susdAmount, address _target) external override {
_deposit(_susdAmount, _target);
}
/// @notice Business logic to deposit sUSD and mint USDA for the caller
function _deposit(uint256 _susdAmount, address _target) internal paysInterest whenNotPaused {
if (_susdAmount == 0) revert USDA_ZeroAmount();
sUSD.safeTransferFrom(_msgSender(), address(this), _susdAmount);
_mint(_target, _susdAmount);
// Account for the susd received
reserveAmount += _susdAmount;
emit Deposit(_target, _susdAmount);
}
/// @notice Withdraw sUSD by burning USDA
/// @dev The caller should obtain 1 sUSD for every 1 USDA
/// @param _susdAmount The amount of sUSD to withdraw
function withdraw(uint256 _susdAmount) external override {
_withdraw(_susdAmount, _msgSender());
}
/// @notice Withdraw sUSD to a specific address by burning USDA from the caller
/// @dev The _target address should obtain 1 sUSD for every 1 USDA burned from the caller
/// @param _susdAmount amount of sUSD to withdraw
/// @param _target address to receive the sUSD
function withdrawTo(uint256 _susdAmount, address _target) external override {
_withdraw(_susdAmount, _target);
}
/// @notice Withdraw sUSD by burning USDA
/// @dev The caller should obtain 1 sUSD for every 1 USDA
/// @dev This function is effectively just withdraw, but we calculate the amount for the sender
/// @param _susdWithdrawn The amount os sUSD withdrawn
function withdrawAll() external override returns (uint256 _susdWithdrawn) {
uint256 _balance = this.balanceOf(_msgSender());
_susdWithdrawn = _balance > reserveAmount ? reserveAmount : _balance;
_withdraw(_susdWithdrawn, _msgSender());
}
/// @notice Withdraw sUSD by burning USDA
/// @dev This function is effectively just withdraw, but we calculate the amount for the _target
/// @param _target should obtain 1 sUSD for every 1 USDA burned from caller
/// @param _susdWithdrawn The amount os sUSD withdrawn
function withdrawAllTo(address _target) external override returns (uint256 _susdWithdrawn) {
uint256 _balance = this.balanceOf(_msgSender());
_susdWithdrawn = _balance > reserveAmount ? reserveAmount : _balance;
_withdraw(_susdWithdrawn, _target);
}
/// @notice business logic to withdraw sUSD and burn USDA from the caller
function _withdraw(uint256 _susdAmount, address _target) internal paysInterest whenNotPaused {
if (reserveAmount == 0) revert USDA_EmptyReserve();
if (_susdAmount == 0) revert USDA_ZeroAmount();
if (_susdAmount > this.balanceOf(_msgSender())) revert USDA_InsufficientFunds();
// Account for the susd withdrawn
reserveAmount -= _susdAmount;
sUSD.safeTransfer(_target, _susdAmount);
_burn(_msgSender(), _susdAmount);
emit Withdraw(_target, _susdAmount);
}
/// @notice Admin function to mint USDA
/// @param _susdAmount The amount of USDA to mint, denominated in sUSD
function mint(uint256 _susdAmount) external override paysInterest onlyOwner {
if (_susdAmount == 0) revert USDA_ZeroAmount();
_mint(_msgSender(), _susdAmount);
}
/// @dev mint a specific `amount` of tokens to the `target`
function _mint(address _target, uint256 _amount) internal {
uint256 __gonsPerFragment = _gonsPerFragment;
// the gonbalances of the sender is in gons, therefore we must multiply the deposit amount, which is in fragments, by gonsperfragment
_gonBalances[_target] += _amount * __gonsPerFragment;
// total supply is in fragments, and so we add amount
_totalSupply += _amount;
// and totalgons of course is in gons, and so we multiply amount by gonsperfragment to get the amount of gons we must add to totalGons
_totalGons += _amount * __gonsPerFragment;
// emit both a mint and transfer event
emit Transfer(address(0), _target, _amount);
emit Mint(_target, _amount);
}
/// @notice Admin function to burn USDA
/// @param _susdAmount The amount of USDA to burn, denominated in sUSD
function burn(uint256 _susdAmount) external override paysInterest onlyOwner {
if (_susdAmount == 0) revert USDA_ZeroAmount();
_burn(_msgSender(), _susdAmount);
}
/// @dev burn a specific `amount` of tokens from the `target`
function _burn(address _target, uint256 _amount) internal {
uint256 __gonsPerFragment = _gonsPerFragment;
// modify the gonbalances of the sender, subtracting the amount of gons, therefore amount * gonsperfragment
_gonBalances[_target] -= (_amount * __gonsPerFragment);
// modify totalSupply and totalGons
_totalSupply -= _amount;
_totalGons -= (_amount * __gonsPerFragment);
// emit both a burn and transfer event
emit Transfer(_target, address(0), _amount);
emit Burn(_target, _amount);
}
/// @notice Donates susd to the protocol reserve
/// @param _susdAmount The amount of sUSD to donate
function donate(uint256 _susdAmount) external override paysInterest whenNotPaused {
if (_susdAmount == 0) revert USDA_ZeroAmount();
// Account for the susd received
reserveAmount += _susdAmount;
sUSD.safeTransferFrom(_msgSender(), address(this), _susdAmount);
_donation(_susdAmount);
}
/// @notice Recovers accidentally sent sUSD to this contract
/// @param _to The receiver of the dust
function recoverDust(address _to) external onlyOwner {
// All sUSD sent directly to the contract is not accounted into the reserveAmount
// This function allows governance to recover it
uint256 _amount = sUSD.balanceOf(address(this)) - reserveAmount;
sUSD.safeTransfer(_to, _amount);
emit RecoveredDust(owner(), _amount);
}
/// @notice Function for the vaultController to mint
/// @param _target The address to mint the USDA to
/// @param _amount The amount of USDA to mint
function vaultControllerMint(address _target, uint256 _amount) external override onlyVaultController whenNotPaused {
_mint(_target, _amount);
}
/// @notice Function for the vaultController to burn
/// @param _target The address to burn the USDA from
/// @param _amount The amount of USDA to burn
function vaultControllerBurn(address _target, uint256 _amount) external override onlyVaultController {
if (_gonBalances[_target] < (_amount * _gonsPerFragment)) revert USDA_NotEnoughBalance();
_burn(_target, _amount);
}
/// @notice Allows VaultController to send sUSD from the reserve
/// @param _target The address to receive the sUSD from reserve
/// @param _susdAmount The amount of sUSD to send
function vaultControllerTransfer(
address _target,
uint256 _susdAmount
) external override onlyVaultController whenNotPaused {
// Account for the susd withdrawn
reserveAmount -= _susdAmount;
// ensure transfer success
sUSD.safeTransfer(_target, _susdAmount);
emit VaultControllerTransfer(_target, _susdAmount);
}
/// @notice Function for the vaultController to scale all USDA balances
/// @param _amount The amount of USDA (e18) to donate
function vaultControllerDonate(uint256 _amount) external override onlyVaultController {
_donation(_amount);
}
/// @notice Function for distributing the donation to all USDA holders
/// @param _amount The amount of USDA to donate
function _donation(uint256 _amount) internal {
_totalSupply += _amount;
if (_totalSupply > MAX_SUPPLY) _totalSupply = MAX_SUPPLY;
_gonsPerFragment = _totalGons / _totalSupply;
emit Donation(_msgSender(), _amount, _totalSupply);
}
/// @notice Returns the reserve ratio
/// @return _e18reserveRatio The USDA reserve ratio
function reserveRatio() external view override returns (uint192 _e18reserveRatio) {
_e18reserveRatio = _safeu192((reserveAmount * EXP_SCALE) / _totalSupply);
}
/*///////////////////////////////////////////////////////////////
ROLES
//////////////////////////////////////////////////////////////*/
/// @notice Adds a new vault controller
/// @param _vaultController The new vault controller to add
function addVaultController(address _vaultController) external onlyOwner {
_vaultControllers.add(_vaultController);
_grantRole(VAULT_CONTROLLER_ROLE, _vaultController);
emit VaultControllerAdded(_vaultController);
}
/// @notice Removes a vault controller
/// @param _vaultController The vault controller to remove
function removeVaultController(address _vaultController) external onlyOwner {
_vaultControllers.remove(_vaultController);
_revokeRole(VAULT_CONTROLLER_ROLE, _vaultController);
emit VaultControllerRemoved(_vaultController);
}
/// @notice Removes a vault controller from the list
/// @param _vaultController The vault controller to remove
/// @dev The vault controller is removed from the list but keeps the role as to not brick it
function removeVaultControllerFromList(address _vaultController) external onlyOwner {
_vaultControllers.remove(_vaultController);
emit VaultControllerRemovedFromList(_vaultController);
}
}
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
// @title Exponential module for storing fixed-precision decimals
// @author Compound
// @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.
// Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:
// `Exp({mantissa: 5100000000000000000})`.
contract ExponentialNoError {
uint256 public constant EXP_SCALE = 1e18;
uint256 public constant DOUBLE_SCALE = 1e36;
uint256 public constant HALF_EXP_SCALE = EXP_SCALE / 2;
uint256 public constant MANTISSA_ONE = EXP_SCALE;
uint256 public constant UINT192_MAX = type(uint192).max;
uint256 public constant UINT128_MAX = type(uint128).max;
struct Exp {
uint256 mantissa;
}
struct Double {
uint256 mantissa;
}
// @dev Truncates the given exp to a whole number value.
// For example, truncate(Exp{mantissa: 15 * EXP_SCALE}) = 15
function _truncate(Exp memory _exp) internal pure returns (uint256 _result) {
return _exp.mantissa / EXP_SCALE;
}
function _truncate(uint256 _u) internal pure returns (uint256 _result) {
return _u / EXP_SCALE;
}
function _safeu192(uint256 _u) internal pure returns (uint192 _result) {
require(_u < UINT192_MAX, 'overflow');
return uint192(_u);
}
function _safeu128(uint256 _u) internal pure returns (uint128 _result) {
require(_u < UINT128_MAX, 'overflow');
return uint128(_u);
}
// @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
function _mulScalarTruncate(Exp memory _a, uint256 _scalar) internal pure returns (uint256 _result) {
Exp memory _product = _mul(_a, _scalar);
return _truncate(_product);
}
// @dev Multiply an Exp by a scalar, truncate, then _add an to an unsigned integer, returning an unsigned integer.
function _mulScalarTruncateAddUInt(
Exp memory _a,
uint256 _scalar,
uint256 _addend
) internal pure returns (uint256 _result) {
Exp memory _product = _mul(_a, _scalar);
return _add(_truncate(_product), _addend);
}
// @dev Checks if first Exp is less than second Exp.
function _lessThanExp(Exp memory _left, Exp memory _right) internal pure returns (bool _result) {
return _left.mantissa < _right.mantissa;
}
// @dev Checks if left Exp <= right Exp.
function _lessThanOrEqualExp(Exp memory _left, Exp memory _right) internal pure returns (bool _result) {
return _left.mantissa <= _right.mantissa;
}
// @dev Checks if left Exp > right Exp.
function _greaterThanExp(Exp memory _left, Exp memory _right) internal pure returns (bool _result) {
return _left.mantissa > _right.mantissa;
}
// @dev returns true if Exp is exactly zero
function _isZeroExp(Exp memory _value) internal pure returns (bool _result) {
return _value.mantissa == 0;
}
function _safe224(uint256 _n, string memory _errorMessage) internal pure returns (uint224 _result) {
require(_n < 2 ** 224, _errorMessage);
return uint224(_n);
}
function _safe32(uint256 _n, string memory _errorMessage) internal pure returns (uint32 _result) {
require(_n < 2 ** 32, _errorMessage);
return uint32(_n);
}
function _add(Exp memory _a, Exp memory _b) internal pure returns (Exp memory _result) {
return Exp({mantissa: _add(_a.mantissa, _b.mantissa)});
}
function _add(Double memory _a, Double memory _b) internal pure returns (Double memory _result) {
return Double({mantissa: _add(_a.mantissa, _b.mantissa)});
}
function _add(uint256 _a, uint256 _b) internal pure returns (uint256 _result) {
return _add(_a, _b, 'addition overflow');
}
function _add(uint256 _a, uint256 _b, string memory _errorMessage) internal pure returns (uint256 _result) {
uint256 _c = _a + _b;
require(_c >= _a, _errorMessage);
return _c;
}
function _sub(Exp memory _a, Exp memory _b) internal pure returns (Exp memory _result) {
return Exp({mantissa: _sub(_a.mantissa, _b.mantissa)});
}
function _sub(Double memory _a, Double memory _b) internal pure returns (Double memory _result) {
return Double({mantissa: _sub(_a.mantissa, _b.mantissa)});
}
function _sub(uint256 _a, uint256 _b) internal pure returns (uint256 _result) {
return _sub(_a, _b, 'subtraction underflow');
}
function _sub(uint256 _a, uint256 _b, string memory _errorMessage) internal pure returns (uint256 _result) {
require(_b <= _a, _errorMessage);
return _a - _b;
}
function _mul(Exp memory _a, Exp memory _b) internal pure returns (Exp memory _result) {
return Exp({mantissa: _mul(_a.mantissa, _b.mantissa) / EXP_SCALE});
}
function _mul(Exp memory _a, uint256 _b) internal pure returns (Exp memory _result) {
return Exp({mantissa: _mul(_a.mantissa, _b)});
}
function _mul(uint256 _a, Exp memory _b) internal pure returns (uint256 _result) {
return _mul(_a, _b.mantissa) / EXP_SCALE;
}
function _mul(Double memory _a, Double memory _b) internal pure returns (Double memory _result) {
return Double({mantissa: _mul(_a.mantissa, _b.mantissa) / DOUBLE_SCALE});
}
function _mul(Double memory _a, uint256 _b) internal pure returns (Double memory _result) {
return Double({mantissa: _mul(_a.mantissa, _b)});
}
function _mul(uint256 _a, Double memory _b) internal pure returns (uint256 _result) {
return _mul(_a, _b.mantissa) / DOUBLE_SCALE;
}
function _mul(uint256 _a, uint256 _b) internal pure returns (uint256 _result) {
return _mul(_a, _b, 'multiplication overflow');
}
function _mul(uint256 _a, uint256 _b, string memory _errorMessage) internal pure returns (uint256 _result) {
if (_a == 0 || _b == 0) return 0;
uint256 _c = _a * _b;
require(_c / _a == _b, _errorMessage);
return _c;
}
function _div(Exp memory _a, Exp memory _b) internal pure returns (Exp memory _result) {
return Exp({mantissa: _div(_mul(_a.mantissa, EXP_SCALE), _b.mantissa)});
}
function _div(Exp memory _a, uint256 _b) internal pure returns (Exp memory _result) {
return Exp({mantissa: _div(_a.mantissa, _b)});
}
function _div(uint256 _a, Exp memory _b) internal pure returns (uint256 _result) {
return _div(_mul(_a, EXP_SCALE), _b.mantissa);
}
function _div(Double memory _a, Double memory _b) internal pure returns (Double memory _result) {
return Double({mantissa: _div(_mul(_a.mantissa, DOUBLE_SCALE), _b.mantissa)});
}
function _div(Double memory _a, uint256 _b) internal pure returns (Double memory _result) {
return Double({mantissa: _div(_a.mantissa, _b)});
}
function _div(uint256 _a, Double memory _b) internal pure returns (uint256 _result) {
return _div(_mul(_a, DOUBLE_SCALE), _b.mantissa);
}
function _div(uint256 _a, uint256 _b) internal pure returns (uint256 _result) {
return _div(_a, _b, 'divide by zero');
}
function _div(uint256 _a, uint256 _b, string memory _errorMessage) internal pure returns (uint256 _result) {
require(_b > 0, _errorMessage);
return _a / _b;
}
function _fraction(uint256 _a, uint256 _b) internal pure returns (Double memory _result) {
return Double({mantissa: _div(_mul(_a, DOUBLE_SCALE), _b)});
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4 <0.9.0;
import {IRoles} from '@interfaces/utils/IRoles.sol';
import {AccessControl} from '@openzeppelin/contracts/access/AccessControl.sol';
abstract contract Roles is IRoles, AccessControl {
// @notice Checks if an account has a particular role
// @param _role The role that the account needs to have
// @param _account The account to check for the role
function _checkRole(bytes32 _role, address _account) internal view override {
if (!hasRole(_role, _account)) revert Roles_Unauthorized(_account, _role);
}
}
// SPDX-License-Identifier: MIT
/* solhint-disable */
pragma solidity ^0.8.9;
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
import {IERC20Metadata} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';
// @title uFragments ERC20 token
// @notice USDA uses the uFragments concept from the Ideal Money project to play interest
// Implementation is shamelessly borrowed from Ampleforth project
// uFragments is a normal ERC20 token, but its supply can be adjusted by splitting and
// combining tokens proportionally across all wallets.
//
//
// uFragment balances are internally represented with a hidden denomination, 'gons'.
// We support splitting the currency in expansion and combining the currency on contraction by
// changing the exchange rate between the hidden 'gons' and the public 'fragments'.
contract UFragments is Ownable, IERC20Metadata {
// PLEASE READ BEFORE CHANGING ANY ACCOUNTING OR MATH
// Anytime there is division, there is a risk of numerical instability from rounding errors. In
// order to minimize this risk, we adhere to the following guidelines:
// 1) The conversion rate adopted is the number of gons that equals 1 fragment.
// The inverse rate must not be used--_totalGons is always the numerator and _totalSupply is
// always the denominator. (i.e. If you want to convert gons to fragments instead of
// multiplying by the inverse rate, you should divide by the normal rate)
// 2) Gon balances converted into Fragments are always rounded down (truncated).
//
// We make the following guarantees:
// - If address 'A' transfers x Fragments to address 'B'. A's resulting external balance will
// be decreased by precisely x Fragments, and B's external balance will be precisely
// increased by x Fragments.
//
// We do not guarantee that the sum of all balances equals the result of calling totalSupply().
// This is because, for any conversion function 'f()' that has non-zero rounding error,
// f(x0) + f(x1) + ... + f(xn) is not always equal to f(x0 + x1 + ... xn).
event LogRebase(uint256 indexed epoch, uint256 totalSupply);
event LogMonetaryPolicyUpdated(address monetaryPolicy);
/// @notice Thrown when the signature is invalid
error UFragments_InvalidSignature();
/// @notice Thrown when the recipient is invalid
error UFragments_InvalidRecipient();
// Used for authentication
address public monetaryPolicy;
modifier onlyMonetaryPolicy() {
require(msg.sender == monetaryPolicy);
_;
}
modifier validRecipient(address _to) {
if (_to == address(0) || _to == address(this)) revert UFragments_InvalidRecipient();
_;
}
uint256 private constant DECIMALS = 18;
uint256 private constant MAX_UINT256 = 2 ** 256 - 1;
uint256 private constant INITIAL_FRAGMENTS_SUPPLY = 1 * 10 ** DECIMALS;
// _totalGons is a multiple of INITIAL_FRAGMENTS_SUPPLY so that _gonsPerFragment is an integer.
// Use the highest value that fits in a uint256 for max granularity.
uint256 public _totalGons; // = INITIAL_FRAGMENTS_SUPPLY * 10**48;
// MAX_SUPPLY = maximum integer < (sqrt(4*_totalGons + 1) - 1) / 2
uint256 public MAX_SUPPLY; // = type(uint128).max; // (2^128) - 1
uint256 public _totalSupply;
uint256 public _gonsPerFragment;
mapping(address => uint256) public _gonBalances;
string public name;
string public symbol;
uint8 public constant decimals = uint8(DECIMALS);
// This is denominated in Fragments, because the gons-fragments conversion might change before
// it's fully paid.
mapping(address => mapping(address => uint256)) private _allowedFragments;
// EIP-2612: permit – 712-signed approvals
// https://eips.ethereum.org/EIPS/eip-2612
string public constant EIP712_REVISION = '1';
bytes32 public constant EIP712_DOMAIN =
keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)');
bytes32 public constant PERMIT_TYPEHASH =
keccak256('Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)');
// EIP-2612: keeps track of number of permits per address
mapping(address => uint256) private _nonces;
constructor(string memory _name, string memory _symbol) {
name = _name;
symbol = _symbol;
//set og initial values
_totalGons = INITIAL_FRAGMENTS_SUPPLY * 10 ** 48;
MAX_SUPPLY = 2 ** 128 - 1;
_totalSupply = INITIAL_FRAGMENTS_SUPPLY;
_gonBalances[address(0x0)] = _totalGons; //send starting supply to a burner address so _totalSupply is never 0
_gonsPerFragment = _totalGons / _totalSupply;
emit Transfer(address(this), address(0x0), _totalSupply);
}
// @param _monetaryPolicy The address of the monetary policy contract to use for authentication.
function setMonetaryPolicy(address _monetaryPolicy) external onlyOwner {
monetaryPolicy = _monetaryPolicy;
emit LogMonetaryPolicyUpdated(_monetaryPolicy);
}
// @notice returns the total supply
// @return __totalSupply The total number of fragments.
function totalSupply() external view override returns (uint256 __totalSupply) {
return _totalSupply;
}
// @param _who The address to query.
// @return _balance The balance of the specified address.
function balanceOf(address _who) external view override returns (uint256 _balance) {
return _gonBalances[_who] / _gonsPerFragment;
}
// @param _who The address to query.
// @return _balance The gon balance of the specified address.
function scaledBalanceOf(address _who) external view returns (uint256 _balance) {
return _gonBalances[_who];
}
// @notice Returns the scaled total supply
// @return __totalGons the total number of gons.
function scaledTotalSupply() external view returns (uint256 __totalGons) {
return _totalGons;
}
// @notice Returns the nonces of a given address
// @param _who The address to query.
// @return _addressNonces The number of successful permits by the specified address.
function nonces(address _who) public view returns (uint256 _addressNonces) {
return _nonces[_who];
}
// @notice Returns the EIP712 domain separator
// @return _domainSeparator The computed DOMAIN_SEPARATOR to be used off-chain services
// which implement EIP-712.
// https://eips.ethereum.org/EIPS/eip-2612
function DOMAIN_SEPARATOR() public view returns (bytes32 _domainSeparator) {
uint256 _chainId;
assembly {
_chainId := chainid()
}
return keccak256(
abi.encode(EIP712_DOMAIN, keccak256(bytes(name)), keccak256(bytes(EIP712_REVISION)), _chainId, address(this))
);
}
// @notice Transfer tokens to a specified address.
// @param _to The address to transfer to.
// @param _value The amount to be transferred.
// @return _success True on success, false otherwise.
function transfer(address _to, uint256 _value) external override validRecipient(_to) returns (bool _success) {
uint256 _gonValue = _value * _gonsPerFragment;
_gonBalances[msg.sender] = _gonBalances[msg.sender] - _gonValue;
_gonBalances[_to] = _gonBalances[_to] + _gonValue;
emit Transfer(msg.sender, _to, _value);
return true;
}
// @notice Transfer all of the sender's wallet balance to a specified address.
// @param _to The address to transfer to.
// @return _success True on success, false otherwise.
function transferAll(address _to) external validRecipient(_to) returns (bool _success) {
uint256 _gonValue = _gonBalances[msg.sender];
uint256 _value = _gonValue / _gonsPerFragment;
delete _gonBalances[msg.sender];
_gonBalances[_to] = _gonBalances[_to] + _gonValue;
emit Transfer(msg.sender, _to, _value);
return true;
}
// @notice Function to check the amount of tokens that an owner has allowed to a spender.
// @param _owner The address which owns the funds.
// @param _spender The address which will spend the funds.
// @return _remaining The number of tokens still available for the _spender.
function allowance(address _owner, address _spender) external view override returns (uint256 _remaining) {
return _allowedFragments[_owner][_spender];
}
// @notice Transfer tokens from one address to another.
// @param _from The address you want to send tokens from.
// @param _to The address you want to transfer to.
// @param _value The amount of tokens to be transferred.
// @return _success True on success, false otherwise.
function transferFrom(
address _from,
address _to,
uint256 _value
) external override validRecipient(_to) returns (bool _success) {
_allowedFragments[_from][msg.sender] = _allowedFragments[_from][msg.sender] - _value;
uint256 _gonValue = _value * _gonsPerFragment;
_gonBalances[_from] = _gonBalances[_from] - _gonValue;
_gonBalances[_to] = _gonBalances[_to] + _gonValue;
emit Transfer(_from, _to, _value);
return true;
}
// @notice Transfer all balance tokens from one address to another.
// @param _from The address you want to send tokens from.
// @param _to The address you want to transfer to.
// @return _success True on success, false otherwise.
function transferAllFrom(address _from, address _to) external validRecipient(_to) returns (bool _success) {
uint256 _gonValue = _gonBalances[_from];
uint256 _value = _gonValue / _gonsPerFragment;
_allowedFragments[_from][msg.sender] = _allowedFragments[_from][msg.sender] - _value;
delete _gonBalances[_from];
_gonBalances[_to] = _gonBalances[_to] + _gonValue;
emit Transfer(_from, _to, _value);
return true;
}
// @notice Approve the passed address to spend the specified amount of tokens on behalf of
// msg.sender. This method is included for ERC20 compatibility.
// increaseAllowance and decreaseAllowance should be used instead.
// Changing an allowance with this method brings the risk that someone may transfer both
// the old and the new allowance - if they are both greater than zero - if a transfer
// transaction is mined before the later approve() call is mined.
//
// @param _spender The address which will spend the funds.
// @param _value The amount of tokens to be spent.
// @return _success True on success, false otherwise.
function approve(address _spender, uint256 _value) external override returns (bool _success) {
_allowedFragments[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
// @notice Increase the amount of tokens that an owner has allowed to a spender.
// This method should be used instead of approve() to avoid the double approval vulnerability
// described above.
// @param _spender The address which will spend the funds.
// @param _addedValue The amount of tokens to increase the allowance by.
// @return _success True on success, false otherwise.
function increaseAllowance(address _spender, uint256 _addedValue) public returns (bool _success) {
_allowedFragments[msg.sender][_spender] = _allowedFragments[msg.sender][_spender] + _addedValue;
emit Approval(msg.sender, _spender, _allowedFragments[msg.sender][_spender]);
return true;
}
// @notice Decrease the amount of tokens that an owner has allowed to a spender.
// @param _spender The address which will spend the funds.
// @param _subtractedValue The amount of tokens to decrease the allowance by.
// @return _success True on success, false otherwise.
function decreaseAllowance(address _spender, uint256 _subtractedValue) external returns (bool _success) {
uint256 _oldValue = _allowedFragments[msg.sender][_spender];
_allowedFragments[msg.sender][_spender] = (_subtractedValue >= _oldValue) ? 0 : _oldValue - _subtractedValue;
emit Approval(msg.sender, _spender, _allowedFragments[msg.sender][_spender]);
return true;
}
// @notice Allows for approvals to be made via secp256k1 signatures.
// @param _owner The owner of the funds
// @param _spender The _spender
// @param _value The amount
// @param _deadline The deadline timestamp, type(uint256).max for max deadline
// @param _v Signature param
// @param _s Signature param
// @param _r Signature param
function permit(
address _owner,
address _spender,
uint256 _value,
uint256 _deadline,
uint8 _v,
bytes32 _r,
bytes32 _s
) public {
require(block.timestamp <= _deadline);
uint256 _ownerNonce = _nonces[_owner];
bytes32 _permitDataDigest = keccak256(abi.encode(PERMIT_TYPEHASH, _owner, _spender, _value, _ownerNonce, _deadline));
bytes32 _digest = keccak256(abi.encodePacked('\x19\x01', DOMAIN_SEPARATOR(), _permitDataDigest));
if (uint256(_s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
revert UFragments_InvalidSignature();
}
require(_owner == ecrecover(_digest, _v, _r, _s));
if (_owner == address(0x0)) revert UFragments_InvalidSignature();
_nonces[_owner] = _ownerNonce + 1;
_allowedFragments[_owner][_spender] = _value;
emit Approval(_owner, _spender, _value);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import {IRoles} from '@interfaces/utils/IRoles.sol';
import {IERC20Metadata, IERC20} from '@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol';
/// @title USDA Interface
/// @notice extends IERC20Metadata
interface IUSDA is IERC20Metadata, IRoles {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
// @notice Emitted when a deposit is made
// @param _from The address which made the deposit
// @param _value The value deposited
event Deposit(address indexed _from, uint256 _value);
// @notice Emitted when a withdraw is made
// @param _from The address which made the withdraw
// @param _value The value withdrawn
event Withdraw(address indexed _from, uint256 _value);
// @notice Emitted when a mint is made
// @param _to The address which made the mint
// @param _value The value minted
event Mint(address _to, uint256 _value);
// @notice Emitted when a burn is made
// @param _from The address which made the burn
// @param _value The value burned
event Burn(address _from, uint256 _value);
// @notice Emitted when a donation is made
// @param _from The address which made the donation
// @param _value The value of the donation
// @param _totalSupply The new total supply
event Donation(address indexed _from, uint256 _value, uint256 _totalSupply);
// @notice Emitted when the owner recovers dust
// @param _receiver The address which made the recover
// @param _amount The value recovered
event RecoveredDust(address indexed _receiver, uint256 _amount);
// @notice Emitted when the owner sets a pauser
// @param _pauser The new pauser address
event PauserSet(address indexed _pauser);
// @notice Emitted when a sUSD transfer is made from the vaultController
// @param _target The receiver of the transfer
// @param _susdAmount The amount sent
event VaultControllerTransfer(address _target, uint256 _susdAmount);
// @notice Emitted when the owner adds a new vaultController giving special roles
// @param _vaultController The address of the vault controller
event VaultControllerAdded(address indexed _vaultController);
// @notice Emitted when the owner removes a vaultController removing special roles
// @param _vaultController The address of the vault controller
event VaultControllerRemoved(address indexed _vaultController);
// @notice Emitted when the owner removes a vaultController from the list
// @param _vaultController The address of the vault controller
event VaultControllerRemovedFromList(address indexed _vaultController);
/*///////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
/// @notice Thrown when trying to deposit zero amount
error USDA_ZeroAmount();
/// @notice Thrown when trying to withdraw more than the balance
error USDA_InsufficientFunds();
/// @notice Thrown when trying to withdraw all but the reserve amount is 0
error USDA_EmptyReserve();
/// @notice Thrown when _msgSender is not the pauser of the contract
error USDA_OnlyPauser();
/// @notice Thrown when vault controller is trying to burn more than the balance
error USDA_NotEnoughBalance();
/*///////////////////////////////////////////////////////////////
VARIABLES
//////////////////////////////////////////////////////////////*/
/// @notice Returns sUSD contract (reserve)
/// @return _sUSD The sUSD contract
function sUSD() external view returns (IERC20 _sUSD);
/// @notice Returns the reserve ratio
/// @return _reserveRatio The reserve ratio
function reserveRatio() external view returns (uint192 _reserveRatio);
/// @notice Returns the reserve amount
/// @return _reserveAmount The reserve amount
function reserveAmount() external view returns (uint256 _reserveAmount);
/// @notice The address of the pauser
function pauser() external view returns (address _pauser);
/*///////////////////////////////////////////////////////////////
LOGIC
//////////////////////////////////////////////////////////////*/
/// @notice Deposit sUSD to mint USDA
/// @dev Caller should obtain 1 USDA for each sUSD
/// the calculations for deposit mimic the calculations done by mint in the ampleforth contract, simply with the susd transfer
/// 'fragments' are the units that we see, so 1000 fragments == 1000 USDA
/// 'gons' are the internal accounting unit, used to keep scale.
/// We use the variable _gonsPerFragment in order to convert between the two
/// try dimensional analysis when doing the math in order to verify units are correct
/// @param _susdAmount The amount of sUSD to deposit
function deposit(uint256 _susdAmount) external;
/// @notice Deposits sUSD to mint USDA and transfer to a different address
/// @param _susdAmount The amount of sUSD to deposit
/// @param _target The address to receive the USDA tokens
function depositTo(uint256 _susdAmount, address _target) external;
/// @notice Withdraw sUSD by burning USDA
/// @dev The caller should obtain 1 sUSD for every 1 USDA
/// @param _susdAmount The amount of sUSD to withdraw
function withdraw(uint256 _susdAmount) external;
/// @notice Withdraw sUSD to a specific address by burning USDA from the caller
/// @dev The _target address should obtain 1 sUSD for every 1 USDA burned from the caller
/// @param _susdAmount amount of sUSD to withdraw
/// @param _target address to receive the sUSD
function withdrawTo(uint256 _susdAmount, address _target) external;
/// @notice Withdraw sUSD by burning USDA
/// @dev The caller should obtain 1 sUSD for every 1 USDA
/// @dev This function is effectively just withdraw, but we calculate the amount for the sender
/// @param _susdWithdrawn The amount os sUSD withdrawn
function withdrawAll() external returns (uint256 _susdWithdrawn);
/// @notice Withdraw sUSD by burning USDA
/// @dev This function is effectively just withdraw, but we calculate the amount for the _target
/// @param _target should obtain 1 sUSD for every 1 USDA burned from caller
/// @param _susdWithdrawn The amount os sUSD withdrawn
function withdrawAllTo(address _target) external returns (uint256 _susdWithdrawn);
/// @notice Donates susd to the protocol reserve
/// @param _susdAmount The amount of sUSD to donate
function donate(uint256 _susdAmount) external;
/// @notice Recovers accidentally sent sUSD to this contract
/// @param _to The receiver of the dust
function recoverDust(address _to) external;
/// @notice Sets the pauser for both USDA and VaultController
/// @dev The pauser is a separate role from the owner
function setPauser(address _pauser) external;
/// @notice Pause contract
/// @dev Can only be called by the pauser
function pause() external;
/// @notice Unpause contract, pauser only
/// @dev Can only be called by the pauser
function unpause() external;
/// @notice Admin function to mint USDA
/// @param _susdAmount The amount of USDA to mint, denominated in sUSD
function mint(uint256 _susdAmount) external;
/// @notice Admin function to burn USDA
/// @param _susdAmount The amount of USDA to burn, denominated in sUSD
function burn(uint256 _susdAmount) external;
/// @notice Function for the vaultController to burn
/// @param _target The address to burn the USDA from
/// @param _amount The amount of USDA to burn
function vaultControllerBurn(address _target, uint256 _amount) external;
/// @notice Function for the vaultController to mint
/// @param _target The address to mint the USDA to
/// @param _amount The amount of USDA to mint
function vaultControllerMint(address _target, uint256 _amount) external;
/// @notice Allows VaultController to send sUSD from the reserve
/// @param _target The address to receive the sUSD from reserve
/// @param _susdAmount The amount of sUSD to send
function vaultControllerTransfer(address _target, uint256 _susdAmount) external;
/// @notice Function for the vaultController to scale all USDA balances
/// @param _amount The amount of USDA (e18) to donate
function vaultControllerDonate(uint256 _amount) external;
/// @notice Adds a new vault controller
/// @param _vaultController The new vault controller to add
function addVaultController(address _vaultController) external;
/// @notice Removes a vault controller
/// @param _vaultController The vault controller to remove
function removeVaultController(address _vaultController) external;
/// @notice Removes a vault controller from the loop list
/// @param _vaultController The vault controller to remove
function removeVaultControllerFromList(address _vaultController) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import {CurveMaster} from '@contracts/periphery/CurveMaster.sol';
import {IOracleRelay} from '@interfaces/periphery/IOracleRelay.sol';
import {IBooster} from '@interfaces/utils/IBooster.sol';
import {IBaseRewardPool} from '@interfaces/utils/IBaseRewardPool.sol';
import {IVaultDeployer} from '@interfaces/core/IVaultDeployer.sol';
import {IAMPHClaimer} from '@interfaces/core/IAMPHClaimer.sol';
import {IUSDA} from '@interfaces/core/IUSDA.sol';
/// @title VaultController Interface
interface IVaultController {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
// @notice Emited when payInterest is called to accrue interest and distribute it
// @param _epoch The block timestamp when the function called
// @param _amount The increase amount of the interest factor
// @param _curveVal The value at the curve
event InterestEvent(uint64 _epoch, uint192 _amount, uint256 _curveVal);
// @notice Emited when a new protocol fee is being set
// @param _protocolFee The new fee for the protocol
event NewProtocolFee(uint192 _protocolFee);
// @notice Emited when a new erc20 token is being registered as acceptable collateral
// @param _tokenAddress The addres of the erc20 token
// @param _ltv The loan to value amount of the erc20
// @param _oracleAddress The address of the oracle to use to fetch the price
// @param _liquidationIncentive The liquidation penalty for the token
// @param _cap The maximum amount that can be deposited
event RegisteredErc20(
address _tokenAddress, uint256 _ltv, address _oracleAddress, uint256 _liquidationIncentive, uint256 _cap
);
// @notice Emited when the information about an acceptable erc20 token is being update
// @param _tokenAddress The addres of the erc20 token to update
// @param _ltv The new loan to value amount of the erc20
// @param _oracleAddress The new address of the oracle to use to fetch the price
// @param _liquidationIncentive The new liquidation penalty for the token
// @param _cap The maximum amount that can be deposited
// @param _poolId The convex pool id of a crv lp token
event UpdateRegisteredErc20(
address _tokenAddress,
uint256 _ltv,
address _oracleAddress,
uint256 _liquidationIncentive,
uint256 _cap,
uint256 _poolId
);
// @notice Emited when a new vault is being minted
// @param _vaultAddress The address of the new vault
// @param _vaultId The id of the vault
// @param _vaultOwner The address of the owner of the vault
event NewVault(address _vaultAddress, uint256 _vaultId, address _vaultOwner);
// @notice Emited when the owner registers a curve master
// @param _curveMasterAddress The address of the curve master
event RegisterCurveMaster(address _curveMasterAddress);
// @notice Emited when someone successfully borrows USDA
// @param _vaultId The id of the vault that borrowed against
// @param _vaultAddress The address of the vault that borrowed against
// @param _borrowAmount The amounnt that was borrowed
// @param _fee The fee assigned to the treasury
event BorrowUSDA(uint256 _vaultId, address _vaultAddress, uint256 _borrowAmount, uint256 _fee);
// @notice Emited when someone successfully repayed a vault's loan
// @param _vaultId The id of the vault that was repayed
// @param _vaultAddress The address of the vault that was repayed
// @param _repayAmount The amount that was repayed
event RepayUSDA(uint256 _vaultId, address _vaultAddress, uint256 _repayAmount);
// @notice Emited when someone successfully liquidates a vault
// @param _vaultId The id of the vault that was liquidated
// @param _assetAddress The address of the token that was liquidated
// @param _usdaToRepurchase The amount of USDA that was repurchased
// @param _tokensToLiquidate The number of tokens that were taken from the vault and sent to the liquidator
// @param _liquidationFee The number of tokens that were taken from the fee and sent to the treasury
event Liquidate(
uint256 _vaultId,
address _assetAddress,
uint256 _usdaToRepurchase,
uint256 _tokensToLiquidate,
uint256 _liquidationFee
);
// @notice Emited when governance changes the claimer contract
// @param _oldClaimerContract The old claimer contract
// @param _newClaimerContract The new claimer contract
event ChangedClaimerContract(IAMPHClaimer _oldClaimerContract, IAMPHClaimer _newClaimerContract);
// @notice Emited when the owner registers the USDA contract
// @param _usdaContractAddress The address of the USDA contract
event RegisterUSDA(address _usdaContractAddress);
// @notice Emited when governance changes the initial borrowing fee
// @param _oldBorrowingFee The old borrowing fee
// @param _newBorrowingFee The new borrowing fee
event ChangedInitialBorrowingFee(uint192 _oldBorrowingFee, uint192 _newBorrowingFee);
// @notice Emited when governance changes the liquidation fee
// @param _oldLiquidationFee The old liquidation fee
// @param _newLiquidationFee The new liquidation fee
event ChangedLiquidationFee(uint192 _oldLiquidationFee, uint192 _newLiquidationFee);
// @notice Emited when collaterals are migrated from old vault controller
// @param _oldVaultController The old vault controller migrated from
// @param _tokenAddresses The list of new collaterals
event CollateralsMigratedFrom(IVaultController _oldVaultController, address[] _tokenAddresses);
/*///////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
/// @notice Thrown when token has invalid amount of decimals
error VaultController_TooManyDecimals();
/// @notice Thrown when _msgSender is not the pauser of the contract
error VaultController_OnlyPauser();
/// @notice Thrown when the fee is too large
error VaultController_FeeTooLarge();
/// @notice Thrown when oracle does not exist
error VaultController_OracleNotRegistered();
/// @notice Thrown when the token is already registered
error VaultController_TokenAlreadyRegistered();
/// @notice Thrown when the token is not registered
error VaultController_TokenNotRegistered();
/// @notice Thrown when the _ltv is incompatible
error VaultController_LTVIncompatible();
/// @notice Thrown when _msgSender is not the minter
error VaultController_OnlyMinter();
/// @notice Thrown when vault is insolvent
error VaultController_VaultInsolvent();
/// @notice Thrown when repay is grater than borrow
error VaultController_RepayTooMuch();
/// @notice Thrown when trying to liquidate 0 tokens
error VaultController_LiquidateZeroTokens();
/// @notice Thrown when trying to liquidate more than is possible
error VaultController_OverLiquidation();
/// @notice Thrown when vault is solvent
error VaultController_VaultSolvent();
/// @notice Thrown when vault does not exist
error VaultController_VaultDoesNotExist();
/// @notice Thrown when migrating collaterals to a new vault controller
error VaultController_WrongCollateralAddress();
/// @notice Thrown when a not valid vault is trying to modify the total deposited
error VaultController_NotValidVault();
/// @notice Thrown when a deposit surpass the cap
error VaultController_CapReached();
/// @notice Thrown when registering a crv lp token with wrong address
error VaultController_TokenAddressDoesNotMatchLpAddress();
/*///////////////////////////////////////////////////////////////
ENUMS
//////////////////////////////////////////////////////////////*/
enum CollateralType {
Single,
CurveLPStakedOnConvex
}
/*///////////////////////////////////////////////////////////////
STRUCTS
//////////////////////////////////////////////////////////////*/
struct VaultSummary {
uint96 id;
uint192 borrowingPower;
uint192 vaultLiability;
address[] tokenAddresses;
uint256[] tokenBalances;
}
struct Interest {
uint64 lastTime;
uint192 factor;
}
struct CollateralInfo {
uint256 tokenId;
uint256 ltv;
uint256 cap;
uint256 totalDeposited;
uint256 liquidationIncentive;
IOracleRelay oracle;
CollateralType collateralType;
IBaseRewardPool crvRewardsContract;
uint256 poolId;
uint256 decimals;
}
/*///////////////////////////////////////////////////////////////
VARIABLES
//////////////////////////////////////////////////////////////*/
/// @notice Total number of tokens registered
function tokensRegistered() external view returns (uint256 _tokensRegistered);
/// @notice Total number of minted vaults
function vaultsMinted() external view returns (uint96 _vaultsMinted);
/// @notice Returns the block timestamp when pay interest was last called
/// @return _lastInterestTime The block timestamp when pay interest was last called
function lastInterestTime() external view returns (uint64 _lastInterestTime);
/// @notice Total base liability
function totalBaseLiability() external view returns (uint192 _totalBaseLiability);
/// @notice Returns the latest interest factor
/// @return _interestFactor The latest interest factor
function interestFactor() external view returns (uint192 _interestFactor);
/// @notice The protocol's fee
function protocolFee() external view returns (uint192 _protocolFee);
/// @notice The max allowed to be set as borrowing fee
function MAX_INIT_BORROWING_FEE() external view returns (uint192 _maxInitBorrowingFee);
/// @notice The initial borrowing fee (1e18 == 100%)
function initialBorrowingFee() external view returns (uint192 _initialBorrowingFee);
/// @notice The fee taken from the liquidator profit (1e18 == 100%)
function liquidationFee() external view returns (uint192 _liquidationFee);
/// @notice Returns an array of all the vault ids a specific wallet has
/// @param _wallet The address of the wallet to target
/// @return _vaultIDs The ids of the vaults the wallet has
function vaultIDs(address _wallet) external view returns (uint96[] memory _vaultIDs);
/// @notice Returns an array of all enabled tokens
/// @return _enabledToken The array containing the token addresses
function enabledTokens(uint256 _index) external view returns (address _enabledToken);
/// @notice Returns the address of the curve master
function curveMaster() external view returns (CurveMaster _curveMaster);
/// @notice Returns the token id given a token's address
/// @param _tokenAddress The address of the token to target
/// @return _tokenId The id of the token
function tokenId(address _tokenAddress) external view returns (uint256 _tokenId);
/// @notice Returns the oracle given a token's address
/// @param _tokenAddress The id of the token
/// @return _oracle The address of the token's oracle
function tokensOracle(address _tokenAddress) external view returns (IOracleRelay _oracle);
/// @notice Returns the ltv of a given token address
/// @param _tokenAddress The address of the token
/// @return _ltv The loan-to-value of a token
function tokenLTV(address _tokenAddress) external view returns (uint256 _ltv);
/// @notice Returns the liquidation incentive of an accepted token collateral
/// @param _tokenAddress The address of the token
/// @return _liquidationIncentive The liquidation incentive of the token
function tokenLiquidationIncentive(address _tokenAddress) external view returns (uint256 _liquidationIncentive);
/// @notice Returns the cap of a given token address
/// @param _tokenAddress The address of the token
/// @return _cap The cap of the token
function tokenCap(address _tokenAddress) external view returns (uint256 _cap);
/// @notice Returns the total deposited of a given token address
/// @param _tokenAddress The address of the token
/// @return _totalDeposited The total deposited of a token
function tokenTotalDeposited(address _tokenAddress) external view returns (uint256 _totalDeposited);
/// @notice Returns the collateral type of a token
/// @param _tokenAddress The address of the token
/// @return _type The collateral type of a token
function tokenCollateralType(address _tokenAddress) external view returns (CollateralType _type);
/// @notice Returns the address of the crvRewards contract
/// @param _tokenAddress The address of the token
/// @return _crvRewardsContract The address of the crvRewards contract
function tokenCrvRewardsContract(address _tokenAddress) external view returns (IBaseRewardPool _crvRewardsContract);
/// @notice Returns the pool id of a curve LP type token
/// @dev If the token is not of type CurveLPStakedOnConvex then it returns 0
/// @param _tokenAddress The address of the token
/// @return _poolId The pool id of a curve LP type token
function tokenPoolId(address _tokenAddress) external view returns (uint256 _poolId);
/// @notice Returns the collateral info of a given token address
/// @param _tokenAddress The address of the token
/// @return _collateralInfo The complete collateral info of the token
function tokenCollateralInfo(address _tokenAddress) external view returns (CollateralInfo memory _collateralInfo);
/// @notice The convex booster interface
function BOOSTER() external view returns (IBooster _booster);
/// @notice The amphora claimer interface
function claimerContract() external view returns (IAMPHClaimer _claimerContract);
/// @notice The vault deployer interface
function VAULT_DEPLOYER() external view returns (IVaultDeployer _vaultDeployer);
/// @notice The max decimals allowed for a listed token
function MAX_DECIMALS() external view returns (uint8 _maxDecimals);
/// @notice Returns an array of all enabled tokens
/// @return _enabledTokens The array containing the token addresses
function getEnabledTokens() external view returns (address[] memory _enabledTokens);
/// @notice Returns the selected collaterals info. Will iterate from `_start` (included) until `_end` (not included)
/// @param _start The start number to loop on the array
/// @param _end The end number to loop on the array
/// @return _collateralsInfo The array containing all the collateral info
function getCollateralsInfo(
uint256 _start,
uint256 _end
) external view returns (CollateralInfo[] memory _collateralsInfo);
/// @notice Returns the address of a vault given it's id
/// @param _vaultID The id of the vault to target
/// @return _vaultAddress The address of the targetted vault
function vaultIdVaultAddress(uint96 _vaultID) external view returns (address _vaultAddress);
/// @notice Returns true if a base reward contract was approved by governance
/// @param _baseRewardContract The address of the baseRewardcontract to check
/// @return _approved True if approved by governance
function baseRewardContracts(address _baseRewardContract) external view returns (bool _approved);
/// @notice Mapping of token address to collateral info
function tokenAddressCollateralInfo(address _token)
external
view
returns (
uint256 _tokenId,
uint256 _ltv,
uint256 _cap,
uint256 _totalDeposited,
uint256 _liquidationIncentive,
IOracleRelay _oracle,
CollateralType _collateralType,
IBaseRewardPool _crvRewardsContract,
uint256 _poolId,
uint256 _decimals
);
/// @notice The interest contract
function interest() external view returns (uint64 _lastTime, uint192 _factor);
/// @notice The usda interface
function usda() external view returns (IUSDA _usda);
/*///////////////////////////////////////////////////////////////
LOGIC
//////////////////////////////////////////////////////////////*/
/// @notice Returns the amount of USDA needed to reach even solvency without state changes
/// @dev This amount is a moving target and changes with each block as payInterest is called
/// @param _id The id of vault we want to target
/// @return _usdaToSolvency The amount of USDA needed to reach even solvency
function amountToSolvency(uint96 _id) external view returns (uint256 _usdaToSolvency);
/// @notice Returns vault liability of vault
/// @param _id The id of vault
/// @return _liability The amount of USDA the vault owes
function vaultLiability(uint96 _id) external view returns (uint192 _liability);
/// @notice Returns the vault borrowing power for vault
/// @dev Implementation in getVaultBorrowingPower
/// @param _id The id of vault we want to target
/// @return _borrowPower The amount of USDA the vault can borrow
function vaultBorrowingPower(uint96 _id) external view returns (uint192 _borrowPower);
/// @notice Returns the calculated amount of tokens to liquidate for a vault
/// @dev The amount of tokens owed is a moving target and changes with each block as payInterest is called
/// This function can serve to give an indication of how many tokens can be liquidated
/// All this function does is call _liquidationMath with 2**256-1 as the amount
/// @param _id The id of vault we want to target
/// @param _token The address of token to calculate how many tokens to liquidate
/// @return _tokensToLiquidate The amount of tokens liquidatable
function tokensToLiquidate(uint96 _id, address _token) external view returns (uint256 _tokensToLiquidate);
/// @notice Check a vault for over-collateralization
/// @dev This function calls peekVaultBorrowingPower so no state change is done
/// @param _id The id of vault we want to target
/// @return _overCollateralized Returns true if vault over-collateralized; false if vault under-collaterlized
function peekCheckVault(uint96 _id) external view returns (bool _overCollateralized);
/// @notice Check a vault for over-collateralization
/// @dev This function calls getVaultBorrowingPower to allow state changes to happen if an oracle need them
/// @param _id The id of vault we want to target
/// @return _overCollateralized Returns true if vault over-collateralized; false if vault under-collaterlized
function checkVault(uint96 _id) external returns (bool _overCollateralized);
/// @notice Returns the status of a range of vaults
/// @dev Special view only function to help liquidators
/// @param _start The id of the vault to start looping
/// @param _stop The id of vault to stop looping
/// @return _vaultSummaries An array of vault information
function vaultSummaries(uint96 _start, uint96 _stop) external view returns (VaultSummary[] memory _vaultSummaries);
/// @notice Returns the initial borrowing fee
/// @param _amount The base amount
/// @return _fee The fee calculated based on a base amount
function getBorrowingFee(uint192 _amount) external view returns (uint192 _fee);
/// @notice Returns the liquidation fee
/// @param _tokensToLiquidate The collateral amount
/// @param _assetAddress The collateral address to liquidate
/// @return _fee The fee calculated based on amount
function getLiquidationFee(uint192 _tokensToLiquidate, address _assetAddress) external view returns (uint192 _fee);
/// @notice Returns the increase amount of the interest factor. Accrues interest to borrowers and distribute it to USDA holders
/// @dev Implementation in payInterest
/// @return _interest The increase amount of the interest factor
function calculateInterest() external returns (uint256 _interest);
/// @notice Creates a new vault and returns it's address
/// @return _vaultAddress The address of the newly created vault
function mintVault() external returns (address _vaultAddress);
/// @notice Simulates the liquidation of an underwater vault
/// @param _id The id of vault we want to target
/// @param _assetAddress The address of the token the liquidator wishes to liquidate
/// @param _tokensToLiquidate The number of tokens to liquidate
/// @return _collateralLiquidated The number of collateral tokens the liquidator will receive
/// @return _usdaPaid The amount of USDA the liquidator will have to pay
function simulateLiquidateVault(
uint96 _id,
address _assetAddress,
uint256 _tokensToLiquidate
) external view returns (uint256 _collateralLiquidated, uint256 _usdaPaid);
/// @notice Liquidates an underwater vault
/// @dev Pays interest before liquidation. Vaults may be liquidated up to the point where they are exactly solvent
/// @param _id The id of vault we want to target
/// @param _assetAddress The address of the token the liquidator wishes to liquidate
/// @param _tokensToLiquidate The number of tokens to liquidate
/// @return _toLiquidate The number of tokens that got liquidated
function liquidateVault(
uint96 _id,
address _assetAddress,
uint256 _tokensToLiquidate
) external returns (uint256 _toLiquidate);
/// @notice Borrows USDA from a vault. Only the vault minter may borrow from their vault
/// @param _id The id of vault we want to target
/// @param _amount The amount of USDA to borrow
function borrowUSDA(uint96 _id, uint192 _amount) external;
/// @notice Borrows USDA from a vault and send the USDA to a specific address
/// @param _id The id of vault we want to target
/// @param _amount The amount of USDA to borrow
/// @param _target The address to receive borrowed USDA
function borrowUSDAto(uint96 _id, uint192 _amount, address _target) external;
/// @notice Borrows sUSD directly from reserve, liability is still in USDA, and USDA must be repaid
/// @param _id The id of vault we want to target
/// @param _susdAmount The amount of sUSD to borrow
/// @param _target The address to receive borrowed sUSD
function borrowsUSDto(uint96 _id, uint192 _susdAmount, address _target) external;
/// @notice Repays a vault's USDA loan. Anyone may repay
/// @dev Pays interest
/// @param _id The id of vault we want to target
/// @param _amount The amount of USDA to repay
function repayUSDA(uint96 _id, uint192 _amount) external;
/// @notice Repays all of a vault's USDA. Anyone may repay a vault's liabilities
/// @dev Pays interest
/// @param _id The id of vault we want to target
function repayAllUSDA(uint96 _id) external;
/// @notice External function used by vaults to increase or decrease the `totalDeposited`.
/// @dev Should only be called by a valid vault
/// @param _vaultID The id of vault which is calling (used to verify)
/// @param _amount The amount to modify
/// @param _token The token address which should modify the total
/// @param _increase Boolean that indicates if should increase or decrease (TRUE -> increase, FALSE -> decrease)
function modifyTotalDeposited(uint96 _vaultID, uint256 _amount, address _token, bool _increase) external;
/// @notice Pauses the functionality of the contract
function pause() external;
/// @notice Unpauses the functionality of the contract
function unpause() external;
/// @notice Emited when the owner registers a curve master
/// @param _masterCurveAddress The address of the curve master
function registerCurveMaster(address _masterCurveAddress) external;
/// @notice Updates the protocol fee
/// @param _newProtocolFee The new protocol fee in terms of 1e18=100%
function changeProtocolFee(uint192 _newProtocolFee) external;
/// @notice Register a new token to be used as collateral
/// @param _tokenAddress The address of the token to register
/// @param _ltv The ltv of the token, 1e18=100%
/// @param _oracleAddress The address of oracle to fetch the price of the token
/// @param _liquidationIncentive The liquidation penalty for the token, 1e18=100%
/// @param _cap The maximum amount to be deposited
function registerErc20(
address _tokenAddress,
uint256 _ltv,
address _oracleAddress,
uint256 _liquidationIncentive,
uint256 _cap,
uint256 _poolId
) external;
/// @notice Registers the USDA contract
/// @param _usdaAddress The address to register as USDA
function registerUSDA(address _usdaAddress) external;
/// @notice Updates an existing collateral with new collateral parameters
/// @param _tokenAddress The address of the token to modify
/// @param _ltv The new loan-to-value of the token, 1e18=100%
/// @param _oracleAddress The address of oracle to modify for the price of the token
/// @param _liquidationIncentive The new liquidation penalty for the token, 1e18=100%
/// @param _cap The maximum amount to be deposited
/// @param _poolId The convex pool id of a crv lp token
function updateRegisteredErc20(
address _tokenAddress,
uint256 _ltv,
address _oracleAddress,
uint256 _liquidationIncentive,
uint256 _cap,
uint256 _poolId
) external;
/// @notice Change the claimer contract, used to exchange a fee from curve lp rewards for AMPH tokens
/// @param _newClaimerContract The new claimer contract
function changeClaimerContract(IAMPHClaimer _newClaimerContract) external;
/// @notice Change the initial borrowing fee
/// @param _newBorrowingFee The new borrowing fee
function changeInitialBorrowingFee(uint192 _newBorrowingFee) external;
/// @notice Change the liquidation fee
/// @param _newLiquidationFee The new liquidation fee
function changeLiquidationFee(uint192 _newLiquidationFee) external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
bool private _paused;
/**
* @dev Initializes the contract in unpaused state.
*/
constructor() {
_paused = false;
}
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
require(!paused(), "Pausable: paused");
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
require(paused(), "Pausable: not paused");
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.
pragma solidity ^0.8.0;
/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/
library EnumerableSet {
// To implement this library for multiple types with as little code
// repetition as possible, we write it in terms of a generic Set type with
// bytes32 values.
// The Set implementation uses private functions, and user-facing
// implementations (such as AddressSet) are just wrappers around the
// underlying Set.
// This means that we can only create new EnumerableSets for types that fit
// in bytes32.
struct Set {
// Storage of set values
bytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0
// means a value is not in the set.
mapping(bytes32 => uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function _add(Set storage set, bytes32 value) private returns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes
// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
return true;
} else {
return false;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function _remove(Set storage set, bytes32 value) private returns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slot
uint256 valueIndex = set._indexes[value];
if (valueIndex != 0) {
// Equivalent to contains(set, value)
// To delete an element from the _values array in O(1), we swap the element to delete with the last one in
// the array, and then remove the last element (sometimes called as 'swap and pop').
// This modifies the order of the array, as noted in {at}.
uint256 toDeleteIndex = valueIndex - 1;
uint256 lastIndex = set._values.length - 1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slot
delete set._indexes[value];
return true;
} else {
return false;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function _contains(Set storage set, bytes32 value) private view returns (bool) {
return set._indexes[value] != 0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/
function _length(Set storage set) private view returns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function _at(Set storage set, uint256 index) private view returns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function _values(Set storage set) private view returns (bytes32[] memory) {
return set._values;
}
// Bytes32Set
struct Bytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(Bytes32Set storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// AddressSet
struct AddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(AddressSet storage set, address value) internal returns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(AddressSet storage set, address value) internal returns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(AddressSet storage set, address value) internal view returns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(AddressSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(AddressSet storage set, uint256 index) internal view returns (address) {
return address(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(AddressSet storage set) internal view returns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
// UintSet
struct UintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/
function add(UintSet storage set, uint256 value) internal returns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/
function remove(UintSet storage set, uint256 value) internal returns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/
function contains(UintSet storage set, uint256 value) internal view returns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/
function length(UintSet storage set) internal view returns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/
function at(UintSet storage set, uint256 index) internal view returns (uint256) {
return uint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/
function values(UintSet storage set) internal view returns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assembly
assembly {
result := store
}
return result;
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/draft-IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
function safeTransfer(
IERC20 token,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
}
function safeTransferFrom(
IERC20 token,
address from,
address to,
uint256 value
) internal {
_callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(
IERC20 token,
address spender,
uint256 value
) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
function safeIncreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
uint256 newAllowance = token.allowance(address(this), spender) + value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
function safeDecreaseAllowance(
IERC20 token,
address spender,
uint256 value
) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
uint256 newAllowance = oldAllowance - value;
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
}
}
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
if (returndata.length > 0) {
// Return data is optional
require(abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
}
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4 <0.9.0;
import {IAccessControl} from '@openzeppelin/contracts/access/IAccessControl.sol';
// @title Roles contract
// @notice Manages the roles for interactions with a contract
interface IRoles is IAccessControl {
/*///////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
// @notice Thrown when the caller of the function is not an authorized role
error Roles_Unauthorized(address _account, bytes32 _role);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../utils/introspection/ERC165.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it.
*/
abstract contract AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
* Overriding this function changes the behavior of the {onlyRole} modifier.
*
* Format of the revert message is described in {_checkRole}.
*
* _Available since v4.6._
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(account),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* May emit a {RoleGranted} event.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)
pragma solidity ^0.8.0;
import "../utils/Context.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/
abstract contract Ownable is Context {
address private _owner;
event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/
constructor() {
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/
modifier onlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/
function owner() public view virtual returns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/
function _checkOwner() internal view virtual {
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/
function renounceOwnership() public virtual onlyOwner {
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/
function transferOwnership(address newOwner) public virtual onlyOwner {
require(newOwner != address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/
function _transferOwnership(address newOwner) internal virtual {
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import {ICurveMaster} from '@interfaces/periphery/ICurveMaster.sol';
import {ICurveSlave} from '@interfaces/utils/ICurveSlave.sol';
import {IVaultController} from '@interfaces/core/IVaultController.sol';
import {Ownable} from '@openzeppelin/contracts/access/Ownable.sol';
/// @notice Curve master keeps a record of CurveSlave contracts and links it with an address
/// @dev All numbers should be scaled to 1e18. for instance, number 5e17 represents 50%
contract CurveMaster is ICurveMaster, Ownable {
/// @dev Mapping of token to address
mapping(address => address) public curves;
/// @dev The vault controller address
address public vaultControllerAddress;
/// @notice Returns the value of curve labled _tokenAddress at _xValue
/// @param _tokenAddress The key to lookup the curve with in the mapping
/// @param _xValue The x value to pass to the slave
/// @return _value The y value of the curve
function getValueAt(address _tokenAddress, int256 _xValue) external view override returns (int256 _value) {
if (curves[_tokenAddress] == address(0)) revert CurveMaster_TokenNotEnabled();
ICurveSlave _curve = ICurveSlave(curves[_tokenAddress]);
_value = _curve.valueAt(_xValue);
if (_value == 0) revert CurveMaster_ZeroResult();
}
/// @notice Set the VaultController addr in order to pay interest on curve setting
/// @param _vaultMasterAddress The address of vault master
function setVaultController(address _vaultMasterAddress) external override onlyOwner {
address _oldCurveAddress = vaultControllerAddress;
vaultControllerAddress = _vaultMasterAddress;
emit VaultControllerSet(_oldCurveAddress, _vaultMasterAddress);
}
/// @notice Setting a new curve should pay interest
/// @param _tokenAddress The address of the token
/// @param _curveAddress The address of the curve for the contract
function setCurve(address _tokenAddress, address _curveAddress) external override onlyOwner {
if (vaultControllerAddress != address(0)) IVaultController(vaultControllerAddress).calculateInterest();
address _oldCurve = curves[_tokenAddress];
curves[_tokenAddress] = _curveAddress;
emit CurveSet(_oldCurve, _tokenAddress, _curveAddress);
}
/// @notice Special function that does not calculate interest, used for deployment
/// @param _tokenAddress The address of the token
/// @param _curveAddress The address of the curve for the contract
function forceSetCurve(address _tokenAddress, address _curveAddress) external override onlyOwner {
address _oldCurve = curves[_tokenAddress];
curves[_tokenAddress] = _curveAddress;
emit CurveForceSet(_oldCurve, _tokenAddress, _curveAddress);
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
/// @title OracleRelay Interface
/// @notice Interface for interacting with OracleRelay
interface IOracleRelay {
/// @notice Emited when the underlyings are different in the anchored view
error OracleRelay_DifferentUnderlyings();
enum OracleType {
Chainlink,
Uniswap,
Price
}
/// @notice returns the price with 18 decimals
/// @return _currentValue the current price
function currentValue() external returns (uint256 _currentValue);
/// @notice returns the price with 18 decimals without any state changes
/// @dev some oracles require a state change to get the exact current price.
/// This is updated when calling other state changing functions that query the price
/// @return _price the current price
function peekValue() external view returns (uint256 _price);
/// @notice returns the type of the oracle
/// @return _type the type (Chainlink/Uniswap/Price)
function oracleType() external view returns (OracleType _type);
/// @notice returns the underlying asset the oracle is pricing
/// @return _underlying the address of the underlying asset
function underlying() external view returns (address _underlying);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
interface IBooster {
function owner() external view returns (address _owner);
function setVoteDelegate(address _voteDelegate) external;
function vote(uint256 _voteId, address _votingAddress, bool _support) external returns (bool _success);
function voteGaugeWeight(address[] calldata _gauge, uint256[] calldata _weight) external returns (bool _success);
function poolInfo(uint256 _pid)
external
view
returns (address _lptoken, address _token, address _gauge, address _cprvRewards, address _stash, bool _shutdown);
function earmarkRewards(uint256 _pid) external returns (bool _claimed);
function earmarkFees() external returns (bool _claimed);
function deposit(uint256 _pid, uint256 _amount, bool _stake) external returns (bool _success);
function isShutdown() external view returns (bool _isShutdown);
function shutdownSystem() external;
function poolManager() external returns (address _poolManager);
function addPool(address _lptoken, address _gauge, uint256 _stashVersion) external returns (bool _success);
function poolLength() external returns (uint256 _poolLength);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {IVirtualBalanceRewardPool} from '@interfaces/utils/IVirtualBalanceRewardPool.sol';
interface IBaseRewardPool {
function stake(uint256 _amount) external returns (bool _staked);
function stakeFor(address _for, uint256 _amount) external returns (bool _staked);
function withdraw(uint256 _amount, bool _claim) external returns (bool _success);
function withdrawAndUnwrap(uint256 _amount, bool _claim) external returns (bool _success);
function getReward(address _account, bool _claimExtras) external returns (bool _success);
function rewardToken() external view returns (IERC20 _rewardToken);
function earned(address _ad) external view returns (uint256 _reward);
function extraRewardsLength() external view returns (uint256 _extraRewardsLength);
function extraRewards(uint256 _position) external view returns (IVirtualBalanceRewardPool _virtualReward);
function queueNewRewards(uint256 _rewards) external returns (bool _success);
function operator() external view returns (address _operator);
function stakingToken() external view returns (address _stakingToken);
}
// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity >=0.8.4 <0.9.0;
import {IVault} from '@interfaces/core/IVault.sol';
import {IVaultController} from '@interfaces/core/IVaultController.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
// @notice Deployer of Vaults
// @dev This contract is needed to reduce the size of the VaultController contract
interface IVaultDeployer {
/*///////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
// @notice Thrown when someone other than the vault controller tries to call the method
error VaultDeployer_OnlyVaultController();
/*///////////////////////////////////////////////////////////////
VARIABLES
//////////////////////////////////////////////////////////////*/
/// @notice The address of the CVX token
/// @return _cvx The address of the CVX token
function CVX() external view returns (IERC20 _cvx);
/// @notice The address of the CRV token
/// @return _crv The address of the CRV token
function CRV() external view returns (IERC20 _crv);
/*///////////////////////////////////////////////////////////////
LOGIC
//////////////////////////////////////////////////////////////*/
/// @notice Deploys a new Vault
/// @param _id The id of the vault
/// @param _minter The address of the minter of the vault
/// @return _vault The vault that was created
function deployVault(uint96 _id, address _minter) external returns (IVault _vault);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {IVaultController} from '@interfaces/core/IVaultController.sol';
/// @title AMPHClaimer Interface
interface IAMPHClaimer {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
// @notice Emited when a vault claims AMPH
// @param _vaultClaimer The address of the vault that claimed
// @param _cvxTotalRewards The amount of CVX sent in exchange of AMPH
// @param _crvTotalRewards The amount of CRV sent in exchange of AMPH
// @param _amphAmount The amount of AMPH received
event ClaimedAmph(
address indexed _vaultClaimer, uint256 _cvxTotalRewards, uint256 _crvTotalRewards, uint256 _amphAmount
);
// @notice Emited when governance changes the vault controller
// @param _newVaultController The address of the new vault controller
event ChangedVaultController(address indexed _newVaultController);
// @notice Emited when governance recovers a token from the contract
// @param _token the token recovered
// @param _receiver the receiver of the tokens
// @param _amount the amount recovered
event RecoveredDust(address indexed _token, address _receiver, uint256 _amount);
// @notice Emited when governance changes the CVX reward fee
// @param _newCvxReward the new fee
event ChangedCvxRewardFee(uint256 _newCvxReward);
// @notice Emited when governance changes the CRV reward fee
// @param _newCrvReward the new fee
event ChangedCrvRewardFee(uint256 _newCrvReward);
/*///////////////////////////////////////////////////////////////
VARIABLES
//////////////////////////////////////////////////////////////*/
/// @notice The address of the CVX token
function CVX() external view returns (IERC20 _cvx);
/// @notice The address of the CRV token
function CRV() external view returns (IERC20 _crv);
/// @notice The address of the AMPH token
function AMPH() external view returns (IERC20 _amph);
/// @notice The base supply of AMPH per cliff, denominated in 1e6
function BASE_SUPPLY_PER_CLIFF() external view returns (uint256 _baseSupplyPerCliff);
/// @notice The total amount of AMPH minted for rewards in CRV, denominated in 1e6
function distributedAmph() external view returns (uint256 _distributedAmph);
/// @notice The total number of cliffs (for both tokens)
function TOTAL_CLIFFS() external view returns (uint256 _totalCliffs);
/// @notice Percentage of rewards taken in CVX (1e18 == 100%)
function cvxRewardFee() external view returns (uint256 _cvxRewardFee);
/// @notice Percentage of rewards taken in CRV (1e18 == 100%)
function crvRewardFee() external view returns (uint256 _crvRewardFee);
/// @notice The vault controller
function vaultController() external view returns (IVaultController _vaultController);
/*///////////////////////////////////////////////////////////////
LOGIC
//////////////////////////////////////////////////////////////*/
/// @notice Claims an amount of AMPH given a CVX and CRV quantity
/// @param _vaultId The vault id that is claiming
/// @param _cvxTotalRewards The max CVX amount to exchange from the sender
/// @param _crvTotalRewards The max CVR amount to exchange from the sender
/// @param _beneficiary The receiver of the AMPH rewards
/// @return _cvxAmountToSend The amount of CVX that the treasury got
/// @return _crvAmountToSend The amount of CRV that the treasury got
/// @return _claimedAmph The amount of AMPH received by the beneficiary
function claimAmph(
uint96 _vaultId,
uint256 _cvxTotalRewards,
uint256 _crvTotalRewards,
address _beneficiary
) external returns (uint256 _cvxAmountToSend, uint256 _crvAmountToSend, uint256 _claimedAmph);
/// @notice Returns the claimable amount of AMPH given a CVX and CRV quantity
/// @param _sender The address of the account claiming
/// @param _vaultId The vault id that is claiming
/// @param _cvxTotalRewards The max CVX amount to exchange from the sender
/// @param _crvTotalRewards The max CVR amount to exchange from the sender
/// @return _cvxAmountToSend The amount of CVX the user will have to send
/// @return _crvAmountToSend The amount of CRV the user will have to send
/// @return _claimableAmph The amount of AMPH that would be received by the beneficiary
function claimable(
address _sender,
uint96 _vaultId,
uint256 _cvxTotalRewards,
uint256 _crvTotalRewards
) external view returns (uint256 _cvxAmountToSend, uint256 _crvAmountToSend, uint256 _claimableAmph);
/// @notice Used by governance to change the vault controller
/// @param _newVaultController The new vault controller
function changeVaultController(address _newVaultController) external;
/// @notice Used by governance to recover tokens from the contract
/// @param _token The token to recover
/// @param _amount The amount to recover
function recoverDust(address _token, uint256 _amount) external;
/// @notice Used by governance to change the fee taken from the CVX reward
/// @param _newFee The new reward fee
function changeCvxRewardFee(uint256 _newFee) external;
/// @notice Used by governance to change the fee taken from the CRV reward
/// @param _newFee The new reward fee
function changeCrvRewardFee(uint256 _newFee) external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(
address from,
address to,
uint256 amount
) external returns (bool);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://diligence.consensys.net/posts/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value
) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControl {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/Math.sol";
/**
* @dev String operations.
*/
library Strings {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = Math.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, Math.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165 is IERC165 {
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
/// @title CurveMaster Interface
/// @notice Interface for interacting with CurveMaster
interface ICurveMaster {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
// @notice Emited when the owner changes the vault controller address
// @param _oldVaultControllerAddress The old address of the vault controller
// @param _newVaultControllerAddress The new address of the vault controller
event VaultControllerSet(address _oldVaultControllerAddress, address _newVaultControllerAddress);
// @notice Emited when the owner changes the curve address
// @param _oldCurveAddress The old address of the curve
// @param _token The token to set
// @param _newCurveAddress The new address of the curve
event CurveSet(address _oldCurveAddress, address _token, address _newCurveAddress);
// @notice Emited when the owner changes the curve address skipping the checks
// @param _oldCurveAddress The old address of the curve
// @param _token The token to set
// @param _newCurveAddress The new address of the curve
event CurveForceSet(address _oldCurveAddress, address _token, address _newCurveAddress);
/*///////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
/// @notice Thrown when the token is not enabled
error CurveMaster_TokenNotEnabled();
/// @notice Thrown when result is zero
error CurveMaster_ZeroResult();
/*///////////////////////////////////////////////////////////////
VARIABLES
//////////////////////////////////////////////////////////////*/
/// @notice The vault controller address
function vaultControllerAddress() external view returns (address _vaultController);
/// @notice Returns the value of curve labled _tokenAddress at _xValue
/// @param _tokenAddress The key to lookup the curve with in the mapping
/// @param _xValue The x value to pass to the slave
/// @return _value The y value of the curve
function getValueAt(address _tokenAddress, int256 _xValue) external view returns (int256 _value);
/// @notice Mapping of token to address
function curves(address _tokenAddress) external view returns (address _curve);
/*///////////////////////////////////////////////////////////////
LOGIC
//////////////////////////////////////////////////////////////*/
/// @notice Set the VaultController addr in order to pay interest on curve setting
/// @param _vaultMasterAddress The address of vault master
function setVaultController(address _vaultMasterAddress) external;
/// @notice Setting a new curve should pay interest
/// @param _tokenAddress The address of the token
/// @param _curveAddress The address of the curve for the contract
function setCurve(address _tokenAddress, address _curveAddress) external;
/// @notice Special function that does not calculate interest, used for deployment
/// @param _tokenAddress The address of the token
/// @param _curveAddress The address of the curve for the contract
function forceSetCurve(address _tokenAddress, address _curveAddress) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
/// @title CurveSlave Interface
/// @notice Interface for interacting with CurveSlaves
interface ICurveSlave {
function valueAt(int256 _xValue) external view returns (int256 _value);
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
interface IVirtualBalanceRewardPool {
function rewardToken() external view returns (IERC20 _rewardToken);
function earned(address _ad) external view returns (uint256 _reward);
function getReward() external;
function queueNewRewards(uint256 _rewards) external;
}
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.9;
import {IVaultController} from '@interfaces/core/IVaultController.sol';
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
import {IBaseRewardPool} from '@interfaces/utils/IBaseRewardPool.sol';
import {ICVX} from '@interfaces/utils/ICVX.sol';
/// @title Vault Interface
interface IVault {
/*///////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
// @notice Emited after depositing a token
// @param _token The address of the token to deposit
// @param _amount The amount to deposit
event Deposit(address _token, uint256 _amount);
// @notice Emited after withdrawing a token
// @param _token The address of the token to withdraw
// @param _amount The amount to withdraw
event Withdraw(address _token, uint256 _amount);
// @notice Emited when claiming a reward
// @param _token The address of the token that was claimed
// @param _amount The amount that was claimed
event ClaimedReward(address _token, uint256 _amount);
// @notice Emited when migrating a crvLP token on convex manually
// @param _token The address of the token to stake
// @param _amount The amount to stake
event Migrated(address _token, uint256 _amount);
/*///////////////////////////////////////////////////////////////
ERRORS
//////////////////////////////////////////////////////////////*/
// @notice Thrown when trying to deposit a token that is not registered
error Vault_TokenNotRegistered();
// @notice Thrown when trying to deposit 0 amount
error Vault_AmountZero();
/// @notice Thrown when trying to withdraw more than it's possible
error Vault_OverWithdrawal();
/// @notice Thrown when trying to repay more than is needed
error Vault_RepayTooMuch();
/// @notice Thrown when _msgSender is not the minter of the vault
error Vault_NotMinter();
/// @notice Thrown when _msgSender is not the controller of the vault
error Vault_NotVaultController();
/// @notice Thrown when depositing and staking on convex fails
error Vault_DepositAndStakeOnConvexFailed();
/// @notice Thrown when trying to withdraw and unstake from convex
error Vault_WithdrawAndUnstakeOnConvexFailed();
/// @notice Thrown when trying to claim rewards with a non staked token
error Vault_TokenNotStaked();
/// @notice Thrown when trying to stake with 0 balance
error Vault_TokenZeroBalance();
/// @notice Thrown when a token is already migrated and trying to migrate again
error Vault_TokenAlreadyMigrated();
/// @notice Thrown when the base reward contract was not approved by governance
error Vault_InvalidBaseRewardContract();
/*///////////////////////////////////////////////////////////////
STRUCTS
//////////////////////////////////////////////////////////////*/
/// @title VaultInfo struct
/// @notice this struct is used to store the vault metadata
/// this should reduce the cost of minting by ~15,000
/// by limiting us to max 2**96-1 vaults
struct VaultInfo {
uint96 id;
address minter;
}
struct Reward {
IERC20 token;
uint256 amount;
}
/*///////////////////////////////////////////////////////////////
VARIABLES
//////////////////////////////////////////////////////////////*/
// @notice Returns the struct containing the vault information
// @return _id Id of the vault
// @return _minter Minter of the vault
function vaultInfo() external view returns (uint96 _id, address _minter);
// @notice Returns the vault's balance of a token
// @param _token The address of the token
// @return _balance The token's balance of the vault
function balances(address _token) external view returns (uint256 _balance);
// @notice Returns the pool that the token is staked on
// @dev Returns 0 if not staked
// @param _token The address of the token
// @return _poolId The poolId that the token is staked on
function currentPoolIds(address _token) external view returns (uint256 _poolId);
// @notice Returns the current vault base liability
// @return _liability The current vault base liability of the vault
function baseLiability() external view returns (uint256 _liability);
// @notice Returns the minter's address of the vault
// @return _minter The minter's address
function minter() external view returns (address _minter);
// @notice Returns the id of the vault
// @return _id The id of the vault
function id() external view returns (uint96 _id);
// @notice Returns the vault controller
// @return _vaultController The vault controller
function CONTROLLER() external view returns (IVaultController _vaultController);
/// @notice Returns the CRV token address
/// @return _crv The CRV token address
function CRV() external view returns (IERC20 _crv);
/// @notice Returns the CVX token address
/// @return _cvx The CVX token address
function CVX() external view returns (ICVX _cvx);
/*///////////////////////////////////////////////////////////////
LOGIC
//////////////////////////////////////////////////////////////*/
// @notice Used to deposit a token to the vault
// @param _token The address of the token to deposit
// @param _amount The amount of the token to deposit
function depositERC20(address _token, uint256 _amount) external;
// @notice Used to withdraw a token from the vault. This can only be called by the minter
// @dev The withdraw will be denied if ones vault would become insolvent
// @param _token The address of the token
// @param _amount The amount of the token to withdraw
function withdrawERC20(address _token, uint256 _amount) external;
/// @notice Let's the user manually stake their crvLP
/// @dev This can be called if the convex pool didn't exist when the token was registered
/// and was later updated
/// @param _tokenAddress The address of erc20 crvLP token
function migrateCrvLPCollateral(address _tokenAddress) external;
/// @notice Returns true when user can manually stake their token balance
/// @param _token The address of the token to check
/// @return _canMigrate Returns true if the token can be staked manually
function canMigrate(address _token) external view returns (bool _canMigrate);
/// @notice Claims available rewards from multiple tokens
/// @dev Transfers a percentage of the crv and cvx rewards to claim AMPH tokens
/// @param _tokenAddresses The addresses of the erc20 tokens
/// @param _claimExtraRewards True if it should claim the extra rewards from convex
function claimRewards(address[] memory _tokenAddresses, bool _claimExtraRewards) external;
/// @notice Returns an array of tokens and amounts available for claim
/// @param _tokenAddress The address of erc20 token
/// @param _claimExtraRewards True if it should claim the extra rewards from convex
/// @return _rewards The array of tokens and amount available for claim
function claimableRewards(
address _tokenAddress,
bool _claimExtraRewards
) external view returns (Reward[] memory _rewards);
/// @notice Used to claim rewards from past baseRewardContract
/// @param _baseRewardContract The base reward contract to claim from
/// @param _claimMainReward True to claim the base rewards also (CRV and CVX)
/// @param _extraIndexesToClaim Indexes to claim the extra rewards
function claimPreviousRewards(
IBaseRewardPool _baseRewardContract,
bool _claimMainReward,
uint256[] memory _extraIndexesToClaim
) external;
/// @notice Function used by the VaultController to transfer tokens
/// @param _token The address of the token to transfer
/// @param _to The address of the person to send the coins to
/// @param _amount The amount of coins to move
function controllerTransfer(address _token, address _to, uint256 _amount) external;
/// @notice function used by the VaultController to withdraw from convex
/// callable by the VaultController only
/// @param _tokenAddress The token address to withdraw from the rewards contract
/// @param _amount amount of coins to withdraw
function controllerWithdrawAndUnwrap(address _tokenAddress, uint256 _amount) external;
// @notice Modifies a vault's liability. Can only be called by VaultController
// @param _increase True to increase liability, false to decrease
// @param _baseAmount The change amount in base liability
// @return _liability The new base liability
function modifyLiability(bool _increase, uint256 _baseAmount) external returns (uint256 _liability);
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator
) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1);
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(
uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10**64) {
value /= 10**64;
result += 64;
}
if (value >= 10**32) {
value /= 10**32;
result += 32;
}
if (value >= 10**16) {
value /= 10**16;
result += 16;
}
if (value >= 10**8) {
value /= 10**8;
result += 8;
}
if (value >= 10**4) {
value /= 10**4;
result += 4;
}
if (value >= 10**2) {
value /= 10**2;
result += 2;
}
if (value >= 10**1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10**result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result * 8) < value ? 1 : 0);
}
}
}
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.4 <0.9.0;
import {IERC20} from '@openzeppelin/contracts/token/ERC20/IERC20.sol';
// @title The interface for the CVX token
interface ICVX is IERC20 {
function totalCliffs() external view returns (uint256 _totalCliffs);
function reductionPerCliff() external view returns (uint256 _reduction);
function maxSupply() external view returns (uint256 _maxSupply);
function operator() external view returns (address _operator);
function updateOperator() external;
function vecrvProxy() external view returns (address _vecrvProxy);
}