diff --git a/contracts/deployer.sol b/contracts/deployer.sol new file mode 100644 index 0000000..56df2ff --- /dev/null +++ b/contracts/deployer.sol @@ -0,0 +1,67 @@ +pragma solidity ^0.6.8; + +interface IProxy { + function setBasic(address, address) external; +} + +contract Deployer { + + event LogNewProxy( + address indexed owner, + address indexed proxy, + address indexed logic, + address token + ); + + /** + * @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) { + 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) + } + IProxy(proxy).setBasic(owner, token); + emit LogNewProxy(owner, proxy, logic, token); + } + + /** + * @dev compute create2 + minimal proxy address + * @param owner owner address used for salt + * @param logic flusher contract address + */ + function getAddress(address owner, address logic) public view returns (address) { + bytes32 codeHash = keccak256(getCreationCode(logic)); + bytes32 salt = keccak256(abi.encodePacked(owner)); + bytes32 rawAddress = keccak256( + abi.encodePacked( + bytes1(0xff), + address(this), + salt, + codeHash + )); + 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/ethPool.sol b/contracts/ethPool.sol index 3190e56..0608841 100644 --- a/contracts/ethPool.sol +++ b/contracts/ethPool.sol @@ -44,6 +44,7 @@ contract PoolToken is ERC20, DSMath { IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); AccountInterface public immutable dsa; // Pool's DSA account + IERC20 public immutable baseToken; // Base token. Eg:- DAI, USDC, etc. uint private tokenBalance; // total token balance since last rebalancing uint public exchangeRate = 10 ** 18; // initial 1 token = 1 uint public insuranceAmt; // insurance amount to keep pool safe @@ -53,9 +54,10 @@ contract PoolToken is ERC20, DSMath { address _registry, string memory _name, string memory _symbol, + address _baseToken, address _origin ) public ERC20(_name, _symbol) { - // baseToken = IERC20(_baseToken); + baseToken = IERC20(_baseToken); registry = RegistryInterface(_registry); address _dsa = instaIndex.build(address(this), 1, _origin); dsa = AccountInterface(_dsa); @@ -148,8 +150,6 @@ contract PoolToken is ERC20, DSMath { emit LogPoolShut(shutPool); } - receive() external payable { - deposit(msg.value); - } + receive() external payable {} } diff --git a/contracts/flusher.sol b/contracts/flusher.sol new file mode 100644 index 0000000..9d7b376 --- /dev/null +++ b/contracts/flusher.sol @@ -0,0 +1,156 @@ +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); +} + +contract Flusher { + using SafeERC20 for IERC20; + + address payable public owner; + RegistryInterface public constant registry = RegistryInterface(address(0)); // TODO - Change while deploying. + bool public shield; + uint256 public shieldBlockTime; + uint256 internal waitBlockTime = 518400; // 90 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 boooool); + + event LogDeposit( + address indexed caller, + address indexed token, + address indexed tokenPool, + uint amount + ); + + event LogWithdraw( + address indexed caller, + address indexed token, + address indexed tokenPool, + uint amount + ); + + event LogWithdrawToOwner( + address indexed caller, + address indexed token, + address indexed owner, + uint amount + ); + + function deposit(address token) public isSigner { + require(address(token) != address(0), "invalid-token"); + + address poolToken = registry.poolToken(token); + IERC20 tokenContract = IERC20(token); + + if (poolToken != address(0)) { + 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(msg.sender, token, address(poolContract), amt); + } else { + uint amt = tokenContract.balanceOf(address(this)); + tokenContract.safeTransfer(owner, amt); + emit LogWithdrawToOwner(msg.sender, token, owner, amt); + } + } + + 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(msg.sender, 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(msg.sender, token, owner, amount); + } + + function setBasic(address newOwner, address token) external { + require(owner == address(0), "already-an-owner"); + 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, "less-than-ninty-days"); + 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) + switch iszero(succeeded) + case 1 { + // throw if delegatecall failed + let size := returndatasize() + returndatacopy(0x00, 0x00, size) + revert(0x00, size) + } + } + require(_owner == owner, "owner-change-denied"); + } + +} \ No newline at end of file diff --git a/contracts/registry.sol b/contracts/registry.sol index acc74f1..45dbdfe 100644 --- a/contracts/registry.sol +++ b/contracts/registry.sol @@ -17,11 +17,14 @@ contract Registry { event LogUpdatePoolCap(address pool, uint newCap); event LogUpdatePoolLogic(address pool, address newLogic); event LogUpdateInsureFee(address pool, uint newFee); + event LogAddPool(address indexed token, address indexed pool); + event LogRemovePool(address indexed token, address indexed pool); IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); mapping (address => bool) public chief; mapping (address => bool) public signer; + mapping (address => address) public poolToken; mapping (address => bool) public isPool; mapping (address => address) public poolLogic; mapping (address => uint) public poolCap; @@ -81,6 +84,28 @@ contract Registry { emit LogRemoveSigner(_signer); } + /** + * @dev Add New Pool + * @param token ERC20 token address + * @param pool pool address + */ + function addPool(address token, address pool) external isMaster { // TODO: all good? + require(token != address(0) && pool != address(0), "address-not-valid"); + poolToken[token] = pool; + emit LogAddPool(token, pool); + } + + /** + * @dev Remove Pool + * @param token ERC20 token address + */ + function removePool(address token) external isMaster { // TODO: all good? + require(token != address(0), "address-not-valid"); + address poolAddr = poolToken[token]; + delete poolToken[token]; + emit LogRemovePool(token, poolAddr); + } + function switchPool(address _pool) external isMaster { isPool[_pool] = !isPool[_pool]; emit LogSwitchPool(_pool, isPool[_pool]);