diff --git a/contracts/deployer.sol b/contracts/deployer.sol index cad6c82..5b32762 100644 --- a/contracts/deployer.sol +++ b/contracts/deployer.sol @@ -1,46 +1,44 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.6.8; -interface IProxy { - function setBasic(address, address) external; -} - contract Deployer { - event LogNewProxy(address indexed owner, address indexed logic, address indexed token); + mapping (address => bool) public isFlusher; - /** - * @dev deploy create2 + minimal proxy - * @param owner owner address used for salt - * @param logic flusher contract address - * @param token token address - */ - function deployLogic(address owner, address logic, address token) public returns (address proxy) { + event LogNewProxy(address indexed owner, address indexed logic); + + // deploy create2 + minimal proxy + function deployLogic(address owner, address logic) public returns (address proxy) { + require(!(isFlusherDeployed(getAddress(owner, logic))), "Flusher-already-deployed"); bytes32 salt = keccak256(abi.encodePacked(owner)); bytes20 targetBytes = bytes20(logic); // solium-disable-next-line security/no-inline-assembly assembly { - let clone := mload(0x40) - mstore( - clone, - 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000 - ) - mstore(add(clone, 0x14), targetBytes) - mstore( - add(clone, 0x28), - 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000 - ) - proxy := create2(0, clone, 0x37, salt) + let clone := mload(0x40) + mstore( + clone, + 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000 + ) + mstore(add(clone, 0x14), targetBytes) + mstore( + add(clone, 0x28), + 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000 + ) + proxy := create2(0, clone, 0x37, salt) } - IProxy(proxy).setBasic(owner, token); - emit LogNewProxy(owner, logic, token); + isFlusher[proxy] = true; + emit LogNewProxy(owner, logic); } - /** - * @dev compute create2 + minimal proxy address - * @param owner owner address used for salt - * @param logic flusher contract address - */ + function isFlusherDeployed(address _address) public view returns (bool) { + uint32 size; + assembly { + size := extcodesize(_address) + } + return (size > 0); + } + + // compute create2 + minimal proxy address function getAddress(address owner, address logic) public view returns (address) { bytes32 codeHash = keccak256(getCreationCode(logic)); bytes32 salt = keccak256(abi.encodePacked(owner)); @@ -50,14 +48,15 @@ contract Deployer { address(this), salt, codeHash - )); - return address(bytes20(rawAddress << 96)); + ) + ); + return address(bytes20(rawAddress << 96)); } - + function getCreationCode(address logic) public pure returns (bytes memory) { bytes20 a = bytes20(0x3D602d80600A3D3981F3363d3d373d3D3D363d73); bytes20 b = bytes20(logic); bytes15 c = bytes15(0x5af43d82803e903d91602b57fd5bf3); return abi.encodePacked(a, b, c); } -} +} \ No newline at end of file diff --git a/contracts/flusher.sol b/contracts/flusher.sol index 45bc46b..0b2cdbb 100644 --- a/contracts/flusher.sol +++ b/contracts/flusher.sol @@ -1,143 +1,43 @@ // SPDX-License-Identifier: MIT + pragma solidity ^0.6.8; pragma experimental ABIEncoderV2; -import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; - -interface YieldPool { - function balanceOf(address) external view returns (uint); - function deposit(uint) external payable returns (uint); - function withdraw(uint, address) external returns (uint); -} - interface RegistryInterface { function signer(address) external view returns (bool); - function chief(address) external view returns (bool); - function poolToken(address) external view returns (address); + function isConnector(address[] calldata) external view returns (bool); } contract Flusher { - using SafeERC20 for IERC20; + event LogCast(address indexed sender, uint value); + + string constant public name = "Flusher-v1"; + uint constant version = 1; - address payable public owner; RegistryInterface public constant registry = RegistryInterface(address(0)); // TODO - Change while deploying. - bool public shield; - uint256 public shieldBlockTime; - uint256 internal waitBlockTime = 172800; // 30 days blocktime. - modifier isSigner { - require(registry.signer(msg.sender), "not-signer"); - _; - } - - modifier isChief { - require(registry.chief(msg.sender), "not-chief"); - _; - } - - event LogInit(address indexed owner); - event LogSwitch(bool indexed shieldState); - event LogDeposit(address indexed token, address indexed tokenPool, uint amount); - event LogWithdraw(address indexed token, address indexed tokenPool,uint amount); - event LogWithdrawToOwner(address indexed token, address indexed owner, uint amount); - - function _deposit(address token) internal { - require(address(token) != address(0), "invalid-token"); - - address poolToken = registry.poolToken(token); - require(poolToken != address(0), "invalid-pool"); - - IERC20 tokenContract = IERC20(token); - - YieldPool poolContract = YieldPool(poolToken); - uint amt; - if (address(tokenContract) == address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)) { - amt = address(this).balance; - poolContract.deposit{value: amt}(amt); - } else { - amt = tokenContract.balanceOf(address(this)); - if (tokenContract.allowance(address(this), address(poolContract)) == 0) - tokenContract.approve(address(poolContract), uint(-1)); - - poolContract.deposit(amt); - } - emit LogDeposit(token, address(poolContract), amt); - } - - function deposit(address token) external payable isSigner { - _deposit(token); - } - - function withdraw(address token, uint amount) external isSigner returns (uint _amount) { - require(address(token) != address(0), "invalid-token"); - address poolToken = registry.poolToken(token); - require(poolToken != address(0), "invalid-pool"); - - _amount = YieldPool(poolToken).withdraw(amount, owner); - emit LogWithdraw(token, poolToken, _amount); - } - - /** - * @dev withdraw to owner (rare case) - */ - function claim(address token) external isSigner returns (uint) { - require(address(token) != address(0), "invalid-token"); - - uint amount; - if (address(token) == address(0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE)) { - amount = address(this).balance; - payable(owner).transfer(amount); - } else { - IERC20 tokenContract = IERC20(token); - amount = tokenContract.balanceOf(address(this)); - tokenContract.safeTransfer(address(owner), amount); - } - emit LogWithdrawToOwner(token, owner, amount); - } - - function setBasic(address newOwner, address token) external { - require(owner == address(0), "already-an-owner"); - require(newOwner != address(0), "not-vaild-owner-address"); - require(token != address(0), "not-vaild-token-address"); - owner = payable(newOwner); - _deposit(token); - emit LogInit(newOwner); - } - - function switchShield() external isChief { - require(registry.chief(msg.sender), "not-chief"); - shield = !shield; - if (!shield) { - shieldBlockTime = block.number + waitBlockTime; - } else { - delete shieldBlockTime; - } - emit LogSwitch(shield); - } - - /** - * @dev backdoor function - */ - function spell(address _target, bytes calldata _data) external isChief { - require(!shield, "shield-access-denied"); - require(shieldBlockTime != 0 && shieldBlockTime <= block.number, "more-than-30-days"); + function spell(address _target, bytes memory _data) internal { require(_target != address(0), "target-invalid"); - require(_data.length > 0, "data-invalid"); - bytes memory _callData = _data; - address _owner = owner; assembly { - let succeeded := delegatecall(gas(), _target, add(_callData, 0x20), mload(_callData), 0, 0) + let succeeded := delegatecall(gas(), _target, add(_data, 0x20), mload(_data), 0, 0) switch iszero(succeeded) - case 1 { - // throw if delegatecall failed - let size := returndatasize() - returndatacopy(0x00, 0x00, size) - revert(0x00, size) - } + case 1 { + let size := returndatasize() + returndatacopy(0x00, 0x00, size) + revert(0x00, size) + } } - require(_owner == owner, "owner-change-denied"); + } + + function cast(address[] calldata _targets, bytes[] calldata _datas) external payable { + require(registry.signer(msg.sender), "not-signer"); + require(_targets.length == _datas.length , "invalid-array-length"); + require(registry.isConnector(_targets), "not-connector"); + for (uint i = 0; i < _targets.length; i++) { + spell(_targets[i], _datas[i]); + } + emit LogCast(msg.sender, msg.value); } receive() external payable {} - -} +} \ No newline at end of file diff --git a/contracts/pools/erc20.sol b/contracts/pools/erc20.sol index 89a4520..7fd0c9a 100644 --- a/contracts/pools/erc20.sol +++ b/contracts/pools/erc20.sol @@ -10,12 +10,12 @@ import { DSMath } from "../libs/safeMath.sol"; interface IndexInterface { function master() external view returns (address); - function build(address _owner, uint accountVersion, address _origin) external returns (address _account); } interface RegistryInterface { function chief(address) external view returns (bool); function poolLogic(address) external returns (address); + function flusherLogic(address) external returns (address); function fee(address) external view returns (uint); function poolCap(address) external view returns (uint); function checkSettleLogics(address, address[] calldata) external view returns (bool); @@ -25,17 +25,18 @@ interface RateInterface { function getTotalToken() external returns (uint totalUnderlyingTkn); } -contract PoolToken is ReentrancyGuard, DSMath, ERC20Pausable { +interface FlusherLogicInterface { + function isFlusher(address) external returns (bool); +} + +contract PoolToken is ReentrancyGuard, ERC20Pausable, DSMath { using SafeERC20 for IERC20; - event LogDeploy(address indexed dsa, address token, uint amount); event LogExchangeRate(uint exchangeRate, uint tokenBalance, uint insuranceAmt); event LogSettle(uint settleBlock); event LogDeposit(address indexed user, uint depositAmt, uint poolMintAmt); event LogWithdraw(address indexed user, uint withdrawAmt, uint poolBurnAmt); - event LogAddFee(uint amount); event LogWithdrawFee(uint amount); - event LogPausePool(bool); IERC20 public immutable baseToken; // Base token. Eg:- DAI, USDC, etc. RegistryInterface public immutable registry; // Pool Registry @@ -45,54 +46,59 @@ contract PoolToken is ReentrancyGuard, DSMath, ERC20Pausable { uint public feeAmt; // fee collected on profits constructor( - address _registry, - string memory _name, - string memory _symbol, - address _baseToken + address _registry, + string memory _name, + string memory _symbol, + address _baseToken ) public ERC20(_name, _symbol) { - baseToken = IERC20(_baseToken); - registry = RegistryInterface(_registry); - exchangeRate = 10 ** uint(36 - ERC20(_baseToken).decimals()); + baseToken = IERC20(_baseToken); + registry = RegistryInterface(_registry); + exchangeRate = 10 ** uint(36 - ERC20(_baseToken).decimals()); } modifier isChief() { - require(registry.chief(msg.sender) || msg.sender == instaIndex.master(), "not-chief"); - _; + require(registry.chief(msg.sender) || msg.sender == instaIndex.master(), "not-chief"); + _; + } + + modifier isFlusher() { + require(FlusherLogicInterface(registry.flusherLogic(address(this))).isFlusher(msg.sender), "not-flusher"); + _; } /** * @dev get pool token rate * @param tokenAmt total token amount - */ + */ function getCurrentRate(uint tokenAmt) internal view returns (uint) { return wdiv(totalSupply(), tokenAmt); } /** - * @dev sets exchange rates + * @dev sets exchange rate */ function setExchangeRate() public { require(msg.sender == address(this), "not-pool-address"); - uint _previousRate = exchangeRate; + uint _prevRate = exchangeRate; uint _totalToken = RateInterface(registry.poolLogic(address(this))).getTotalToken(); _totalToken = sub(_totalToken, feeAmt); - uint _currentRate = getCurrentRate(_totalToken); - uint _tokenBal; - require(_currentRate != 0, "current-rate-is-zero"); - if (_currentRate > _previousRate) { // loss => deduct partially/fully from insurance amount - _currentRate = _previousRate; - } else { // profit => add to insurance amount + uint _newRate = getCurrentRate(_totalToken); + require(_newRate != 0, "current-rate-is-zero"); + uint _tokenBal = wdiv(totalSupply(), _prevRate); + if (_newRate > _prevRate) { + _newRate = _prevRate; + } else { uint _newFee = wmul(sub(_totalToken, _tokenBal), registry.fee(address(this))); feeAmt = add(feeAmt, _newFee); _tokenBal = sub(_totalToken, _newFee); - _currentRate = getCurrentRate(_tokenBal); + _newRate = getCurrentRate(_tokenBal); } - exchangeRate = _currentRate; + exchangeRate = _newRate; emit LogExchangeRate(exchangeRate, _tokenBal, feeAmt); } /** - * @dev Delegate the calls to Connector And this function is ran by cast(). + * @dev delegate the calls to connector and this function is ran by settle() * @param _target Target to of Connector. * @param _data CallData of function in Connector. */ @@ -128,48 +134,48 @@ contract PoolToken is ReentrancyGuard, DSMath, ERC20Pausable { /** * @dev Deposit token. * @param tknAmt token amount - * @return _mintAmt amount of wrap token minted + * @return mintAmt amount of wrap token minted */ - function deposit(uint tknAmt) external whenNotPaused payable returns (uint _mintAmt) { + function deposit(uint tknAmt) public payable whenNotPaused isFlusher returns (uint mintAmt) { require(msg.value == 0, "non-eth-pool"); uint _tokenBal = wdiv(totalSupply(), exchangeRate); uint _newTknBal = add(_tokenBal, tknAmt); - require(_newTknBal < registry.poolCap(address(this)), "unmatched-amount"); + require(_newTknBal < registry.poolCap(address(this)), "pool-cap-reached"); baseToken.safeTransferFrom(msg.sender, address(this), tknAmt); - _mintAmt = wmul(tknAmt, exchangeRate); - _mint(msg.sender, _mintAmt); - emit LogDeposit(msg.sender, tknAmt, _mintAmt); + mintAmt = wmul(tknAmt, exchangeRate); + _mint(msg.sender, mintAmt); + emit LogDeposit(msg.sender, tknAmt, mintAmt); } /** * @dev Withdraw tokens. * @param tknAmt token amount - * @param to withdraw tokens to address - * @return _tknAmt amount of token withdrawn + * @param target withdraw tokens to address + * @return wdAmt amount of token withdrawn */ - function withdraw(uint tknAmt, address to) external nonReentrant whenNotPaused returns (uint _tknAmt) { - require(to != address(0), "to-address-not-vaild"); + function withdraw(uint tknAmt, address target) external nonReentrant whenNotPaused returns (uint wdAmt) { + require(target != address(0), "invalid-target-address"); uint _userBal = wdiv(balanceOf(msg.sender), exchangeRate); uint _burnAmt; if (tknAmt >= _userBal) { _burnAmt = balanceOf(msg.sender); - _tknAmt = _userBal; + wdAmt = _userBal; } else { _burnAmt = wmul(tknAmt, exchangeRate); - _tknAmt = tknAmt; + wdAmt = tknAmt; } - require(_tknAmt <= baseToken.balanceOf(address(this)), "not-enough-liquidity-available"); + require(wdAmt <= baseToken.balanceOf(address(this)), "not-enough-liquidity-available"); _burn(msg.sender, _burnAmt); - baseToken.safeTransfer(to, _tknAmt); + baseToken.safeTransfer(target, wdAmt); - emit LogWithdraw(msg.sender, _tknAmt, _burnAmt); + emit LogWithdraw(msg.sender, wdAmt, _burnAmt); } /** - * @dev Withdraw Insurance from the pool. - * @notice only master can call this function. - * @param wdAmt insurance token amount to remove + * @dev withdraw fee from the pool + * @notice only master can call this function + * @param wdAmt fee amount to withdraw */ function withdrawFee(uint wdAmt) external { require(msg.sender == instaIndex.master(), "not-master"); @@ -189,5 +195,4 @@ contract PoolToken is ReentrancyGuard, DSMath, ERC20Pausable { } receive() external payable {} - -} +} \ No newline at end of file diff --git a/contracts/pools/eth.sol b/contracts/pools/eth.sol index 8dedd21..ba77eee 100644 --- a/contracts/pools/eth.sol +++ b/contracts/pools/eth.sol @@ -10,12 +10,12 @@ import { DSMath } from "../libs/safeMath.sol"; interface IndexInterface { function master() external view returns (address); - function build(address _owner, uint accountVersion, address _origin) external returns (address _account); } interface RegistryInterface { function chief(address) external view returns (bool); function poolLogic(address) external returns (address); + function flusherLogic(address) external returns (address); function fee(address) external view returns (uint); function poolCap(address) external view returns (uint); function checkSettleLogics(address, address[] calldata) external view returns (bool); @@ -25,6 +25,10 @@ interface RateInterface { function getTotalToken() external returns (uint totalUnderlyingTkn); } +interface FlusherLogicInterface { + function isFlusher(address) external returns (bool); +} + contract PoolETH is ReentrancyGuard, ERC20Pausable, DSMath { using SafeERC20 for IERC20; @@ -33,7 +37,6 @@ contract PoolETH is ReentrancyGuard, ERC20Pausable, DSMath { event LogDeposit(address indexed user, uint depositAmt, uint poolMintAmt); event LogWithdraw(address indexed user, uint withdrawAmt, uint poolBurnAmt); event LogWithdrawFee(uint amount); - event LogPausePool(bool); IERC20 public immutable baseToken; // Base token. RegistryInterface public immutable registry; // Pool Registry @@ -57,6 +60,11 @@ contract PoolETH is ReentrancyGuard, ERC20Pausable, DSMath { _; } + modifier isFlusher() { + require(FlusherLogicInterface(registry.flusherLogic(address(this))).isFlusher(msg.sender), "not-flusher"); + _; + } + /** * @dev get pool token rate * @param tokenAmt total token amount @@ -66,7 +74,7 @@ contract PoolETH is ReentrancyGuard, ERC20Pausable, DSMath { } /** - * @dev sets exchange rates + * @dev sets exchange rate */ function setExchangeRate() public { require(msg.sender == address(this), "not-pool-address"); @@ -74,12 +82,11 @@ contract PoolETH is ReentrancyGuard, ERC20Pausable, DSMath { uint _totalToken = RateInterface(registry.poolLogic(address(this))).getTotalToken(); _totalToken = sub(_totalToken, feeAmt); uint _newRate = getCurrentRate(_totalToken); - uint _tokenBal; require(_newRate != 0, "current-rate-is-zero"); + uint _tokenBal = wdiv(totalSupply(), _prevRate); if (_newRate > _prevRate) { _newRate = _prevRate; } else { - _tokenBal = wdiv(totalSupply(), _prevRate); uint _newFee = wmul(sub(_totalToken, _tokenBal), registry.fee(address(this))); feeAmt = add(feeAmt, _newFee); _tokenBal = sub(_totalToken, _newFee); @@ -128,11 +135,11 @@ contract PoolETH is ReentrancyGuard, ERC20Pausable, DSMath { * @param tknAmt token amount * @return mintAmt amount of wrap token minted */ - function deposit(uint tknAmt) public whenNotPaused payable returns (uint mintAmt) { + function deposit(uint tknAmt) public whenNotPaused payable isFlusher returns (uint mintAmt) { require(tknAmt == msg.value, "unmatched-amount"); uint _tokenBal = wdiv(totalSupply(), exchangeRate); uint _newTknBal = add(_tokenBal, tknAmt); - require(_newTknBal < registry.poolCap(address(this)), "unmatched-amount"); + require(_newTknBal < registry.poolCap(address(this)), "pool-cap-reached"); mintAmt = wmul(msg.value, exchangeRate); _mint(msg.sender, mintAmt); emit LogDeposit(msg.sender, tknAmt, mintAmt); diff --git a/contracts/registry.sol b/contracts/registry.sol index 50d79fc..c428d93 100644 --- a/contracts/registry.sol +++ b/contracts/registry.sol @@ -15,12 +15,15 @@ contract Registry { event LogAddSigner(address indexed signer); event LogRemoveSigner(address indexed signer); event LogUpdatePoolLogic(address token, address newLogic); + event LogUpdateFlusherLogic(address token, address newLogic); event LogUpdateFee(address token, uint newFee); event LogUpdateCap(address token, uint newFee); event LogAddPool(address indexed token, address indexed pool); event LogRemovePool(address indexed token, address indexed pool); event LogAddSettleLogic(address indexed token, address indexed logic); event LogRemoveSettleLogic(address indexed token, address indexed logic); + event LogConnectorEnable(address indexed connector); + event LogConnectorDisable(address indexed connector); IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); @@ -28,9 +31,11 @@ contract Registry { mapping (address => bool) public signer; mapping (address => address) public poolToken; mapping (address => address) public poolLogic; + mapping (address => address) public flusherLogic; mapping (address => uint) public poolCap; mapping (address => uint) public fee; mapping (address => mapping(address => bool)) public settleLogic; + mapping(address => bool) public connectors; modifier isMaster() { require(msg.sender == instaIndex.master(), "not-master"); @@ -124,6 +129,20 @@ contract Registry { emit LogUpdatePoolLogic(_pool, _newLogic); } + /** + * @dev update flusher logic + * @param _token pool address + * @param _newLogic new flusher logic address + */ + function updateFlusherLogic(address _token, address _newLogic) external isMaster { + address _pool = poolToken[_token]; + require(_pool != address(0), "invalid-pool"); + require(_newLogic != address(0), "invalid-address"); + require(flusherLogic[_pool] != _newLogic, "same-pool-logic"); + flusherLogic[_pool] = _newLogic; + emit LogUpdateFlusherLogic(_pool, _newLogic); + } + /** * @dev update pool fee * @param _token pool address @@ -164,13 +183,37 @@ contract Registry { emit LogRemoveSettleLogic(_pool, _logic); } - function checkSettleLogics(address _pool, address[] calldata _logics) external view returns(bool) { + function enableConnector(address _connector) external isChief { + require(!connectors[_connector], "already-enabled"); + require(_connector != address(0), "invalid-connector"); + connectors[_connector] = true; + emit LogConnectorEnable(_connector); + } + + function disableConnector(address _connector) external isChief { + require(connectors[_connector], "already-disabled"); + delete connectors[_connector]; + emit LogConnectorDisable(_connector); + } + + function checkSettleLogics(address _pool, address[] calldata _logics) external view returns(bool isOk) { + isOk = true; for (uint i = 0; i < _logics.length; i++) { if (!settleLogic[_pool][_logics[i]]) { - return false; + isOk = false; + break; + } + } + } + + function isConnector(address[] calldata _connectors) external view returns (bool isOk) { + isOk = true; + for (uint i = 0; i < _connectors.length; i++) { + if (!connectors[_connectors[i]]) { + isOk = false; + break; } } - return true; } constructor(address _chief) public {