diff --git a/contracts/Samyak/ethPool.sol b/contracts/Samyak/ethPool.sol deleted file mode 100644 index 740a3c9..0000000 --- a/contracts/Samyak/ethPool.sol +++ /dev/null @@ -1,160 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.6.8; -pragma experimental ABIEncoderV2; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; - -import { DSMath } from "./../libs/safeMath.sol"; - -// TODO - Add ReentrancyGuard lib - -interface AccountInterface { - function enable(address authority) external; - 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 poolCap(address) external view returns (uint); - function insureFee(address) external view returns (uint); -} - -interface RateInterface { - function totalBalance() external view returns (uint); - function getTotalToken() external returns (uint totalUnderlyingTkn); -} - -contract PoolToken is ERC20, DSMath { - using SafeERC20 for IERC20; - - event LogDeploy(uint amount); - event LogExchangeRate(uint exchangeRate, uint tokenBalance, uint insuranceAmt); - event LogSettle(uint settleTime); - event LogDeposit(uint depositAmt, uint poolMintAmt); - event LogWithdraw(uint withdrawAmt, uint poolBurnAmt); - event LogAddInsurance(uint amount); - event LogPoolShut(bool); - - // IERC20 public immutable baseToken; - RegistryInterface public immutable registry; // Pool Registry - IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); - AccountInterface public immutable dsa; // Pool's DSA account - - uint private tokenBalance; // total token balance since last rebalancing - uint public exchangeRate = 1000000000000000000; // initial 1 token = 1 - uint public insuranceAmt; // insurance amount to keep pool safe - bool public shutPool; // shutdown deposits and withdrawals - - constructor( - address _registry, - string memory _name, - string memory _symbol, - address _origin - ) public ERC20(_name, _symbol) { - // TODO - 0 - // baseToken = IERC20(_baseToken); - registry = RegistryInterface(_registry); - address _dsa = instaIndex.build(address(this), 1, _origin); - dsa = AccountInterface(_dsa); - } - - modifier isChief() { - require(registry.chief(msg.sender) || msg.sender == instaIndex.master(), "not-chief"); - _; - } - - function deploy(uint amount) external isChief { - payable(address(dsa)).transfer(amount); - emit LogDeploy(amount); - } - - function setExchangeRate() public isChief { - uint _previousRate = exchangeRate; - uint _totalToken = RateInterface(registry.poolLogic(address(this))).getTotalToken(); - uint _currentRate = wdiv(_totalToken, totalSupply()); - if (_currentRate < _previousRate) { - uint difRate = _previousRate - _currentRate; - uint difTkn = wmul(_totalToken, difRate); - insuranceAmt = sub(insuranceAmt, difTkn); - _currentRate = _previousRate; - } else { - uint difRate = _currentRate - _previousRate; - uint insureFee = wmul(difRate, registry.insureFee(address(this))); // 1e17 - uint insureFeeAmt = wmul(_totalToken, insureFee); - insuranceAmt = add(insuranceAmt, insureFeeAmt); - _currentRate = sub(_currentRate, insureFee); - tokenBalance = sub(_totalToken, insuranceAmt); - } - exchangeRate = _currentRate; - emit LogExchangeRate(exchangeRate, tokenBalance, insuranceAmt); - } - - function settle(address[] calldata _targets, bytes[] calldata _datas, address _origin) external isChief { - if (_targets.length > 0 && _datas.length > 0) { - dsa.cast(_targets, _datas, _origin); - } - setExchangeRate(); - - emit LogSettle(block.timestamp); - } - - function deposit(uint tknAmt) public payable returns(uint) { - require(!shutPool, "pool-shut"); - require(tknAmt == msg.value, "unmatched-amount"); - uint _newTokenBal = add(tokenBalance, msg.value); - require(_newTokenBal <= registry.poolCap(address(this)), "deposit-cap-reached"); - - uint _mintAmt = wdiv(msg.value, exchangeRate); - _mint(msg.sender, _mintAmt); - - emit LogDeposit(tknAmt, _mintAmt); - } - - function withdraw(uint tknAmt, address to) external returns (uint _tknAmt) { - require(!shutPool, "pool-shut"); - uint poolBal = address(this).balance; - require(tknAmt <= poolBal, "not-enough-liquidity-available"); - uint _bal = balanceOf(msg.sender); - uint _tknBal = wmul(_bal, exchangeRate); - uint _burnAmt; - if (tknAmt == uint(-1)) { - _burnAmt = _bal; - _tknAmt = wmul(_burnAmt, exchangeRate); - } else { - require(tknAmt <= _tknBal, "balance-exceeded"); - _burnAmt = wdiv(tknAmt, exchangeRate); - _tknAmt = tknAmt; - } - - _burn(msg.sender, _burnAmt); - - payable(to).transfer(_tknAmt); - - emit LogWithdraw(tknAmt, _burnAmt); - } - - function addInsurance(uint tknAmt) external payable { - require(tknAmt == msg.value, "unmatched-amount"); - insuranceAmt += tknAmt; - emit LogAddInsurance(tknAmt); - } - - function shutdown() external { - require(msg.sender == instaIndex.master(), "not-master"); - shutPool = !shutPool; - emit LogPoolShut(shutPool); - } - - receive() external payable { - deposit(msg.value); - } - -} diff --git a/contracts/Samyak/registry.sol b/contracts/Samyak/registry.sol deleted file mode 100644 index dd10eb4..0000000 --- a/contracts/Samyak/registry.sol +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-License-Identifier: MIT - -pragma solidity ^0.6.8; -pragma experimental ABIEncoderV2; - -// TODO -// function for adding and removing rate logics. -// Added fee variable and required function to change it. -// Link all the contract more properly. -// Have to think more on pricePerToken function. - -interface IndexInterface { - function master() external view returns (address); -} - -contract Registry { - - event LogAddChief(address indexed chief); - event LogRemoveChief(address indexed chief); - event LogSwitchPool(address pool, bool); - event LogUpdatePoolCap(address pool, uint newCap); - event LogUpdatePoolLogic(address pool, address newLogic); - event LogUpdateInsureFee(address pool, uint newFee); - - IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); - - mapping (address => bool) public chief; - mapping (address => bool) public isPool; - mapping (address => address) public poolLogic; - mapping (address => uint) public poolCap; - mapping (address => uint) public insureFee; - - modifier isMaster() { - require(msg.sender == instaIndex.master(), "not-master"); - _; - } - - modifier isChief() { - require(chief[msg.sender] || msg.sender == instaIndex.master(), "not-chief"); - _; - } - - /** - * @dev Enable New Chief. - * @param _chief Address of the new chief. - */ - function enableChief(address _chief) external isMaster { - require(_chief != address(0), "address-not-valid"); - require(!chief[_chief], "chief-already-enabled"); - chief[_chief] = true; - emit LogAddChief(_chief); - } - - /** - * @dev Disable Chief. - * @param _chief Address of the existing chief. - */ - function disableChief(address _chief) external isMaster { - require(_chief != address(0), "address-not-valid"); - require(chief[_chief], "chief-already-disabled"); - delete chief[_chief]; - emit LogRemoveChief(_chief); - } - - function switchPool(address _pool) external isMaster { - isPool[_pool] = !isPool[_pool]; - emit LogSwitchPool(_pool, isPool[_pool]); - } - - function updatePoolCap(address _pool, uint _newCap) external isChief { - require(isPool[_pool], "not-a-pool"); - poolCap[_pool] = _newCap; - emit LogUpdatePoolCap(_pool, _newCap); - } - - function updatePoolLogic(address _pool, address _newLogic) external isChief { - require(isPool[_pool], "not-a-pool"); - require(_newLogic != address(0), "address-0"); - poolLogic[_pool] = _newLogic; - emit LogUpdatePoolLogic(_pool, _newLogic); - } - - function updateInsureFee(address _pool, uint _newFee) external isChief { - require(isPool[_pool], "not-a-pool"); - require(_newFee < 1000000000000000000, "insure-fee-limit-reached"); - insureFee[_pool] = _newFee; - emit LogUpdateInsureFee(_pool, _newFee); - } - - constructor(address _chief) public { - chief[_chief] = true; - emit LogAddChief(_chief); - } - -} diff --git a/contracts/Samyak/tokenPool.sol b/contracts/Samyak/tokenPool.sol deleted file mode 100644 index 56b245f..0000000 --- a/contracts/Samyak/tokenPool.sol +++ /dev/null @@ -1,155 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity ^0.6.8; -pragma experimental ABIEncoderV2; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; - -import { DSMath } from "./../libs/safeMath.sol"; - -// TODO - Add ReentrancyGuard lib - -interface AccountInterface { - function enable(address authority) external; - 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 poolCap(address) external view returns (uint); - function insureFee(address) external view returns (uint); -} - -interface RateInterface { - function totalBalance() external view returns (uint); - function getTotalToken() external returns (uint totalUnderlyingTkn); -} - -contract PoolToken is ERC20, DSMath { - using SafeERC20 for IERC20; - - event LogDeploy(uint amount); - event LogExchangeRate(uint exchangeRate, uint tokenBalance, uint insuranceAmt); - event LogSettle(uint settleTime); - event LogDeposit(uint depositAmt, uint poolMintAmt); - event LogWithdraw(uint withdrawAmt, uint poolBurnAmt); - event LogAddInsurance(uint amount); - event LogPoolShut(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 - AccountInterface public immutable dsa; // Pool's DSA account - - uint private tokenBalance; // total token balance since last rebalancing - uint public exchangeRate = 1000000000000000000; // initial 1 token = 1 - uint public insuranceAmt; // insurance amount to keep pool safe - bool public shutPool; // shutdown deposits and withdrawals - - constructor( - address _registry, - string memory _name, - string memory _symbol, - address _baseToken, - address _origin - ) public ERC20(_name, _symbol) { - baseToken = IERC20(_baseToken); - registry = RegistryInterface(_registry); - address _dsa = instaIndex.build(address(this), 1, _origin); - dsa = AccountInterface(_dsa); - } - - modifier isChief() { - require(registry.chief(msg.sender) || msg.sender == instaIndex.master(), "not-chief"); - _; - } - - function deploy(uint amount) public isChief { - baseToken.safeTransfer(address(dsa), amount); - emit LogDeploy(amount); - } - - function setExchangeRate() public isChief { - uint _previousRate = exchangeRate; - uint _totalToken = RateInterface(registry.poolLogic(address(this))).getTotalToken(); - uint _currentRate = wdiv(_totalToken, totalSupply()); - if (_currentRate < _previousRate) { - uint difRate = _previousRate - _currentRate; - uint difTkn = wmul(_totalToken, difRate); - insuranceAmt = sub(insuranceAmt, difTkn); - _currentRate = _previousRate; - } else { - uint difRate = _currentRate - _previousRate; - uint insureFee = wmul(difRate, registry.insureFee(address(this))); // 1e17 - uint insureFeeAmt = wmul(_totalToken, insureFee); - insuranceAmt = add(insuranceAmt, insureFeeAmt); - _currentRate = sub(_currentRate, insureFee); - tokenBalance = sub(_totalToken, insuranceAmt); - } - exchangeRate = _currentRate; - emit LogExchangeRate(exchangeRate, tokenBalance, insuranceAmt); - } - - function settle(address[] calldata _targets, bytes[] calldata _datas, address _origin) external isChief { - if (_targets.length > 0 && _datas.length > 0) { - dsa.cast(_targets, _datas, _origin); - } - setExchangeRate(); - emit LogSettle(block.timestamp); - } - - function deposit(uint tknAmt) external payable returns(uint) { - require(!shutPool, "pool-shut"); - uint _newTokenBal = add(tokenBalance, tknAmt); - require(_newTokenBal <= registry.poolCap(address(this)), "deposit-cap-reached"); - - baseToken.safeTransferFrom(msg.sender, address(this), tknAmt); - uint _mintAmt = wdiv(tknAmt, exchangeRate); - _mint(msg.sender, _mintAmt); - - emit LogDeposit(tknAmt, _mintAmt); - } - - function withdraw(uint tknAmt, address to) external returns (uint _tknAmt) { - require(!shutPool, "pool-shut"); - uint poolBal = baseToken.balanceOf(address(this)); - require(tknAmt <= poolBal, "not-enough-liquidity-available"); - uint _bal = balanceOf(msg.sender); - uint _tknBal = wmul(_bal, exchangeRate); - uint _burnAmt; - if (tknAmt == uint(-1)) { - _burnAmt = _bal; - _tknAmt = wmul(_burnAmt, exchangeRate); - } else { - require(tknAmt <= _tknBal, "balance-exceeded"); - _burnAmt = wdiv(tknAmt, exchangeRate); - _tknAmt = tknAmt; - } - - _burn(msg.sender, _burnAmt); - - baseToken.safeTransfer(to, _tknAmt); - - emit LogWithdraw(tknAmt, _burnAmt); - } - - function addInsurance(uint tknAmt) external payable { - baseToken.safeTransferFrom(msg.sender, address(this), tknAmt); - insuranceAmt += tknAmt; - emit LogAddInsurance(tknAmt); - } - - function shutdown() external { - require(msg.sender == instaIndex.master(), "not-master"); - shutPool = !shutPool; - emit LogPoolShut(shutPool); - } - -} diff --git a/contracts/ethPool.sol b/contracts/ethPool.sol index 3a281ba..740a3c9 100644 --- a/contracts/ethPool.sol +++ b/contracts/ethPool.sol @@ -1,13 +1,160 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.6.8; +pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; + +import { DSMath } from "./../libs/safeMath.sol"; + +// TODO - Add ReentrancyGuard lib + +interface AccountInterface { + function enable(address authority) external; + 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 poolCap(address) external view returns (uint); + function insureFee(address) external view returns (uint); +} + +interface RateInterface { + function totalBalance() external view returns (uint); + function getTotalToken() external returns (uint totalUnderlyingTkn); +} + +contract PoolToken is ERC20, DSMath { + using SafeERC20 for IERC20; + + event LogDeploy(uint amount); + event LogExchangeRate(uint exchangeRate, uint tokenBalance, uint insuranceAmt); + event LogSettle(uint settleTime); + event LogDeposit(uint depositAmt, uint poolMintAmt); + event LogWithdraw(uint withdrawAmt, uint poolBurnAmt); + event LogAddInsurance(uint amount); + event LogPoolShut(bool); + + // IERC20 public immutable baseToken; + RegistryInterface public immutable registry; // Pool Registry + IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); + AccountInterface public immutable dsa; // Pool's DSA account + + uint private tokenBalance; // total token balance since last rebalancing + uint public exchangeRate = 1000000000000000000; // initial 1 token = 1 + uint public insuranceAmt; // insurance amount to keep pool safe + bool public shutPool; // shutdown deposits and withdrawals -contract PoolToken is ERC20 { constructor( + address _registry, string memory _name, - string memory _symbol + string memory _symbol, + address _origin ) public ERC20(_name, _symbol) { // TODO - 0 + // baseToken = IERC20(_baseToken); + registry = RegistryInterface(_registry); + address _dsa = instaIndex.build(address(this), 1, _origin); + dsa = AccountInterface(_dsa); } + + modifier isChief() { + require(registry.chief(msg.sender) || msg.sender == instaIndex.master(), "not-chief"); + _; + } + + function deploy(uint amount) external isChief { + payable(address(dsa)).transfer(amount); + emit LogDeploy(amount); + } + + function setExchangeRate() public isChief { + uint _previousRate = exchangeRate; + uint _totalToken = RateInterface(registry.poolLogic(address(this))).getTotalToken(); + uint _currentRate = wdiv(_totalToken, totalSupply()); + if (_currentRate < _previousRate) { + uint difRate = _previousRate - _currentRate; + uint difTkn = wmul(_totalToken, difRate); + insuranceAmt = sub(insuranceAmt, difTkn); + _currentRate = _previousRate; + } else { + uint difRate = _currentRate - _previousRate; + uint insureFee = wmul(difRate, registry.insureFee(address(this))); // 1e17 + uint insureFeeAmt = wmul(_totalToken, insureFee); + insuranceAmt = add(insuranceAmt, insureFeeAmt); + _currentRate = sub(_currentRate, insureFee); + tokenBalance = sub(_totalToken, insuranceAmt); + } + exchangeRate = _currentRate; + emit LogExchangeRate(exchangeRate, tokenBalance, insuranceAmt); + } + + function settle(address[] calldata _targets, bytes[] calldata _datas, address _origin) external isChief { + if (_targets.length > 0 && _datas.length > 0) { + dsa.cast(_targets, _datas, _origin); + } + setExchangeRate(); + + emit LogSettle(block.timestamp); + } + + function deposit(uint tknAmt) public payable returns(uint) { + require(!shutPool, "pool-shut"); + require(tknAmt == msg.value, "unmatched-amount"); + uint _newTokenBal = add(tokenBalance, msg.value); + require(_newTokenBal <= registry.poolCap(address(this)), "deposit-cap-reached"); + + uint _mintAmt = wdiv(msg.value, exchangeRate); + _mint(msg.sender, _mintAmt); + + emit LogDeposit(tknAmt, _mintAmt); + } + + function withdraw(uint tknAmt, address to) external returns (uint _tknAmt) { + require(!shutPool, "pool-shut"); + uint poolBal = address(this).balance; + require(tknAmt <= poolBal, "not-enough-liquidity-available"); + uint _bal = balanceOf(msg.sender); + uint _tknBal = wmul(_bal, exchangeRate); + uint _burnAmt; + if (tknAmt == uint(-1)) { + _burnAmt = _bal; + _tknAmt = wmul(_burnAmt, exchangeRate); + } else { + require(tknAmt <= _tknBal, "balance-exceeded"); + _burnAmt = wdiv(tknAmt, exchangeRate); + _tknAmt = tknAmt; + } + + _burn(msg.sender, _burnAmt); + + payable(to).transfer(_tknAmt); + + emit LogWithdraw(tknAmt, _burnAmt); + } + + function addInsurance(uint tknAmt) external payable { + require(tknAmt == msg.value, "unmatched-amount"); + insuranceAmt += tknAmt; + emit LogAddInsurance(tknAmt); + } + + function shutdown() external { + require(msg.sender == instaIndex.master(), "not-master"); + shutPool = !shutPool; + emit LogPoolShut(shutPool); + } + + receive() external payable { + deposit(msg.value); + } + } diff --git a/contracts/registry.sol b/contracts/registry.sol index 0f287eb..375b708 100644 --- a/contracts/registry.sol +++ b/contracts/registry.sol @@ -3,12 +3,6 @@ pragma solidity ^0.6.8; pragma experimental ABIEncoderV2; -// TODO -// function for adding and removing rate logics. -// Added fee variable and required function to change it. -// Link all the contract more properly. -// Have to think more on pricePerToken function. - interface IndexInterface { function master() external view returns (address); } @@ -17,36 +11,38 @@ contract Registry { event LogAddChief(address indexed chief); event LogRemoveChief(address indexed chief); + event LogSwitchPool(address pool, bool); + event LogUpdatePoolCap(address pool, uint newCap); + event LogUpdatePoolLogic(address pool, address newLogic); + event LogUpdateInsureFee(address pool, uint newFee); - IndexInterface public instaIndex; + IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); mapping (address => bool) public chief; - mapping (address => address) public dsa; - mapping (address => address) public logic; + mapping (address => bool) public isPool; + mapping (address => address) public poolLogic; + mapping (address => uint) public poolCap; + mapping (address => uint) public insureFee; modifier isMaster() { require(msg.sender == instaIndex.master(), "not-master"); _; } - modifier isController() { + modifier isChief() { require(chief[msg.sender] || msg.sender == instaIndex.master(), "not-chief"); _; } - constructor(address _index) public { - instaIndex = IndexInterface(_index); - } - /** * @dev Enable New Chief. * @param _chief Address of the new chief. */ function enableChief(address _chief) external isMaster { - require(_chief != address(0), "address-not-valid"); - require(!chief[_chief], "chief-already-enabled"); - chief[_chief] = true; - emit LogAddChief(_chief); + require(_chief != address(0), "address-not-valid"); + require(!chief[_chief], "chief-already-enabled"); + chief[_chief] = true; + emit LogAddChief(_chief); } /** @@ -54,9 +50,40 @@ contract Registry { * @param _chief Address of the existing chief. */ function disableChief(address _chief) external isMaster { - require(_chief != address(0), "address-not-valid"); - require(chief[_chief], "chief-already-disabled"); - delete chief[_chief]; - emit LogRemoveChief(_chief); + require(_chief != address(0), "address-not-valid"); + require(chief[_chief], "chief-already-disabled"); + delete chief[_chief]; + emit LogRemoveChief(_chief); } + + function switchPool(address _pool) external isMaster { + isPool[_pool] = !isPool[_pool]; + emit LogSwitchPool(_pool, isPool[_pool]); + } + + function updatePoolCap(address _pool, uint _newCap) external isChief { + require(isPool[_pool], "not-a-pool"); + poolCap[_pool] = _newCap; + emit LogUpdatePoolCap(_pool, _newCap); + } + + function updatePoolLogic(address _pool, address _newLogic) external isChief { + require(isPool[_pool], "not-a-pool"); + require(_newLogic != address(0), "address-0"); + poolLogic[_pool] = _newLogic; + emit LogUpdatePoolLogic(_pool, _newLogic); + } + + function updateInsureFee(address _pool, uint _newFee) external isChief { + require(isPool[_pool], "not-a-pool"); + require(_newFee < 1000000000000000000, "insure-fee-limit-reached"); + insureFee[_pool] = _newFee; + emit LogUpdateInsureFee(_pool, _newFee); + } + + constructor(address _chief) public { + chief[_chief] = true; + emit LogAddChief(_chief); + } + } diff --git a/contracts/tokenPool.sol b/contracts/tokenPool.sol index 3273950..36202a4 100644 --- a/contracts/tokenPool.sol +++ b/contracts/tokenPool.sol @@ -8,8 +8,6 @@ import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol"; import { DSMath } from "./libs/safeMath.sol"; -// TODO - Add ReentrancyGuard lib - interface AccountInterface { function enable(address authority) external; function cast(address[] calldata _targets, bytes[] calldata _datas, address _origin) external payable; @@ -17,96 +15,139 @@ interface AccountInterface { 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 dsa(address) external view returns (address); - function rateLogic(address) external view returns (address); + function poolLogic(address) external returns (address); + function poolCap(address) external view returns (uint); + function insureFee(address) external view returns (uint); } interface RateInterface { - function pricePerToken() external view returns (uint); function totalBalance() external view returns (uint); + function getTotalToken() external returns (uint totalUnderlyingTkn); } contract PoolToken is ERC20, DSMath { using SafeERC20 for IERC20; - IERC20 public immutable baseToken; - RegistryInterface public immutable registry; - IndexInterface public immutable instaIndex; + event LogDeploy(uint amount); + event LogExchangeRate(uint exchangeRate, uint tokenBalance, uint insuranceAmt); + event LogSettle(uint settleTime); + event LogDeposit(uint depositAmt, uint poolMintAmt); + event LogWithdraw(uint withdrawAmt, uint poolBurnAmt); + event LogAddInsurance(uint amount); + event LogPoolShut(bool); - uint private tokenBalance; - uint private tokenProfit; - uint private tokenCap; + IERC20 public immutable baseToken; // Base token. Eg:- DAI, USDC, etc. + RegistryInterface public immutable registry; // Pool Registry + IndexInterface public constant instaIndex = IndexInterface(0x2971AdFa57b20E5a416aE5a708A8655A9c74f723); // Main Index + AccountInterface public immutable dsa; // Pool's DSA account + + uint private tokenBalance; // total token balance since last rebalancing + uint public exchangeRate = 1000000000000000000; // initial 1 token = 1 + uint public insuranceAmt; // insurance amount to keep pool safe + bool public shutPool; // shutdown deposits and withdrawals constructor( address _registry, - address _index, string memory _name, string memory _symbol, - address _baseToken + address _baseToken, + address _origin ) public ERC20(_name, _symbol) { - // TODO - 0 baseToken = IERC20(_baseToken); registry = RegistryInterface(_registry); - instaIndex = IndexInterface(_index); - } - - modifier isMaster() { - require(msg.sender == instaIndex.master(), "not-master"); - _; + address _dsa = instaIndex.build(address(this), 1, _origin); + dsa = AccountInterface(_dsa); } modifier isChief() { require(registry.chief(msg.sender) || msg.sender == instaIndex.master(), "not-chief"); _; } - - uint dsaAmount; + function deploy(uint amount) public isChief { - address _dsa = registry.dsa(address(this)); - baseToken.safeTransfer(_dsa, amount); - dsaAmount = add(dsaAmount, amount); + baseToken.safeTransfer(address(dsa), amount); + emit LogDeploy(amount); } - function claim(uint amount) public isChief { - // address _dsa = registry.dsa(address(this)); - baseToken.safeTransferFrom(msg.sender, address(this), amount); - uint totalAmountWithProfit = RateInterface(address(0)).totalBalance(); // TODO - change to => totalBalanceDSA - uint _amount = wdiv(wmul(amount, dsaAmount), totalAmountWithProfit); - uint profit = sub(amount, _amount); - tokenProfit = add(tokenProfit, profit); - dsaAmount = sub(dsaAmount, _amount); + function setExchangeRate() public isChief { + uint _previousRate = exchangeRate; + uint _totalToken = RateInterface(registry.poolLogic(address(this))).getTotalToken(); + uint _currentRate = wdiv(_totalToken, totalSupply()); + if (_currentRate < _previousRate) { + uint difRate = _previousRate - _currentRate; + uint difTkn = wmul(_totalToken, difRate); + insuranceAmt = sub(insuranceAmt, difTkn); + _currentRate = _previousRate; + } else { + uint difRate = _currentRate - _previousRate; + uint insureFee = wmul(difRate, registry.insureFee(address(this))); // 1e17 + uint insureFeeAmt = wmul(_totalToken, insureFee); + insuranceAmt = add(insuranceAmt, insureFeeAmt); + _currentRate = sub(_currentRate, insureFee); + tokenBalance = sub(_totalToken, insuranceAmt); + } + exchangeRate = _currentRate; + emit LogExchangeRate(exchangeRate, tokenBalance, insuranceAmt); } - function getBalance() public view returns(uint) { - return sub(add(dsaAmount, baseToken.balanceOf(address(this))), tokenProfit); + function settle(address[] calldata _targets, bytes[] calldata _datas, address _origin) external isChief { + if (_targets.length > 0 && _datas.length > 0) { + dsa.cast(_targets, _datas, _origin); + } + setExchangeRate(); + emit LogSettle(block.timestamp); } - function pricePerToken() public view returns(uint) { - return 1e18; // TODO - Link to rate logic contract + function deposit(uint tknAmt) external payable returns(uint) { + require(!shutPool, "pool-shut"); + uint _newTokenBal = add(tokenBalance, tknAmt); + require(_newTokenBal <= registry.poolCap(address(this)), "deposit-cap-reached"); + + baseToken.safeTransferFrom(msg.sender, address(this), tknAmt); + uint _mintAmt = wdiv(tknAmt, exchangeRate); + _mint(msg.sender, _mintAmt); + + emit LogDeposit(tknAmt, _mintAmt); } - function deposit(uint amount) public returns(uint) { - uint _newTokenBal = add(tokenBalance, amount); - require(_newTokenBal <= getBalance(), "deposit-cap-reached"); + function withdraw(uint tknAmt, address to) external returns (uint _tknAmt) { + require(!shutPool, "pool-shut"); + uint poolBal = baseToken.balanceOf(address(this)); + require(tknAmt <= poolBal, "not-enough-liquidity-available"); + uint _bal = balanceOf(msg.sender); + uint _tknBal = wmul(_bal, exchangeRate); + uint _burnAmt; + if (tknAmt == uint(-1)) { + _burnAmt = _bal; + _tknAmt = wmul(_burnAmt, exchangeRate); + } else { + require(tknAmt <= _tknBal, "balance-exceeded"); + _burnAmt = wdiv(tknAmt, exchangeRate); + _tknAmt = tknAmt; + } - baseToken.safeTransferFrom(msg.sender, address(this), amount); - uint iAmt = wdiv(amount, pricePerToken()); - _mint(msg.sender, iAmt); + _burn(msg.sender, _burnAmt); + + baseToken.safeTransfer(to, _tknAmt); + + emit LogWithdraw(tknAmt, _burnAmt); } - function withdraw(address owner, uint iAmount) public returns (uint) { - // TODO - check balance before withdrawing - uint amount = wmul(iAmount, pricePerToken()); - _burn(msg.sender, iAmount); - - baseToken.safeTransfer(owner, amount); + function addInsurance(uint tknAmt) external payable { + baseToken.safeTransferFrom(msg.sender, address(this), tknAmt); + insuranceAmt += tknAmt; + emit LogAddInsurance(tknAmt); } - function withdraw(uint amount) public returns (uint) { - return withdraw(msg.sender, amount); + function shutdown() external { + require(msg.sender == instaIndex.master(), "not-master"); + shutPool = !shutPool; + emit LogPoolShut(shutPool); } + }