diff --git a/contracts/deployer.sol b/contracts/deployer.sol index cad6c82..c1b2cab 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 flushers; - /** - * @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 LogNewFlusher(address indexed owner, address indexed flusher, 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); + flushers[proxy] = true; + emit LogNewFlusher(owner, proxy, 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..b14f38b 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); - 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. + string constant public name = "Flusher-v1"; + uint constant version = 1; - modifier isSigner { - require(registry.signer(msg.sender), "not-signer"); - _; - } + RegistryInterface public constant registry = RegistryInterface(address(0)); // TODO - Change while deploying - 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 171cf59..7fd0c9a 100644 --- a/contracts/pools/erc20.sol +++ b/contracts/pools/erc20.sol @@ -8,223 +8,191 @@ import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import { DSMath } from "../libs/safeMath.sol"; -interface AccountInterface { - function isAuth(address) external view returns(bool); - function cast(address[] calldata _targets, bytes[] calldata _datas, address _origin) external payable; -} - 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 insureFee(address) external view returns (uint); - function withdrawalFee(address) external view returns (uint); - function isDsa(address, address) external view returns (bool); + 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); } interface RateInterface { function getTotalToken() external returns (uint totalUnderlyingTkn); } -contract PoolToken is ReentrancyGuard, DSMath, ERC20Pausable { - 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, uint feeAmt); - event LogAddInsurance(uint amount); - event LogWithdrawInsurance(uint amount); - event LogPausePool(bool); - - IERC20 public immutable baseToken; // Base token. Eg:- DAI, USDC, etc. - RegistryInterface public immutable registry; // Pool Registry - IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); // Main Index - - uint private tokenBalance; // total token balance - uint public exchangeRate; // initial 1 token = 1 - uint public insuranceAmt; // insurance amount to keep pool safe - - constructor( - 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()); - } - - modifier isChief() { - require(registry.chief(msg.sender) || msg.sender == instaIndex.master(), "not-chief"); - _; - } - - /** - * @dev Deploy assets to DSA. - * @param _dsa DSA address - * @param token token address - * @param amount token amount - */ - function deploy(address _dsa, address token, uint amount) public isChief { - require(registry.isDsa(address(this), _dsa), "not-autheticated-dsa"); - require(AccountInterface(_dsa).isAuth(address(this)), "token-pool-not-auth"); - if (token == address(0)) { // pool base token - baseToken.safeTransfer(_dsa, amount); - } else if (token == 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE){ // non-pool ethereum - payable(_dsa).transfer(amount); - } else { // non-pool other tokens - IERC20(token).safeTransfer(_dsa, amount); - } - emit LogDeploy(_dsa, token, amount); - } - - /** - * @dev get pool token rate - * @param tokenAmt total token amount - */ - function getCurrentRate(uint tokenAmt) public view returns (uint) { - return wdiv(totalSupply(), tokenAmt); - } - - /** - * @dev sets exchange rates - */ - function setExchangeRate() public isChief { - uint _previousRate = exchangeRate; - uint _totalToken = RateInterface(registry.poolLogic(address(this))).getTotalToken(); - _totalToken = sub(_totalToken, insuranceAmt); - uint _currentRate = getCurrentRate(_totalToken); - require(_currentRate != 0, "current-rate-is-zero"); - if (_currentRate > _previousRate) { // loss => deduct partially/fully from insurance amount - uint _loss = sub(tokenBalance, _totalToken); - if (_loss <= insuranceAmt) { - insuranceAmt = sub(insuranceAmt, _loss); - _currentRate = _previousRate; - } else { - tokenBalance = add(_totalToken, insuranceAmt); - insuranceAmt = 0; - _currentRate = getCurrentRate(tokenBalance); - } - } else { // profit => add to insurance amount - uint insureFeeAmt = wmul(sub(_totalToken, tokenBalance), registry.insureFee(address(this))); - insuranceAmt = add(insuranceAmt, insureFeeAmt); - tokenBalance = sub(_totalToken, insureFeeAmt); - _currentRate = getCurrentRate(tokenBalance); - } - exchangeRate = _currentRate; - emit LogExchangeRate(exchangeRate, tokenBalance, insuranceAmt); - } - - /** - * @dev Settle the assets on dsa and update exchange rate - * @param _dsa DSA address - * @param _targets array of connector's address - * @param _datas array of connector's function calldata - * @param _origin origin address - */ - function settle(address _dsa, address[] calldata _targets, bytes[] calldata _datas, address _origin) external isChief { - require(registry.isDsa(address(this), _dsa), "not-autheticated-dsa"); - AccountInterface dsaWallet = AccountInterface(_dsa); - if (_targets.length > 0 && _datas.length > 0) { - dsaWallet.cast(_targets, _datas, _origin); - } - require(dsaWallet.isAuth(address(this)), "token-pool-not-auth"); - setExchangeRate(); - emit LogSettle(block.number); - } - - /** - * @dev Deposit token. - * @param tknAmt token amount - * @return _mintAmt amount of wrap token minted - */ - function deposit(uint tknAmt) external whenNotPaused payable returns (uint _mintAmt) { - require(msg.value == 0, "non-eth-pool"); - tokenBalance = add(tokenBalance, tknAmt); - - baseToken.safeTransferFrom(msg.sender, address(this), tknAmt); - _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 - */ - function withdraw(uint tknAmt, address to) external nonReentrant whenNotPaused returns (uint _tknAmt) { - uint poolBal = baseToken.balanceOf(address(this)); - require(to != address(0), "to-address-not-vaild"); - uint _bal = balanceOf(msg.sender); - uint _tknBal = wdiv(_bal, exchangeRate); - uint _burnAmt; - if (tknAmt >= _tknBal) { - _burnAmt = _bal; - _tknAmt = _tknBal; - } else { - _burnAmt = wmul(tknAmt, exchangeRate); - _tknAmt = tknAmt; - } - require(_tknAmt <= poolBal, "not-enough-liquidity-available"); - - tokenBalance = sub(tokenBalance, _tknAmt); - - _burn(msg.sender, _burnAmt); - - uint _withdrawalFee = registry.withdrawalFee(address(this)); - uint _feeAmt; - if (_withdrawalFee > 0) { - _feeAmt = wmul(_tknAmt, _withdrawalFee); - insuranceAmt = add(insuranceAmt, _feeAmt); - _tknAmt = sub(_tknAmt, _feeAmt); - } - - baseToken.safeTransfer(to, _tknAmt); - - emit LogWithdraw(msg.sender, _tknAmt, _burnAmt, _feeAmt); - } - - /** - * @dev Add Insurance to the pool. - * @param tknAmt insurance token amount to add - */ - function addInsurance(uint tknAmt) external { - baseToken.safeTransferFrom(msg.sender, address(this), tknAmt); - insuranceAmt = add(insuranceAmt, tknAmt); - emit LogAddInsurance(tknAmt); - } - - /** - * @dev Withdraw Insurance from the pool. - * @notice only master can call this function. - * @param tknAmt insurance token amount to remove - */ - function withdrawInsurance(uint tknAmt) external { - require(msg.sender == instaIndex.master(), "not-master"); - require(tknAmt <= insuranceAmt, "not-enough-insurance"); - baseToken.safeTransfer(msg.sender, tknAmt); - insuranceAmt = sub(insuranceAmt, tknAmt); - emit LogWithdrawInsurance(tknAmt); - } - - /** - * @dev Shut the pool. - * @notice only master can call this function. - */ - function shutdown() external { - require(msg.sender == instaIndex.master(), "not-master"); - paused() ? _unpause() : _pause(); - } - - receive() external payable {} +interface FlusherLogicInterface { + function isFlusher(address) external returns (bool); } + +contract PoolToken is ReentrancyGuard, ERC20Pausable, DSMath { + using SafeERC20 for IERC20; + + 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 LogWithdrawFee(uint amount); + + IERC20 public immutable baseToken; // Base token. Eg:- DAI, USDC, etc. + RegistryInterface public immutable registry; // Pool Registry + IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); // Main Index + + uint public exchangeRate; // initial 1 token = 1 + uint public feeAmt; // fee collected on profits + + constructor( + 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()); + } + + modifier isChief() { + 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 rate + */ + function setExchangeRate() public { + require(msg.sender == address(this), "not-pool-address"); + uint _prevRate = exchangeRate; + uint _totalToken = RateInterface(registry.poolLogic(address(this))).getTotalToken(); + _totalToken = sub(_totalToken, feeAmt); + 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); + _newRate = getCurrentRate(_tokenBal); + } + exchangeRate = _newRate; + emit LogExchangeRate(exchangeRate, _tokenBal, feeAmt); + } + + /** + * @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. + */ + function spell(address _target, bytes memory _data) internal { + require(_target != address(0), "target-invalid"); + assembly { + 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) + } + } + } + + /** + * @dev Settle the assets on dsa and update exchange rate + * @param _targets array of connector's address + * @param _data array of connector's function calldata + */ + function settle(address[] calldata _targets, bytes[] calldata _data) external isChief { + require(_targets.length == _data.length , "array-length-invalid"); + require(registry.checkSettleLogics(address(this), _targets), "not-logic"); + for (uint i = 0; i < _targets.length; i++) { + spell(_targets[i], _data[i]); + } + emit LogSettle(block.number); + } + + /** + * @dev Deposit token. + * @param tknAmt token amount + * @return mintAmt amount of wrap token minted + */ + 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)), "pool-cap-reached"); + baseToken.safeTransferFrom(msg.sender, address(this), tknAmt); + mintAmt = wmul(tknAmt, exchangeRate); + _mint(msg.sender, mintAmt); + emit LogDeposit(msg.sender, tknAmt, mintAmt); + } + + /** + * @dev Withdraw tokens. + * @param tknAmt token amount + * @param target withdraw tokens to address + * @return wdAmt amount of token withdrawn + */ + 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); + wdAmt = _userBal; + } else { + _burnAmt = wmul(tknAmt, exchangeRate); + wdAmt = tknAmt; + } + require(wdAmt <= baseToken.balanceOf(address(this)), "not-enough-liquidity-available"); + + _burn(msg.sender, _burnAmt); + baseToken.safeTransfer(target, wdAmt); + + emit LogWithdraw(msg.sender, wdAmt, _burnAmt); + } + + /** + * @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"); + if (wdAmt > feeAmt) wdAmt = feeAmt; + baseToken.safeTransfer(msg.sender, wdAmt); + feeAmt = sub(feeAmt, wdAmt); + emit LogWithdrawFee(wdAmt); + } + + /** + * @dev Shut the pool. + * @notice only master can call this function. + */ + function shutdown() external { + require(msg.sender == instaIndex.master(), "not-master"); + paused() ? _unpause() : _pause(); + } + + receive() external payable {} +} \ No newline at end of file diff --git a/contracts/pools/eth.sol b/contracts/pools/eth.sol index 996e294..ba77eee 100644 --- a/contracts/pools/eth.sol +++ b/contracts/pools/eth.sol @@ -8,47 +8,42 @@ import "@openzeppelin/contracts/utils/ReentrancyGuard.sol"; import { DSMath } from "../libs/safeMath.sol"; -interface AccountInterface { - function isAuth(address) external view returns(bool); - function cast(address[] calldata _targets, bytes[] calldata _datas, address _origin) external payable; -} - 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 insureFee(address) external view returns (uint); - function withdrawalFee(address) external view returns (uint); - function isDsa(address, address) external view returns (bool); + 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); } 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; - event LogDeploy(address indexed dsa, address indexed 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, uint feeAmt); - event LogAddInsurance(uint amount); - event LogWithdrawInsurance(uint amount); - event LogPausePool(bool); + event LogWithdraw(address indexed user, uint withdrawAmt, uint poolBurnAmt); + event LogWithdrawFee(uint amount); + IERC20 public immutable baseToken; // Base token. RegistryInterface public immutable registry; // Pool Registry IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); - IERC20 public immutable baseToken; // Base token. - uint private tokenBalance; // total token balance uint public exchangeRate = 10 ** 18; // initial 1 token = 1 - uint public insuranceAmt; // insurance amount to keep pool safe + uint public feeAmt; // fee collected on profits constructor( address _registry, @@ -65,152 +60,127 @@ contract PoolETH is ReentrancyGuard, ERC20Pausable, DSMath { _; } - /** - * @dev Deploy assets to DSA. - * @param _dsa DSA address - * @param token token address - * @param amount token amount - */ - function deploy(address _dsa, address token, uint amount) external isChief { - require(registry.isDsa(address(this), _dsa), "not-autheticated-dsa"); - require(AccountInterface(_dsa).isAuth(address(this)), "token-pool-not-auth"); - if (token == address(0)) { // pool base ETH - payable(_dsa).transfer(amount); - } else { // non-pool other tokens - IERC20(token).safeTransfer(_dsa, amount); - } - emit LogDeploy(_dsa, token, amount); + 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) public view returns (uint) { + function getCurrentRate(uint tokenAmt) internal view returns (uint) { return wdiv(totalSupply(), tokenAmt); } /** - * @dev sets exchange rates - */ - function setExchangeRate() public isChief { - uint _previousRate = exchangeRate; + * @dev sets exchange rate + */ + function setExchangeRate() public { + require(msg.sender == address(this), "not-pool-address"); + uint _prevRate = exchangeRate; uint _totalToken = RateInterface(registry.poolLogic(address(this))).getTotalToken(); - _totalToken = sub(_totalToken, insuranceAmt); - uint _currentRate = getCurrentRate(_totalToken); - require(_currentRate != 0, "current-rate-is-zero"); - if (_currentRate > _previousRate) { // loss => deduct partially/fully from insurance amount - uint _loss = sub(tokenBalance, _totalToken); - if (_loss <= insuranceAmt) { - insuranceAmt = sub(insuranceAmt, _loss); - _currentRate = _previousRate; - } else { - tokenBalance = add(_totalToken, insuranceAmt); - insuranceAmt = 0; - _currentRate = getCurrentRate(tokenBalance); - } - } else { // profit => add to insurance amount - uint insureFeeAmt = wmul(sub(_totalToken, tokenBalance), registry.insureFee(address(this))); - insuranceAmt = add(insuranceAmt, insureFeeAmt); - tokenBalance = sub(_totalToken, insureFeeAmt); - _currentRate = getCurrentRate(tokenBalance); + _totalToken = sub(_totalToken, feeAmt); + 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); + _newRate = getCurrentRate(_tokenBal); + } + exchangeRate = _newRate; + emit LogExchangeRate(exchangeRate, _tokenBal, feeAmt); + } + + /** + * @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. + */ + function spell(address _target, bytes memory _data) internal { + require(_target != address(0), "target-invalid"); + assembly { + 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) + } } - exchangeRate = _currentRate; - emit LogExchangeRate(exchangeRate, tokenBalance, insuranceAmt); } /** * @dev Settle the assets on dsa and update exchange rate - * @param _dsa DSA address * @param _targets array of connector's address - * @param _datas array of connector's function calldata - * @param _origin origin address + * @param _data array of connector's function calldata */ - function settle(address _dsa, address[] calldata _targets, bytes[] calldata _datas, address _origin) external isChief { - require(registry.isDsa(address(this), _dsa), "not-autheticated-dsa"); - AccountInterface dsaWallet = AccountInterface(_dsa); - if (_targets.length > 0 && _datas.length > 0) { - dsaWallet.cast(_targets, _datas, _origin); + function settle(address[] calldata _targets, bytes[] calldata _data) external isChief { + require(_targets.length == _data.length , "array-length-invalid"); + require(registry.checkSettleLogics(address(this), _targets), "not-logic"); + for (uint i = 0; i < _targets.length; i++) { + spell(_targets[i], _data[i]); } - require(dsaWallet.isAuth(address(this)), "token-pool-not-auth"); - setExchangeRate(); emit LogSettle(block.number); } /** * @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) public whenNotPaused payable returns (uint _mintAmt) { + function deposit(uint tknAmt) public whenNotPaused payable isFlusher returns (uint mintAmt) { require(tknAmt == msg.value, "unmatched-amount"); - tokenBalance = add(tokenBalance, tknAmt); - - _mintAmt = wmul(msg.value, exchangeRate); - _mint(msg.sender, _mintAmt); - - emit LogDeposit(msg.sender, tknAmt, _mintAmt); + uint _tokenBal = wdiv(totalSupply(), exchangeRate); + uint _newTknBal = add(_tokenBal, tknAmt); + require(_newTknBal < registry.poolCap(address(this)), "pool-cap-reached"); + mintAmt = wmul(msg.value, 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) { - uint poolBal = address(this).balance; - require(to != address(0), "to-address-not-vaild"); - uint _bal = balanceOf(msg.sender); - uint _tknBal = wdiv(_bal, exchangeRate); + 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 >= _tknBal) { - _burnAmt = _bal; - _tknAmt = _tknBal; + if (tknAmt >= _userBal) { + _burnAmt = balanceOf(msg.sender); + wdAmt = _userBal; } else { _burnAmt = wmul(tknAmt, exchangeRate); - _tknAmt = tknAmt; + wdAmt = tknAmt; } - require(_tknAmt <= poolBal, "not-enough-liquidity-available"); - - tokenBalance = sub(tokenBalance, _tknAmt); + require(wdAmt <= address(this).balance, "not-enough-liquidity-available"); _burn(msg.sender, _burnAmt); + payable(target).transfer(wdAmt); - uint _withdrawalFee = registry.withdrawalFee(address(this)); - uint _feeAmt; - if (_withdrawalFee > 0) { - _feeAmt = wmul(_tknAmt, _withdrawalFee); - insuranceAmt = add(insuranceAmt, _feeAmt); - _tknAmt = sub(_tknAmt, _feeAmt); - } - - payable(to).transfer(_tknAmt); - - emit LogWithdraw(msg.sender, _tknAmt, _burnAmt, _feeAmt); + emit LogWithdraw(msg.sender, wdAmt, _burnAmt); } /** - * @dev Add Insurance to the pool. - * @param tknAmt insurance token amount to add + * @dev withdraw fee from the pool + * @notice only master can call this function + * @param wdAmt fee amount to withdraw */ - function addInsurance(uint tknAmt) external payable { - require(tknAmt == msg.value, "unmatched-amount"); - insuranceAmt = add(insuranceAmt, tknAmt); - emit LogAddInsurance(tknAmt); - } - - /** - * @dev Withdraw Insurance from the pool. - * @notice only master can call this function. - * @param tknAmt insurance token amount to remove - */ - function withdrawInsurance(uint tknAmt) external { + function withdrawFee(uint wdAmt) external { require(msg.sender == instaIndex.master(), "not-master"); - require(tknAmt <= insuranceAmt, "not-enough-insurance"); - msg.sender.transfer(tknAmt); - insuranceAmt = sub(insuranceAmt, tknAmt); - emit LogWithdrawInsurance(tknAmt); + if (wdAmt > feeAmt) wdAmt = feeAmt; + msg.sender.transfer(wdAmt); + feeAmt = sub(feeAmt, wdAmt); + emit LogWithdrawFee(wdAmt); } /** @@ -223,4 +193,4 @@ contract PoolETH is ReentrancyGuard, ERC20Pausable, DSMath { } receive() external payable {} -} +} \ No newline at end of file diff --git a/contracts/registry.sol b/contracts/registry.sol index eead9fc..e3a7e6b 100644 --- a/contracts/registry.sol +++ b/contracts/registry.sol @@ -14,27 +14,28 @@ contract Registry { event LogRemoveChief(address indexed chief); event LogAddSigner(address indexed signer); event LogRemoveSigner(address indexed signer); - event LogUpdatePool(address pool, bool poolState); - event LogUpdatePoolLogic(address pool, address newLogic); - event LogUpdateInsureFee(address pool, uint newFee); - event LogUpdateWithdrawalFee(address pool, uint newFee); + 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 LogNewDSA(address indexed pool, address indexed dsa); - event LogRemoveDSA(address indexed pool, address indexed dsa); + 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); mapping (address => bool) public chief; mapping (address => bool) public signer; - mapping (address => bool) public isPool; mapping (address => address) public poolToken; mapping (address => address) public poolLogic; + mapping (address => address) public flusherLogic; mapping (address => uint) public poolCap; - mapping (address => uint) public insureFee; - mapping (address => uint) public withdrawalFee; - mapping (address => mapping(address => bool)) public isDsa; // pool => dsa => bool - mapping (address => address[]) public dsaArr; // pool => all dsa in array + 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"); @@ -46,10 +47,6 @@ contract Registry { _; } - function getDsaLength(address _pool) external view returns(uint) { - return dsaArr[_pool].length; - } - /** * @dev Enable New Chief. * @param _chief Address of the new chief. @@ -96,99 +93,153 @@ contract Registry { /** * @dev Add New Pool - * @param token ERC20 token address + * @param _token ERC20 token address * @param pool pool address */ - function addPool(address token, address pool) external isMaster { - require(token != address(0) && pool != address(0), "invalid-address"); - require(poolToken[token] == address(0), "pool-already-added"); - poolToken[token] = pool; - emit LogAddPool(token, pool); + function addPool(address _token, address pool) external isMaster { + require(_token != address(0) && pool != address(0), "invalid-token-address"); + require(poolToken[_token] == address(0), "pool-already-added"); + poolToken[_token] = pool; + emit LogAddPool(_token, pool); } /** * @dev Remove Pool - * @param token ERC20 token address + * @param _token ERC20 token address */ - function removePool(address token) external isMaster { - require(token != address(0), "invalid-address"); - require(poolToken[token] != address(0), "pool-already-removed"); - address poolAddr = poolToken[token]; - delete poolToken[token]; - emit LogRemovePool(token, poolAddr); - } - - /** - * @dev enable / disable pool - * @param _pool pool address - */ - function updatePool(address _pool) external isMaster { - isPool[_pool] = !isPool[_pool]; - emit LogUpdatePool(_pool, isPool[_pool]); + function removePool(address _token) external isMaster { + require(_token != address(0), "invalid-token-address"); + require(poolToken[_token] != address(0), "pool-already-removed"); + address poolAddr = poolToken[_token]; + delete poolToken[_token]; + emit LogRemovePool(_token, poolAddr); } /** * @dev update pool rate logic - * @param _pool pool address + * @param _token token address * @param _newLogic new rate logic address */ - function updatePoolLogic(address _pool, address _newLogic) external isMaster { - require(isPool[_pool], "not-pool"); + function updatePoolLogic(address _token, address _newLogic) external isMaster { + address _pool = poolToken[_token]; + require(_pool != address(0), "invalid-pool"); require(_newLogic != address(0), "invalid-address"); - require( poolLogic[_pool] != _newLogic, "same-pool-logic"); + require(poolLogic[_pool] != _newLogic, "same-pool-logic"); poolLogic[_pool] = _newLogic; emit LogUpdatePoolLogic(_pool, _newLogic); } /** - * @dev update pool insure fee - * @param _pool pool address + * @dev update flusher logic + * @param _token token 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 token address * @param _newFee new fee amount */ - function updateInsureFee(address _pool, uint _newFee) external isMaster { - require(isPool[_pool], "not-pool"); - require(_newFee < 10 ** 18, "insure-fee-limit-reached"); - require(insureFee[_pool] != _newFee, "same-pool-fee"); - insureFee[_pool] = _newFee; - emit LogUpdateInsureFee(_pool, _newFee); + function updateFee(address _token, uint _newFee) external isMaster { + address _pool = poolToken[_token]; + require(_pool != address(0), "invalid-pool"); + require(_newFee < 3 * 10 ** 17, "insure-fee-limit-reached"); + require(fee[_pool] != _newFee, "same-pool-fee"); + fee[_pool] = _newFee; + emit LogUpdateFee(_pool, _newFee); } /** - * @dev update pool withdrawal fee - * @param _pool pool address - * @param _newFee new withdrawal fee amount + * @dev update pool fee + * @param _token token address + * @param _newCap new fee amount */ - function updateWithdrawalFee(address _pool, uint _newFee) external isMaster { - require(isPool[_pool], "not-pool"); - require(_newFee < 1 * 10 ** 16, "withdrawal-fee-limit-reached"); - require(withdrawalFee[_pool] != _newFee, "same-pool-fee"); - withdrawalFee[_pool] = _newFee; - emit LogUpdateWithdrawalFee(_pool, _newFee); + function updateCap(address _token, uint _newCap) external isMaster { + address _pool = poolToken[_token]; + require(_pool != address(0), "invalid-pool"); + poolCap[_pool] = _newCap; + emit LogUpdateCap(_pool, _newCap); } /** - * @dev add dsa for a pool - * @param _pool pool address - * @param _dsa DSA address + * @dev adding settlement logic + * @param _token token address + * @param _logic logic proxy */ - function addDsa(address _pool, address _dsa) external isMaster { - require(isPool[_pool], "not-pool"); - if (_dsa == address(0)) _dsa = instaIndex.build(_pool, 1, address(this)); - isDsa[_pool][_dsa] = true; - dsaArr[_pool].push(_dsa); - emit LogNewDSA(_pool, _dsa); + function addSettleLogic(address _token, address _logic) external isMaster { + address _pool = poolToken[_token]; + require(_pool != address(0), "invalid-pool"); + settleLogic[_pool][_logic] = true; + emit LogAddSettleLogic(_pool, _logic); } /** - * @dev remove dsa from a pool - * @param _pool pool address - * @param _dsa DSA address + * @dev removing settlement logic + * @param _token token address + * @param _logic logic proxy */ - function removeDsa(address _pool, address _dsa) external isMaster { - require(isPool[_pool], "not-pool"); - require(isDsa[_pool][_dsa], "not-dsa-for-pool"); - delete isDsa[_pool][_dsa]; - emit LogRemoveDSA(_pool, _dsa); + function removeSettleLogic(address _token, address _logic) external isMaster { + address _pool = poolToken[_token]; + require(_pool != address(0), "invalid-pool"); + delete settleLogic[_pool][_logic]; + emit LogRemoveSettleLogic(_pool, _logic); + } + + /** + * @dev enable pool connector + * @param _connector logic proxy + */ + function enableConnector(address _connector) external isChief { + require(!connectors[_connector], "already-enabled"); + require(_connector != address(0), "invalid-connector"); + connectors[_connector] = true; + emit LogConnectorEnable(_connector); + } + + /** + * @dev disable pool connector + * @param _connector logic proxy + */ + function disableConnector(address _connector) external isChief { + require(connectors[_connector], "already-disabled"); + delete connectors[_connector]; + emit LogConnectorDisable(_connector); + } + + /** + * @dev disable pool connector + * @param _connector logic proxy + */ + 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]]) { + isOk = false; + break; + } + } + } + + /** + * @dev check if connectors are enabled + * @param _connectors array of logic proxy + */ + 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; + } + } } constructor(address _chief) public {