tokenbridge-contracts/contracts/upgradeable_contracts/erc20_to_native/InterestConnector.sol

288 lines
11 KiB
Solidity

pragma solidity 0.4.24;
import "../Ownable.sol";
import "../ERC20Bridge.sol";
import "../../interfaces/IInterestReceiver.sol";
/**
* @title InterestConnector
* @dev This contract gives an abstract way of receiving interest on locked tokens.
*/
contract InterestConnector is Ownable, ERC20Bridge {
event PaidInterest(address indexed token, address to, uint256 value);
/**
* @dev Throws if interest is bearing not enabled.
* @param token address, for which interest should be enabled.
*/
modifier interestEnabled(address token) {
require(isInterestEnabled(token));
/* solcov ignore next */
_;
}
/**
* @dev Ensures that caller is an EOA.
* Functions with such modifier cannot be called from other contract (as well as from GSN-like approaches)
*/
modifier onlyEOA {
// solhint-disable-next-line avoid-tx-origin
require(msg.sender == tx.origin);
/* solcov ignore next */
_;
}
/**
* @dev Tells if interest earning was enabled for particular token.
* @return true, if interest bearing is enabled.
*/
function isInterestEnabled(address _token) public view returns (bool) {
return boolStorage[keccak256(abi.encodePacked("interestEnabled", _token))];
}
/**
* @dev Initializes interest receiving functionality.
* Only owner can call this method.
* @param _token address of the token for interest earning.
* @param _minCashThreshold minimum amount of underlying tokens that are not invested.
* @param _minInterestPaid minimum amount of interest that can be paid in a single call.
*/
function initializeInterest(
address _token,
uint256 _minCashThreshold,
uint256 _minInterestPaid,
address _interestReceiver
) external onlyOwner {
require(_isInterestSupported(_token));
require(!isInterestEnabled(_token));
_setInterestEnabled(_token, true);
_setMinCashThreshold(_token, _minCashThreshold);
_setMinInterestPaid(_token, _minInterestPaid);
_setInterestReceiver(_token, _interestReceiver);
}
/**
* @dev Sets minimum amount of tokens that cannot be invested.
* Only owner can call this method.
* @param _token address of the token contract.
* @param _minCashThreshold minimum amount of underlying tokens that are not invested.
*/
function setMinCashThreshold(address _token, uint256 _minCashThreshold) external onlyOwner {
_setMinCashThreshold(_token, _minCashThreshold);
}
/**
* @dev Tells minimum amount of tokens that are not being invested.
* @param _token address of the invested token contract.
* @return amount of tokens.
*/
function minCashThreshold(address _token) public view returns (uint256) {
return uintStorage[keccak256(abi.encodePacked("minCashThreshold", _token))];
}
/**
* @dev Sets lower limit for the paid interest amount.
* Only owner can call this method.
* @param _token address of the token contract.
* @param _minInterestPaid minimum amount of interest paid in a single call.
*/
function setMinInterestPaid(address _token, uint256 _minInterestPaid) external onlyOwner {
_setMinInterestPaid(_token, _minInterestPaid);
}
/**
* @dev Tells minimum amount of paid interest in a single call.
* @param _token address of the invested token contract.
* @return paid interest minimum limit.
*/
function minInterestPaid(address _token) public view returns (uint256) {
return uintStorage[keccak256(abi.encodePacked("minInterestPaid", _token))];
}
/**
* @dev Internal function that disables interest for locked funds.
* Only owner can call this method.
* @param _token of token to disable interest for.
*/
function disableInterest(address _token) external onlyOwner {
_withdraw(_token, uint256(-1));
_setInterestEnabled(_token, false);
}
/**
* @dev Tells configured address of the interest receiver.
* @param _token address of the invested token contract.
* @return address of the interest receiver.
*/
function interestReceiver(address _token) public view returns (address) {
return addressStorage[keccak256(abi.encodePacked("interestReceiver", _token))];
}
/**
* Updates the interest receiver address.
* Only owner can call this method.
* @param _token address of the invested token contract.
* @param _receiver new receiver address.
*/
function setInterestReceiver(address _token, address _receiver) external onlyOwner {
_setInterestReceiver(_token, _receiver);
}
/**
* @dev Pays collected interest for the specific underlying token.
* Requires interest for the given token to be enabled.
* @param _token address of the token contract.
*/
function payInterest(address _token) external onlyEOA interestEnabled(_token) {
uint256 interest = interestAmount(_token);
require(interest >= minInterestPaid(_token));
uint256 redeemed = _safeWithdrawTokens(_token, interest);
_transferInterest(_token, redeemed);
}
/**
* @dev Tells the amount of underlying tokens that are currently invested.
* @param _token address of the token contract.
* @return amount of underlying tokens.
*/
function investedAmount(address _token) public view returns (uint256) {
return uintStorage[keccak256(abi.encodePacked("investedAmount", _token))];
}
/**
* @dev Invests all excess tokens.
* Requires interest for the given token to be enabled.
* @param _token address of the token contract considered.
*/
function invest(address _token) public interestEnabled(_token) {
uint256 balance = _selfBalance(_token);
uint256 minCash = minCashThreshold(_token);
require(balance > minCash);
uint256 amount = balance - minCash;
_setInvestedAmount(_token, investedAmount(_token).add(amount));
_invest(_token, amount);
}
/**
* @dev Internal function for transferring interest.
* Calls a callback on the receiver, if it is a contract.
* @param _token address of the underlying token contract.
* @param _amount amount of collected tokens that should be sent.
*/
function _transferInterest(address _token, uint256 _amount) internal {
address receiver = interestReceiver(_token);
require(receiver != address(0));
ERC20(_token).transfer(receiver, _amount);
if (AddressUtils.isContract(receiver)) {
IInterestReceiver(receiver).onInterestReceived(_token);
}
emit PaidInterest(_token, receiver, _amount);
}
/**
* @dev Internal function for setting interest enabled flag for some token.
* @param _token address of the token contract.
* @param _enabled true to enable interest earning, false to disable.
*/
function _setInterestEnabled(address _token, bool _enabled) internal {
boolStorage[keccak256(abi.encodePacked("interestEnabled", _token))] = _enabled;
}
/**
* @dev Internal function for setting the amount of underlying tokens that are currently invested.
* @param _token address of the token contract.
* @param _amount new amount of invested tokens.
*/
function _setInvestedAmount(address _token, uint256 _amount) internal {
uintStorage[keccak256(abi.encodePacked("investedAmount", _token))] = _amount;
}
/**
* @dev Internal function for withdrawing some amount of the invested tokens.
* Reverts if given amount cannot be withdrawn.
* @param _token address of the token contract withdrawn.
* @param _amount amount of requested tokens to be withdrawn.
*/
function _withdraw(address _token, uint256 _amount) internal {
if (_amount == 0) return;
uint256 invested = investedAmount(_token);
uint256 withdrawal = _amount > invested ? invested : _amount;
uint256 redeemed = _safeWithdrawTokens(_token, withdrawal);
_setInvestedAmount(_token, invested > redeemed ? invested - redeemed : 0);
}
/**
* @dev Internal function for safe withdrawal of invested tokens.
* Reverts if given amount cannot be withdrawn.
* Additionally verifies that at least _amount of tokens were withdrawn.
* @param _token address of the token contract withdrawn.
* @param _amount amount of requested tokens to be withdrawn.
*/
function _safeWithdrawTokens(address _token, uint256 _amount) private returns (uint256) {
uint256 balance = _selfBalance(_token);
_withdrawTokens(_token, _amount);
uint256 redeemed = _selfBalance(_token) - balance;
require(redeemed >= _amount);
return redeemed;
}
/**
* @dev Internal function for setting minimum amount of tokens that cannot be invested.
* @param _token address of the token contract.
* @param _minCashThreshold minimum amount of underlying tokens that are not invested.
*/
function _setMinCashThreshold(address _token, uint256 _minCashThreshold) internal {
uintStorage[keccak256(abi.encodePacked("minCashThreshold", _token))] = _minCashThreshold;
}
/**
* @dev Internal function for setting lower limit for paid interest amount.
* @param _token address of the token contract.
* @param _minInterestPaid minimum amount of interest paid in a single call.
*/
function _setMinInterestPaid(address _token, uint256 _minInterestPaid) internal {
uintStorage[keccak256(abi.encodePacked("minInterestPaid", _token))] = _minInterestPaid;
}
/**
* @dev Internal function for setting interest receiver address.
* @param _token address of the invested token contract.
* @param _receiver address of the interest receiver.
*/
function _setInterestReceiver(address _token, address _receiver) internal {
require(_receiver != address(this));
addressStorage[keccak256(abi.encodePacked("interestReceiver", _token))] = _receiver;
}
/**
* @dev Tells this contract balance of some specific token contract
* @param _token address of the token contract.
* @return contract balance.
*/
function _selfBalance(address _token) internal view returns (uint256) {
return ERC20(_token).balanceOf(address(this));
}
function _isInterestSupported(address _token) internal pure returns (bool);
function _invest(address _token, uint256 _amount) internal;
function _withdrawTokens(address _token, uint256 _amount) internal;
function interestAmount(address _token) public view returns (uint256);
}