I'm getting this error while deploying smart contract
// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.7.5;
import "./libraries/SafeMath.sol";
// import "./libraries/SafeERC20.sol";
import "./interfaces/IOwnable.sol";
import "./interfaces/IERC20.sol";
import "./interfaces/IERC20Metadata.sol";
import "./interfaces/IOHM.sol";
import "./interfaces/IsOHM.sol";
import "./interfaces/IBondingCalculator.sol";
import "./interfaces/ITreasury.sol";
import "./types/OlympusAccessControlled.sol";
interface IERC20Mintable {
function mint( uint256 amount_ ) external;
function mint( address account_, uint256 ammount_ ) external;
}
contract OKPTreasury is OlympusAccessControlled {
using SafeMath for uint;
// using SafeERC20 for IERC20;
event Deposit( address indexed token, uint amount, uint value );
event Withdrawal( address indexed token, uint amount, uint value );
event CreateDebt( address indexed debtor, address indexed token, uint amount, uint value );
event RepayDebt( address indexed debtor, address indexed token, uint amount, uint value );
event ReservesManaged( address indexed token, uint amount );
event ReservesUpdated( uint indexed totalReserves );
event ReservesAudited( uint indexed totalReserves );
event RewardsMinted( address indexed caller, address indexed recipient, uint amount );
event ChangeQueued( MANAGING indexed managing, address queued );
event ChangeActivated( MANAGING indexed managing, address activated, bool result );
enum MANAGING { RESERVEDEPOSITOR, RESERVESPENDER, RESERVETOKEN, RESERVEMANAGER, LIQUIDITYDEPOSITOR, LIQUIDITYTOKEN, LIQUIDITYMANAGER, DEBTOR, REWARDMANAGER, SOKP }
address public immutable OKP;
uint public blocksNeededForQueue;
address[] public reserveTokens; // Push only, beware false-positives.
mapping( address => bool ) public isReserveToken;
mapping( address => uint ) public reserveTokenQueue; // Delays changes to mapping.
address[] public reserveDepositors; // Push only, beware false-positives. Only for viewing.
mapping( address => bool ) public isReserveDepositor;
mapping( address => uint ) public reserveDepositorQueue; // Delays changes to mapping.
address[] public reserveSpenders; // Push only, beware false-positives. Only for viewing.
mapping( address => bool ) public isReserveSpender;
mapping( address => uint ) public reserveSpenderQueue; // Delays changes to mapping.
address[] public liquidityTokens; // Push only, beware false-positives.
mapping( address => bool ) public isLiquidityToken;
mapping( address => uint ) public LiquidityTokenQueue; // Delays changes to mapping.
address[] public liquidityDepositors; // Push only, beware false-positives. Only for viewing.
mapping( address => bool ) public isLiquidityDepositor;
mapping( address => uint ) public LiquidityDepositorQueue; // Delays changes to mapping.
mapping( address => address ) public bondCalculator; // bond calculator for liquidity token
address[] public reserveManagers; // Push only, beware false-positives. Only for viewing.
mapping( address => bool ) public isReserveManager;
mapping( address => uint ) public ReserveManagerQueue; // Delays changes to mapping.
address[] public liquidityManagers; // Push only, beware false-positives. Only for viewing.
mapping( address => bool ) public isLiquidityManager;
mapping( address => uint ) public LiquidityManagerQueue; // Delays changes to mapping.
address[] public debtors; // Push only, beware false-positives. Only for viewing.
mapping( address => bool ) public isDebtor;
mapping( address => uint ) public debtorQueue; // Delays changes to mapping.
mapping( address => uint ) public debtorBalance;
address[] public rewardManagers; // Push only, beware false-positives. Only for viewing.
mapping( address => bool ) public isRewardManager;
mapping( address => uint ) public rewardManagerQueue; // Delays changes to mapping.
address public sOKP;
uint public sOKPQueue; // Delays change to sOKP address
uint public totalReserves; // Risk-free value of all assets
uint public totalDebt;
constructor (
address _OKP,
address _BUSD,
address _authority,
uint _blocksNeededForQueue
) OlympusAccessControlled(IOlympusAuthority(_authority)) {
require( _OKP != address(0) );
OKP = _OKP;
isReserveToken[ _BUSD ] = true;
reserveTokens.push( _BUSD );
blocksNeededForQueue = _blocksNeededForQueue;
}
/**
@notice allow approved address to deposit an asset for OKP
@param _amount uint
@param _token address
@param _profit uint
@return send_ uint
*/
function deposit( uint _amount, address _token, uint _profit ) external returns ( uint send_ ) {
require( isReserveToken[ _token ] || isLiquidityToken[ _token ], "Not accepted" );
IERC20( _token ).transferFrom( msg.sender, address(this), _amount );
if ( isReserveToken[ _token ] ) {
require( isReserveDepositor[ msg.sender ], "Not approved" );
} else {
require( isLiquidityDepositor[ msg.sender ], "Not approved" );
}
uint value = valueOf(_token, _amount);
// mint OKP needed and store amount of rewards for distribution
// s = 100000000000 - 99909426854 = 0.090573146
send_ = value.sub( _profit );
IERC20Mintable( OKP ).mint( msg.sender, send_ );
totalReserves = totalReserves.add( value );
emit ReservesUpdated( totalReserves );
emit Deposit( _token, _amount, value );
}
/**
@notice allow approved address to burn OKP for reserves
@param _amount uint
@param _token address
*/
function withdraw( uint _amount, address _token ) external {
require( isReserveToken[ _token ], "Not accepted" ); // Only reserves can be used for redemptions
require( isReserveSpender[ msg.sender ] == true, "Not approved" );
uint value = valueOf( _token, _amount );
IOHM( OKP ).burnFrom( msg.sender, value );
totalReserves = totalReserves.sub( value );
emit ReservesUpdated( totalReserves );
IERC20( _token ).transfer( msg.sender, _amount );
emit Withdrawal( _token, _amount, value );
}
/**
@notice allow approved address to borrow reserves
@param _amount uint
@param _token address
*/
function incurDebt( uint _amount, address _token ) external {
require( isDebtor[ msg.sender ], "Not approved" );
require( isReserveToken[ _token ], "Not accepted" );
uint value = valueOf( _token, _amount );
uint maximumDebt = IERC20( sOKP ).balanceOf( msg.sender ); // Can only borrow against sOKP held
uint availableDebt = maximumDebt.sub( debtorBalance[ msg.sender ] );
require( value <= availableDebt, "Exceeds debt limit" );
debtorBalance[ msg.sender ] = debtorBalance[ msg.sender ].add( value );
totalDebt = totalDebt.add( value );
totalReserves = totalReserves.sub( value );
emit ReservesUpdated( totalReserves );
IERC20( _token ).transfer( msg.sender, _amount );
emit CreateDebt( msg.sender, _token, _amount, value );
}
/**
@notice allow approved address to repay borrowed reserves with reserves
@param _amount uint
@param _token address
*/
function repayDebtWithReserve( uint _amount, address _token ) external {
require( isDebtor[ msg.sender ], "Not approved" );
require( isReserveToken[ _token ], "Not accepted" );
IERC20( _token ).transferFrom( msg.sender, address(this), _amount );
uint value = valueOf( _token, _amount );
debtorBalance[ msg.sender ] = debtorBalance[ msg.sender ].sub( value );
totalDebt = totalDebt.sub( value );
totalReserves = totalReserves.add( value );
emit ReservesUpdated( totalReserves );
emit RepayDebt( msg.sender, _token, _amount, value );
}
/**
@notice allow approved address to repay borrowed reserves with OKP
@param _amount uint
*/
function repayDebtWithOKP( uint _amount ) external {
require( isDebtor[ msg.sender ], "Not approved" );
IOHM( OKP ).burnFrom( msg.sender, _amount );
debtorBalance[ msg.sender ] = debtorBalance[ msg.sender ].sub( _amount );
totalDebt = totalDebt.sub( _amount );
emit RepayDebt( msg.sender, OKP, _amount, _amount );
}
/**
@notice allow approved address to withdraw assets
@param _token address
@param _amount uint
*/
function manage( address _token, uint _amount ) external {
if( isLiquidityToken[ _token ] ) {
require( isLiquidityManager[ msg.sender ], "Not approved" );
} else {
require( isReserveManager[ msg.sender ], "Not approved" );
}
uint value = valueOf(_token, _amount);
require( value <= excessReserves(), "Insufficient reserves" );
totalReserves = totalReserves.sub( value );
emit ReservesUpdated( totalReserves );
IERC20( _token ).transfer( msg.sender, _amount );
emit ReservesManaged( _token, _amount );
}
/**
@notice send epoch reward to staking contract
*/
function mintRewards( address _recipient, uint _amount ) external {
require( isRewardManager[ msg.sender ], "Not approved" );
require( _amount <= excessReserves(), "Insufficient reserves" );
IERC20Mintable( OKP ).mint( _recipient, _amount );
emit RewardsMinted( msg.sender, _recipient, _amount );
}
/**
@notice returns excess reserves not backing tokens
@return uint
*/
function excessReserves() public view returns ( uint ) {
return totalReserves.sub( IERC20( OKP ).totalSupply().sub( totalDebt ) );
}
/**
@notice takes inventory of all tracked assets
@notice always consolidate to recognized reserves before audit
*/
function auditReserves() external onlyGovernor() {
uint reserves;
for( uint i = 0; i < reserveTokens.length; i++ ) {
reserves = reserves.add (
valueOf( reserveTokens[ i ], IERC20( reserveTokens[ i ] ).balanceOf( address(this) ) )
);
}
for( uint i = 0; i < liquidityTokens.length; i++ ) {
reserves = reserves.add (
valueOf( liquidityTokens[ i ], IERC20( liquidityTokens[ i ] ).balanceOf( address(this) ) )
);
}
totalReserves = reserves;
emit ReservesUpdated( reserves );
emit ReservesAudited( reserves );
}
/**
@notice returns OKP valuation of asset
@param _token address
@param _amount uint
@return value_ uint
*/
function valueOf( address _token, uint _amount ) public view returns ( uint value_ ) {
if ( isReserveToken[ _token ] ) {
// convert amount to match OKP decimals
value_ = _amount.mul( 10 ** IERC20Metadata( OKP ).decimals() ).div( 10 ** IERC20Metadata( _token ).decimals() );
} else if ( isLiquidityToken[ _token ] ) {
value_ = IBondingCalculator( bondCalculator[ _token ] ).valuation( _token, _amount );
}
}
function setNeededQueueBlock(uint _blocksNeededForQueue) public onlyGovernor() {
blocksNeededForQueue = _blocksNeededForQueue;
}
/**
@notice queue address to change boolean in mapping
@param _managing MANAGING
@param _address address
@return bool
*/
function queue( MANAGING _managing, address _address ) external onlyGovernor() returns ( bool ) {
require( _address != address(0) );
if ( _managing == MANAGING.RESERVEDEPOSITOR ) { // 0
reserveDepositorQueue[ _address ] = block.number.add( blocksNeededForQueue );
} else if ( _managing == MANAGING.RESERVESPENDER ) { // 1
reserveSpenderQueue[ _address ] = block.number.add( blocksNeededForQueue );
} else if ( _managing == MANAGING.RESERVETOKEN ) { // 2
reserveTokenQueue[ _address ] = block.number.add( blocksNeededForQueue );
} else if ( _managing == MANAGING.RESERVEMANAGER ) { // 3
ReserveManagerQueue[ _address ] = block.number.add( blocksNeededForQueue.mul( 2 ) );
} else if ( _managing == MANAGING.LIQUIDITYDEPOSITOR ) { // 4
LiquidityDepositorQueue[ _address ] = block.number.add( blocksNeededForQueue );
} else if ( _managing == MANAGING.LIQUIDITYTOKEN ) { // 5
LiquidityTokenQueue[ _address ] = block.number.add( blocksNeededForQueue );
} else if ( _managing == MANAGING.LIQUIDITYMANAGER ) { // 6
LiquidityManagerQueue[ _address ] = block.number.add( blocksNeededForQueue.mul( 2 ) );
} else if ( _managing == MANAGING.DEBTOR ) { // 7
debtorQueue[ _address ] = block.number.add( blocksNeededForQueue );
} else if ( _managing == MANAGING.REWARDMANAGER ) { // 8
rewardManagerQueue[ _address ] = block.number.add( blocksNeededForQueue );
} else if ( _managing == MANAGING.SOKP ) { // 9
sOKPQueue = block.number.add( blocksNeededForQueue );
} else return false;
emit ChangeQueued( _managing, _address );
return true;
}
/**
@notice verify queue then set boolean in mapping
@param _managing MANAGING
@param _address address
@param _calculator address
@return bool
*/
function toggle( MANAGING _managing, address _address, address _calculator ) external onlyGovernor() returns ( bool ) {
require( _address != address(0) );
bool result;
if ( _managing == MANAGING.RESERVEDEPOSITOR ) { // 0
if ( requirements( reserveDepositorQueue, isReserveDepositor, _address ) ) {
reserveDepositorQueue[ _address ] = 0;
if( !listContains( reserveDepositors, _address ) ) {
reserveDepositors.push( _address );
}
}
result = !isReserveDepositor[ _address ];
isReserveDepositor[ _address ] = result;
} else if ( _managing == MANAGING.RESERVESPENDER ) { // 1
if ( requirements( reserveSpenderQueue, isReserveSpender, _address ) ) {
reserveSpenderQueue[ _address ] = 0;
if( !listContains( reserveSpenders, _address ) ) {
reserveSpenders.push( _address );
}
}
result = !isReserveSpender[ _address ];
isReserveSpender[ _address ] = result;
} else if ( _managing == MANAGING.RESERVETOKEN ) { // 2
if ( requirements( reserveTokenQueue, isReserveToken, _address ) ) {
reserveTokenQueue[ _address ] = 0;
if( !listContains( reserveTokens, _address ) ) {
reserveTokens.push( _address );
}
}
result = !isReserveToken[ _address ];
isReserveToken[ _address ] = result;
} else if ( _managing == MANAGING.RESERVEMANAGER ) { // 3
if ( requirements( ReserveManagerQueue, isReserveManager, _address ) ) {
reserveManagers.push( _address );
ReserveManagerQueue[ _address ] = 0;
if( !listContains( reserveManagers, _address ) ) {
reserveManagers.push( _address );
}
}
result = !isReserveManager[ _address ];
isReserveManager[ _address ] = result;
} else if ( _managing == MANAGING.LIQUIDITYDEPOSITOR ) { // 4
if ( requirements( LiquidityDepositorQueue, isLiquidityDepositor, _address ) ) {
liquidityDepositors.push( _address );
LiquidityDepositorQueue[ _address ] = 0;
if( !listContains( liquidityDepositors, _address ) ) {
liquidityDepositors.push( _address );
}
}
result = !isLiquidityDepositor[ _address ];
isLiquidityDepositor[ _address ] = result;
} else if ( _managing == MANAGING.LIQUIDITYTOKEN ) { // 5
if ( requirements( LiquidityTokenQueue, isLiquidityToken, _address ) ) {
LiquidityTokenQueue[ _address ] = 0;
if( !listContains( liquidityTokens, _address ) ) {
liquidityTokens.push( _address );
}
}
result = !isLiquidityToken[ _address ];
isLiquidityToken[ _address ] = result;
bondCalculator[ _address ] = _calculator;
} else if ( _managing == MANAGING.LIQUIDITYMANAGER ) { // 6
if ( requirements( LiquidityManagerQueue, isLiquidityManager, _address ) ) {
LiquidityManagerQueue[ _address ] = 0;
if( !listContains( liquidityManagers, _address ) ) {
liquidityManagers.push( _address );
}
}
result = !isLiquidityManager[ _address ];
isLiquidityManager[ _address ] = result;
} else if ( _managing == MANAGING.DEBTOR ) { // 7
if ( requirements( debtorQueue, isDebtor, _address ) ) {
debtorQueue[ _address ] = 0;
if( !listContains( debtors, _address ) ) {
debtors.push( _address );
}
}
result = !isDebtor[ _address ];
isDebtor[ _address ] = result;
} else if ( _managing == MANAGING.REWARDMANAGER ) { // 8
if ( requirements( rewardManagerQueue, isRewardManager, _address ) ) {
rewardManagerQueue[ _address ] = 0;
if( !listContains( rewardManagers, _address ) ) {
rewardManagers.push( _address );
}
}
result = !isRewardManager[ _address ];
isRewardManager[ _address ] = result;
} else if ( _managing == MANAGING.SOKP ) { // 9
sOKPQueue = 0;
sOKP = _address;
result = true;
} else return false;
emit ChangeActivated( _managing, _address, result );
return true;
}
/**
@notice checks requirements and returns altered structs
@param queue_ mapping( address => uint )
@param status_ mapping( address => bool )
@param _address address
@return bool
*/
function requirements(
mapping( address => uint ) storage queue_,
mapping( address => bool ) storage status_,
address _address
) internal view returns ( bool ) {
if ( !status_[ _address ] ) {
require( queue_[ _address ] != 0, "Must queue" );
require( queue_[ _address ] <= block.number, "Queue not expired" );
return true;
} return false;
}
/**
@notice checks array to ensure against duplicate
@param _list address[]
@param _token address
@return bool
*/
function listContains( address[] storage _list, address _token ) internal view returns ( bool ) {
for( uint i = 0; i < _list.length; i++ ) {
if( _list[ i ] == _token ) {
return true;
}
}
return false;
}
}