From 57a9efe9c260168f05ab8797d2a9b42e4e312296 Mon Sep 17 00:00:00 2001 From: 0xViktor Date: Sat, 14 Aug 2021 00:07:05 +0900 Subject: [PATCH 01/16] Hard Work Now! For Punkers by 0xViktor... - Renewal Forge --- contracts/FairLaunch.sol | 283 ++++---- contracts/Forge.sol | 744 ++++++++++----------- contracts/ForgeStorage.sol | 5 - contracts/Saver.sol | 8 - contracts/Variables.sol | 107 +-- contracts/interfaces/ForgeEthInterface.sol | 36 - contracts/interfaces/ForgeInterface.sol | 8 +- contracts/libs/CommitmentWeight.sol | 1 + contracts/libs/Score.sol | 7 + contracts/mock/ScoreMock.sol | 23 - test/shared/fixtures.ts | 49 +- test/unit/Score/Score.ts | 48 -- test/unit/Variables/setup.behavior.ts | 8 + test/unit/before.behavior.ts | 69 +- test/unit/index.ts | 6 +- 15 files changed, 573 insertions(+), 829 deletions(-) delete mode 100644 contracts/interfaces/ForgeEthInterface.sol delete mode 100644 contracts/mock/ScoreMock.sol delete mode 100644 test/unit/Score/Score.ts diff --git a/contracts/FairLaunch.sol b/contracts/FairLaunch.sol index 11bc314..01c7d34 100644 --- a/contracts/FairLaunch.sol +++ b/contracts/FairLaunch.sol @@ -10,156 +10,157 @@ import "./Ownable.sol"; import "./Referral.sol"; import "./Saver.sol"; +/** Do Not Used This Contract */ contract FairLaunch is Ownable, ReentrancyGuard{ - using SafeERC20 for IERC20; - using SafeMath for uint256; + // using SafeERC20 for IERC20; + // using SafeMath for uint256; - event Deposit( uint256 blockNumber, uint256 blockTime, address account, uint256 amount, uint256 accAmount,uint256 totalAmount, uint256 cap, bytes12 referralCode); - event Withdraw( uint256 blockNumber, uint256 blockTime, address account, uint256 amount, uint256 accAmount,uint256 totalAmount, uint256 cap, bytes12 referralCode); + // event Deposit( uint256 blockNumber, uint256 blockTime, address account, uint256 amount, uint256 accAmount,uint256 totalAmount, uint256 cap, bytes12 referralCode); + // event Withdraw( uint256 blockNumber, uint256 blockTime, address account, uint256 amount, uint256 accAmount,uint256 totalAmount, uint256 cap, bytes12 referralCode); - uint256 constant private OPEN_TIMESTAMP = 1628553600; - uint256 constant private CLOSED_TIMESTAMP = 1630022400; + // uint256 constant private OPEN_TIMESTAMP = 1628553600; + // uint256 constant private CLOSED_TIMESTAMP = 1630022400; - Referral private _referral; - IERC20 private _token; - ForgeInterface private _forge; + // Referral private _referral; + // IERC20 private _token; + // ForgeInterface private _forge; - uint256 private _count; - uint256 private _decimals; - uint256 private _cap; - uint256 private _totalAmount; - uint256 private _rewardCap; - string private _name; - - mapping( address=>bool ) _entered; - mapping( address=>uint256 ) _indexes; + // uint256 private _count; + // uint256 private _decimals; + // uint256 private _cap; + // uint256 private _totalAmount; + // uint256 private _rewardCap; + // string private _name; + + // mapping( address=>bool ) _entered; + // mapping( address=>uint256 ) _indexes; - uint256 [] private _caps; - uint256 [] private _capUpdateTimestamps; - - function initialize( - address storage_, - address forge_, - address token_, - address referral_, - uint8 decimals_, - string memory name_ - ) public initializer { - - Ownable.initialize( storage_ ); - - _forge = ForgeInterface(forge_); - _token = IERC20(token_); - _referral = Referral(referral_); - _decimals = decimals_; - _totalAmount = 0; - _count = 0; - _rewardCap = 70000 * 10**18; - _name = name_; - - _token.safeApprove(forge_, 2**256 - 1); - } - - function enter( uint256 amount ) public returns(bool){ - return enter(amount, 0x0); - } - - function enter( uint256 amount, bytes12 ref ) public nonReentrant returns(bool) { - require( block.timestamp > OPEN_TIMESTAMP, "FL : Not Opened yet"); - require( _totalAmount + amount <= _cap, "FL : Amount Overflow" ); - require( _token.allowance(msg.sender, address(this)) >= amount, "FL : Allowance Error"); + // uint256 [] private _caps; + // uint256 [] private _capUpdateTimestamps; + + // function initialize( + // address storage_, + // address forge_, + // address token_, + // address referral_, + // uint8 decimals_, + // string memory name_ + // ) public initializer { + + // Ownable.initialize( storage_ ); + + // _forge = ForgeInterface(forge_); + // _token = IERC20(token_); + // _referral = Referral(referral_); + // _decimals = decimals_; + // _totalAmount = 0; + // _count = 0; + // _rewardCap = 70000 * 10**18; + // _name = name_; + + // _token.safeApprove(forge_, 2**256 - 1); + // } + + // function enter( uint256 amount ) public returns(bool){ + // return enter(amount, 0x0); + // } + + // function enter( uint256 amount, bytes12 ref ) public nonReentrant returns(bool) { + // require( block.timestamp > OPEN_TIMESTAMP, "FL : Not Opened yet"); + // require( _totalAmount + amount <= _cap, "FL : Amount Overflow" ); + // require( _token.allowance(msg.sender, address(this)) >= amount, "FL : Allowance Error"); - ( bool entered, uint index ) = isEntered( msg.sender ); - - address issuer = _referral.validate( ref ); - _totalAmount = _totalAmount.add(amount); - _token.safeTransferFrom(msg.sender, address(this), amount); - - if( entered ){ - _forge.addDeposit(index, amount); - }else{ - _indexes[msg.sender] = _forge.countByAccount(address(this)); - _entered[msg.sender] = true; - if( ref == 0x0 ){ - _count += 1; - _forge.craftingSaver(amount, CLOSED_TIMESTAMP, 1, 1); - }else{ - require( issuer != address(0x0), "FL : Not Registry Ref Code"); - _count += 1; - _forge.craftingSaver(amount, CLOSED_TIMESTAMP, 1, 1, ref); - } - } - ( entered, index ) = isEntered( msg.sender ); - emit Deposit(block.number, block.timestamp, msg.sender, amount, _forge.saver(address(this), index).accAmount, _totalAmount, _cap, _forge.saver(address(this), index).ref); - - return true; - } - - function exit() public nonReentrant returns(bool) { - require(_entered[msg.sender], "FL : Not Entering"); - uint index = _indexes[msg.sender]; - - _entered[msg.sender] = false; - _totalAmount = _totalAmount.sub(_forge.saver(address(this), index).accAmount); - _count -= 1; + // ( bool entered, uint index ) = isEntered( msg.sender ); + + // address issuer = _referral.validate( ref ); + // _totalAmount = _totalAmount.add(amount); + // _token.safeTransferFrom(msg.sender, address(this), amount); + + // if( entered ){ + // _forge.addDeposit(index, amount); + // }else{ + // _indexes[msg.sender] = _forge.countByAccount(address(this)); + // _entered[msg.sender] = true; + // if( ref == 0x0 ){ + // _count += 1; + // _forge.craftingSaver(amount, CLOSED_TIMESTAMP, 1, 1); + // }else{ + // require( issuer != address(0x0), "FL : Not Registry Ref Code"); + // _count += 1; + // _forge.craftingSaver(amount, CLOSED_TIMESTAMP, 1, 1, ref); + // } + // } + // ( entered, index ) = isEntered( msg.sender ); + // emit Deposit(block.number, block.timestamp, msg.sender, amount, _forge.saver(address(this), index).accAmount, _totalAmount, _cap, _forge.saver(address(this), index).ref); + + // return true; + // } + + // function exit() public nonReentrant returns(bool) { + // require(_entered[msg.sender], "FL : Not Entering"); + // uint index = _indexes[msg.sender]; + + // _entered[msg.sender] = false; + // _totalAmount = _totalAmount.sub(_forge.saver(address(this), index).accAmount); + // _count -= 1; - uint beforeBalanceOf = _token.balanceOf(address(this)); - _forge.terminateSaver(index); - uint afterBalanceOf = _token.balanceOf(address(this)); - uint repayable = afterBalanceOf.sub(beforeBalanceOf); - _token.safeTransfer(msg.sender, repayable); + // uint beforeBalanceOf = _token.balanceOf(address(this)); + // _forge.terminateSaver(index); + // uint afterBalanceOf = _token.balanceOf(address(this)); + // uint repayable = afterBalanceOf.sub(beforeBalanceOf); + // _token.safeTransfer(msg.sender, repayable); - emit Withdraw(block.number, block.timestamp, msg.sender, repayable, 0, _totalAmount, _cap, _forge.saver(address(this), index).ref); + // emit Withdraw(block.number, block.timestamp, msg.sender, repayable, 0, _totalAmount, _cap, _forge.saver(address(this), index).ref); - return true; - } - - function isEntered( address account ) public view returns(bool, uint256){ - return ( _entered[account], _indexes[account] ); - } - - function setCap( uint256 cap_ ) public OnlyAdmin returns(bool){ - _cap = cap_; - _caps.push(cap_); - _capUpdateTimestamps.push(block.timestamp); - return true; - } - - function cap() public view returns(uint256){ - return _cap; - } - - function rewardCap() public view returns(uint256){ - return _rewardCap; - } - - function count() public view returns(uint256){ - return _count; - } - - function totalAmount() public view returns(uint256){ - return _totalAmount; - } - - function decimals() public view returns(uint256){ - return _decimals; - } - - function name() public view returns(string memory){ - return _name; - } - - function caps() public view returns(uint256 [] memory, uint256 [] memory){ - return ( _caps, _capUpdateTimestamps ); - } - - function withdrawable( address account ) public view returns(uint256 amount ){ - ( bool entered, uint index ) = isEntered( msg.sender ); - if( entered ){ - Saver memory saver = _forge.saver( account, index ); - amount = saver.mint.mul( 10**_decimals ).div( _forge.getExchangeRate() ); - amount = amount.mul( 99 ).div(100); - } - } + // return true; + // } + + // function isEntered( address account ) public view returns(bool, uint256){ + // return ( _entered[account], _indexes[account] ); + // } + + // function setCap( uint256 cap_ ) public OnlyAdmin returns(bool){ + // _cap = cap_; + // _caps.push(cap_); + // _capUpdateTimestamps.push(block.timestamp); + // return true; + // } + + // function cap() public view returns(uint256){ + // return _cap; + // } + + // function rewardCap() public view returns(uint256){ + // return _rewardCap; + // } + + // function count() public view returns(uint256){ + // return _count; + // } + + // function totalAmount() public view returns(uint256){ + // return _totalAmount; + // } + + // function decimals() public view returns(uint256){ + // return _decimals; + // } + + // function name() public view returns(string memory){ + // return _name; + // } + + // function caps() public view returns(uint256 [] memory, uint256 [] memory){ + // return ( _caps, _capUpdateTimestamps ); + // } + + // function withdrawable( address account ) public view returns(uint256 amount ){ + // ( bool entered, uint index ) = isEntered( msg.sender ); + // if( entered ){ + // Saver memory saver = _forge.saver( account, index ); + // amount = saver.mint.mul( 10**_decimals ).div( _forge.getExchangeRate() ); + // amount = amount.mul( 99 ).div(100); + // } + // } } \ No newline at end of file diff --git a/contracts/Forge.sol b/contracts/Forge.sol index ef3f9f2..dff01b3 100644 --- a/contracts/Forge.sol +++ b/contracts/Forge.sol @@ -5,513 +5,458 @@ pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "./interfaces/ForgeInterface.sol"; import "./interfaces/ModelInterface.sol"; import "./interfaces/PunkRewardPoolInterface.sol"; -import "./interfaces/ReferralInterface.sol"; import "./Ownable.sol"; import "./ForgeStorage.sol"; -import "./libs/Score.sol"; -import "./Referral.sol"; -contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ - using SafeMath for uint; +contract Forge is + ForgeInterface, + ForgeStorage, + Ownable, + ERC20, + ReentrancyGuard +{ + using SafeMath for uint256; using SafeERC20 for IERC20; - uint constant SECONDS_DAY = 86400; + uint256 constant SECONDS_DAY = 86400; + uint256 constant SECONDS_YEAR = 31556952; - constructor() ERC20("PunkFinance","Forge"){} + constructor() ERC20("PunkFinance", "Forge") {} /** - * Initializing Forge's Variables, If already initialized, it will be reverted. - * - * @param storage_ deployed OnwableStroage's address - * @param variables_ deployed Variables's address - * @param name_ Forge's name - * @param symbol_ Forge's symbol - * @param model_ Address of the Model associated - * @param token_ ERC20 Token's address - * @param decimals_ ERC20 (tokens_)'s decimals - */ - function initializeForge( - address storage_, - address variables_, - string memory name_, - string memory symbol_, - address model_, - address token_, - uint8 decimals_ - ) public initializer { - - Ownable.initialize( storage_ ); - _variables = Variables( variables_ ); - - __name = name_; - __symbol = symbol_; - - _model = model_; - _token = token_; - _tokenUnit = 10**decimals_; - __decimals = decimals_; - - _count = 0; - _totalScore = 0; + * Initializing Forge's Variables, If already initialized, it will be reverted. + * + * @param storage_ deployed OnwableStroage's address + * @param variables_ deployed Variables's address + * @param name_ Forge's name + * @param symbol_ Forge's symbol + * @param token_ ERC20 Token's address + * @param decimals_ ERC20 (tokens_)'s decimals + */ + function initializeForge( + address storage_, + address variables_, + string memory name_, + string memory symbol_, + address token_, + uint8 decimals_ + ) public initializer { + Ownable.initialize(storage_); + _variables = Variables(variables_); + + __name = name_; + __symbol = symbol_; + __decimals = decimals_; + + _token = token_; + _tokenUnit = 1000 * 10**decimals_; + + _count = 0; emit Initialize(); } - + /** - * Replace the model. If model_ isn't CA(ContractAddress), it will be reverted. - * - * @param model_ Address of the associated Model - */ - function setModel( address model_ ) public OnlyAdminOrGovernance returns( bool ){ - require( Address.isContract( model_), "FORGE : Model address must be the contract address."); - require( _model != model_, "FORGE : Current Model" ); - - ModelInterface( _model ).withdrawAllToForge(); - IERC20( _token ).safeTransfer( model_, IERC20( _token ).balanceOf( address( this ) ) ); - ModelInterface( model_ ).invest(); - + * Replace the model. If model_ isn't CA(ContractAddress), it will be reverted. + * + * @param model_ Address of the associated Model + */ + function setModel(address model_) + public + OnlyAdminOrGovernance + returns (bool) + { + require( + Address.isContract(model_), + "FORGE : Model address must be the contract address." + ); + require(_model != model_, "FORGE : Current Model"); + + ModelInterface(_model).withdrawAllToForge(); + IERC20(_token).safeTransfer( + model_, + IERC20(_token).balanceOf(address(this)) + ); + ModelInterface(model_).invest(); + emit SetModel(_model, model_); _model = model_; return true; } /** - * Return the withdrawable amount. - * - * @param account Saver's owner address - * @param index Saver's index - * - * @return the withdrawable amount. - */ - function withdrawable( address account, uint index ) public view override returns( uint ){ - Saver memory s = saver( account, index ); - if( s.startTimestamp > block.timestamp ) return 0; - if( s.status == 2 ) return 0; - - uint diff = block.timestamp.sub( s.startTimestamp ); - uint count = diff.div( SECONDS_DAY.mul( s.interval ) ).add( 1 ); + * Return the withdrawable amount. + * + * @param account Saver's owner address + * @param index Saver's index + * + * @return the withdrawable amount. + */ + function withdrawable(address account, uint256 index) + public + view + override + returns (uint256) + { + Saver memory s = saver(account, index); + if (s.startTimestamp > block.timestamp) return 0; + if (s.status == 2) return 0; + + uint256 diff = block.timestamp.sub(s.startTimestamp); + uint256 count = diff.div(SECONDS_DAY.mul(s.interval)).add(1); count = count < s.count ? count : s.count; - return s.mint.mul( count ).div( s.count ).sub( s.released ); + return s.mint.mul(count).div(s.count).sub(s.released); } /** - * Return the number of savers created with the account. - * - * @param account : Saver's owner account - * - * @return the number of savers created with the account. - */ - function countByAccount( address account ) public view override returns ( uint ){ - return _savers[account].length; - } - - /** - * Create Saver with ERC20 Token and set the required parameters - * - * This function only stores newly created Savers. The actual investment is operated on AddDeposit. - * - * @param amount ERC20 Amount - * @param startTimestamp When do you want to start receiving (unixTime:seconds) - * @param count How often do you want to receive. - * @param interval Number of times to receive (unit: 1 day) - */ - function craftingSaver( uint amount, uint startTimestamp, uint count, uint interval ) public override returns( bool ){ - craftingSaver(amount, startTimestamp, count, interval, 0); - return true; + * Return the number of savers created with the account. + * + * @param account : Saver's owner account + * + * @return the number of savers created with the account. + */ + function countByAccount(address account) + public + view + override + returns (uint256) + { + return _savers[account].length; } /** - * Create Saver with ERC20 Token and set the required parameters - * - * This function only stores newly created Savers. The actual investment is operated on AddDeposit. - * - * @param amount ERC20 Amount - * @param startTimestamp When do you want to start receiving (unixTime:seconds) - * @param count How often do you want to receive. - * @param interval Number of times to receive (unit: 1 day) - * @param referral Referral code issued from "Referral" Contract - */ - function craftingSaver( uint amount, uint startTimestamp, uint count, uint interval, bytes12 referral ) public override returns( bool ){ - require( amount > 0 && count > 0 && interval > 0 && startTimestamp > block.timestamp.add( 24 * 60 * 60 ), "FORGE : Invalid Parameters"); - uint index = countByAccount( msg.sender ); - - _savers[ msg.sender ].push( Saver( block.timestamp, startTimestamp, count, interval, 0, 0, 0, 0, 0, 0, block.timestamp, referral ) ); - _transactions[ msg.sender ][ index ].push( Transaction( true, block.timestamp, 0 ) ); + * Create Saver with ERC20 Token and set the required parameters + * + * This function only stores newly created Savers. The actual investment is operated on AddDeposit. + * + * @param amount ERC20 Amount + * @param startTimestamp When do you want to start receiving (unixTime:seconds) + * @param count How often do you want to receive. + * @param interval Number of times to receive (unit: 1 day) + */ + function craftingSaver( + uint256 amount, + uint256 startTimestamp, + uint256 count, + uint256 interval + ) public override returns (bool) { + require( + amount > 0 && + count > 0 && + interval > 0 && + startTimestamp > block.timestamp.add(24 * 60 * 60), + "FORGE : Invalid Parameters" + ); + uint256 index = countByAccount(msg.sender); + + _savers[msg.sender].push( + Saver( + block.timestamp, + startTimestamp, + count, + interval, + 0, + 0, + 0, + 0, + 0, + block.timestamp + ) + ); _count++; - - emit CraftingSaver( msg.sender, index, amount ); + + emit CraftingSaver(msg.sender, index, amount); addDeposit(index, amount); return true; } - - /** - * Add deposit to Saver - * - * It functions to operate the actual investment. - * It stores the amount deposited on Saver, the new score, the amount, and the timestamp added on _transactions[msg.sender]. - * Within 24 hours, it will be combined to the list of the latest _transactions[msg.sender]. - * - * @param index Saver's index - * @param amount ERC20 Amount - */ - function addDeposit( uint index, uint amount ) public nonReentrant override returns( bool ){ - require( saver( msg.sender, index ).startTimestamp > block.timestamp, "FORGE : Unable to deposit" ); - require( saver( msg.sender, index ).status < 2, "FORGE : Terminated Saver" ); - - uint mint = 0; - uint i = index; + /** + * Add deposit to Saver + * + * It functions to operate the actual investment. + * + * @param index Saver's index + * @param amount ERC20 Amount + */ + function addDeposit(uint256 index, uint256 amount) + public + override + nonReentrant + returns (bool) + { + require( saver(msg.sender, index).startTimestamp > block.timestamp, "FORGE : Unable to deposit" ); + require( saver(msg.sender, index).status < 2, "FORGE : Terminated Saver" ); + + uint256 mint = 0; + uint256 i = index; { // Avoid Stack Too Deep issue i = i + 0; - mint = amount.mul( getExchangeRate() ).div( _tokenUnit ); - _mint( msg.sender, mint ); - if( _variables.reward() != address(0) ) { - approve( _variables.reward(), mint); - PunkRewardPoolInterface( _variables.reward() ).staking( address(this), mint, msg.sender ); - } + mint = amount.mul(getExchangeRate()).div(_tokenUnit); + _mint(msg.sender, mint); + autoStake( mint ); } { // Avoid Stack Too Deep issue i = i + 0; - uint lastIndex = transactions(msg.sender, i ).length.sub( 1 ); - if( block.timestamp.sub( transactions(msg.sender, i )[ lastIndex ].timestamp ) < SECONDS_DAY ){ - _transactions[msg.sender][ index ][ lastIndex ].amount += amount; - }else{ - _transactions[msg.sender][ index ].push( Transaction( true, block.timestamp, amount ) ); - } _savers[msg.sender][i].mint += mint; _savers[msg.sender][i].accAmount += amount; _savers[msg.sender][i].updatedTimestamp = block.timestamp; - _updateScore( msg.sender, i ); } { - IERC20( _token ).safeTransferFrom( msg.sender, _model, amount ); - ModelInterface( _model ).invest(); - emit AddDeposit( msg.sender, index, amount ); + IERC20(_token).safeTransferFrom(msg.sender, _model, amount); + ModelInterface(_model).invest(); + emit AddDeposit(msg.sender, index, amount); } return true; } - + /** - * Withdraw - * - * Enter the amount of pLP token ( Do not enter ERC20 Token's Amount ) - * Withdraw excluding service fee. if saver has referral code, then discount service fee. - * - * @param index Saver's index - * @param amountPlp Forge's LP Token Amount - */ - function withdraw( uint index, uint amountPlp ) public nonReentrant override returns( bool ){ - Saver memory s = saver( msg.sender, index ); - uint withdrawablePlp = withdrawable( msg.sender, index ); - require( s.status < 2 , "FORGE : Terminated Saver"); - require( withdrawablePlp >= amountPlp, "FORGE : Insufficient Amount" ); - - uint i = index; + * Withdraw + * + * Enter the amount of pLP token ( Do not enter ERC20 Token's Amount ) + * Withdraw excluding service fee. if saver has referral code, then discount service fee. + * + * @param index Saver's index + * @param hopeUnderlying Forge's LP Token Amount + */ + function withdrawUnderlying(uint256 index, uint256 hopeUnderlying) public override nonReentrant returns (bool){ + return withdraw(index, hopeUnderlying.mul(getExchangeRate()).div(_tokenUnit)); + } + + /** + * Withdraw + * + * Enter the amount of pLP token ( Do not enter ERC20 Token's Amount ) + * Withdraw excluding service fee. if saver has referral code, then discount service fee. + * + * @param index Saver's index + * @param hope Forge's LP Token Amount + */ + function withdraw(uint256 index, uint256 hope) public override nonReentrant returns (bool){ + Saver memory s = saver(msg.sender, index); + uint256 withdrawablePlp = withdrawable(msg.sender, index); + + require(s.status < 2, "FORGE : Terminated Saver"); + require(withdrawablePlp >= hope, "FORGE : Insufficient Amount"); + + uint256 i = index; + { + // For LP Tokens + i = i + 0; + autoUnstake(hope); + _burn(msg.sender, hope); + } + /* for Underlying ERC20 token */ { i = i + 0; - ( uint amountOfWithdraw, uint amountOfServiceFee, uint amountOfBuyback , uint amountOfReferral, address ref ) = _withdrawValues(msg.sender, i, amountPlp); - - _savers[msg.sender][i].status = 1; - _savers[msg.sender][i].released += amountPlp; + ( + uint256 amountOfWithdraw, + uint256 amountOfServiceFee + ) = _withdrawValues(msg.sender, i, hope, false); + + _savers[msg.sender][i].released += hope; _savers[msg.sender][i].relAmount += amountOfWithdraw; _savers[msg.sender][i].updatedTimestamp = block.timestamp; - if( _savers[msg.sender][i].mint == _savers[msg.sender][i].released ){ - _savers[msg.sender][i].status = 3; - _totalScore = _totalScore.sub( s.score ); - } - emit Withdraw( msg.sender, i, amountOfWithdraw ); + _savers[msg.sender][i].status = _savers[msg.sender][i].mint == _savers[msg.sender][i].released ? 3 : 1; + + emit Withdraw(msg.sender, i, amountOfWithdraw); _withdrawTo(amountOfWithdraw, msg.sender); - _withdrawTo(amountOfServiceFee, _variables.opTreasury() ); - _withdrawTo(amountOfBuyback, _variables.treasury()); - /* If referral code is valid, referral code providers will be rewarded. */ - if( amountOfReferral > 0 && ref != address(0)){ - _withdrawTo( amountOfReferral, ref ); - } + _withdrawTo(amountOfServiceFee, _variables.treasury()); } - { - // For LP Tokens - i = i+0; - uint amount = amountPlp; - uint bonus = balanceOf(address(this)).mul( amountPlp ).mul( s.score ).div( _totalScore ).div( s.mint ); - if( _variables.reward() != address(0) ) PunkRewardPoolInterface( _variables.reward() ).unstaking(address(this), amount, msg.sender ); - _burn( msg.sender, amount ); - _burn( address( this ), bonus ); - } return true; } - - /** - * Terminate Saver - * - * Forcibly terminate Saver and return the deposit. However, early termination fee and service fee are charged. - * - * @param index Saver's index - */ - function terminateSaver( uint index ) public nonReentrant override returns( bool ){ - require( saver( msg.sender, index ).status < 2, "FORGE : Already Terminated" ); - Saver memory s = saver( msg.sender, index ); - - uint i = index; - /* for Underlying ERC20 token */ - { + /** + * Terminate Saver + * + * Forcibly terminate Saver and return the deposit. However, early termination fee and service fee are charged. + * + * @param index Saver's index + */ + function terminateSaver(uint256 index) public override nonReentrant returns (bool){ + Saver memory s = saver(msg.sender, index); + require(s.status < 2, "FORGE : Already Terminated"); + + uint256 i = index; + /* for pLP token */ + { i = i + 0; - (uint amountOfWithdraw, uint amountOfServiceFee, uint amountOfReferral, address ref ) = _terminateValues( msg.sender, i ); - uint remain = s.mint.sub(s.released).mul( _tokenUnit ).div( getExchangeRate() ); - require( remain >= amountOfWithdraw, "FORGE : Insufficient Terminate Fee" ); - - _totalScore = _totalScore.sub( s.score ); - _savers[msg.sender][i].status = 2; - _savers[msg.sender][i].updatedTimestamp = block.timestamp; - emit Terminate( msg.sender, index, amountOfWithdraw ); - - /* the actual amount to be withdrawn. */ - _withdrawTo( amountOfWithdraw, msg.sender ); - /* service fee is charged. */ - _withdrawTo( amountOfServiceFee, _variables.opTreasury() ); - /* If referral code is valid, referral code providers will be rewarded. */ - if( amountOfReferral > 0 && ref != address(0)){ - _withdrawTo( amountOfReferral, ref ); - } + uint256 hope = s.mint.sub(s.released); + autoUnstake( hope ); + /* If the amount is already withdrawn and the remaining amount is less than the fee, it will be reverted. */ + _burn(msg.sender, hope); } - /* for pLP token */ + /* for Underlying ERC20 token */ { i = i + 0; - uint lp = s.mint.sub(s.released); - uint bonus = s.mint.mul( _variables.earlyTerminateFee( address(this) ) ).div( 100 ); - if( _variables.reward() != address(0) ) PunkRewardPoolInterface( _variables.reward() ).unstaking(address(this), lp, msg.sender ); + ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, i, s.mint.sub(s.released), true); - /* If the amount is already withdrawn and the remaining amount is less than the fee, it will be reverted. */ - _burn( msg.sender, lp ); - _mint( address( this ), bonus ); - emit Bonus( msg.sender, index, bonus ); + _savers[msg.sender][i].status = 2; + _savers[msg.sender][i].updatedTimestamp = block.timestamp; + + emit Terminate(msg.sender, index, amountOfWithdraw); + + /* the actual amount to be withdrawn. */ + _withdrawTo(amountOfWithdraw, msg.sender); + /* service fee is charged. */ + _withdrawTo(amountOfServiceFee, _variables.treasury()); } return true; } /** - * Return the exchange rate of ERC20 Token to pLP token, utilizing the balance of the total ERC20 Token invested into the model and the total supply of pLP token. - * - * @return the exchange rate of ERC20 Token to pLP token - */ - function getExchangeRate() public view override returns( uint ){ - if( ModelInterface(_model ).underlyingBalanceWithInvestment() == 0 || totalSupply() == 0 ){ + * Return the exchange rate of ERC20 Token to pLP token, utilizing the balance of the total ERC20 Token invested into the model and the total supply of pLP token. + * + * @return the exchange rate of ERC20 Token to pLP token + */ + function getExchangeRate() public view override returns (uint256) { + if ( ModelInterface(_model).underlyingBalanceWithInvestment() == 0 || totalSupply() == 0 ) { return _tokenUnit; - }else{ - return _tokenUnit.mul( totalSupply() ).div( ModelInterface(_model ).underlyingBalanceWithInvestment() ); + } else { + return _tokenUnit.mul(totalSupply()).div( ModelInterface(_model).underlyingBalanceWithInvestment() ); } } /** - * Return the bonus(ERC20) amount - * - * Bonus is sum of EarlyTerminationFee - * - * @return total bonus amount - */ - function getBonus() public view override returns( uint ){ - return balanceOf( address( this ) ).mul( _tokenUnit ).div( getExchangeRate( ) ); + * Return the invested amount(ERC20) + * + * @return total invested amount + */ + function getTotalVolume() public view override returns (uint256) { + return ModelInterface(_model).underlyingBalanceWithInvestment(); } /** - * Return the invested amount(ERC20) - * - * @return total invested amount - */ - function getTotalVolume() public view override returns( uint ){ - return ModelInterface(_model ).underlyingBalanceWithInvestment(); + * Return the associated model address. + * + * @return model address. + */ + function modelAddress() public view override returns (address) { + return _model; } - - /** - * Return the associated model address. - * - * @return model address. - */ - function modelAddress() public view override returns ( address ){ return _model; } /** - * Return the number of all created savers, including terminated Saver - * - * @return the number of all created savers - */ - function countAll() public view override returns( uint ){ return _count; } - - /** - * Return the Saver's all properties - * - * @param account Saver's index - * @param index Forge's pLP Token Amount - * - * @return model address. - */ - function saver( address account, uint index ) public view override returns( Saver memory ){ return _savers[account][index]; } + * Return the number of all created savers, including terminated Saver + * + * @return the number of all created savers + */ + function countAll() public view override returns (uint256) { + return _count; + } /** - * Return deposit & withdrawn histories - * - * @param account Saver's index - * @param index Forge's pLP Token Amount - * - * @return deposit & withdrawn histories - */ - function transactions( address account, uint index ) public view override returns ( Transaction [] memory ){ return _transactions[account][index]; } - + * Return the Saver's all properties + * + * @param account Saver's index + * @param index Forge's pLP Token Amount + * + * @return model address. + */ + function saver(address account, uint256 index) public view override returns (Saver memory){ + return _savers[account][index]; + } /** - * Change the address of Variables. - * - * this function checks the admin address through OwnableStorage - * - * @param variables_ Vaiables's address - */ - function setVariable( address variables_ ) public OnlyAdmin{ - _variables = Variables( variables_ ); + * Change the address of Variables. + * + * this function checks the admin address through OwnableStorage + * + * @param variables_ Vaiables's address + */ + function setVariable(address variables_) public OnlyAdmin { + _variables = Variables(variables_); } /** - * Call a function withdrawTo from Model contract - * - * @param amount amount of withdraw - * @param account subject to be withdrawn to - */ - function _withdrawTo( uint amount, address account ) private { - if( amount != 0 ) ModelInterface( modelAddress() ).withdrawTo( amount, account ); + * Call a function withdrawTo from Model contract + * + * @param amount amount of withdraw + * @param account subject to be withdrawn to + */ + function _withdrawTo(uint256 amount, address account) private { + if (amount != 0) + ModelInterface(modelAddress()).withdrawTo(amount, account); } /** - * Update Saver's score - * - * @param account Saver's owner account - * @param index Saver's index - */ - function _updateScore( address account, uint index ) internal { + * Return the calculated variables needed to withdraw. + * + * @param account Saver's owner account + * @param index Saver's index + * @param hope Saver's index + * @param isTerminate Saver's index + * + * @return amountOfWithdraw + * @return amountOfFee + */ + function _withdrawValues( address account, uint256 index, uint256 hope, bool isTerminate ) public view returns (uint256 amountOfWithdraw, uint256 amountOfFee) { Saver memory s = saver(account, index); - uint oldScore = s.score; - uint newScore = Score.calculate( - s.createTimestamp, - s.startTimestamp, - _transactions[account][index], - s.count, - s.interval, - 1 - ); - _savers[account][index].score = newScore; - _totalScore = _totalScore.add( newScore ).sub( oldScore ); - } + uint256 fm = _variables.feeMultiplier(); - /** - * Return the calculated variables needed to termiate. - * - * @param account Saver's owner account - * @param index Saver's index - * - * @return amountOfWithdraw - * @return amountOfServiceFee - * @return amountOfReferral - * @return compensation : subject to be rewarded - */ - function _terminateValues( address account, uint index ) public view returns( uint amountOfWithdraw, uint amountOfServiceFee, uint amountOfReferral, address compensation ){ - Saver memory s = saver( account, index ); - uint tf = _variables.earlyTerminateFee(address(this)); - uint sf = _variables.serviceFee(); - uint dc = _variables.discount(); - uint cm = _variables.compensation(); - - compensation = Referral(_variables.referral()).validate( s.ref ); - uint amount = s.mint.mul( _tokenUnit ).div( getExchangeRate() ); - - if( compensation == address(0) ){ - uint amountOfTermiateFee = amount.mul( tf ).div( 100 ); - amountOfServiceFee = amount.mul( sf ).div( 100 ); - amountOfWithdraw = amount.sub( amountOfServiceFee ).sub( amountOfTermiateFee ); - amountOfReferral = 0; - }else{ - uint amountOfTermiateFee = amount.mul( tf ).div( 100 ); - amountOfServiceFee = amount.mul( sf ).div( 100 ); - - uint amountOfDc = amountOfServiceFee.mul( dc ).div( 100 ); - amountOfReferral = amountOfServiceFee.mul( cm ).div( 100 ); - amountOfServiceFee = amountOfServiceFee.sub( amountOfDc ).sub( amountOfReferral ); - amountOfWithdraw = amount.sub( amountOfServiceFee ).sub( amountOfTermiateFee ); - } + uint256 amount = hope.mul(_tokenUnit).div(getExchangeRate()); + uint256 successFee = _successFee(s, hope).mul(isTerminate ? fm : 100).div(100); + uint256 managementFee = _managementFee(s, hope); + + amountOfFee = successFee.add(managementFee); + amountOfWithdraw = amount.sub(amountOfFee); } /** - * Return the calculated variables needed to withdraw. - * - * @param account Saver's owner account - * @param index Saver's index - * - * @return amountOfWithdraw - * @return amountOfServiceFee - * @return amountOfBuyback - * @return amountOfReferral - * @return compensation : subject to be rewarded - */ - function _withdrawValues( address account, uint index, uint hope ) public view returns( uint amountOfWithdraw, uint amountOfServiceFee, uint amountOfBuyback ,uint amountOfReferral, address compensation ){ - Saver memory s = saver( account, index ); - - uint sf = _variables.serviceFee(); - uint dc = _variables.discount(); - uint cm = _variables.compensation(); - - compensation = Referral(_variables.referral()).validate( s.ref ); - amountOfBuyback = _calculateBuyback( account, index, hope ); - - uint amount = hope.mul( _tokenUnit ).div( getExchangeRate() ); - uint bonus = getBonus().mul( s.score ).div( _totalScore ); - - if( compensation == address(0) ){ - bonus = bonus.mul( hope ).div( s.mint ); - amount = amount.add(bonus); - amountOfServiceFee = amount.mul( sf ).div( 100 ); - amountOfWithdraw = amount.sub(amountOfServiceFee).sub(amountOfBuyback); - }else{ - bonus = bonus.mul( hope ).div( s.mint ); - amount = amount.add(bonus); - amountOfServiceFee = amount.mul( sf ).div( 100 ); - uint amountOfDc = amountOfServiceFee.mul( dc ).div( 100 ); - amountOfReferral = amountOfServiceFee.mul( cm ).div( 100 ); - amountOfServiceFee = amountOfServiceFee.sub( amountOfDc ).sub(amountOfReferral); - amountOfWithdraw = amount.sub(amountOfServiceFee).sub(amountOfBuyback); - } - + * Calculate the amount to buyback. + * + * It transfers to treasury to buyback a part of profit. + * + */ + function _successFee(Saver memory s, uint256 hope) public view returns (uint256 successFee){ + uint256 sf = _variables.successFee(); + uint256 balance = s.mint.mul(_tokenUnit).div(getExchangeRate()); + successFee = balance.sub(s.mint).mul(hope).mul(sf).div(s.mint).div(100); } - /** - * Calculate the amount to buyback. - * - * It transfers to treasury to buyback a part of profit. - * - * @param account Saver's owner account - * @param index Saver's index - */ - function _calculateBuyback( address account, uint index, uint hope ) public view returns( uint buyback ) { - Saver memory s = saver( account, index ); - uint br = _variables.buybackRate(); - uint balance = s.mint.mul( _tokenUnit ).div( getExchangeRate() ); - buyback = balance.sub( s.mint ).mul( hope ).mul (br ).div( s.mint ).div(100); + function _managementFee(Saver memory s, uint256 hope) public view returns (uint256 managementFee){ + uint256 period = block.timestamp.sub(s.createTimestamp); + uint256 balance = hope.mul(_tokenUnit).div(getExchangeRate()); + managementFee = balance.mul(period).div(SECONDS_YEAR); } + function autoStake(uint256 hope) public { + if (_variables.reward() != address(0)) { + approve(_variables.reward(), balanceOf(msg.sender)); + PunkRewardPoolInterface(_variables.reward()).staking( + address(this), + hope, + msg.sender + ); + } + } + function autoUnstake(uint256 hope) public { + if (_variables.reward() != address(0)) { + PunkRewardPoolInterface(_variables.reward()).unstaking( + address(this), + hope, + msg.sender + ); + } + } // Override ERC20 function symbol() public view override returns (string memory) { - return symbol(); + return __symbol; } function name() public view override returns (string memory) { @@ -521,9 +466,4 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ function decimals() public view override returns (uint8) { return __decimals; } - - function totalScore() public view override returns(uint256){ - return _totalScore; - } - -} \ No newline at end of file +} diff --git a/contracts/ForgeStorage.sol b/contracts/ForgeStorage.sol index ba9dd49..b78f74d 100644 --- a/contracts/ForgeStorage.sol +++ b/contracts/ForgeStorage.sol @@ -16,15 +16,10 @@ abstract contract ForgeStorage{ string internal __symbol; uint8 internal __decimals; - - mapping( address => uint ) internal _tokensBalances; - mapping( address => Saver [] ) _savers; - mapping( address => mapping( uint => Transaction [] ) ) _transactions; // set to address uint internal _count; - uint internal _totalScore; uint256[50] private ______gap; } \ No newline at end of file diff --git a/contracts/Saver.sol b/contracts/Saver.sol index 3ca8094..cf4ecd4 100644 --- a/contracts/Saver.sol +++ b/contracts/Saver.sol @@ -10,14 +10,6 @@ struct Saver{ uint256 released; uint256 accAmount; uint256 relAmount; - uint score; uint status; uint updatedTimestamp; - bytes12 ref; -} - -struct Transaction{ - bool pos; - uint timestamp; - uint amount; } \ No newline at end of file diff --git a/contracts/Variables.sol b/contracts/Variables.sol index a3eae98..e8d2dfc 100644 --- a/contracts/Variables.sol +++ b/contracts/Variables.sol @@ -9,42 +9,36 @@ contract Variables is Ownable{ address private _initializer; - uint256 private _earlyTerminateFee; - uint256 private _buybackRate; - uint256 private _serviceFee; - uint256 private _discount; - uint256 private _compensation; - + uint256 private _successFee; + uint256 private _managementFee; + uint256 private _feeMultiplier; + address private _treasury; - address private _opTreasury; address private _reward; - address private _referral; - - mapping( address => bool ) _emergency; event Initialize(); function initialize( address storage_) public override initializer{ Ownable.initialize(storage_); - _serviceFee = 1; - _earlyTerminateFee = 1; - _buybackRate = 20; - _discount = 5; - _compensation = 5; + _successFee= 20; + _managementFee = 1; + _feeMultiplier = 200; emit Initialize(); } - function setEarlyTerminateFee( uint256 earlyTerminateFee_ ) public OnlyGovernance { - require( 0 <= earlyTerminateFee_ && earlyTerminateFee_ < 2, "VARIABLES : Fees range from 0 to 2." ); - _earlyTerminateFee = earlyTerminateFee_; + function setSuccessFee( uint256 setSuccessFee_ ) public OnlyGovernance { + require( 0 <= setSuccessFee_ && setSuccessFee_ <= 20, "VARIABLES : SuccessFee range from 0 to 20." ); + _successFee = setSuccessFee_; } - function setBuybackRate( uint256 buybackRate_ ) public OnlyGovernance { - require( 0 <= buybackRate_ && buybackRate_ <= 20, "VARIABLES : BuybackRate range from 0 to 20." ); - _buybackRate = buybackRate_; + + function setManagementFee( uint256 managementFee_ ) public OnlyGovernance { + require( 0 <= managementFee_ && managementFee_ <= 2, "VARIABLES : ManagementFee range from 0 to 2." ); + _managementFee = managementFee_; } - function setEmergency( address forge, bool emergency ) public OnlyAdmin { - _emergency[ forge ] = emergency; + function setFeeMultiplier( uint256 feeMultiplier_ ) public OnlyGovernance { + require( 100 <= feeMultiplier_ && feeMultiplier_ <= 200, "VARIABLES : feeMultiplier range from 100 to 200." ); + _feeMultiplier = feeMultiplier_; } function setTreasury( address treasury_ ) public OnlyAdmin { @@ -57,71 +51,14 @@ contract Variables is Ownable{ _reward = reward_; } - function setOpTreasury( address opTreasury_ ) public OnlyAdmin { - require(Address.isContract(opTreasury_), "VARIABLES : must be the contract address."); - _opTreasury = opTreasury_; - } - - function setReferral( address referral_ ) public OnlyAdmin { - require(Address.isContract(referral_), "VARIABLES : must be the contract address."); - _referral = referral_; - } - - function setServiceFee( uint256 serviceFee_ ) public OnlyGovernance { - require( 0 <= serviceFee_ && serviceFee_ <= 2, "VARIABLES : ServiceFees range from 0 to 2." ); - _serviceFee = serviceFee_; - } + function successFee() public view returns( uint256 ){ return _successFee; } - function setDiscount( uint256 discount_ ) public OnlyAdmin { - require( discount_ + _compensation <= 100, "VARIABLES : discount + compensation <= 100" ); - _discount = discount_; - } + function managementFee() public view returns( uint256 ){ return _managementFee; } - function setCompensation( uint256 compensation_ ) public OnlyAdmin { - require( _discount + compensation_ <= 100, "VARIABLES : discount + compensation <= 100" ); - _compensation = compensation_; - } + function feeMultiplier() public view returns( uint256 ){ return _feeMultiplier; } - function earlyTerminateFee( ) public view returns( uint256 ){ - return _earlyTerminateFee; - } + function treasury() public view returns( address ){ return _treasury; } - function earlyTerminateFee( address forge ) public view returns( uint256 ){ - return isEmergency( forge ) ? 0 : _earlyTerminateFee; - } - - function buybackRate() public view returns( uint256 ){ return _buybackRate; } - - function isEmergency( address forge ) public view returns( bool ){ - return _emergency[ forge ]; - } - - function treasury() public view returns( address ){ - return _treasury; - } - - function reward() public view returns( address ){ - return _reward; - } - - function opTreasury() public view returns( address ){ - return _opTreasury; - } - - function referral() public view returns( address ){ - return _referral; - } - - function serviceFee() public view returns( uint256 ){ - return _serviceFee; - } - - function discount() public view returns( uint256 ){ - return _discount; - } - - function compensation() public view returns( uint256 ){ - return _compensation; - } + function reward() public view returns( address ){ return _reward; } } diff --git a/contracts/interfaces/ForgeEthInterface.sol b/contracts/interfaces/ForgeEthInterface.sol deleted file mode 100644 index 3a15c53..0000000 --- a/contracts/interfaces/ForgeEthInterface.sol +++ /dev/null @@ -1,36 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.5.0 <0.9.0; -pragma experimental ABIEncoderV2; - -import "../Saver.sol"; - -interface ForgeEthInterface{ - - event CraftingSaver ( address owner, uint index, uint deposit ); - event AddDeposit ( address owner, uint index, uint deposit ); - event Withdraw ( address owner, uint index, uint amount ); - event Terminate ( address owner, uint index, uint amount ); - event Bonus ( address owner, uint index, uint amount ); - event SetModel ( address from, address to ); - - function modelAddress() external view returns (address); - - function withdrawable( address account, uint index ) external view returns(uint); - function countByAccount( address account ) external view returns (uint); - - function craftingSaver( uint startTimestamp, uint count, uint interval ) external payable returns(bool); - function craftingSaver( uint startTimestamp, uint count, uint interval, bytes12 referral ) external payable returns(bool); - function addDeposit( uint index ) external payable returns(bool); - function withdraw( uint index, uint amount ) external returns(bool); - function terminateSaver( uint index ) external returns(bool); - - function countAll() external view returns(uint); - function saver( address account, uint index ) external view returns( Saver memory ); - function transactions( address account, uint index ) external view returns ( Transaction [] memory ); - - function totalScore() external view returns(uint256); - function getExchangeRate() external view returns( uint ); - function getBonus() external view returns( uint ); - function getTotalVolume( ) external view returns( uint ); - -} \ No newline at end of file diff --git a/contracts/interfaces/ForgeInterface.sol b/contracts/interfaces/ForgeInterface.sol index 1fcac9a..ba14074 100644 --- a/contracts/interfaces/ForgeInterface.sol +++ b/contracts/interfaces/ForgeInterface.sol @@ -11,7 +11,6 @@ interface ForgeInterface{ event AddDeposit ( address owner, uint index, uint deposit ); event Withdraw ( address owner, uint index, uint amount ); event Terminate ( address owner, uint index, uint amount ); - event Bonus ( address owner, uint index, uint amount ); event SetModel ( address from, address to ); function modelAddress() external view returns (address); @@ -20,18 +19,15 @@ interface ForgeInterface{ function countByAccount( address account ) external view returns (uint); function craftingSaver( uint amount, uint startTimestamp, uint count, uint interval ) external returns(bool); - function craftingSaver( uint amount, uint startTimestamp, uint count, uint interval, bytes12 referral ) external returns(bool); function addDeposit( uint index, uint amount ) external returns(bool); - function withdraw( uint index, uint amount ) external returns(bool); + function withdrawUnderlying( uint index, uint hopeUnderlying ) external returns(bool); + function withdraw( uint index, uint hope ) external returns(bool); function terminateSaver( uint index ) external returns(bool); function countAll() external view returns(uint); function saver( address account, uint index ) external view returns( Saver memory ); - function transactions( address account, uint index ) external view returns ( Transaction [] memory ); - function totalScore() external view returns(uint256); function getExchangeRate() external view returns( uint ); - function getBonus() external view returns( uint ); function getTotalVolume( ) external view returns( uint ); } \ No newline at end of file diff --git a/contracts/libs/CommitmentWeight.sol b/contracts/libs/CommitmentWeight.sol index 1f1336f..b9c0833 100644 --- a/contracts/libs/CommitmentWeight.sol +++ b/contracts/libs/CommitmentWeight.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.5.0 <0.9.0; +/** Do Not Used This Contract */ library CommitmentWeight { uint constant DECIMALS = 8; diff --git a/contracts/libs/Score.sol b/contracts/libs/Score.sol index c3e22e3..781d95e 100644 --- a/contracts/libs/Score.sol +++ b/contracts/libs/Score.sol @@ -6,6 +6,13 @@ import "@openzeppelin/contracts/utils/math/SafeMath.sol"; import "./CommitmentWeight.sol"; import "../Saver.sol"; +/** Do Not Used This Contract */ +struct Transaction{ + bool pos; + uint256 amount; + uint256 timestamp; +} + library Score { using SafeMath for uint; diff --git a/contracts/mock/ScoreMock.sol b/contracts/mock/ScoreMock.sol deleted file mode 100644 index f6d0fa9..0000000 --- a/contracts/mock/ScoreMock.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.5.0 <0.9.0; -pragma experimental ABIEncoderV2; - -import "../libs/Score.sol"; -import "../Saver.sol"; - -contract ScoreMock { - constructor() { - - } - - function scoreCalculation( - uint createTimestamp, - uint startTimestamp, - Transaction [] memory transactions, - uint count, - uint interval, - uint decimals - ) external pure returns (uint score) { - score = Score.calculate(createTimestamp, startTimestamp, transactions, count, interval, decimals); - } -} \ No newline at end of file diff --git a/test/shared/fixtures.ts b/test/shared/fixtures.ts index 97a3e6b..ed24fa1 100644 --- a/test/shared/fixtures.ts +++ b/test/shared/fixtures.ts @@ -14,53 +14,22 @@ export async function unitPunkMockFixtures([,,,,,owner] : Wallet[]): Promise { - const CommitmentWeight = await ethers.getContractFactory("CommitmentWeight") - const commietmentWeight = await CommitmentWeight.deploy() - await commietmentWeight.deployed() - - const Score = await ethers.getContractFactory("Score", { - libraries: { - CommitmentWeight: commietmentWeight.address - } - }); - const score = await Score.deploy() - await score.deployed(); - - return score -} - export async function unitFixtureForge(): Promise { - const score = await libraryFixtures(); - const Forge = await ethers.getContractFactory("Forge", { - libraries: { - Score: score.address, - } - }); + const Forge = await ethers.getContractFactory("Forge"); const forge = await Forge.deploy() await forge.deployed(); return forge; } export async function unitFixtureForge2nd(): Promise { - const score = await libraryFixtures(); - const Forge = await ethers.getContractFactory("Forge", { - libraries: { - Score: score.address, - } - }); + const Forge = await ethers.getContractFactory("Forge"); const forge = await Forge.deploy() await forge.deployed(); return forge; } export async function unitFixtureForge3rd(): Promise { - const score = await libraryFixtures(); - const Forge = await ethers.getContractFactory("Forge", { - libraries: { - Score: score.address, - } - }); + const Forge = await ethers.getContractFactory("Forge"); const forge = await Forge.deploy() await forge.deployed(); return forge; @@ -113,18 +82,6 @@ export async function unitFixtureOwnableStorage([,,,,,owner] : Wallet[]): Promis return ownableStorage } -export async function unitFixtureScoreMock(): Promise { - const score = await libraryFixtures(); - const ScoreMock = await ethers.getContractFactory("ScoreMock", { - libraries: { - Score: score.address, - } - }); - const scoreMock = await ScoreMock.deploy() - - return scoreMock -} - export async function unitFixtureReferral(): Promise { const Referral = await ethers.getContractFactory("Referral") const referral = await Referral.deploy() diff --git a/test/unit/Score/Score.ts b/test/unit/Score/Score.ts deleted file mode 100644 index 8ffcc6f..0000000 --- a/test/unit/Score/Score.ts +++ /dev/null @@ -1,48 +0,0 @@ -import { unitFixtureScoreMock } from "../../shared/fixtures" -import { ScoreMock } from '../../../typechain' -import { expect } from "chai" -export function unitTestScore(): void { - describe("Score", function() { - before(async function() { - const scoreMock: ScoreMock = await this.loadFixture(unitFixtureScoreMock) - this.contracts.scoreMock = scoreMock; - }) - - context("Score model test", function() { - it("Calculate score correctly", async function() { - const createTimestamp = 1622520000 // 2021-06-01 00:00:00 - const startTimestamp = 1638334800 // 2021-12-01 00:00:00 - const count = 1; - const interval = 1; - const transactions = [ - { - timestamp:1622520000, - amount:100000, - pos: true, - }, - { - timestamp:1622520000, - amount:100000, - pos: true, - }, - { - timestamp:1622520000, - amount:100000, - pos: true, - } - ]; - - const scoreResult = await this.contracts.scoreMock.scoreCalculation( - createTimestamp, - startTimestamp, - transactions, - count, - interval, - 8 - ) - - await expect(scoreResult).to.be.equal(55659665) - }) - }) - }) -} \ No newline at end of file diff --git a/test/unit/Variables/setup.behavior.ts b/test/unit/Variables/setup.behavior.ts index 17779e0..4403c8b 100644 --- a/test/unit/Variables/setup.behavior.ts +++ b/test/unit/Variables/setup.behavior.ts @@ -4,6 +4,8 @@ export function setUpBehavior(): void { context("SetUp", function() { it('should Revert setReferral Address Not Admin', async function() { + await expect(true).eq(true) + return; const variables = this.contracts.variables const referral = this.contracts.referral const account1 = this.signers.account1 @@ -11,6 +13,8 @@ export function setUpBehavior(): void { }) it('should Revert setReferral Address Not Contract', async function() { + await expect(true).eq(true) + return; const variables = this.contracts.variables const owner = this.signers.owner const account1 = this.signers.account1 @@ -18,6 +22,8 @@ export function setUpBehavior(): void { }) it('should Success setReferral Address', async function() { + await expect(true).eq(true) + return; const variables = this.contracts.variables const referral = this.contracts.referral const owner = this.signers.owner @@ -69,6 +75,7 @@ export function setUpBehavior(): void { await expect( await variables.treasury() ).to.be.eq( treasury.address ) }) + /* it('should Revert setOperatorTreasury Address Not Admin', async function() { const variables = this.contracts.variables const opTreasury = this.contracts.opTreasury @@ -245,6 +252,7 @@ export function setUpBehavior(): void { const emergencyEalryTerminateFee = await variables['earlyTerminateFee(address)']( forge.address ); await expect( emergencyEalryTerminateFee ).to.be.eq( ealryTerminateFee ) }) + */ }) } \ No newline at end of file diff --git a/test/unit/before.behavior.ts b/test/unit/before.behavior.ts index 031ae52..852d0c3 100644 --- a/test/unit/before.behavior.ts +++ b/test/unit/before.behavior.ts @@ -1,28 +1,47 @@ -import { unitPunkMockFixtures, unitFixtureCompoundModel, unitFixtureDaiToken, unitFixtureForge,unitFixtureForge2nd, unitFixtureForge3rd, unitFixtureOwnableStorage, unitFixtureUniswapV2, unitFixtureUniswapFactoryV2, unitFixtureVariables, unitFixtureReferral, unitFixturePunkRewardPool, unitFixtureTreasury, unitFixtureOpTreasury, unitFixtureRecoveryFund } from "../shared/fixtures" +import { + unitPunkMockFixtures, + unitFixtureCompoundModel, + unitFixtureDaiToken, + unitFixtureForge, + unitFixtureForge2nd, + unitFixtureForge3rd, + unitFixtureOwnableStorage, + unitFixtureUniswapV2, + unitFixtureUniswapFactoryV2, + unitFixtureVariables, + unitFixtureReferral, + unitFixturePunkRewardPool, + unitFixtureTreasury, + unitFixtureOpTreasury, + unitFixtureRecoveryFund, +} from "../shared/fixtures"; export function beforeBehavior(): void { - before(async function() { - this.contracts = { - punkMock:await this.loadFixture(unitPunkMockFixtures), - ownableStorage:await this.loadFixture(unitFixtureOwnableStorage), - variables:await this.loadFixture(unitFixtureVariables), - forge:await this.loadFixture(unitFixtureForge), - forge2:await this.loadFixture(unitFixtureForge2nd), - forge3:await this.loadFixture(unitFixtureForge3rd), - compoundModel:await this.loadFixture(unitFixtureCompoundModel), - compoundModel2:await this.loadFixture(unitFixtureCompoundModel), - compoundModel3:await this.loadFixture(unitFixtureCompoundModel), - uniswapV2Router:await this.loadFixture(unitFixtureUniswapV2), - uniswapV2Factory:await this.loadFixture(unitFixtureUniswapFactoryV2), - daiContract:await this.loadFixture(unitFixtureDaiToken), - referral:await this.loadFixture(unitFixtureReferral), - rewardPool:await this.loadFixture(unitFixturePunkRewardPool), - treasury:await this.loadFixture(unitFixtureTreasury), - opTreasury:await this.loadFixture(unitFixtureOpTreasury), - grinder:await this.loadFixture(unitFixtureOpTreasury), - recoveryFundMock:await this.loadFixture(unitFixtureRecoveryFund), - } + before(async function () { + this.contracts = { + punkMock: await this.loadFixture(unitPunkMockFixtures), + ownableStorage: await this.loadFixture(unitFixtureOwnableStorage), + variables: await this.loadFixture(unitFixtureVariables), + forge: await this.loadFixture(unitFixtureForge), + forge2: await this.loadFixture(unitFixtureForge2nd), + forge3: await this.loadFixture(unitFixtureForge3rd), + compoundModel: await this.loadFixture(unitFixtureCompoundModel), + compoundModel2: await this.loadFixture(unitFixtureCompoundModel), + compoundModel3: await this.loadFixture(unitFixtureCompoundModel), + uniswapV2Router: await this.loadFixture(unitFixtureUniswapV2), + uniswapV2Factory: await this.loadFixture(unitFixtureUniswapFactoryV2), + daiContract: await this.loadFixture(unitFixtureDaiToken), + referral: await this.loadFixture(unitFixtureReferral), + rewardPool: await this.loadFixture(unitFixturePunkRewardPool), + treasury: await this.loadFixture(unitFixtureTreasury), + opTreasury: await this.loadFixture(unitFixtureOpTreasury), + grinder: await this.loadFixture(unitFixtureOpTreasury), + recoveryFundMock: await this.loadFixture(unitFixtureRecoveryFund), + }; - await this.contracts.uniswapV2Factory.createPair( this.contracts.punkMock.address, await this.contracts.uniswapV2Router.WETH() ) - }) -} \ No newline at end of file + await this.contracts.uniswapV2Factory.createPair( + this.contracts.punkMock.address, + await this.contracts.uniswapV2Router.WETH() + ); + }); +} diff --git a/test/unit/index.ts b/test/unit/index.ts index 4e8da22..c4f00e5 100644 --- a/test/unit/index.ts +++ b/test/unit/index.ts @@ -14,7 +14,6 @@ import { unitTestCompoundModel } from "./CompoundModel/CompoundModel" import { unitTestRecoveryFund } from "./RecoveryFund/RecoveryFund" import { unitTestForge } from "./Forge/Forge" -import { unitTestScore } from "./Score/Score" import {use} from 'chai'; @@ -31,7 +30,8 @@ baseContext("Unit Tests", async function() { unitTestVariables(); - unitTestReferral(); + // Do Not used Contract + // unitTestReferral(); unitTestTreasury(); @@ -41,6 +41,4 @@ baseContext("Unit Tests", async function() { unitTestForge(); - unitTestScore(); - }) \ No newline at end of file From 1b4f911b0e71c20dbe3f32cba1212c662359941a Mon Sep 17 00:00:00 2001 From: 0xViktor Date: Sat, 14 Aug 2021 12:09:27 +0900 Subject: [PATCH 02/16] Hard Work Now! For Punkers by 0xViktor... - Renewal Forge with Unit Test - TODO Confirm withdrawal of currency after use of balance. --- contracts/Forge.sol | 75 ++--- test/shared/errors.ts | 1 - test/unit/Forge/initialize.behavior.ts | 30 +- test/unit/Forge/saver.behavior.ts | 49 ++-- test/unit/Variables/setup.behavior.ts | 389 +++++++++---------------- test/unit/index.ts | 2 +- 6 files changed, 204 insertions(+), 342 deletions(-) diff --git a/contracts/Forge.sol b/contracts/Forge.sol index dff01b3..723bebc 100644 --- a/contracts/Forge.sol +++ b/contracts/Forge.sol @@ -12,13 +12,7 @@ import "./interfaces/PunkRewardPoolInterface.sol"; import "./Ownable.sol"; import "./ForgeStorage.sol"; -contract Forge is - ForgeInterface, - ForgeStorage, - Ownable, - ERC20, - ReentrancyGuard -{ +contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ using SafeMath for uint256; using SafeERC20 for IERC20; @@ -65,23 +59,16 @@ contract Forge is * * @param model_ Address of the associated Model */ - function setModel(address model_) - public - OnlyAdminOrGovernance - returns (bool) - { - require( - Address.isContract(model_), - "FORGE : Model address must be the contract address." - ); - require(_model != model_, "FORGE : Current Model"); - - ModelInterface(_model).withdrawAllToForge(); - IERC20(_token).safeTransfer( - model_, - IERC20(_token).balanceOf(address(this)) - ); - ModelInterface(model_).invest(); + function setModel(address model_) public OnlyAdminOrGovernance returns (bool){ + require( model_ != address(0), "FORGE : Model address is zero" ); + require( Address.isContract(model_), "FORGE : Model address must be the contract address."); + require( _model != model_, "FORGE : Current Model"); + + if( _model != address(0) ){ + ModelInterface(_model).withdrawAllToForge(); + IERC20(_token).safeTransfer( model_, IERC20(_token).balanceOf(address(this))); + ModelInterface(model_).invest(); + } emit SetModel(_model, model_); _model = model_; @@ -96,12 +83,7 @@ contract Forge is * * @return the withdrawable amount. */ - function withdrawable(address account, uint256 index) - public - view - override - returns (uint256) - { + function withdrawable(address account, uint256 index) public view override returns (uint256){ Saver memory s = saver(account, index); if (s.startTimestamp > block.timestamp) return 0; if (s.status == 2) return 0; @@ -139,19 +121,8 @@ contract Forge is * @param count How often do you want to receive. * @param interval Number of times to receive (unit: 1 day) */ - function craftingSaver( - uint256 amount, - uint256 startTimestamp, - uint256 count, - uint256 interval - ) public override returns (bool) { - require( - amount > 0 && - count > 0 && - interval > 0 && - startTimestamp > block.timestamp.add(24 * 60 * 60), - "FORGE : Invalid Parameters" - ); + function craftingSaver( uint256 amount, uint256 startTimestamp, uint256 count, uint256 interval ) public override returns (bool) { + require( amount > 0 && count > 0 && interval > 0 && startTimestamp > block.timestamp.add(24 * 60 * 60), "FORGE : Invalid Parameters"); uint256 index = countByAccount(msg.sender); _savers[msg.sender].push( @@ -183,13 +154,7 @@ contract Forge is * @param index Saver's index * @param amount ERC20 Amount */ - function addDeposit(uint256 index, uint256 amount) - public - override - nonReentrant - returns (bool) - { - require( saver(msg.sender, index).startTimestamp > block.timestamp, "FORGE : Unable to deposit" ); + function addDeposit(uint256 index, uint256 amount) public override nonReentrant returns (bool){ require( saver(msg.sender, index).status < 2, "FORGE : Terminated Saver" ); uint256 mint = 0; @@ -256,18 +221,16 @@ contract Forge is _burn(msg.sender, hope); } + // TODO Confirm withdrawal of currency after use of balance. /* for Underlying ERC20 token */ { i = i + 0; - ( - uint256 amountOfWithdraw, - uint256 amountOfServiceFee - ) = _withdrawValues(msg.sender, i, hope, false); + ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, i, hope, false); _savers[msg.sender][i].released += hope; _savers[msg.sender][i].relAmount += amountOfWithdraw; _savers[msg.sender][i].updatedTimestamp = block.timestamp; - _savers[msg.sender][i].status = _savers[msg.sender][i].mint == _savers[msg.sender][i].released ? 3 : 1; + _savers[msg.sender][i].status = (_savers[msg.sender][i].mint == _savers[msg.sender][i].released) ? 3 : 1; emit Withdraw(msg.sender, i, amountOfWithdraw); @@ -287,7 +250,7 @@ contract Forge is */ function terminateSaver(uint256 index) public override nonReentrant returns (bool){ Saver memory s = saver(msg.sender, index); - require(s.status < 2, "FORGE : Already Terminated"); + require(s.status == 2, "FORGE : Already Terminated or Completed"); uint256 i = index; /* for pLP token */ diff --git a/test/shared/errors.ts b/test/shared/errors.ts index 93ca986..da4d63c 100644 --- a/test/shared/errors.ts +++ b/test/shared/errors.ts @@ -1,3 +1,2 @@ export enum ForgeErrors { - NotContract = "function call to a non-contract account", } \ No newline at end of file diff --git a/test/unit/Forge/initialize.behavior.ts b/test/unit/Forge/initialize.behavior.ts index d407f7c..48dcefd 100644 --- a/test/unit/Forge/initialize.behavior.ts +++ b/test/unit/Forge/initialize.behavior.ts @@ -8,14 +8,12 @@ export function initialBehavior(): void { const forge = this.contracts.forge; const ownableStorage = this.contracts.ownableStorage; const variables = this.contracts.variables; - const compoundModel = this.contracts.compoundModel; await expect(forge.initializeForge( ownableStorage.address, variables.address, "Punk-Forge-DAI-0", "pDAI", - compoundModel.address, Tokens.Dai, 18 )).emit(forge, "Initialize") @@ -24,18 +22,42 @@ export function initialBehavior(): void { it('should Revert Forge Initialize', async function() { const ownableStorage = this.contracts.ownableStorage; const variables = this.contracts.variables; - const compoundModel = this.contracts.compoundModel; await expect(this.contracts.forge.connect(this.signers.owner).initializeForge( ownableStorage.address, variables.address, "Punk-Forge-DAI-0", "pDAI", - compoundModel.address, Tokens.Dai, 18 )).to.be.reverted }) + it('should Revert Forge setModel Not Admin or Gov', async function() { + const forge = this.contracts.forge; + const compoundModel = this.contracts.compoundModel; + const account = this.signers.account1; + await expect(forge.connect(account).setModel(compoundModel.address)).to.be.reverted + }) + + it('should Revert Forge setModel address zero', async function() { + const forge = this.contracts.forge; + const owner = this.signers.owner; + await expect(forge.connect(owner).setModel("0x0000000000000000000000000000000000000000")).to.be.reverted + }) + + it('should Revert Forge setModel address EOA', async function() { + const forge = this.contracts.forge; + const owner = this.signers.owner; + await expect(forge.connect(owner).setModel(owner.address)).to.be.reverted + }) + + it('should Success Forge setModel', async function() { + const forge = this.contracts.forge; + const compoundModel = this.contracts.compoundModel; + const owner = this.signers.owner; + await expect(forge.connect(owner).setModel(compoundModel.address)).emit(forge, "SetModel").withArgs("0x0000000000000000000000000000000000000000", compoundModel.address); + }) + }) } \ No newline at end of file diff --git a/test/unit/Forge/saver.behavior.ts b/test/unit/Forge/saver.behavior.ts index 38c8e94..50c234d 100644 --- a/test/unit/Forge/saver.behavior.ts +++ b/test/unit/Forge/saver.behavior.ts @@ -27,51 +27,40 @@ export function saverBehavior(): void { const blockNumber = await ethers.provider.getBlockNumber() const blockInfo = await ethers.provider.getBlock(blockNumber) const startTimestamp = blockInfo.timestamp + 25 *60 * 60 - await expect(this.contracts.forge['craftingSaver(uint256,uint256,uint256,uint256)'](100, startTimestamp, 1, 1)).to.be.reverted + const forge = this.contracts.forge; + + await expect(forge.craftingSaver(100, startTimestamp, 1, 2)).to.be.reverted }) it('should Revert due to startTimestamp', async function() { const blockNumber = await ethers.provider.getBlockNumber() const blockInfo = await ethers.provider.getBlock(blockNumber) - await expect(this.contracts.forge['craftingSaver(uint256,uint256,uint256,uint256)'](100, blockInfo.timestamp, 1, 1)).to.be.reverted + const forge = this.contracts.forge; + + await expect(forge.craftingSaver(100, blockInfo.timestamp, 1, 2)).to.be.reverted }) it('should Success craftingSaver', async function () { const blockNumber = await ethers.provider.getBlockNumber() const blockInfo = await ethers.provider.getBlock(blockNumber) const account = this.signers.accountDai - const forgeDai = this.contracts.forge + const forge = this.contracts.forge const daiContract = this.contracts.daiContract; - const startTimestamp = blockInfo.timestamp + 25 *60 * 60 - const saverIndex = await forgeDai.connect(account).countByAccount(account.address) + const saverIndex = await forge.connect(account).countByAccount(account.address) const balance = await daiContract.balanceOf( account.address ); - await daiContract.connect(account).approve(forgeDai.address, ethToWei("100000")) - await expect(forgeDai - .connect(account)['craftingSaver(uint256,uint256,uint256,uint256)'](balance.div(3).toString(), startTimestamp, 1, 1)) - .to.emit(forgeDai, 'CraftingSaver') + await daiContract.connect(account).approve(forge.address, ethToWei("100000")) + await expect(forge + .connect(account).craftingSaver(balance.div(3).toString(), startTimestamp, 2, 4)) + .to.emit(forge, 'CraftingSaver') .withArgs(account.address, saverIndex, balance.div(3).toString()) }) it('should Success addDeposit', async function() { - const prevTransactionCount = (await this.contracts.forge.transactions(this.signers.accountDai.address, 0)).length; await expect(this.contracts.forge.connect(this.signers.accountDai).addDeposit(0, 1000000)).to.emit(this.contracts.forge, 'AddDeposit').withArgs(this.signers.accountDai.address, 0, 1000000) - let afterTransactionCount = (await this.contracts.forge.transactions(this.signers.accountDai.address, 0)).length - await expect(afterTransactionCount).to.be.eq(prevTransactionCount, "Transaction is not added"); - - // increase time 86400 secs - await network.provider.send("evm_increaseTime", [86400]); - await network.provider.send("evm_mine") - await expect(this.contracts.forge.connect(this.signers.accountDai).addDeposit(0, 1000000)) - .to.emit(this.contracts.forge, 'AddDeposit') - .withArgs(this.signers.accountDai.address, 0, 1000000) - - afterTransactionCount = (await this.contracts.forge.transactions(this.signers.accountDai.address, 0)).length - - await expect(afterTransactionCount).to.be.eq(prevTransactionCount + 1, "Transaction is not added"); }) it('should Revert withdraw Not yet', async function() { @@ -81,13 +70,23 @@ export function saverBehavior(): void { }) it('should Success withdraw', async function() { - await network.provider.send("evm_increaseTime", [86400]); + await network.provider.send("evm_increaseTime", [106400]); await network.provider.send("evm_mine") const forge = this.contracts.forge; const account = this.signers.accountDai + const saver = await forge.saver(account.address, 0); + + const blockNumber = await ethers.provider.getBlockNumber() + const blockInfo = await ethers.provider.getBlock(blockNumber) + const daiContract = this.contracts.daiContract; + + const withdrawable = await forge.connect(account).withdrawable(account.address, 0); - await expect(forge.connect(account).withdraw(0, 100000000000000)).emit(forge, "Withdraw") + + const _withdrawValues = await forge.connect(account)._withdrawValues(account.address, 0, withdrawable.toString(), false ); + + await forge.connect(account).withdraw(0, withdrawable.toString()) }) it('should Success terminateSaver', async function() { diff --git a/test/unit/Variables/setup.behavior.ts b/test/unit/Variables/setup.behavior.ts index 4403c8b..905ca98 100644 --- a/test/unit/Variables/setup.behavior.ts +++ b/test/unit/Variables/setup.behavior.ts @@ -1,258 +1,137 @@ import { expect } from "chai"; export function setUpBehavior(): void { - context("SetUp", function() { - - it('should Revert setReferral Address Not Admin', async function() { - await expect(true).eq(true) - return; - const variables = this.contracts.variables - const referral = this.contracts.referral - const account1 = this.signers.account1 - await expect( variables.connect(account1).setReferral(referral.address) ).to.be.reverted - }) - - it('should Revert setReferral Address Not Contract', async function() { - await expect(true).eq(true) - return; - const variables = this.contracts.variables - const owner = this.signers.owner - const account1 = this.signers.account1 - await expect( variables.connect(owner).setReferral(account1) ).to.be.reverted - }) - - it('should Success setReferral Address', async function() { - await expect(true).eq(true) - return; - const variables = this.contracts.variables - const referral = this.contracts.referral - const owner = this.signers.owner - await variables.connect(owner).setReferral(referral.address); - await expect( await variables.referral() ).to.be.eq( referral.address ) - }) - - it('should Revert setRewardPool Address Not Admin', async function() { - const variables = this.contracts.variables - const rewardPool = this.contracts.rewardPool - const account1 = this.signers.account1 - await expect( variables.connect(account1).setReward(rewardPool.address) ).to.be.reverted - }) - - it('should Revert setRewardPool Address Not Contract', async function() { - const variables = this.contracts.variables - const owner = this.signers.owner - const account1 = this.signers.account1 - await expect( variables.connect(owner).setReward(account1) ).to.be.reverted - }) - - it('should Success setRewardPool Address', async function() { - const variables = this.contracts.variables - const rewardPool = this.contracts.rewardPool - const owner = this.signers.owner - await variables.connect(owner).setReward(rewardPool.address); - await expect( await variables.reward() ).to.be.eq( rewardPool.address ) - }) - - it('should Revert setTreasury Address Not Admin', async function() { - const variables = this.contracts.variables - const treasury = this.contracts.treasury - const account1 = this.signers.account1 - await expect( variables.connect(account1).setTreasury(treasury.address) ).to.be.reverted - }) - - it('should Revert setTreasury Address Not Contract', async function() { - const variables = this.contracts.variables - const owner = this.signers.owner - const account1 = this.signers.account1 - await expect( variables.connect(owner).setTreasury(account1) ).to.be.reverted - }) - - it('should Success setTreasury Address', async function() { - const variables = this.contracts.variables - const treasury = this.contracts.treasury - const owner = this.signers.owner - await variables.connect(owner).setTreasury(treasury.address); - await expect( await variables.treasury() ).to.be.eq( treasury.address ) - }) - - /* - it('should Revert setOperatorTreasury Address Not Admin', async function() { - const variables = this.contracts.variables - const opTreasury = this.contracts.opTreasury - const account1 = this.signers.account1 - await expect( variables.connect(account1).setOpTreasury(opTreasury.address) ).to.be.reverted - }) - - it('should Revert setOperatorTreasury Address Not Contract', async function() { - const variables = this.contracts.variables - const owner = this.signers.owner - const account1 = this.signers.account1 - await expect( variables.connect(owner).setOpTreasury(account1) ).to.be.reverted - }) - - it('should Success setOperatorTreasury Address', async function() { - const variables = this.contracts.variables - const opTreasury = this.contracts.opTreasury - const owner = this.signers.owner - await variables.connect(owner).setOpTreasury(opTreasury.address); - await expect( await variables.opTreasury() ).to.be.eq( opTreasury.address ) - }) - - it('should Revert setEarlyTerminateFee Address Not Gov and Admin', async function() { - const variables = this.contracts.variables - const account1 = this.signers.account1 - await expect( variables.connect(account1).setEarlyTerminateFee(1) ).to.be.reverted - }) - - it('should Revert setEarlyTerminateFee Address Admin', async function() { - const variables = this.contracts.variables - const owner = this.signers.owner - await expect( variables.connect(owner).setEarlyTerminateFee(1) ).to.be.reverted - }) - - it('should Success setEarlyTerminateFee', async function() { - const variables = this.contracts.variables - const gov = this.signers.gov - const fee = 1 - await variables.connect(gov).setEarlyTerminateFee(fee); - await expect( await variables['earlyTerminateFee()']() ).to.be.eq( fee ) - }) - - it('should Revert setEarlyTerminateFee Overflow Valeus', async function() { - const variables = this.contracts.variables - const gov = this.signers.gov - await expect( variables.connect(gov).setEarlyTerminateFee(10) ).to.be.reverted - }) - - it('should Revert setBuybackRate Address Not Gov and Admin', async function() { - const variables = this.contracts.variables - const account1 = this.signers.account1 - await expect( variables.connect(account1).setBuybackRate(20) ).to.be.reverted - }) - - it('should Revert setBuybackRate Address Admin', async function() { - const variables = this.contracts.variables - const owner = this.signers.owner - await expect( variables.connect(owner).setBuybackRate(20) ).to.be.reverted - }) - - it('should Success setBuybackRate', async function() { - const variables = this.contracts.variables - const gov = this.signers.gov - await variables.connect(gov).setBuybackRate(20); - await expect( await variables.buybackRate() ).to.be.eq( 20 ) - }) - - it('should Revert setBuybackRate Overflow Valeus', async function() { - const variables = this.contracts.variables - const gov = this.signers.gov - await expect( variables.connect(gov).setBuybackRate(21) ).to.be.reverted - }) - - it('should Revert setServiceFee Address Not Gov and Admin', async function() { - const variables = this.contracts.variables - const account1 = this.signers.account1 - await expect( variables.connect(account1).setServiceFee(1) ).to.be.reverted - }) - - it('should Revert setServiceFee Address Admin', async function() { - const variables = this.contracts.variables - const owner = this.signers.owner - await expect( variables.connect(owner).setServiceFee(1) ).to.be.reverted - }) - - it('should Success setServiceFee', async function() { - const variables = this.contracts.variables - const gov = this.signers.gov - await variables.connect(gov).setServiceFee(1); - await expect( await variables.serviceFee() ).to.be.eq( 1 ) - }) - - it('should Revert setServiceFee Overflow Valeus', async function() { - const variables = this.contracts.variables - const gov = this.signers.gov - await expect( variables.connect(gov).setServiceFee(3) ).to.be.reverted - }) - - it('should Revert setDiscount Address Not Admin', async function() { - const variables = this.contracts.variables - const account1 = this.signers.account1 - await expect( variables.connect(account1).setDiscount(20) ).to.be.reverted - }) - - it('should Success setDiscount Overflow Valeus', async function() { - const variables = this.contracts.variables - const owner = this.signers.owner - const compensation = await variables.compensation() - await expect( variables.connect(owner).setDiscount( 101 - compensation ) ).to.be.reverted - }) - - it('should Success setDiscount', async function() { - const variables = this.contracts.variables - const owner = this.signers.owner - const setUpPoint = 5; - await expect( variables.connect(owner).setDiscount( setUpPoint ) ) - const discount = await variables.discount(); - await expect( discount ).eq( setUpPoint ) - }) - - it('should Revert setCompensation Address Not Admin', async function() { - const variables = this.contracts.variables - const account1 = this.signers.account1 - await expect( variables.connect(account1).setCompensation(20) ).to.be.reverted - }) - - it('should Success setCompensation Overflow Valeus', async function() { - const variables = this.contracts.variables - const owner = this.signers.owner - const discount = await variables.discount() - await expect( variables.connect(owner).setCompensation( 101 - discount ) ).to.be.reverted - }) - - it('should Success setCompensation', async function() { - const variables = this.contracts.variables - const owner = this.signers.owner - const setUpPoint = 5; - await expect( variables.connect(owner).setCompensation( setUpPoint ) ) - const compensation = await variables.compensation(); - await expect( compensation ).eq( setUpPoint ) - }) - - it('should Revert setEmergency Address Not Admin', async function() { - const variables = this.contracts.variables - const account1 = this.signers.account1 - const forge = this.contracts.forge; - await expect( variables.connect(account1).setEmergency(forge.address) ).to.be.reverted - }) - - it('should Success setEmergency for True', async function() { - const variables = this.contracts.variables - const owner = this.signers.owner; - const forge = this.contracts.forge; - let ealryTerminateFee = await variables['earlyTerminateFee()'](); - if( ealryTerminateFee == 0 ){ - await variables.connect(owner).setEarlyTerminateFee( 1 ) - ealryTerminateFee = await variables['earlyTerminateFee()'](); - } - await variables.connect(owner).setEmergency( forge.address, true ); - const emergencyEalryTerminateFee = await variables['earlyTerminateFee(address)']( forge.address ); - await expect( emergencyEalryTerminateFee + ealryTerminateFee ).to.be.eq( ealryTerminateFee ) - }) - - it('should Success setEmergency for False', async function() { - const variables = this.contracts.variables - const owner = this.signers.owner; - const forge = this.contracts.forge; - let ealryTerminateFee = await variables['earlyTerminateFee()'](); - if( ealryTerminateFee == 0 ){ - await variables.connect(owner).setEarlyTerminateFee( 1 ) - ealryTerminateFee = await variables['earlyTerminateFee()'](); - } - await variables.connect(owner).setEmergency( forge.address, false ); - const emergencyEalryTerminateFee = await variables['earlyTerminateFee(address)']( forge.address ); - await expect( emergencyEalryTerminateFee ).to.be.eq( ealryTerminateFee ) - }) - */ - - }) -} \ No newline at end of file + context("SetUp", function () { + it("should Revert setRewardPool Address Not Admin", async function () { + const variables = this.contracts.variables; + const rewardPool = this.contracts.rewardPool; + const account1 = this.signers.account1; + await expect( + variables.connect(account1).setReward(rewardPool.address) + ).to.be.reverted; + }); + + it("should Revert setRewardPool Address Not Contract", async function () { + const variables = this.contracts.variables; + const owner = this.signers.owner; + const account1 = this.signers.account1; + await expect(variables.connect(owner).setReward(account1)).to.be.reverted; + }); + + it("should Success setRewardPool Address", async function () { + const variables = this.contracts.variables; + const rewardPool = this.contracts.rewardPool; + const owner = this.signers.owner; + await variables.connect(owner).setReward(rewardPool.address); + await expect(await variables.reward()).to.be.eq(rewardPool.address); + }); + + it("should Revert setTreasury Address Not Admin", async function () { + const variables = this.contracts.variables; + const treasury = this.contracts.treasury; + const account1 = this.signers.account1; + await expect( + variables.connect(account1).setTreasury(treasury.address) + ).to.be.reverted; + }); + + it("should Revert setTreasury Address Not Contract", async function () { + const variables = this.contracts.variables; + const owner = this.signers.owner; + const account1 = this.signers.account1; + await expect( + variables.connect(owner).setTreasury(account1) + ).to.be.reverted; + }); + + it("should Success setTreasury Address", async function () { + const variables = this.contracts.variables; + const treasury = this.contracts.treasury; + const owner = this.signers.owner; + await variables.connect(owner).setTreasury(treasury.address); + await expect(await variables.treasury()).to.be.eq(treasury.address); + }); + + it("should Revert setSuccessFee Address Not Gov and Admin", async function () { + const variables = this.contracts.variables; + const account1 = this.signers.account1; + await expect(variables.connect(account1).setSuccessFee(20)).to.be.reverted; + }); + + it("should Revert setSuccessFee Address Admin", async function () { + const variables = this.contracts.variables; + const owner = this.signers.owner; + await expect(variables.connect(owner).setSuccessFee(20)).to.be.reverted; + }); + + it("should Success setSuccessFee", async function () { + const variables = this.contracts.variables; + const gov = this.signers.gov; + const fee = 20; + await variables.connect(gov).setSuccessFee(fee); + await expect(await variables.successFee()).to.be.eq(fee); + }); + + it("should Revert setSuccessFee Overflow Valeus", async function () { + const variables = this.contracts.variables; + const gov = this.signers.gov; + const fee = 21; + await expect(variables.connect(gov).setSuccessFee(fee)).to.be.reverted; + }); + + it("should Revert setManagementFee Address Not Gov and Admin", async function () { + const variables = this.contracts.variables; + const account1 = this.signers.account1; + await expect(variables.connect(account1).setManagementFee(10)).to.be.reverted; + }); + + it("should Revert setManagementFee Address Admin", async function () { + const variables = this.contracts.variables; + const owner = this.signers.owner; + const fee = 1; + await expect(variables.connect(owner).setManagementFee(fee)).to.be.reverted; + }); + + it("should Success setManagementFee", async function () { + const variables = this.contracts.variables; + const gov = this.signers.gov; + const fee = 1; + await variables.connect(gov).setManagementFee(fee); + await expect(await variables.managementFee()).to.be.eq(fee); + }); + + it("should Revert setManagementFee Overflow Valeus", async function () { + const variables = this.contracts.variables; + const gov = this.signers.gov; + await expect(variables.connect(gov).setManagementFee(10)).to.be.reverted; + }); + + it("should Revert setFeeMultiplier Address Not Gov and Admin", async function () { + const variables = this.contracts.variables; + const account1 = this.signers.account1; + await expect( + variables.connect(account1).setFeeMultiplier(100) + ).to.be.reverted; + }); + + it("should Revert setFeeMultiplier Address Admin", async function () { + const variables = this.contracts.variables; + const owner = this.signers.owner; + await expect(variables.connect(owner).setFeeMultiplier(100)).to.be.reverted; + }); + + it("should Success setFeeMultiplier", async function () { + const variables = this.contracts.variables; + const gov = this.signers.gov; + const fee = 200; + await variables.connect(gov).setFeeMultiplier(fee); + await expect(await variables.feeMultiplier()).to.be.eq(fee); + }); + + it("should Revert setFeeMultiplier Overflow Valeus", async function () { + const variables = this.contracts.variables; + const gov = this.signers.gov; + await expect(variables.connect(gov).setFeeMultiplier(300)).to.be.reverted; + }); + }); +} diff --git a/test/unit/index.ts b/test/unit/index.ts index c4f00e5..113499d 100644 --- a/test/unit/index.ts +++ b/test/unit/index.ts @@ -24,7 +24,7 @@ baseContext("Unit Tests", async function() { beforeBehavior(); - unitTestRecoveryFund(); + // unitTestRecoveryFund(); unitTestOwnableStorage(); From 5f6e56235ad4f558344c02e630191c50c0ccb43d Mon Sep 17 00:00:00 2001 From: 0xViktor Date: Sun, 15 Aug 2021 12:58:20 +0900 Subject: [PATCH 03/16] Hard Work Now! For Punkers by 0xViktor... - Renewal Forge with Unit Test - TODO : Check Compound Model underlyingBalance to down --- contracts/Forge.sol | 52 +++++++++++++------------ contracts/interfaces/ForgeInterface.sol | 4 +- contracts/models/CompoundModel.sol | 2 +- test/unit/Forge/saver.behavior.ts | 44 ++++++++++++++------- 4 files changed, 62 insertions(+), 40 deletions(-) diff --git a/contracts/Forge.sol b/contracts/Forge.sol index 723bebc..cb651a5 100644 --- a/contracts/Forge.sol +++ b/contracts/Forge.sol @@ -162,7 +162,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ { // Avoid Stack Too Deep issue i = i + 0; - mint = amount.mul(getExchangeRate()).div(_tokenUnit); + mint = amount.mul(exchangeRate()).div(_tokenUnit); _mint(msg.sender, mint); autoStake( mint ); } @@ -194,7 +194,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * @param hopeUnderlying Forge's LP Token Amount */ function withdrawUnderlying(uint256 index, uint256 hopeUnderlying) public override nonReentrant returns (bool){ - return withdraw(index, hopeUnderlying.mul(getExchangeRate()).div(_tokenUnit)); + return withdraw(index, hopeUnderlying.mul(exchangeRate()).div(_tokenUnit)); } /** @@ -250,7 +250,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ */ function terminateSaver(uint256 index) public override nonReentrant returns (bool){ Saver memory s = saver(msg.sender, index); - require(s.status == 2, "FORGE : Already Terminated or Completed"); + require(s.status < 2, "FORGE : Already Terminated or Completed"); uint256 i = index; /* for pLP token */ @@ -268,6 +268,8 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, i, s.mint.sub(s.released), true); _savers[msg.sender][i].status = 2; + _savers[msg.sender][i].released += s.mint.sub(s.released); + _savers[msg.sender][i].relAmount += amountOfWithdraw; _savers[msg.sender][i].updatedTimestamp = block.timestamp; emit Terminate(msg.sender, index, amountOfWithdraw); @@ -286,12 +288,11 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * * @return the exchange rate of ERC20 Token to pLP token */ - function getExchangeRate() public view override returns (uint256) { - if ( ModelInterface(_model).underlyingBalanceWithInvestment() == 0 || totalSupply() == 0 ) { - return _tokenUnit; - } else { - return _tokenUnit.mul(totalSupply()).div( ModelInterface(_model).underlyingBalanceWithInvestment() ); - } + function exchangeRate() public view override returns (uint256) { + return ModelInterface(_model).underlyingBalanceWithInvestment() == 0 ? _tokenUnit : + _tokenUnit + .mul( totalSupply() ) + .div( ModelInterface(_model).underlyingBalanceWithInvestment() ); } /** @@ -299,7 +300,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * * @return total invested amount */ - function getTotalVolume() public view override returns (uint256) { + function totalVolume() public view override returns (uint256) { return ModelInterface(_model).underlyingBalanceWithInvestment(); } @@ -356,7 +357,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ } /** - * Return the calculated variables needed to withdraw. + * Return the calculated variables needed to withdraw and terminate. * * @param account Saver's owner account * @param index Saver's index @@ -370,30 +371,33 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ Saver memory s = saver(account, index); uint256 fm = _variables.feeMultiplier(); - uint256 amount = hope.mul(_tokenUnit).div(getExchangeRate()); - uint256 successFee = _successFee(s, hope).mul(isTerminate ? fm : 100).div(100); + uint256 amount = hope.mul(_tokenUnit).div(exchangeRate()); + uint256 successFee = _successFee(s, hope); uint256 managementFee = _managementFee(s, hope); - amountOfFee = successFee.add(managementFee); + amountOfFee = successFee.add(managementFee).mul(isTerminate ? fm : 100).div(100); amountOfWithdraw = amount.sub(amountOfFee); } - /** - * Calculate the amount to buyback. - * - * It transfers to treasury to buyback a part of profit. - * - */ + function profit( address account, uint index ) public view returns(uint256){ + Saver memory s = saver(account, index); + uint256 balance = s.mint.mul(_tokenUnit).div(exchangeRate()); + uint256 accAmount = s.accAmount; + return balance.sub(accAmount); + } + function _successFee(Saver memory s, uint256 hope) public view returns (uint256 successFee){ uint256 sf = _variables.successFee(); - uint256 balance = s.mint.mul(_tokenUnit).div(getExchangeRate()); - successFee = balance.sub(s.mint).mul(hope).mul(sf).div(s.mint).div(100); + uint256 balance = s.mint.mul(_tokenUnit).div(exchangeRate()); + uint256 accAmount = s.accAmount; + successFee = balance.sub(accAmount).mul(hope).mul(sf).div(s.mint).div(100); } function _managementFee(Saver memory s, uint256 hope) public view returns (uint256 managementFee){ + uint256 mf = _variables.managementFee(); uint256 period = block.timestamp.sub(s.createTimestamp); - uint256 balance = hope.mul(_tokenUnit).div(getExchangeRate()); - managementFee = balance.mul(period).div(SECONDS_YEAR); + uint256 balance = hope.mul(_tokenUnit).div(exchangeRate()); + managementFee = balance.mul(period).mul(mf).div(SECONDS_YEAR).div(100); } function autoStake(uint256 hope) public { diff --git a/contracts/interfaces/ForgeInterface.sol b/contracts/interfaces/ForgeInterface.sol index ba14074..8b2da70 100644 --- a/contracts/interfaces/ForgeInterface.sol +++ b/contracts/interfaces/ForgeInterface.sol @@ -27,7 +27,7 @@ interface ForgeInterface{ function countAll() external view returns(uint); function saver( address account, uint index ) external view returns( Saver memory ); - function getExchangeRate() external view returns( uint ); - function getTotalVolume( ) external view returns( uint ); + function exchangeRate() external view returns( uint ); + function totalVolume( ) external view returns( uint ); } \ No newline at end of file diff --git a/contracts/models/CompoundModel.sol b/contracts/models/CompoundModel.sol index 9e6ec81..0409b55 100644 --- a/contracts/models/CompoundModel.sol +++ b/contracts/models/CompoundModel.sol @@ -94,7 +94,7 @@ contract CompoundModel is ModelInterface, ModelStorage, Initializable{ uint oldBalance = IERC20( token(0) ).balanceOf( address( this ) ); CTokenInterface( _cToken ).redeemUnderlying( amount ); uint newBalance = IERC20( token(0) ).balanceOf( address( this ) ); - require(newBalance.sub( oldBalance ) > 0, "MODEL : REDEEM BALANCE IS ZERO"); + require( newBalance.sub( oldBalance ) > 0, "MODEL : REDEEM BALANCE IS ZERO"); IERC20( token( 0 ) ).safeTransfer( to, newBalance.sub( oldBalance ) ); emit Withdraw( amount, forge(), block.timestamp); diff --git a/test/unit/Forge/saver.behavior.ts b/test/unit/Forge/saver.behavior.ts index 50c234d..2c05b7d 100644 --- a/test/unit/Forge/saver.behavior.ts +++ b/test/unit/Forge/saver.behavior.ts @@ -1,4 +1,5 @@ import { expect } from "chai" +import { BigNumber } from "ethers"; import { ethers, network } from "hardhat"; import { Tokens } from "../../shared/mockInfo"; import { ethToWei } from '../../shared/utils' @@ -45,22 +46,33 @@ export function saverBehavior(): void { const blockInfo = await ethers.provider.getBlock(blockNumber) const account = this.signers.accountDai const forge = this.contracts.forge - const daiContract = this.contracts.daiContract; + const daiContract = this.contracts.daiContract const startTimestamp = blockInfo.timestamp + 25 *60 * 60 const saverIndex = await forge.connect(account).countByAccount(account.address) const balance = await daiContract.balanceOf( account.address ); - + await daiContract.connect(account).approve(forge.address, ethToWei("100000")) await expect(forge - .connect(account).craftingSaver(balance.div(3).toString(), startTimestamp, 2, 4)) + .connect(account).craftingSaver( ethToWei("1") , startTimestamp, 2, 4)) + .to.emit(forge, 'CraftingSaver') + .withArgs(account.address, saverIndex, ethToWei("1") ) + + const saverIndex2 = await forge.connect(account).countByAccount(account.address) + await expect(forge + .connect(account).craftingSaver( ethToWei("1") , startTimestamp, 2, 4)) .to.emit(forge, 'CraftingSaver') - .withArgs(account.address, saverIndex, balance.div(3).toString()) + .withArgs(account.address, saverIndex2, ethToWei("1") ) }) it('should Success addDeposit', async function() { - await expect(this.contracts.forge.connect(this.signers.accountDai).addDeposit(0, 1000000)).to.emit(this.contracts.forge, 'AddDeposit').withArgs(this.signers.accountDai.address, 0, 1000000) + const account = this.signers.accountDai + const forge = this.contracts.forge + const daiContract = this.contracts.daiContract; + const balance = await daiContract.balanceOf( account.address ); + // await expect(forge.connect(account).addDeposit(0, balance.div(3).toString())).to.emit(forge, 'AddDeposit').withArgs(account.address, 0, balance.div(3).toString()) + await expect(forge.connect(account).addDeposit(0, ethToWei("1"))).to.emit(forge, 'AddDeposit').withArgs(account.address, 0, ethToWei("1")) }) it('should Revert withdraw Not yet', async function() { @@ -75,21 +87,27 @@ export function saverBehavior(): void { const forge = this.contracts.forge; const account = this.signers.accountDai - const saver = await forge.saver(account.address, 0); const blockNumber = await ethers.provider.getBlockNumber() const blockInfo = await ethers.provider.getBlock(blockNumber) - const daiContract = this.contracts.daiContract; - - const withdrawable = await forge.connect(account).withdrawable(account.address, 0); - - const _withdrawValues = await forge.connect(account)._withdrawValues(account.address, 0, withdrawable.toString(), false ); - - await forge.connect(account).withdraw(0, withdrawable.toString()) + await expect(forge.connect(account).withdraw(0, withdrawable.toString())).emit(forge, "Withdraw") }) it('should Success terminateSaver', async function() { + await network.provider.send("evm_increaseTime", [306400]); + await network.provider.send("evm_mine") + + const forge = this.contracts.forge; + const compoundModel = this.contracts.compoundModel; + const account = this.signers.accountDai + const daiContract = this.contracts.daiContract + + await daiContract.connect(account).transfer(compoundModel.address, ethToWei("1")); + const saver = await forge.saver(account.address, 0); + + const _withdrawValues = await forge.connect(account)._withdrawValues(account.address, 0, BigNumber.from(saver.mint).sub(saver.released).toString(), true ); + await this.contracts.forge.connect(this.signers.accountDai).terminateSaver(0) await expect(this.contracts.forge.connect(this.signers.accountDai).terminateSaver(0)).to.be.reverted await expect(this.contracts.forge.connect(this.signers.accountDai).withdraw(0, 100)).to.be.reverted From 7255cf716eb3339f0587c4942e3bb5d1fd0be158 Mon Sep 17 00:00:00 2001 From: 0xViktor Date: Sun, 15 Aug 2021 18:22:57 +0900 Subject: [PATCH 04/16] Hard Work Now! For Punkers by 0xViktor... - Fee structure reform. --- contracts/Forge.sol | 81 ++++++++++++++------------- contracts/Variables.sol | 12 ++-- test/unit/Forge/saver.behavior.ts | 24 ++------ test/unit/Variables/setup.behavior.ts | 24 ++++---- 4 files changed, 65 insertions(+), 76 deletions(-) diff --git a/contracts/Forge.sol b/contracts/Forge.sol index cb651a5..7d3ad18 100644 --- a/contracts/Forge.sol +++ b/contracts/Forge.sol @@ -47,7 +47,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ __decimals = decimals_; _token = token_; - _tokenUnit = 1000 * 10**decimals_; + _tokenUnit = 10**decimals_; _count = 0; @@ -159,10 +159,11 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ uint256 mint = 0; uint256 i = index; + { // Avoid Stack Too Deep issue i = i + 0; - mint = amount.mul(exchangeRate()).div(_tokenUnit); + mint = _exchangeToLp(amount); _mint(msg.sender, mint); autoStake( mint ); } @@ -194,7 +195,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * @param hopeUnderlying Forge's LP Token Amount */ function withdrawUnderlying(uint256 index, uint256 hopeUnderlying) public override nonReentrant returns (bool){ - return withdraw(index, hopeUnderlying.mul(exchangeRate()).div(_tokenUnit)); + return withdraw(index, _exchangeToLp(hopeUnderlying)); } /** @@ -209,22 +210,16 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ function withdraw(uint256 index, uint256 hope) public override nonReentrant returns (bool){ Saver memory s = saver(msg.sender, index); uint256 withdrawablePlp = withdrawable(msg.sender, index); - require(s.status < 2, "FORGE : Terminated Saver"); require(withdrawablePlp >= hope, "FORGE : Insufficient Amount"); - uint256 i = index; - { - // For LP Tokens - i = i + 0; - autoUnstake(hope); - _burn(msg.sender, hope); - } - // TODO Confirm withdrawal of currency after use of balance. /* for Underlying ERC20 token */ + + uint256 i = index; { i = i + 0; + autoUnstake(hope); ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, i, hope, false); _savers[msg.sender][i].released += hope; @@ -236,6 +231,8 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ _withdrawTo(amountOfWithdraw, msg.sender); _withdrawTo(amountOfServiceFee, _variables.treasury()); + + _burn(msg.sender, hope); } return true; @@ -253,22 +250,15 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ require(s.status < 2, "FORGE : Already Terminated or Completed"); uint256 i = index; - /* for pLP token */ + uint256 hope = s.mint.sub(s.released); + { i = i + 0; - uint256 hope = s.mint.sub(s.released); autoUnstake( hope ); - /* If the amount is already withdrawn and the remaining amount is less than the fee, it will be reverted. */ - _burn(msg.sender, hope); - } - - /* for Underlying ERC20 token */ - { - i = i + 0; - ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, i, s.mint.sub(s.released), true); + ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, i, hope, true); _savers[msg.sender][i].status = 2; - _savers[msg.sender][i].released += s.mint.sub(s.released); + _savers[msg.sender][i].released += hope; _savers[msg.sender][i].relAmount += amountOfWithdraw; _savers[msg.sender][i].updatedTimestamp = block.timestamp; @@ -278,6 +268,8 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ _withdrawTo(amountOfWithdraw, msg.sender); /* service fee is charged. */ _withdrawTo(amountOfServiceFee, _variables.treasury()); + + _burn(msg.sender, hope); } return true; @@ -289,10 +281,10 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * @return the exchange rate of ERC20 Token to pLP token */ function exchangeRate() public view override returns (uint256) { - return ModelInterface(_model).underlyingBalanceWithInvestment() == 0 ? _tokenUnit : + return totalVolume() == 0 ? _tokenUnit : _tokenUnit .mul( totalSupply() ) - .div( ModelInterface(_model).underlyingBalanceWithInvestment() ); + .div( totalVolume() ); } /** @@ -353,7 +345,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ */ function _withdrawTo(uint256 amount, address account) private { if (amount != 0) - ModelInterface(modelAddress()).withdrawTo(amount, account); + ModelInterface(_model).withdrawTo(amount, account); } /** @@ -371,33 +363,34 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ Saver memory s = saver(account, index); uint256 fm = _variables.feeMultiplier(); - uint256 amount = hope.mul(_tokenUnit).div(exchangeRate()); + uint256 amount = _exchangeToUnderlying(hope); uint256 successFee = _successFee(s, hope); - uint256 managementFee = _managementFee(s, hope); + uint256 serviceFee = _serviceFee(s, hope).mul(isTerminate ? fm : 100).div(100); - amountOfFee = successFee.add(managementFee).mul(isTerminate ? fm : 100).div(100); + amountOfFee = successFee.add(serviceFee); amountOfWithdraw = amount.sub(amountOfFee); } - function profit( address account, uint index ) public view returns(uint256){ - Saver memory s = saver(account, index); - uint256 balance = s.mint.mul(_tokenUnit).div(exchangeRate()); + function _profit( Saver memory s ) private view returns(uint256){ + uint256 allValueToUnderlying = _exchangeToUnderlying(s.mint); uint256 accAmount = s.accAmount; - return balance.sub(accAmount); + if( allValueToUnderlying > accAmount ){ + allValueToUnderlying.sub(accAmount); + } + return 0; } function _successFee(Saver memory s, uint256 hope) public view returns (uint256 successFee){ uint256 sf = _variables.successFee(); - uint256 balance = s.mint.mul(_tokenUnit).div(exchangeRate()); - uint256 accAmount = s.accAmount; - successFee = balance.sub(accAmount).mul(hope).mul(sf).div(s.mint).div(100); + uint256 profit = _profit( s ); + successFee = profit > 0 ? profit.mul(hope).mul(sf).div(100).div(s.mint) : 0; } - function _managementFee(Saver memory s, uint256 hope) public view returns (uint256 managementFee){ - uint256 mf = _variables.managementFee(); + function _serviceFee(Saver memory s, uint256 hope) public view returns (uint256 serviceFee){ + uint256 mf = _variables.serviceFee(); uint256 period = block.timestamp.sub(s.createTimestamp); - uint256 balance = hope.mul(_tokenUnit).div(exchangeRate()); - managementFee = balance.mul(period).mul(mf).div(SECONDS_YEAR).div(100); + uint256 amount = _exchangeToUnderlying(hope); + serviceFee = amount.mul(period).mul(mf).div(SECONDS_YEAR).div(100); } function autoStake(uint256 hope) public { @@ -433,4 +426,12 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ function decimals() public view override returns (uint8) { return __decimals; } + + function _exchangeToUnderlying( uint256 amount ) public view returns( uint256 ){ + return amount.mul(_tokenUnit).div(exchangeRate()); + } + + function _exchangeToLp( uint256 amount ) public view returns( uint256 ){ + return amount.mul(exchangeRate()).div(_tokenUnit); + } } diff --git a/contracts/Variables.sol b/contracts/Variables.sol index e8d2dfc..58b6dbc 100644 --- a/contracts/Variables.sol +++ b/contracts/Variables.sol @@ -10,7 +10,7 @@ contract Variables is Ownable{ address private _initializer; uint256 private _successFee; - uint256 private _managementFee; + uint256 private _serviceFee; uint256 private _feeMultiplier; address private _treasury; @@ -21,7 +21,7 @@ contract Variables is Ownable{ function initialize( address storage_) public override initializer{ Ownable.initialize(storage_); _successFee= 20; - _managementFee = 1; + _serviceFee = 1; _feeMultiplier = 200; emit Initialize(); } @@ -31,9 +31,9 @@ contract Variables is Ownable{ _successFee = setSuccessFee_; } - function setManagementFee( uint256 managementFee_ ) public OnlyGovernance { - require( 0 <= managementFee_ && managementFee_ <= 2, "VARIABLES : ManagementFee range from 0 to 2." ); - _managementFee = managementFee_; + function setServiceFee( uint256 serviceFee_ ) public OnlyGovernance { + require( 0 <= serviceFee_ && serviceFee_ <= 2, "VARIABLES : ServiceFee range from 0 to 2." ); + _serviceFee = serviceFee_; } function setFeeMultiplier( uint256 feeMultiplier_ ) public OnlyGovernance { @@ -53,7 +53,7 @@ contract Variables is Ownable{ function successFee() public view returns( uint256 ){ return _successFee; } - function managementFee() public view returns( uint256 ){ return _managementFee; } + function serviceFee() public view returns( uint256 ){ return _serviceFee; } function feeMultiplier() public view returns( uint256 ){ return _feeMultiplier; } diff --git a/test/unit/Forge/saver.behavior.ts b/test/unit/Forge/saver.behavior.ts index 2c05b7d..4bb911d 100644 --- a/test/unit/Forge/saver.behavior.ts +++ b/test/unit/Forge/saver.behavior.ts @@ -57,20 +57,17 @@ export function saverBehavior(): void { .connect(account).craftingSaver( ethToWei("1") , startTimestamp, 2, 4)) .to.emit(forge, 'CraftingSaver') .withArgs(account.address, saverIndex, ethToWei("1") ) - - const saverIndex2 = await forge.connect(account).countByAccount(account.address) - await expect(forge - .connect(account).craftingSaver( ethToWei("1") , startTimestamp, 2, 4)) - .to.emit(forge, 'CraftingSaver') - .withArgs(account.address, saverIndex2, ethToWei("1") ) - + }) + it('should Success addDeposit', async function() { const account = this.signers.accountDai const forge = this.contracts.forge const daiContract = this.contracts.daiContract; const balance = await daiContract.balanceOf( account.address ); + + const saver = await forge.saver(account.address, 0); // await expect(forge.connect(account).addDeposit(0, balance.div(3).toString())).to.emit(forge, 'AddDeposit').withArgs(account.address, 0, balance.div(3).toString()) await expect(forge.connect(account).addDeposit(0, ethToWei("1"))).to.emit(forge, 'AddDeposit').withArgs(account.address, 0, ethToWei("1")) }) @@ -87,26 +84,15 @@ export function saverBehavior(): void { const forge = this.contracts.forge; const account = this.signers.accountDai - - const blockNumber = await ethers.provider.getBlockNumber() - const blockInfo = await ethers.provider.getBlock(blockNumber) const withdrawable = await forge.connect(account).withdrawable(account.address, 0); await expect(forge.connect(account).withdraw(0, withdrawable.toString())).emit(forge, "Withdraw") }) it('should Success terminateSaver', async function() { - await network.provider.send("evm_increaseTime", [306400]); - await network.provider.send("evm_mine") const forge = this.contracts.forge; - const compoundModel = this.contracts.compoundModel; const account = this.signers.accountDai - const daiContract = this.contracts.daiContract - - await daiContract.connect(account).transfer(compoundModel.address, ethToWei("1")); - const saver = await forge.saver(account.address, 0); - - const _withdrawValues = await forge.connect(account)._withdrawValues(account.address, 0, BigNumber.from(saver.mint).sub(saver.released).toString(), true ); + var saver = await forge.saver(account.address, 0); await this.contracts.forge.connect(this.signers.accountDai).terminateSaver(0) await expect(this.contracts.forge.connect(this.signers.accountDai).terminateSaver(0)).to.be.reverted diff --git a/test/unit/Variables/setup.behavior.ts b/test/unit/Variables/setup.behavior.ts index 905ca98..c534beb 100644 --- a/test/unit/Variables/setup.behavior.ts +++ b/test/unit/Variables/setup.behavior.ts @@ -28,7 +28,8 @@ export function setUpBehavior(): void { it("should Revert setTreasury Address Not Admin", async function () { const variables = this.contracts.variables; - const treasury = this.contracts.treasury; + // const treasury = this.contracts.treasury; + const treasury = this.contracts.opTreasury; const account1 = this.signers.account1; await expect( variables.connect(account1).setTreasury(treasury.address) @@ -46,7 +47,8 @@ export function setUpBehavior(): void { it("should Success setTreasury Address", async function () { const variables = this.contracts.variables; - const treasury = this.contracts.treasury; + // const treasury = this.contracts.treasury; + const treasury = this.contracts.opTreasury; const owner = this.signers.owner; await variables.connect(owner).setTreasury(treasury.address); await expect(await variables.treasury()).to.be.eq(treasury.address); @@ -79,31 +81,31 @@ export function setUpBehavior(): void { await expect(variables.connect(gov).setSuccessFee(fee)).to.be.reverted; }); - it("should Revert setManagementFee Address Not Gov and Admin", async function () { + it("should Revert setServiceFee Address Not Gov and Admin", async function () { const variables = this.contracts.variables; const account1 = this.signers.account1; - await expect(variables.connect(account1).setManagementFee(10)).to.be.reverted; + await expect(variables.connect(account1).setServiceFee(10)).to.be.reverted; }); - it("should Revert setManagementFee Address Admin", async function () { + it("should Revert setServiceFee Address Admin", async function () { const variables = this.contracts.variables; const owner = this.signers.owner; const fee = 1; - await expect(variables.connect(owner).setManagementFee(fee)).to.be.reverted; + await expect(variables.connect(owner).setServiceFee(fee)).to.be.reverted; }); - it("should Success setManagementFee", async function () { + it("should Success setServiceFee", async function () { const variables = this.contracts.variables; const gov = this.signers.gov; const fee = 1; - await variables.connect(gov).setManagementFee(fee); - await expect(await variables.managementFee()).to.be.eq(fee); + await variables.connect(gov).setServiceFee(fee); + await expect(await variables.serviceFee()).to.be.eq(fee); }); - it("should Revert setManagementFee Overflow Valeus", async function () { + it("should Revert setServiceFee Overflow Valeus", async function () { const variables = this.contracts.variables; const gov = this.signers.gov; - await expect(variables.connect(gov).setManagementFee(10)).to.be.reverted; + await expect(variables.connect(gov).setServiceFee(10)).to.be.reverted; }); it("should Revert setFeeMultiplier Address Not Gov and Admin", async function () { From e1cf6300880b065b38ebfed66f227c717ea6bf5b Mon Sep 17 00:00:00 2001 From: 0xViktor Date: Mon, 16 Aug 2021 15:20:23 +0900 Subject: [PATCH 05/16] Hard Work Now! For Punkers by 0xViktor... - Exchange Rate Values Reverse --- contracts/Forge.sol | 23 +++++++++-------- contracts/ForgeStorage.sol | 5 ++++ contracts/ModelStorage.sol | 34 ++++++++++++------------- contracts/RecoveryFund.sol | 1 - contracts/interfaces/ModelInterface.sol | 4 +++ contracts/models/CompoundModel.sol | 14 +++++----- 6 files changed, 45 insertions(+), 36 deletions(-) diff --git a/contracts/Forge.sol b/contracts/Forge.sol index 7d3ad18..9425f5d 100644 --- a/contracts/Forge.sol +++ b/contracts/Forge.sol @@ -62,6 +62,8 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ function setModel(address model_) public OnlyAdminOrGovernance returns (bool){ require( model_ != address(0), "FORGE : Model address is zero" ); require( Address.isContract(model_), "FORGE : Model address must be the contract address."); + require( ModelInterface(model_).token() == _token, "FORGE : Model has Token address is not equals"); + require( ModelInterface(model_).forge() == address(this), "FORGE : Model has Forge address is not equals this address"); require( _model != model_, "FORGE : Current Model"); if( _model != address(0) ){ @@ -121,9 +123,10 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * @param count How often do you want to receive. * @param interval Number of times to receive (unit: 1 day) */ - function craftingSaver( uint256 amount, uint256 startTimestamp, uint256 count, uint256 interval ) public override returns (bool) { + function craftingSaver( uint256 amount, uint256 startTimestamp, uint256 count, uint256 interval ) public override onlyNormalUser returns (bool) { require( amount > 0 && count > 0 && interval > 0 && startTimestamp > block.timestamp.add(24 * 60 * 60), "FORGE : Invalid Parameters"); uint256 index = countByAccount(msg.sender); + require( index < 10, "FORGE : Too many crafting Account"); _savers[msg.sender].push( Saver( @@ -154,7 +157,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * @param index Saver's index * @param amount ERC20 Amount */ - function addDeposit(uint256 index, uint256 amount) public override nonReentrant returns (bool){ + function addDeposit(uint256 index, uint256 amount) public override nonReentrant onlyNormalUser returns (bool){ require( saver(msg.sender, index).status < 2, "FORGE : Terminated Saver" ); uint256 mint = 0; @@ -194,7 +197,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * @param index Saver's index * @param hopeUnderlying Forge's LP Token Amount */ - function withdrawUnderlying(uint256 index, uint256 hopeUnderlying) public override nonReentrant returns (bool){ + function withdrawUnderlying(uint256 index, uint256 hopeUnderlying) public override nonReentrant onlyNormalUser returns (bool){ return withdraw(index, _exchangeToLp(hopeUnderlying)); } @@ -207,7 +210,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * @param index Saver's index * @param hope Forge's LP Token Amount */ - function withdraw(uint256 index, uint256 hope) public override nonReentrant returns (bool){ + function withdraw(uint256 index, uint256 hope) public override nonReentrant onlyNormalUser returns (bool){ Saver memory s = saver(msg.sender, index); uint256 withdrawablePlp = withdrawable(msg.sender, index); require(s.status < 2, "FORGE : Terminated Saver"); @@ -245,7 +248,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * * @param index Saver's index */ - function terminateSaver(uint256 index) public override nonReentrant returns (bool){ + function terminateSaver(uint256 index) public override nonReentrant onlyNormalUser returns (bool){ Saver memory s = saver(msg.sender, index); require(s.status < 2, "FORGE : Already Terminated or Completed"); @@ -281,10 +284,10 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * @return the exchange rate of ERC20 Token to pLP token */ function exchangeRate() public view override returns (uint256) { - return totalVolume() == 0 ? _tokenUnit : + return totalSupply() == 0 ? _tokenUnit : _tokenUnit - .mul( totalSupply() ) - .div( totalVolume() ); + .mul( totalVolume() ) + .div( totalSupply() ); } /** @@ -428,10 +431,10 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ } function _exchangeToUnderlying( uint256 amount ) public view returns( uint256 ){ - return amount.mul(_tokenUnit).div(exchangeRate()); + return amount.mul(exchangeRate()).div(_tokenUnit); } function _exchangeToLp( uint256 amount ) public view returns( uint256 ){ - return amount.mul(exchangeRate()).div(_tokenUnit); + return amount.mul(_tokenUnit).div(exchangeRate()); } } diff --git a/contracts/ForgeStorage.sol b/contracts/ForgeStorage.sol index b78f74d..a794750 100644 --- a/contracts/ForgeStorage.sol +++ b/contracts/ForgeStorage.sol @@ -22,4 +22,9 @@ abstract contract ForgeStorage{ uint internal _count; uint256[50] private ______gap; + + modifier onlyNormalUser{ + require(msg.sender == tx.origin, "FORGE : Not Wokring this Function SmartContract"); + _; + } } \ No newline at end of file diff --git a/contracts/ModelStorage.sol b/contracts/ModelStorage.sol index 51c81a1..3edf0ec 100644 --- a/contracts/ModelStorage.sol +++ b/contracts/ModelStorage.sol @@ -2,9 +2,14 @@ pragma solidity >=0.5.0 <0.9.0; pragma experimental ABIEncoderV2; -contract ModelStorage{ +import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; +import "@openzeppelin/contracts/utils/Address.sol"; +import "./interfaces/ModelInterface.sol"; +import "./interfaces/ModelInterface.sol"; + +abstract contract ModelStorage is ModelInterface{ - address [] private _tokens; + address private _token; address private _forge; /** @@ -16,13 +21,12 @@ contract ModelStorage{ } /** - * @dev Add a 'token' ERC20 to be used in the model. + * @dev set a 'token' ERC20 to be used in the model. */ - function addToken( address token_ ) internal returns( bool ){ - for( uint i = 0 ; i < tokens().length ; i++ ){ - if( token( i ) == token_ ){ return false; } - } - _tokens.push( token_ ); + function setToken( address token_ ) internal returns( bool ){ + require( Address.isContract(token_), "MODEL : the address is not contract address" ); + require( IERC20(token_).totalSupply() > 0, "MODEL : the address is not ERC20 Token" ); + _token = token_; return true; } @@ -32,6 +36,7 @@ contract ModelStorage{ * IMPORTANT: 'Forge' should be non-replaceable by default. */ function setForge( address forge_ ) internal returns( bool ){ + require( Address.isContract(forge_), "MODEL : the address is not contract address" ); _forge = forge_; return true; } @@ -39,21 +44,14 @@ contract ModelStorage{ /** * @dev Returns the address of the token as 'index'. */ - function token( uint index ) public view returns( address ){ - return _tokens[index]; - } - - /** - * @dev Returns a list of addresses of tokens. - */ - function tokens() public view returns( address [] memory ){ - return _tokens; + function token() public view override returns( address ){ + return _token; } /** * @dev Returns the address of Forge. */ - function forge() public view returns( address ){ + function forge() public view override returns( address ){ return _forge; } } \ No newline at end of file diff --git a/contracts/RecoveryFund.sol b/contracts/RecoveryFund.sol index 8987a91..1938be0 100644 --- a/contracts/RecoveryFund.sol +++ b/contracts/RecoveryFund.sol @@ -22,7 +22,6 @@ contract RecoveryFund is ERC20Pausable, ReentrancyGuard { _initialMint(address(0xe1cd21e5d6f4323E91dA943B0A4F1732acC7a138), 1213998517300000000000000); _initialMint(address(0xf49a12fE6a05bdFc7C0cd4FE2A19724CCFbA18d3), 898535671700000000000000); _initialMint(address(0x82dc92b01c7fF54911842956083795f60f6F64f4), 673946680643589900000000); - _initialMint(address(0x8ed32Ed24303092c016Cdb24d51e153AD88c4875), 89858957570000000000000); _initialMint(address(0xf2CcE4EcB119038dA9F4E18F82E07bb555FbAe2C), 77613054250000000000000); _initialMint(address(0x5522234194F499F1DBF2E26C6eBD802bc2Cd9A2f), 77095278220000000000000); _initialMint(address(0x896b94f4f27f12369698C302e2049cAe86936BbB), 62897497019999995000000); diff --git a/contracts/interfaces/ModelInterface.sol b/contracts/interfaces/ModelInterface.sol index b5131c6..dbb2a98 100644 --- a/contracts/interfaces/ModelInterface.sol +++ b/contracts/interfaces/ModelInterface.sol @@ -49,5 +49,9 @@ interface ModelInterface{ * Emits a {Withdraw} event. */ function withdrawTo( uint256 amount, address to ) external; + + function forge() external view returns( address ); + + function token() external view returns( address ); } diff --git a/contracts/models/CompoundModel.sol b/contracts/models/CompoundModel.sol index 0409b55..11ac414 100644 --- a/contracts/models/CompoundModel.sol +++ b/contracts/models/CompoundModel.sol @@ -43,7 +43,7 @@ contract CompoundModel is ModelInterface, ModelStorage, Initializable{ address comptroller_, address uRouterV2_ ) public initializer onlyCreator { - addToken( token_ ); + setToken( token_ ); setForge( forge_ ); _cToken = cToken_; _comp = comp_; @@ -54,7 +54,7 @@ contract CompoundModel is ModelInterface, ModelStorage, Initializable{ } function underlyingBalanceInModel() public override view returns ( uint256 ){ - return IERC20( token( 0 ) ).balanceOf( address( this ) ); + return IERC20( token() ).balanceOf( address( this ) ); } function underlyingBalanceWithInvestment() public override view returns ( uint256 ){ @@ -64,7 +64,7 @@ contract CompoundModel is ModelInterface, ModelStorage, Initializable{ function invest() public override { // Hard Work Now! For Punkers by 0xViktor - IERC20( token( 0 ) ).safeApprove( _cToken, underlyingBalanceInModel() ); + IERC20( token() ).safeApprove( _cToken, underlyingBalanceInModel() ); emit Invest( underlyingBalanceInModel(), block.timestamp ); CTokenInterface( _cToken ).mint( underlyingBalanceInModel() ); } @@ -91,11 +91,11 @@ contract CompoundModel is ModelInterface, ModelStorage, Initializable{ function withdrawTo( uint256 amount, address to ) public OnlyForge override{ // Hard Work Now! For Punkers by 0xViktor - uint oldBalance = IERC20( token(0) ).balanceOf( address( this ) ); + uint oldBalance = IERC20( token() ).balanceOf( address( this ) ); CTokenInterface( _cToken ).redeemUnderlying( amount ); - uint newBalance = IERC20( token(0) ).balanceOf( address( this ) ); + uint newBalance = IERC20( token() ).balanceOf( address( this ) ); require( newBalance.sub( oldBalance ) > 0, "MODEL : REDEEM BALANCE IS ZERO"); - IERC20( token( 0 ) ).safeTransfer( to, newBalance.sub( oldBalance ) ); + IERC20( token() ).safeTransfer( to, newBalance.sub( oldBalance ) ); emit Withdraw( amount, forge(), block.timestamp); } @@ -119,7 +119,7 @@ contract CompoundModel is ModelInterface, ModelStorage, Initializable{ address[] memory path = new address[](3); path[0] = address(_comp); path[1] = IUniswapV2Router( _uRouterV2 ).WETH(); - path[2] = address( token( 0 ) ); + path[2] = address( token() ); IUniswapV2Router(_uRouterV2).swapExactTokensForTokens( balance, From b77f71051feab1237ca452e6d8963b595e3ad439 Mon Sep 17 00:00:00 2001 From: 0xViktor Date: Wed, 18 Aug 2021 18:18:44 +0900 Subject: [PATCH 06/16] Hard Work Now! For Punkers by 0xViktor... - Add RecoveryFund_V2 --- contracts/FairLaunch.sol | 166 ------------------ contracts/Forge.sol | 8 +- contracts/RecoveryFund_V2.sol | 118 +++++++++++++ test/unit/RecoveryFund/initialize.behavior.ts | 3 +- test/unit/RecoveryFund/setup.behavior.ts | 1 - test/unit/index.ts | 56 +++--- types/index.ts | 2 - 7 files changed, 142 insertions(+), 212 deletions(-) delete mode 100644 contracts/FairLaunch.sol create mode 100644 contracts/RecoveryFund_V2.sol diff --git a/contracts/FairLaunch.sol b/contracts/FairLaunch.sol deleted file mode 100644 index 01c7d34..0000000 --- a/contracts/FairLaunch.sol +++ /dev/null @@ -1,166 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.5.0 <0.9.0; - -import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; -import "@openzeppelin/contracts/utils/math/SafeMath.sol"; -import "./interfaces/ForgeInterface.sol"; -import "./Ownable.sol"; -import "./Referral.sol"; -import "./Saver.sol"; - -/** Do Not Used This Contract */ -contract FairLaunch is Ownable, ReentrancyGuard{ - // using SafeERC20 for IERC20; - // using SafeMath for uint256; - - // event Deposit( uint256 blockNumber, uint256 blockTime, address account, uint256 amount, uint256 accAmount,uint256 totalAmount, uint256 cap, bytes12 referralCode); - // event Withdraw( uint256 blockNumber, uint256 blockTime, address account, uint256 amount, uint256 accAmount,uint256 totalAmount, uint256 cap, bytes12 referralCode); - - // uint256 constant private OPEN_TIMESTAMP = 1628553600; - // uint256 constant private CLOSED_TIMESTAMP = 1630022400; - - // Referral private _referral; - // IERC20 private _token; - // ForgeInterface private _forge; - - // uint256 private _count; - // uint256 private _decimals; - // uint256 private _cap; - // uint256 private _totalAmount; - // uint256 private _rewardCap; - // string private _name; - - // mapping( address=>bool ) _entered; - // mapping( address=>uint256 ) _indexes; - - // uint256 [] private _caps; - // uint256 [] private _capUpdateTimestamps; - - // function initialize( - // address storage_, - // address forge_, - // address token_, - // address referral_, - // uint8 decimals_, - // string memory name_ - // ) public initializer { - - // Ownable.initialize( storage_ ); - - // _forge = ForgeInterface(forge_); - // _token = IERC20(token_); - // _referral = Referral(referral_); - // _decimals = decimals_; - // _totalAmount = 0; - // _count = 0; - // _rewardCap = 70000 * 10**18; - // _name = name_; - - // _token.safeApprove(forge_, 2**256 - 1); - // } - - // function enter( uint256 amount ) public returns(bool){ - // return enter(amount, 0x0); - // } - - // function enter( uint256 amount, bytes12 ref ) public nonReentrant returns(bool) { - // require( block.timestamp > OPEN_TIMESTAMP, "FL : Not Opened yet"); - // require( _totalAmount + amount <= _cap, "FL : Amount Overflow" ); - // require( _token.allowance(msg.sender, address(this)) >= amount, "FL : Allowance Error"); - - // ( bool entered, uint index ) = isEntered( msg.sender ); - - // address issuer = _referral.validate( ref ); - // _totalAmount = _totalAmount.add(amount); - // _token.safeTransferFrom(msg.sender, address(this), amount); - - // if( entered ){ - // _forge.addDeposit(index, amount); - // }else{ - // _indexes[msg.sender] = _forge.countByAccount(address(this)); - // _entered[msg.sender] = true; - // if( ref == 0x0 ){ - // _count += 1; - // _forge.craftingSaver(amount, CLOSED_TIMESTAMP, 1, 1); - // }else{ - // require( issuer != address(0x0), "FL : Not Registry Ref Code"); - // _count += 1; - // _forge.craftingSaver(amount, CLOSED_TIMESTAMP, 1, 1, ref); - // } - // } - // ( entered, index ) = isEntered( msg.sender ); - // emit Deposit(block.number, block.timestamp, msg.sender, amount, _forge.saver(address(this), index).accAmount, _totalAmount, _cap, _forge.saver(address(this), index).ref); - - // return true; - // } - - // function exit() public nonReentrant returns(bool) { - // require(_entered[msg.sender], "FL : Not Entering"); - // uint index = _indexes[msg.sender]; - - // _entered[msg.sender] = false; - // _totalAmount = _totalAmount.sub(_forge.saver(address(this), index).accAmount); - // _count -= 1; - - // uint beforeBalanceOf = _token.balanceOf(address(this)); - // _forge.terminateSaver(index); - // uint afterBalanceOf = _token.balanceOf(address(this)); - // uint repayable = afterBalanceOf.sub(beforeBalanceOf); - // _token.safeTransfer(msg.sender, repayable); - - // emit Withdraw(block.number, block.timestamp, msg.sender, repayable, 0, _totalAmount, _cap, _forge.saver(address(this), index).ref); - - // return true; - // } - - // function isEntered( address account ) public view returns(bool, uint256){ - // return ( _entered[account], _indexes[account] ); - // } - - // function setCap( uint256 cap_ ) public OnlyAdmin returns(bool){ - // _cap = cap_; - // _caps.push(cap_); - // _capUpdateTimestamps.push(block.timestamp); - // return true; - // } - - // function cap() public view returns(uint256){ - // return _cap; - // } - - // function rewardCap() public view returns(uint256){ - // return _rewardCap; - // } - - // function count() public view returns(uint256){ - // return _count; - // } - - // function totalAmount() public view returns(uint256){ - // return _totalAmount; - // } - - // function decimals() public view returns(uint256){ - // return _decimals; - // } - - // function name() public view returns(string memory){ - // return _name; - // } - - // function caps() public view returns(uint256 [] memory, uint256 [] memory){ - // return ( _caps, _capUpdateTimestamps ); - // } - - // function withdrawable( address account ) public view returns(uint256 amount ){ - // ( bool entered, uint index ) = isEntered( msg.sender ); - // if( entered ){ - // Saver memory saver = _forge.saver( account, index ); - // amount = saver.mint.mul( 10**_decimals ).div( _forge.getExchangeRate() ); - // amount = amount.mul( 99 ).div(100); - // } - // } - -} \ No newline at end of file diff --git a/contracts/Forge.sol b/contracts/Forge.sol index 9425f5d..3f851d4 100644 --- a/contracts/Forge.sol +++ b/contracts/Forge.sol @@ -16,7 +16,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ using SafeMath for uint256; using SafeERC20 for IERC20; - uint256 constant SECONDS_DAY = 86400; + uint256 constant SECONDS_DAY = 1 days; uint256 constant SECONDS_YEAR = 31556952; constructor() ERC20("PunkFinance", "Forge") {} @@ -164,16 +164,12 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ uint256 i = index; { - // Avoid Stack Too Deep issue - i = i + 0; mint = _exchangeToLp(amount); _mint(msg.sender, mint); autoStake( mint ); } { - // Avoid Stack Too Deep issue - i = i + 0; _savers[msg.sender][i].mint += mint; _savers[msg.sender][i].accAmount += amount; _savers[msg.sender][i].updatedTimestamp = block.timestamp; @@ -221,7 +217,6 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ uint256 i = index; { - i = i + 0; autoUnstake(hope); ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, i, hope, false); @@ -256,7 +251,6 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ uint256 hope = s.mint.sub(s.released); { - i = i + 0; autoUnstake( hope ); ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, i, hope, true); diff --git a/contracts/RecoveryFund_V2.sol b/contracts/RecoveryFund_V2.sol new file mode 100644 index 0000000..57c8a9f --- /dev/null +++ b/contracts/RecoveryFund_V2.sol @@ -0,0 +1,118 @@ +// SPDX-License-Identifier: MIT +pragma solidity >=0.5.0 <0.9.0; +pragma experimental ABIEncoderV2; + +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; + +contract RecoveryFund_V2 is ERC20, ReentrancyGuard, Ownable { + using SafeERC20 for IERC20; + using SafeMath for uint256; + + event Refund( address account, uint256 refundAmount ); + + bool private open = false; + address public addressOfDAI = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); + uint256 public totalRefund; + + constructor() ERC20("RecoveryFund", "peUSD") { + _mint(address(0xe1cd21e5d6f4323E91dA943B0A4F1732acC7a138), 1213998517300000000000000); + _mint(address(0xf49a12fE6a05bdFc7C0cd4FE2A19724CCFbA18d3), 898535671700000000000000); + _mint(address(0x82dc92b01c7fF54911842956083795f60f6F64f4), 673946680643589900000000); + _mint(address(0xf2CcE4EcB119038dA9F4E18F82E07bb555FbAe2C), 77613054250000000000000); + _mint(address(0x5522234194F499F1DBF2E26C6eBD802bc2Cd9A2f), 77095278220000000000000); + _mint(address(0x896b94f4f27f12369698C302e2049cAe86936BbB), 62897497019999995000000); + _mint(address(0xac1f46771cF300bD4D43442960CD5e3d7476Ca34), 53912140300000000000000); + _mint(address(0xa52D39E63900992F36286953F884E014AC1F688D), 53905401290000000000000); + _mint(address(0xE5EC1103810217a3F83262C66c6a38a8e6387366), 53905401290000000000000); + _mint(address(0x577502784eDd9b0D84d08334c30D378975e8F5AC), 45751912140000000000000); + _mint(address(0x469Adaf766fb35F1a3c2569FE8C57a50F4B39131), 45355619800000000000000); + _mint(address(0x544Fc1845DF225679f5A939eF930837B8cBA0552), 44926783590000000000000); + _mint(address(0x4c3a58357f42a7741F61D2c7Fa3e7700996A5A60), 44920044570000000000000); + _mint(address(0xD9F0834E7Ca9B9262Df3189b850eBf9514027e82), 38383197560000000000000); + _mint(address(0x3E269201B45622c88E1cca18CE9A02609d4D2A54), 35934687850000000000000); + _mint(address(0xe9017c8De5040968D9752A18d805cD2A983E558c), 32003782980000000000000); + _mint(address(0x55d72CbcbA1Ab5C784bC52641D16c613E3b9BAD4), 31755408880000000000000); + _mint(address(0xf76CF36f638c7bCD83f4756beDb86243D98982F9), 29253624400000000000000); + _mint(address(0x29227FB595D091bcA244E76201c0dd50641D96C8), 27239560830000002000000); + _mint(address(0x2572a193DA3DEf3BAeA04cB18e06A52186aC1a98), 26954824385000000000000); + _mint(address(0x81a7E267Fd8339a01beb175f5A3d644FcF0B48Dc), 24234548190999998000000); + _mint(address(0x67D33CF1C7c699078f86D517A5a1cd1444A1E85C), 23536011086000000000000); + _mint(address(0x78d58Cc37cDAaBFde72c9a45B57Acbc3aBc3D7C9), 22546913770000000000000); + _mint(address(0x9dcAfb9A5A2A948ee7A70cDce6abC46322171ACD), 22463391790000000000000); + _mint(address(0x28C01B64F2492D82fd58FEECF8c71603c769BB4A), 22458456610000000000000); + _mint(address(0xE01f94F635028394cB15B572179DaEbbfC231761), 20217052610000000000000); + _mint(address(0x58e316d51aDA862d71fBa22ADA6f56D00186Af13), 18190064880000000000000); + _mint(address(0x09CF915e195aF33FA7B932C253352Ae9FBdB0106), 18090301810000000000000); + _mint(address(0x833A11B69873151fBA6D14f455db5392542825Ca), 17970855662000000000000); + _mint(address(0xffD851CFE0A50E1f1Cf2739Fc6D97754d49943a0), 17303601743000000000000); + _mint(address(0xe43458e1F964EEf5F707E81fb6f16Ab27c261071), 16930658390000000000000); + _mint(address(0x185C034Bdc0ca1d66Dc75650D33DEA190315595b), 14422499490000000000000); + _mint(address(0x0056BcFe33f5c6DfA62B6d3d3CF5A957429828BB), 14104489260000000000000); + _mint(address(0x963aD4Cc7798601cC36526162FDa203C21E90872), 13602254460000000000000); + _mint(address(0x6FdED91556cfBf602f05e279d270327382d9206E), 13471296060000000000000); + _mint(address(0xbEcB3451c1724Ce87801e6A09b547bB704e47884), 13380094687000000000000); + _mint(address(0x00F928a6aDC7c345EF919fe5A8Be2458580eF9f6), 11227034090000000000000); + _mint(address(0x241a119A8C634b58E78Ec4Ce9bF786756f9FD310), 8985356717000000000000); + _mint(address(0xd6b4D675F749F951B706Da0dF2CF99f913143B0C), 8985356717000000000000); + _mint(address(0x956D079B656a3955AB4f2f596d1bbfd6F3Ae60dC), 5197498877000000000000); + _mint(address(0x53274F93Bcf4647C39E8Af160226736c037574e6), 5177541065000000000000); + _mint(address(0x57d6C4efB2cA6703674343d1a7c0492fba49A6f3), 4492678359000000000000); + _mint(address(0xA2F8e4744FD85E9089a337fed259F05dd864a91e), 4492678359000000000000); + _mint(address(0xDcbBf294D08F70571666f7d08BBDf93189341B0e), 4488634499000000000000); + _mint(address(0xc28466bBD0F2f519829fEaEBA61470A381c5d07b), 4348912651000000000000); + _mint(address(0x7c25bB0ac944691322849419DF917c0ACc1d379B), 4043410523000000000000); + _mint(address(0xf6cDbA86999372f8B9104234885edFDAA1Eb01D1), 3832978820000000300000); + _mint(address(0x72f6A96a49B41467eef03924211e422Eaf639e3f), 3813708894000000000000); + _mint(address(0x9412b1e09ad73Dff95D099B2d1eFCD389c1422cf), 2243161150000000000000); + _mint(address(0x27EF5f1Fa276E8394681A260F77208ccD0305013), 1827100390000000000000); + _mint(address(0xB1f72d694711FcE55C62eA97BD3739Ff11ceF986), 1790766736000000000000); + _mint(address(0xE81e90222E0BD7D114cce6495B57C307Df342373), 1682368129000000000000); + _mint(address(0x7bF4aef8731377eC73a53f5032092d1699c56526), 1349221073000000000000); + _mint(address(0x38Bc5196d8b21782372a843E5A505d9F457e6ff8), 1311525431000000000000); + _mint(address(0x7E1BeE475e5EFF0e0810cfd1a940c50d7294f15F), 1156118143000000000000); + _mint(address(0xac1886d1ef0a19aFb8392ae4231472d4bBF39c4F), 1118439356000000000000); + _mint(address(0xd40Cd4D9f9BcF3fE57E37bC88EbFDB6ad2244578), 1108871318000000000000); + _mint(address(0x3F8a2B94578D73AF4CBE63C38D2C481Cc7D09F98), 949716401200000000000); + _mint(address(0xf934a267C6fB6170FAf5940fb3c12033a1f14d74), 924521947800000000000); + _mint(address(0xEcc03538d9Af264725dABd36417441F7971c91F6), 898535671700000000000); + _mint(address(0x41833aAE8C10641045fFA17E2377919201A71dDb), 896733830900000000000); + _mint(address(0xe75D13941a26be0d5E5C1a104494eFC67dEAfb38), 693526172300000000000); + _mint(address(0x9b95F760A7eBCd6dFbEb91FACdA9B8ac2296820f), 633523800100000000000); + _mint(address(0x9bb64e40DCBe4645F99F0a9e2507b5A53795fa70), 580593317000000000000); + _mint(address(0x8eA035d0995F0568969020A00D1891d1D29c7517), 461972401500000000000); + _mint(address(0x5E87597C7eCD23bA209B5354881e4Cafd2FC4D28), 457250243900000000000); + _mint(address(0xa3eC8fB0aCc93d0BB8CA73B9e90FE57275855E94), 358417937300000000000); + _mint(address(0x392027fDc620d397cA27F0c1C3dCB592F27A4dc3), 314487485100000000000); + _mint(address(0xD47835d494AC1Bf8C140B6A04E69b67e75928022), 262821684000000000000); + _mint(address(0x8e0e3D4a18AA44904ebda87C5B42545d1cEC2e5f), 213698063100000000000); + _mint(address(0xB444b304C55fa9c6fa995a758CBd5a96cF84782B), 64620692110000000000); + _mint(address(0x8E1D10aaeF9c0C0D337Aa47022BF0d96D21b56B9), 49312416690000000000); + totalRefund = totalSupply(); + } + + function balanceOfDai() public view returns(uint256){ + return IERC20(addressOfDAI).balanceOf(address(this)); + } + + function openRefund() public onlyOwner { + require(!open, "REFUND : Already Opened"); + require( balanceOfDai() >= totalRefund, "REFUND : Not Yet Full RecoveryRefund" ); + open = true; + } + + function refund( uint256 refundAmount ) public nonReentrant returns(bool){ + require( open, "REFUND : Not Opened"); + require( balanceOf(msg.sender) >= refundAmount, "REFUND : sender's balance is insufficient" ); + + _burn( msg.sender, refundAmount ); + IERC20(addressOfDAI).safeTransfer(msg.sender, refundAmount); + + emit Refund( msg.sender, refundAmount ); + return false; + } + +} diff --git a/test/unit/RecoveryFund/initialize.behavior.ts b/test/unit/RecoveryFund/initialize.behavior.ts index 518290f..928cd77 100644 --- a/test/unit/RecoveryFund/initialize.behavior.ts +++ b/test/unit/RecoveryFund/initialize.behavior.ts @@ -27,7 +27,7 @@ export function initialBehavior(): void { it('should Check total refund', async function() { const recoveryFundMock = this.contracts.recoveryFundMock; - await expect( await recoveryFundMock.totalRefund() ).eq(BigNumber.from("4041503630429289895300000")) + await expect( await recoveryFundMock.totalRefund() ).eq(BigNumber.from("4041503630429289895300000").sub("89858957570000000000000")) }) it('should Check peUSD balances', async function(){ @@ -35,7 +35,6 @@ export function initialBehavior(): void { await expect( await recoveryFundMock.balanceOf("0xe1cd21e5d6f4323E91dA943B0A4F1732acC7a138")).eq( BigNumber.from("1213998517300000000000000")); await expect( await recoveryFundMock.balanceOf("0xf49a12fE6a05bdFc7C0cd4FE2A19724CCFbA18d3")).eq( BigNumber.from("898535671700000000000000")); await expect( await recoveryFundMock.balanceOf("0x82dc92b01c7fF54911842956083795f60f6F64f4")).eq( BigNumber.from("673946680643589900000000")); - await expect( await recoveryFundMock.balanceOf("0x8ed32Ed24303092c016Cdb24d51e153AD88c4875")).eq( BigNumber.from("89858957570000000000000")); await expect( await recoveryFundMock.balanceOf("0xf2CcE4EcB119038dA9F4E18F82E07bb555FbAe2C")).eq( BigNumber.from("77613054250000000000000")); await expect( await recoveryFundMock.balanceOf("0x5522234194F499F1DBF2E26C6eBD802bc2Cd9A2f")).eq( BigNumber.from("77095278220000000000000")); await expect( await recoveryFundMock.balanceOf("0x896b94f4f27f12369698C302e2049cAe86936BbB")).eq( BigNumber.from("62897497019999995000000")); diff --git a/test/unit/RecoveryFund/setup.behavior.ts b/test/unit/RecoveryFund/setup.behavior.ts index 66d6c1d..106878e 100644 --- a/test/unit/RecoveryFund/setup.behavior.ts +++ b/test/unit/RecoveryFund/setup.behavior.ts @@ -41,7 +41,6 @@ export function setUpBehavior(): void { await expect( await recoveryFundMock.balanceOf("0xe1cd21e5d6f4323E91dA943B0A4F1732acC7a138")).eq( BigNumber.from("1213998517300000000000000").sub(BigNumber.from("1213998517300000000000000").mul(balance).div(beforeTotalSupply))); await expect( await recoveryFundMock.balanceOf("0xf49a12fE6a05bdFc7C0cd4FE2A19724CCFbA18d3")).eq( BigNumber.from("898535671700000000000000").sub(BigNumber.from("898535671700000000000000").mul(balance).div(beforeTotalSupply))); await expect( await recoveryFundMock.balanceOf("0x82dc92b01c7fF54911842956083795f60f6F64f4")).eq( BigNumber.from("673946680643589900000000").sub(BigNumber.from("673946680643589900000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x8ed32Ed24303092c016Cdb24d51e153AD88c4875")).eq( BigNumber.from("89858957570000000000000").sub(BigNumber.from("89858957570000000000000").mul(balance).div(beforeTotalSupply))); await expect( await recoveryFundMock.balanceOf("0xf2CcE4EcB119038dA9F4E18F82E07bb555FbAe2C")).eq( BigNumber.from("77613054250000000000000").sub(BigNumber.from("77613054250000000000000").mul(balance).div(beforeTotalSupply))); await expect( await recoveryFundMock.balanceOf("0x5522234194F499F1DBF2E26C6eBD802bc2Cd9A2f")).eq( BigNumber.from("77095278220000000000000").sub(BigNumber.from("77095278220000000000000").mul(balance).div(beforeTotalSupply))); await expect( await recoveryFundMock.balanceOf("0x896b94f4f27f12369698C302e2049cAe86936BbB")).eq( BigNumber.from("62897497019999995000000").sub(BigNumber.from("62897497019999995000000").mul(balance).div(beforeTotalSupply))); diff --git a/test/unit/index.ts b/test/unit/index.ts index 113499d..53526c7 100644 --- a/test/unit/index.ts +++ b/test/unit/index.ts @@ -1,44 +1,32 @@ import { solidity } from "ethereum-waffle"; -import { baseContext } from "../shared/contexts" - -import { beforeBehavior } from "./before.behavior" - -import { unitTestOwnableStorage } from "./OwnableStorage/OwnableStorage" -import { unitTestVariables } from "./Variables/Variables" -import { unitTestReferral } from "./Referral/Referral" -import { unitTestTreasury } from "./Treasury/Treasury" - -import { unitTestPunkRewardPool } from "./PunkRewardPool/PunkRewardPool" -import { unitTestCompoundModel } from "./CompoundModel/CompoundModel" - -import { unitTestRecoveryFund } from "./RecoveryFund/RecoveryFund" - -import { unitTestForge } from "./Forge/Forge" - -import {use} from 'chai'; - +import { baseContext } from "../shared/contexts"; +import { beforeBehavior } from "./before.behavior"; +import { use } from "chai"; + +import { unitTestOwnableStorage } from "./OwnableStorage/OwnableStorage"; +import { unitTestVariables } from "./Variables/Variables"; +import { unitTestTreasury } from "./Treasury/Treasury"; +import { unitTestPunkRewardPool } from "./PunkRewardPool/PunkRewardPool"; +import { unitTestCompoundModel } from "./CompoundModel/CompoundModel"; +import { unitTestRecoveryFund } from "./RecoveryFund/RecoveryFund"; +import { unitTestForge } from "./Forge/Forge"; use(solidity); -baseContext("Unit Tests", async function() { - - beforeBehavior(); +baseContext("Unit Tests", async function () { + beforeBehavior(); + + unitTestRecoveryFund(); - // unitTestRecoveryFund(); - - unitTestOwnableStorage(); - - unitTestVariables(); - - // Do Not used Contract - // unitTestReferral(); + unitTestOwnableStorage(); - unitTestTreasury(); + unitTestVariables(); - unitTestPunkRewardPool(); + unitTestTreasury(); - unitTestCompoundModel(); + unitTestPunkRewardPool(); - unitTestForge(); + unitTestCompoundModel(); -}) \ No newline at end of file + unitTestForge(); +}); diff --git a/types/index.ts b/types/index.ts index 86431df..f4fbcc7 100644 --- a/types/index.ts +++ b/types/index.ts @@ -1,11 +1,9 @@ import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/dist/src/signer-with-address"; import { Forge } from '../typechain/Forge' -import { ForgeEth } from '../typechain/ForgeEth' export interface Contracts { forge: Forge; - forgeEth: ForgeEth, } export interface Signers { From e40b35384aec07577383bb94a2beb9b0df036ad8 Mon Sep 17 00:00:00 2001 From: Khanh Date: Wed, 18 Aug 2021 10:37:07 -0400 Subject: [PATCH 07/16] feat: add enum varaible for Forge --- contracts/Forge.sol | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/contracts/Forge.sol b/contracts/Forge.sol index 3f851d4..2324b53 100644 --- a/contracts/Forge.sol +++ b/contracts/Forge.sol @@ -18,7 +18,9 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ uint256 constant SECONDS_DAY = 1 days; uint256 constant SECONDS_YEAR = 31556952; - + enum Status { + WITHDRAW_NOT_YET, NOTHING, ALREADY_WITHDRAWN_OR_IS_TERMINATED, ALL_WITHDRAWN + } constructor() ERC20("PunkFinance", "Forge") {} /** @@ -88,7 +90,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ function withdrawable(address account, uint256 index) public view override returns (uint256){ Saver memory s = saver(account, index); if (s.startTimestamp > block.timestamp) return 0; - if (s.status == 2) return 0; + if (s.status == Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED) return 0; uint256 diff = block.timestamp.sub(s.startTimestamp); uint256 count = diff.div(SECONDS_DAY.mul(s.interval)).add(1); @@ -158,7 +160,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * @param amount ERC20 Amount */ function addDeposit(uint256 index, uint256 amount) public override nonReentrant onlyNormalUser returns (bool){ - require( saver(msg.sender, index).status < 2, "FORGE : Terminated Saver" ); + require( saver(msg.sender, index).status < Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED, "FORGE : Terminated Saver" ); uint256 mint = 0; uint256 i = index; @@ -209,7 +211,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ function withdraw(uint256 index, uint256 hope) public override nonReentrant onlyNormalUser returns (bool){ Saver memory s = saver(msg.sender, index); uint256 withdrawablePlp = withdrawable(msg.sender, index); - require(s.status < 2, "FORGE : Terminated Saver"); + require(s.status < Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED, "FORGE : Terminated Saver"); require(withdrawablePlp >= hope, "FORGE : Insufficient Amount"); // TODO Confirm withdrawal of currency after use of balance. @@ -245,7 +247,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ */ function terminateSaver(uint256 index) public override nonReentrant onlyNormalUser returns (bool){ Saver memory s = saver(msg.sender, index); - require(s.status < 2, "FORGE : Already Terminated or Completed"); + require(s.status < Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED, "FORGE : Already Terminated or Completed"); uint256 i = index; uint256 hope = s.mint.sub(s.released); @@ -254,7 +256,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ autoUnstake( hope ); ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, i, hope, true); - _savers[msg.sender][i].status = 2; + _savers[msg.sender][i].status = Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED; _savers[msg.sender][i].released += hope; _savers[msg.sender][i].relAmount += amountOfWithdraw; _savers[msg.sender][i].updatedTimestamp = block.timestamp; From daec778a1827fc6e03a41fcb2f05a562d5d2fb74 Mon Sep 17 00:00:00 2001 From: Khanh Date: Wed, 18 Aug 2021 11:14:48 -0400 Subject: [PATCH 08/16] fix: enum to uint256 --- contracts/Forge.sol | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/contracts/Forge.sol b/contracts/Forge.sol index 2324b53..27de74a 100644 --- a/contracts/Forge.sol +++ b/contracts/Forge.sol @@ -90,7 +90,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ function withdrawable(address account, uint256 index) public view override returns (uint256){ Saver memory s = saver(account, index); if (s.startTimestamp > block.timestamp) return 0; - if (s.status == Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED) return 0; + if (s.status == uint256 (Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED)) return 0; uint256 diff = block.timestamp.sub(s.startTimestamp); uint256 count = diff.div(SECONDS_DAY.mul(s.interval)).add(1); @@ -160,7 +160,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * @param amount ERC20 Amount */ function addDeposit(uint256 index, uint256 amount) public override nonReentrant onlyNormalUser returns (bool){ - require( saver(msg.sender, index).status < Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED, "FORGE : Terminated Saver" ); + require( saver(msg.sender, index).status < uint256(Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED), "FORGE : Terminated Saver" ); uint256 mint = 0; uint256 i = index; @@ -211,23 +211,22 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ function withdraw(uint256 index, uint256 hope) public override nonReentrant onlyNormalUser returns (bool){ Saver memory s = saver(msg.sender, index); uint256 withdrawablePlp = withdrawable(msg.sender, index); - require(s.status < Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED, "FORGE : Terminated Saver"); + require(s.status < uint256(Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED), "FORGE : Terminated Saver"); require(withdrawablePlp >= hope, "FORGE : Insufficient Amount"); // TODO Confirm withdrawal of currency after use of balance. /* for Underlying ERC20 token */ - uint256 i = index; { autoUnstake(hope); - ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, i, hope, false); + ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, index, hope, false); - _savers[msg.sender][i].released += hope; - _savers[msg.sender][i].relAmount += amountOfWithdraw; - _savers[msg.sender][i].updatedTimestamp = block.timestamp; - _savers[msg.sender][i].status = (_savers[msg.sender][i].mint == _savers[msg.sender][i].released) ? 3 : 1; + _savers[msg.sender][index].released += hope; + _savers[msg.sender][index].relAmount += amountOfWithdraw; + _savers[msg.sender][index].updatedTimestamp = block.timestamp; + _savers[msg.sender][index].status = (_savers[msg.sender][index].mint == _savers[msg.sender][index].released) ? 3 : 1; - emit Withdraw(msg.sender, i, amountOfWithdraw); + emit Withdraw(msg.sender, index, amountOfWithdraw); _withdrawTo(amountOfWithdraw, msg.sender); _withdrawTo(amountOfServiceFee, _variables.treasury()); @@ -247,7 +246,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ */ function terminateSaver(uint256 index) public override nonReentrant onlyNormalUser returns (bool){ Saver memory s = saver(msg.sender, index); - require(s.status < Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED, "FORGE : Already Terminated or Completed"); + require(s.status < uint256(Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED), "FORGE : Already Terminated or Completed"); uint256 i = index; uint256 hope = s.mint.sub(s.released); @@ -256,7 +255,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ autoUnstake( hope ); ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, i, hope, true); - _savers[msg.sender][i].status = Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED; + _savers[msg.sender][i].status = uint256(Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED); _savers[msg.sender][i].released += hope; _savers[msg.sender][i].relAmount += amountOfWithdraw; _savers[msg.sender][i].updatedTimestamp = block.timestamp; From 5db198a75e5a62fa15ae2723d441832614c64f7f Mon Sep 17 00:00:00 2001 From: Khanh Date: Wed, 18 Aug 2021 11:30:54 -0400 Subject: [PATCH 09/16] fix: remove unnecessary variable --- contracts/Forge.sol | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/contracts/Forge.sol b/contracts/Forge.sol index 27de74a..e8a91e9 100644 --- a/contracts/Forge.sol +++ b/contracts/Forge.sol @@ -224,7 +224,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ _savers[msg.sender][index].released += hope; _savers[msg.sender][index].relAmount += amountOfWithdraw; _savers[msg.sender][index].updatedTimestamp = block.timestamp; - _savers[msg.sender][index].status = (_savers[msg.sender][index].mint == _savers[msg.sender][index].released) ? 3 : 1; + _savers[msg.sender][index].status = (_savers[msg.sender][index].mint == _savers[msg.sender][index].released) ? uint256(Status.ALL_WITHDRAWN) : uint256(Status.NOTHING); emit Withdraw(msg.sender, index, amountOfWithdraw); @@ -248,17 +248,16 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ Saver memory s = saver(msg.sender, index); require(s.status < uint256(Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED), "FORGE : Already Terminated or Completed"); - uint256 i = index; uint256 hope = s.mint.sub(s.released); { autoUnstake( hope ); - ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, i, hope, true); + ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, index, hope, true); - _savers[msg.sender][i].status = uint256(Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED); - _savers[msg.sender][i].released += hope; - _savers[msg.sender][i].relAmount += amountOfWithdraw; - _savers[msg.sender][i].updatedTimestamp = block.timestamp; + _savers[msg.sender][index].status = uint256(Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED); + _savers[msg.sender][index].released += hope; + _savers[msg.sender][index].relAmount += amountOfWithdraw; + _savers[msg.sender][index].updatedTimestamp = block.timestamp; emit Terminate(msg.sender, index, amountOfWithdraw); From c39a2e90808a05e6a8fce83f0f9c6e3734ab27c7 Mon Sep 17 00:00:00 2001 From: 0xViktor Date: Thu, 19 Aug 2021 14:19:11 +0900 Subject: [PATCH 10/16] Hard Work Now! For Punkers by 0xViktor... - Replace RecoveryFund to RecoveryFund_V2 - add RecoveryFunds unit test codes - Write a comment on Saver struct --- contracts/Forge.sol | 39 ++-- contracts/RecoveryFund.sol | 206 ++++++++--------- contracts/RecoveryFund_V2.sol | 118 ---------- contracts/Saver.sol | 19 ++ contracts/mock/RecoveryFundMock.sol | 5 +- test/shared/fixtures.ts | 4 +- test/unit/RecoveryFund/initialize.behavior.ts | 11 +- test/unit/RecoveryFund/setup.behavior.ts | 212 +++++++++--------- 8 files changed, 257 insertions(+), 357 deletions(-) delete mode 100644 contracts/RecoveryFund_V2.sol diff --git a/contracts/Forge.sol b/contracts/Forge.sol index e8a91e9..17b908c 100644 --- a/contracts/Forge.sol +++ b/contracts/Forge.sol @@ -164,14 +164,9 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ uint256 mint = 0; uint256 i = index; - + { mint = _exchangeToLp(amount); - _mint(msg.sender, mint); - autoStake( mint ); - } - - { _savers[msg.sender][i].mint += mint; _savers[msg.sender][i].accAmount += amount; _savers[msg.sender][i].updatedTimestamp = block.timestamp; @@ -181,6 +176,9 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ IERC20(_token).safeTransferFrom(msg.sender, _model, amount); ModelInterface(_model).invest(); emit AddDeposit(msg.sender, index, amount); + + _mint(msg.sender, mint); + _autoStake( mint ); } return true; @@ -218,7 +216,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ /* for Underlying ERC20 token */ { - autoUnstake(hope); + _autoUnstake(hope); ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, index, hope, false); _savers[msg.sender][index].released += hope; @@ -251,7 +249,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ uint256 hope = s.mint.sub(s.released); { - autoUnstake( hope ); + _autoUnstake( hope ); ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, index, hope, true); _savers[msg.sender][index].status = uint256(Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED); @@ -364,8 +362,13 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ uint256 successFee = _successFee(s, hope); uint256 serviceFee = _serviceFee(s, hope).mul(isTerminate ? fm : 100).div(100); - amountOfFee = successFee.add(serviceFee); - amountOfWithdraw = amount.sub(amountOfFee); + if( successFee.add(serviceFee) >= amount ){ + amountOfWithdraw = 0; + amountOfFee = successFee.add(serviceFee); + }else{ + amountOfFee = successFee.add(serviceFee); + amountOfWithdraw = amount.sub(amountOfFee); + } } function _profit( Saver memory s ) private view returns(uint256){ @@ -377,20 +380,24 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ return 0; } - function _successFee(Saver memory s, uint256 hope) public view returns (uint256 successFee){ + function _successFee(Saver memory s, uint256 hope) private view returns (uint256 successFee){ uint256 sf = _variables.successFee(); uint256 profit = _profit( s ); successFee = profit > 0 ? profit.mul(hope).mul(sf).div(100).div(s.mint) : 0; } - function _serviceFee(Saver memory s, uint256 hope) public view returns (uint256 serviceFee){ - uint256 mf = _variables.serviceFee(); + function _serviceFee(Saver memory s, uint256 hope) private view returns (uint256 serviceFee){ + uint256 sf = _variables.serviceFee(); uint256 period = block.timestamp.sub(s.createTimestamp); uint256 amount = _exchangeToUnderlying(hope); - serviceFee = amount.mul(period).mul(mf).div(SECONDS_YEAR).div(100); + if( period >= SECONDS_YEAR ){ + serviceFee = amount.mul(sf).div(100); + }else{ + serviceFee = amount.mul(period).mul(sf).div(SECONDS_YEAR).div(100); + } } - function autoStake(uint256 hope) public { + function _autoStake(uint256 hope) private { if (_variables.reward() != address(0)) { approve(_variables.reward(), balanceOf(msg.sender)); PunkRewardPoolInterface(_variables.reward()).staking( @@ -401,7 +408,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ } } - function autoUnstake(uint256 hope) public { + function _autoUnstake(uint256 hope) private { if (_variables.reward() != address(0)) { PunkRewardPoolInterface(_variables.reward()).unstaking( address(this), diff --git a/contracts/RecoveryFund.sol b/contracts/RecoveryFund.sol index 1938be0..26f5c82 100644 --- a/contracts/RecoveryFund.sol +++ b/contracts/RecoveryFund.sol @@ -2,135 +2,127 @@ pragma solidity >=0.5.0 <0.9.0; pragma experimental ABIEncoderV2; -import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol"; +import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; +import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/math/SafeMath.sol"; -contract RecoveryFund is ERC20Pausable, ReentrancyGuard { +contract RecoveryFund is ERC20, ReentrancyGuard, Ownable { using SafeERC20 for IERC20; using SafeMath for uint256; - event Refund( uint256 refundAmount, uint256 refunded ); + event Refund( address account, uint256 refundAmount ); - address [] private _victims; + bool private open = false; address public addressOfDAI = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); uint256 public totalRefund; uint256 public refunded; constructor() ERC20("RecoveryFund", "peUSD") { - _initialMint(address(0xe1cd21e5d6f4323E91dA943B0A4F1732acC7a138), 1213998517300000000000000); - _initialMint(address(0xf49a12fE6a05bdFc7C0cd4FE2A19724CCFbA18d3), 898535671700000000000000); - _initialMint(address(0x82dc92b01c7fF54911842956083795f60f6F64f4), 673946680643589900000000); - _initialMint(address(0xf2CcE4EcB119038dA9F4E18F82E07bb555FbAe2C), 77613054250000000000000); - _initialMint(address(0x5522234194F499F1DBF2E26C6eBD802bc2Cd9A2f), 77095278220000000000000); - _initialMint(address(0x896b94f4f27f12369698C302e2049cAe86936BbB), 62897497019999995000000); - _initialMint(address(0xac1f46771cF300bD4D43442960CD5e3d7476Ca34), 53912140300000000000000); - _initialMint(address(0xa52D39E63900992F36286953F884E014AC1F688D), 53905401290000000000000); - _initialMint(address(0xE5EC1103810217a3F83262C66c6a38a8e6387366), 53905401290000000000000); - _initialMint(address(0x577502784eDd9b0D84d08334c30D378975e8F5AC), 45751912140000000000000); - _initialMint(address(0x469Adaf766fb35F1a3c2569FE8C57a50F4B39131), 45355619800000000000000); - _initialMint(address(0x544Fc1845DF225679f5A939eF930837B8cBA0552), 44926783590000000000000); - _initialMint(address(0x4c3a58357f42a7741F61D2c7Fa3e7700996A5A60), 44920044570000000000000); - _initialMint(address(0xD9F0834E7Ca9B9262Df3189b850eBf9514027e82), 38383197560000000000000); - _initialMint(address(0x3E269201B45622c88E1cca18CE9A02609d4D2A54), 35934687850000000000000); - _initialMint(address(0xe9017c8De5040968D9752A18d805cD2A983E558c), 32003782980000000000000); - _initialMint(address(0x55d72CbcbA1Ab5C784bC52641D16c613E3b9BAD4), 31755408880000000000000); - _initialMint(address(0xf76CF36f638c7bCD83f4756beDb86243D98982F9), 29253624400000000000000); - _initialMint(address(0x29227FB595D091bcA244E76201c0dd50641D96C8), 27239560830000002000000); - _initialMint(address(0x2572a193DA3DEf3BAeA04cB18e06A52186aC1a98), 26954824385000000000000); - _initialMint(address(0x81a7E267Fd8339a01beb175f5A3d644FcF0B48Dc), 24234548190999998000000); - _initialMint(address(0x67D33CF1C7c699078f86D517A5a1cd1444A1E85C), 23536011086000000000000); - _initialMint(address(0x78d58Cc37cDAaBFde72c9a45B57Acbc3aBc3D7C9), 22546913770000000000000); - _initialMint(address(0x9dcAfb9A5A2A948ee7A70cDce6abC46322171ACD), 22463391790000000000000); - _initialMint(address(0x28C01B64F2492D82fd58FEECF8c71603c769BB4A), 22458456610000000000000); - _initialMint(address(0xE01f94F635028394cB15B572179DaEbbfC231761), 20217052610000000000000); - _initialMint(address(0x58e316d51aDA862d71fBa22ADA6f56D00186Af13), 18190064880000000000000); - _initialMint(address(0x09CF915e195aF33FA7B932C253352Ae9FBdB0106), 18090301810000000000000); - _initialMint(address(0x833A11B69873151fBA6D14f455db5392542825Ca), 17970855662000000000000); - _initialMint(address(0xffD851CFE0A50E1f1Cf2739Fc6D97754d49943a0), 17303601743000000000000); - _initialMint(address(0xe43458e1F964EEf5F707E81fb6f16Ab27c261071), 16930658390000000000000); - _initialMint(address(0x185C034Bdc0ca1d66Dc75650D33DEA190315595b), 14422499490000000000000); - _initialMint(address(0x0056BcFe33f5c6DfA62B6d3d3CF5A957429828BB), 14104489260000000000000); - _initialMint(address(0x963aD4Cc7798601cC36526162FDa203C21E90872), 13602254460000000000000); - _initialMint(address(0x6FdED91556cfBf602f05e279d270327382d9206E), 13471296060000000000000); - _initialMint(address(0xbEcB3451c1724Ce87801e6A09b547bB704e47884), 13380094687000000000000); - _initialMint(address(0x00F928a6aDC7c345EF919fe5A8Be2458580eF9f6), 11227034090000000000000); - _initialMint(address(0x241a119A8C634b58E78Ec4Ce9bF786756f9FD310), 8985356717000000000000); - _initialMint(address(0xd6b4D675F749F951B706Da0dF2CF99f913143B0C), 8985356717000000000000); - _initialMint(address(0x956D079B656a3955AB4f2f596d1bbfd6F3Ae60dC), 5197498877000000000000); - _initialMint(address(0x53274F93Bcf4647C39E8Af160226736c037574e6), 5177541065000000000000); - _initialMint(address(0x57d6C4efB2cA6703674343d1a7c0492fba49A6f3), 4492678359000000000000); - _initialMint(address(0xA2F8e4744FD85E9089a337fed259F05dd864a91e), 4492678359000000000000); - _initialMint(address(0xDcbBf294D08F70571666f7d08BBDf93189341B0e), 4488634499000000000000); - _initialMint(address(0xc28466bBD0F2f519829fEaEBA61470A381c5d07b), 4348912651000000000000); - _initialMint(address(0x7c25bB0ac944691322849419DF917c0ACc1d379B), 4043410523000000000000); - _initialMint(address(0xf6cDbA86999372f8B9104234885edFDAA1Eb01D1), 3832978820000000300000); - _initialMint(address(0x72f6A96a49B41467eef03924211e422Eaf639e3f), 3813708894000000000000); - _initialMint(address(0x9412b1e09ad73Dff95D099B2d1eFCD389c1422cf), 2243161150000000000000); - _initialMint(address(0x27EF5f1Fa276E8394681A260F77208ccD0305013), 1827100390000000000000); - _initialMint(address(0xB1f72d694711FcE55C62eA97BD3739Ff11ceF986), 1790766736000000000000); - _initialMint(address(0xE81e90222E0BD7D114cce6495B57C307Df342373), 1682368129000000000000); - _initialMint(address(0x7bF4aef8731377eC73a53f5032092d1699c56526), 1349221073000000000000); - _initialMint(address(0x38Bc5196d8b21782372a843E5A505d9F457e6ff8), 1311525431000000000000); - _initialMint(address(0x7E1BeE475e5EFF0e0810cfd1a940c50d7294f15F), 1156118143000000000000); - _initialMint(address(0xac1886d1ef0a19aFb8392ae4231472d4bBF39c4F), 1118439356000000000000); - _initialMint(address(0xd40Cd4D9f9BcF3fE57E37bC88EbFDB6ad2244578), 1108871318000000000000); - _initialMint(address(0x3F8a2B94578D73AF4CBE63C38D2C481Cc7D09F98), 949716401200000000000); - _initialMint(address(0xf934a267C6fB6170FAf5940fb3c12033a1f14d74), 924521947800000000000); - _initialMint(address(0xEcc03538d9Af264725dABd36417441F7971c91F6), 898535671700000000000); - _initialMint(address(0x41833aAE8C10641045fFA17E2377919201A71dDb), 896733830900000000000); - _initialMint(address(0xe75D13941a26be0d5E5C1a104494eFC67dEAfb38), 693526172300000000000); - _initialMint(address(0x9b95F760A7eBCd6dFbEb91FACdA9B8ac2296820f), 633523800100000000000); - _initialMint(address(0x9bb64e40DCBe4645F99F0a9e2507b5A53795fa70), 580593317000000000000); - _initialMint(address(0x8eA035d0995F0568969020A00D1891d1D29c7517), 461972401500000000000); - _initialMint(address(0x5E87597C7eCD23bA209B5354881e4Cafd2FC4D28), 457250243900000000000); - _initialMint(address(0xa3eC8fB0aCc93d0BB8CA73B9e90FE57275855E94), 358417937300000000000); - _initialMint(address(0x392027fDc620d397cA27F0c1C3dCB592F27A4dc3), 314487485100000000000); - _initialMint(address(0xD47835d494AC1Bf8C140B6A04E69b67e75928022), 262821684000000000000); - _initialMint(address(0x8e0e3D4a18AA44904ebda87C5B42545d1cEC2e5f), 213698063100000000000); - _initialMint(address(0xB444b304C55fa9c6fa995a758CBd5a96cF84782B), 64620692110000000000); - _initialMint(address(0x8E1D10aaeF9c0C0D337Aa47022BF0d96D21b56B9), 49312416690000000000); + _mint(address(0xe1cd21e5d6f4323E91dA943B0A4F1732acC7a138), 1213998517300000000000000); + _mint(address(0xf49a12fE6a05bdFc7C0cd4FE2A19724CCFbA18d3), 898535671700000000000000); + _mint(address(0x82dc92b01c7fF54911842956083795f60f6F64f4), 673946680643589900000000); + _mint(address(0xf2CcE4EcB119038dA9F4E18F82E07bb555FbAe2C), 77613054250000000000000); + _mint(address(0x5522234194F499F1DBF2E26C6eBD802bc2Cd9A2f), 77095278220000000000000); + _mint(address(0x896b94f4f27f12369698C302e2049cAe86936BbB), 62897497019999995000000); + _mint(address(0xac1f46771cF300bD4D43442960CD5e3d7476Ca34), 53912140300000000000000); + _mint(address(0xa52D39E63900992F36286953F884E014AC1F688D), 53905401290000000000000); + _mint(address(0xE5EC1103810217a3F83262C66c6a38a8e6387366), 53905401290000000000000); + _mint(address(0x577502784eDd9b0D84d08334c30D378975e8F5AC), 45751912140000000000000); + _mint(address(0x469Adaf766fb35F1a3c2569FE8C57a50F4B39131), 45355619800000000000000); + _mint(address(0x544Fc1845DF225679f5A939eF930837B8cBA0552), 44926783590000000000000); + _mint(address(0x4c3a58357f42a7741F61D2c7Fa3e7700996A5A60), 44920044570000000000000); + _mint(address(0xD9F0834E7Ca9B9262Df3189b850eBf9514027e82), 38383197560000000000000); + _mint(address(0x3E269201B45622c88E1cca18CE9A02609d4D2A54), 35934687850000000000000); + _mint(address(0xe9017c8De5040968D9752A18d805cD2A983E558c), 32003782980000000000000); + _mint(address(0x55d72CbcbA1Ab5C784bC52641D16c613E3b9BAD4), 31755408880000000000000); + _mint(address(0xf76CF36f638c7bCD83f4756beDb86243D98982F9), 29253624400000000000000); + _mint(address(0x29227FB595D091bcA244E76201c0dd50641D96C8), 27239560830000002000000); + _mint(address(0x2572a193DA3DEf3BAeA04cB18e06A52186aC1a98), 26954824385000000000000); + _mint(address(0x81a7E267Fd8339a01beb175f5A3d644FcF0B48Dc), 24234548190999998000000); + _mint(address(0x67D33CF1C7c699078f86D517A5a1cd1444A1E85C), 23536011086000000000000); + _mint(address(0x78d58Cc37cDAaBFde72c9a45B57Acbc3aBc3D7C9), 22546913770000000000000); + _mint(address(0x9dcAfb9A5A2A948ee7A70cDce6abC46322171ACD), 22463391790000000000000); + _mint(address(0x28C01B64F2492D82fd58FEECF8c71603c769BB4A), 22458456610000000000000); + _mint(address(0xE01f94F635028394cB15B572179DaEbbfC231761), 20217052610000000000000); + _mint(address(0x58e316d51aDA862d71fBa22ADA6f56D00186Af13), 18190064880000000000000); + _mint(address(0x09CF915e195aF33FA7B932C253352Ae9FBdB0106), 18090301810000000000000); + _mint(address(0x833A11B69873151fBA6D14f455db5392542825Ca), 17970855662000000000000); + _mint(address(0xffD851CFE0A50E1f1Cf2739Fc6D97754d49943a0), 17303601743000000000000); + _mint(address(0xe43458e1F964EEf5F707E81fb6f16Ab27c261071), 16930658390000000000000); + _mint(address(0x185C034Bdc0ca1d66Dc75650D33DEA190315595b), 14422499490000000000000); + _mint(address(0x0056BcFe33f5c6DfA62B6d3d3CF5A957429828BB), 14104489260000000000000); + _mint(address(0x963aD4Cc7798601cC36526162FDa203C21E90872), 13602254460000000000000); + _mint(address(0x6FdED91556cfBf602f05e279d270327382d9206E), 13471296060000000000000); + _mint(address(0xbEcB3451c1724Ce87801e6A09b547bB704e47884), 13380094687000000000000); + _mint(address(0x00F928a6aDC7c345EF919fe5A8Be2458580eF9f6), 11227034090000000000000); + _mint(address(0x241a119A8C634b58E78Ec4Ce9bF786756f9FD310), 8985356717000000000000); + _mint(address(0xd6b4D675F749F951B706Da0dF2CF99f913143B0C), 8985356717000000000000); + _mint(address(0x956D079B656a3955AB4f2f596d1bbfd6F3Ae60dC), 5197498877000000000000); + _mint(address(0x53274F93Bcf4647C39E8Af160226736c037574e6), 5177541065000000000000); + _mint(address(0x57d6C4efB2cA6703674343d1a7c0492fba49A6f3), 4492678359000000000000); + _mint(address(0xA2F8e4744FD85E9089a337fed259F05dd864a91e), 4492678359000000000000); + _mint(address(0xDcbBf294D08F70571666f7d08BBDf93189341B0e), 4488634499000000000000); + _mint(address(0xc28466bBD0F2f519829fEaEBA61470A381c5d07b), 4348912651000000000000); + _mint(address(0x7c25bB0ac944691322849419DF917c0ACc1d379B), 4043410523000000000000); + _mint(address(0xf6cDbA86999372f8B9104234885edFDAA1Eb01D1), 3832978820000000300000); + _mint(address(0x72f6A96a49B41467eef03924211e422Eaf639e3f), 3813708894000000000000); + _mint(address(0x9412b1e09ad73Dff95D099B2d1eFCD389c1422cf), 2243161150000000000000); + _mint(address(0x27EF5f1Fa276E8394681A260F77208ccD0305013), 1827100390000000000000); + _mint(address(0xB1f72d694711FcE55C62eA97BD3739Ff11ceF986), 1790766736000000000000); + _mint(address(0xE81e90222E0BD7D114cce6495B57C307Df342373), 1682368129000000000000); + _mint(address(0x7bF4aef8731377eC73a53f5032092d1699c56526), 1349221073000000000000); + _mint(address(0x38Bc5196d8b21782372a843E5A505d9F457e6ff8), 1311525431000000000000); + _mint(address(0x7E1BeE475e5EFF0e0810cfd1a940c50d7294f15F), 1156118143000000000000); + _mint(address(0xac1886d1ef0a19aFb8392ae4231472d4bBF39c4F), 1118439356000000000000); + _mint(address(0xd40Cd4D9f9BcF3fE57E37bC88EbFDB6ad2244578), 1108871318000000000000); + _mint(address(0x3F8a2B94578D73AF4CBE63C38D2C481Cc7D09F98), 949716401200000000000); + _mint(address(0xf934a267C6fB6170FAf5940fb3c12033a1f14d74), 924521947800000000000); + _mint(address(0xEcc03538d9Af264725dABd36417441F7971c91F6), 898535671700000000000); + _mint(address(0x41833aAE8C10641045fFA17E2377919201A71dDb), 896733830900000000000); + _mint(address(0xe75D13941a26be0d5E5C1a104494eFC67dEAfb38), 693526172300000000000); + _mint(address(0x9b95F760A7eBCd6dFbEb91FACdA9B8ac2296820f), 633523800100000000000); + _mint(address(0x9bb64e40DCBe4645F99F0a9e2507b5A53795fa70), 580593317000000000000); + _mint(address(0x8eA035d0995F0568969020A00D1891d1D29c7517), 461972401500000000000); + _mint(address(0x5E87597C7eCD23bA209B5354881e4Cafd2FC4D28), 457250243900000000000); + _mint(address(0xa3eC8fB0aCc93d0BB8CA73B9e90FE57275855E94), 358417937300000000000); + _mint(address(0x392027fDc620d397cA27F0c1C3dCB592F27A4dc3), 314487485100000000000); + _mint(address(0xD47835d494AC1Bf8C140B6A04E69b67e75928022), 262821684000000000000); + _mint(address(0x8e0e3D4a18AA44904ebda87C5B42545d1cEC2e5f), 213698063100000000000); + _mint(address(0xB444b304C55fa9c6fa995a758CBd5a96cF84782B), 64620692110000000000); + _mint(address(0x8E1D10aaeF9c0C0D337Aa47022BF0d96D21b56B9), 49312416690000000000); totalRefund = totalSupply(); - _pause(); } - function _initialMint(address account, uint256 amount) internal whenNotPaused{ - require(balanceOf(account) == 0, "REFUND : account has tokens"); - _mint(account, amount); - _victims.push(account); + function balanceOfDai() public view returns(uint256){ + return IERC20(addressOfDAI).balanceOf(address(this)); } - function minRefundAmount() public view returns(uint256){ - return totalSupply().div(balanceOf(address(0x8E1D10aaeF9c0C0D337Aa47022BF0d96D21b56B9))); + function startRefund() public onlyOwner { + require(!open, "REFUND : Already Opened"); + open = true; + } + + function stopRefund() public onlyOwner { + require(open, "REFUND : Not Opened"); + open = false; + } + + function emergency() public onlyOwner { + IERC20(addressOfDAI).safeTransfer(owner(), balanceOfDai()); } function refund( uint256 refundAmount ) public nonReentrant returns(bool){ - require( IERC20(addressOfDAI).allowance(msg.sender, address(this)) >= refundAmount, "REFUND : allowance is insufficient" ); - require( IERC20(addressOfDAI).balanceOf(msg.sender) >= refundAmount, "REFUND : sender's balance is insufficient" ); - require( minRefundAmount() <= refundAmount, "REFUND : refundAmount less than minRefundAmount" ); - - IERC20 DAI = IERC20(addressOfDAI); - uint256 guaranteedSupply = totalSupply(); - uint256 amountToBeSent = refundAmount.sub( refundAmount.mod(minRefundAmount()) ); - uint256 amountSent = 0; - uint8 i = 0; - - _unpause(); - for (i; i < _victims.length; i++) { - uint value = amountToBeSent.mul( balanceOf(_victims[i]) ).div( guaranteedSupply ); - require( value > 0, "REFUND : sent value is zero" ); - _burn(_victims[i], value); - DAI.safeTransferFrom(msg.sender, _victims[i], value); - amountSent += value; - } - _pause(); + require( open, "REFUND : Not Opened"); + require( balanceOf(msg.sender) >= refundAmount, "REFUND : sender's balance is insufficient" ); + + _burn( msg.sender, refundAmount ); + IERC20(addressOfDAI).safeTransfer(msg.sender, refundAmount); + + emit Refund( msg.sender, refundAmount ); - require( guaranteedSupply.sub(amountSent) == totalSupply(), "REFUND : guaranteedSupply is not amount sent" ); - refunded += amountSent; - - emit Refund( amountSent, refunded ); - + refunded += refundAmount; return false; } diff --git a/contracts/RecoveryFund_V2.sol b/contracts/RecoveryFund_V2.sol deleted file mode 100644 index 57c8a9f..0000000 --- a/contracts/RecoveryFund_V2.sol +++ /dev/null @@ -1,118 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.5.0 <0.9.0; -pragma experimental ABIEncoderV2; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -import "@openzeppelin/contracts/access/Ownable.sol"; -import "@openzeppelin/contracts/utils/math/SafeMath.sol"; - -contract RecoveryFund_V2 is ERC20, ReentrancyGuard, Ownable { - using SafeERC20 for IERC20; - using SafeMath for uint256; - - event Refund( address account, uint256 refundAmount ); - - bool private open = false; - address public addressOfDAI = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); - uint256 public totalRefund; - - constructor() ERC20("RecoveryFund", "peUSD") { - _mint(address(0xe1cd21e5d6f4323E91dA943B0A4F1732acC7a138), 1213998517300000000000000); - _mint(address(0xf49a12fE6a05bdFc7C0cd4FE2A19724CCFbA18d3), 898535671700000000000000); - _mint(address(0x82dc92b01c7fF54911842956083795f60f6F64f4), 673946680643589900000000); - _mint(address(0xf2CcE4EcB119038dA9F4E18F82E07bb555FbAe2C), 77613054250000000000000); - _mint(address(0x5522234194F499F1DBF2E26C6eBD802bc2Cd9A2f), 77095278220000000000000); - _mint(address(0x896b94f4f27f12369698C302e2049cAe86936BbB), 62897497019999995000000); - _mint(address(0xac1f46771cF300bD4D43442960CD5e3d7476Ca34), 53912140300000000000000); - _mint(address(0xa52D39E63900992F36286953F884E014AC1F688D), 53905401290000000000000); - _mint(address(0xE5EC1103810217a3F83262C66c6a38a8e6387366), 53905401290000000000000); - _mint(address(0x577502784eDd9b0D84d08334c30D378975e8F5AC), 45751912140000000000000); - _mint(address(0x469Adaf766fb35F1a3c2569FE8C57a50F4B39131), 45355619800000000000000); - _mint(address(0x544Fc1845DF225679f5A939eF930837B8cBA0552), 44926783590000000000000); - _mint(address(0x4c3a58357f42a7741F61D2c7Fa3e7700996A5A60), 44920044570000000000000); - _mint(address(0xD9F0834E7Ca9B9262Df3189b850eBf9514027e82), 38383197560000000000000); - _mint(address(0x3E269201B45622c88E1cca18CE9A02609d4D2A54), 35934687850000000000000); - _mint(address(0xe9017c8De5040968D9752A18d805cD2A983E558c), 32003782980000000000000); - _mint(address(0x55d72CbcbA1Ab5C784bC52641D16c613E3b9BAD4), 31755408880000000000000); - _mint(address(0xf76CF36f638c7bCD83f4756beDb86243D98982F9), 29253624400000000000000); - _mint(address(0x29227FB595D091bcA244E76201c0dd50641D96C8), 27239560830000002000000); - _mint(address(0x2572a193DA3DEf3BAeA04cB18e06A52186aC1a98), 26954824385000000000000); - _mint(address(0x81a7E267Fd8339a01beb175f5A3d644FcF0B48Dc), 24234548190999998000000); - _mint(address(0x67D33CF1C7c699078f86D517A5a1cd1444A1E85C), 23536011086000000000000); - _mint(address(0x78d58Cc37cDAaBFde72c9a45B57Acbc3aBc3D7C9), 22546913770000000000000); - _mint(address(0x9dcAfb9A5A2A948ee7A70cDce6abC46322171ACD), 22463391790000000000000); - _mint(address(0x28C01B64F2492D82fd58FEECF8c71603c769BB4A), 22458456610000000000000); - _mint(address(0xE01f94F635028394cB15B572179DaEbbfC231761), 20217052610000000000000); - _mint(address(0x58e316d51aDA862d71fBa22ADA6f56D00186Af13), 18190064880000000000000); - _mint(address(0x09CF915e195aF33FA7B932C253352Ae9FBdB0106), 18090301810000000000000); - _mint(address(0x833A11B69873151fBA6D14f455db5392542825Ca), 17970855662000000000000); - _mint(address(0xffD851CFE0A50E1f1Cf2739Fc6D97754d49943a0), 17303601743000000000000); - _mint(address(0xe43458e1F964EEf5F707E81fb6f16Ab27c261071), 16930658390000000000000); - _mint(address(0x185C034Bdc0ca1d66Dc75650D33DEA190315595b), 14422499490000000000000); - _mint(address(0x0056BcFe33f5c6DfA62B6d3d3CF5A957429828BB), 14104489260000000000000); - _mint(address(0x963aD4Cc7798601cC36526162FDa203C21E90872), 13602254460000000000000); - _mint(address(0x6FdED91556cfBf602f05e279d270327382d9206E), 13471296060000000000000); - _mint(address(0xbEcB3451c1724Ce87801e6A09b547bB704e47884), 13380094687000000000000); - _mint(address(0x00F928a6aDC7c345EF919fe5A8Be2458580eF9f6), 11227034090000000000000); - _mint(address(0x241a119A8C634b58E78Ec4Ce9bF786756f9FD310), 8985356717000000000000); - _mint(address(0xd6b4D675F749F951B706Da0dF2CF99f913143B0C), 8985356717000000000000); - _mint(address(0x956D079B656a3955AB4f2f596d1bbfd6F3Ae60dC), 5197498877000000000000); - _mint(address(0x53274F93Bcf4647C39E8Af160226736c037574e6), 5177541065000000000000); - _mint(address(0x57d6C4efB2cA6703674343d1a7c0492fba49A6f3), 4492678359000000000000); - _mint(address(0xA2F8e4744FD85E9089a337fed259F05dd864a91e), 4492678359000000000000); - _mint(address(0xDcbBf294D08F70571666f7d08BBDf93189341B0e), 4488634499000000000000); - _mint(address(0xc28466bBD0F2f519829fEaEBA61470A381c5d07b), 4348912651000000000000); - _mint(address(0x7c25bB0ac944691322849419DF917c0ACc1d379B), 4043410523000000000000); - _mint(address(0xf6cDbA86999372f8B9104234885edFDAA1Eb01D1), 3832978820000000300000); - _mint(address(0x72f6A96a49B41467eef03924211e422Eaf639e3f), 3813708894000000000000); - _mint(address(0x9412b1e09ad73Dff95D099B2d1eFCD389c1422cf), 2243161150000000000000); - _mint(address(0x27EF5f1Fa276E8394681A260F77208ccD0305013), 1827100390000000000000); - _mint(address(0xB1f72d694711FcE55C62eA97BD3739Ff11ceF986), 1790766736000000000000); - _mint(address(0xE81e90222E0BD7D114cce6495B57C307Df342373), 1682368129000000000000); - _mint(address(0x7bF4aef8731377eC73a53f5032092d1699c56526), 1349221073000000000000); - _mint(address(0x38Bc5196d8b21782372a843E5A505d9F457e6ff8), 1311525431000000000000); - _mint(address(0x7E1BeE475e5EFF0e0810cfd1a940c50d7294f15F), 1156118143000000000000); - _mint(address(0xac1886d1ef0a19aFb8392ae4231472d4bBF39c4F), 1118439356000000000000); - _mint(address(0xd40Cd4D9f9BcF3fE57E37bC88EbFDB6ad2244578), 1108871318000000000000); - _mint(address(0x3F8a2B94578D73AF4CBE63C38D2C481Cc7D09F98), 949716401200000000000); - _mint(address(0xf934a267C6fB6170FAf5940fb3c12033a1f14d74), 924521947800000000000); - _mint(address(0xEcc03538d9Af264725dABd36417441F7971c91F6), 898535671700000000000); - _mint(address(0x41833aAE8C10641045fFA17E2377919201A71dDb), 896733830900000000000); - _mint(address(0xe75D13941a26be0d5E5C1a104494eFC67dEAfb38), 693526172300000000000); - _mint(address(0x9b95F760A7eBCd6dFbEb91FACdA9B8ac2296820f), 633523800100000000000); - _mint(address(0x9bb64e40DCBe4645F99F0a9e2507b5A53795fa70), 580593317000000000000); - _mint(address(0x8eA035d0995F0568969020A00D1891d1D29c7517), 461972401500000000000); - _mint(address(0x5E87597C7eCD23bA209B5354881e4Cafd2FC4D28), 457250243900000000000); - _mint(address(0xa3eC8fB0aCc93d0BB8CA73B9e90FE57275855E94), 358417937300000000000); - _mint(address(0x392027fDc620d397cA27F0c1C3dCB592F27A4dc3), 314487485100000000000); - _mint(address(0xD47835d494AC1Bf8C140B6A04E69b67e75928022), 262821684000000000000); - _mint(address(0x8e0e3D4a18AA44904ebda87C5B42545d1cEC2e5f), 213698063100000000000); - _mint(address(0xB444b304C55fa9c6fa995a758CBd5a96cF84782B), 64620692110000000000); - _mint(address(0x8E1D10aaeF9c0C0D337Aa47022BF0d96D21b56B9), 49312416690000000000); - totalRefund = totalSupply(); - } - - function balanceOfDai() public view returns(uint256){ - return IERC20(addressOfDAI).balanceOf(address(this)); - } - - function openRefund() public onlyOwner { - require(!open, "REFUND : Already Opened"); - require( balanceOfDai() >= totalRefund, "REFUND : Not Yet Full RecoveryRefund" ); - open = true; - } - - function refund( uint256 refundAmount ) public nonReentrant returns(bool){ - require( open, "REFUND : Not Opened"); - require( balanceOf(msg.sender) >= refundAmount, "REFUND : sender's balance is insufficient" ); - - _burn( msg.sender, refundAmount ); - IERC20(addressOfDAI).safeTransfer(msg.sender, refundAmount); - - emit Refund( msg.sender, refundAmount ); - return false; - } - -} diff --git a/contracts/Saver.sol b/contracts/Saver.sol index cf4ecd4..9eda5b7 100644 --- a/contracts/Saver.sol +++ b/contracts/Saver.sol @@ -2,14 +2,33 @@ pragma solidity >=0.5.0 <0.9.0; struct Saver{ + // Saver CreateTimestmap uint256 createTimestamp; + + // When do you want to start receiving (unixTime:seconds) uint256 startTimestamp; + + // How often do you want to receive. uint count; + + // Number of times to receive (unit: 1 day) uint interval; + + // Total pLP tokens issued according to the amount deposited. uint256 mint; + + // Total pLP tokens burned for withdrawal. uint256 released; + + // Total amount deposited. (underlying token) uint256 accAmount; + + // Total amount withdrawal. (underlying token) uint256 relAmount; + + // Saver's status (WITHDRAW_NOT_YET, NOTHING, ALREADY_WITHDRAWN_OR_IS_TERMINATED, ALL_WITHDRAWN) uint status; + + // Last updated time. uint updatedTimestamp; } \ No newline at end of file diff --git a/contracts/mock/RecoveryFundMock.sol b/contracts/mock/RecoveryFundMock.sol index 1f3dd36..4a3dd0b 100644 --- a/contracts/mock/RecoveryFundMock.sol +++ b/contracts/mock/RecoveryFundMock.sol @@ -7,5 +7,8 @@ import "../RecoveryFund.sol"; contract RecoveryFundMock is RecoveryFund { using SafeERC20 for IERC20; - constructor() RecoveryFund() {} + constructor() RecoveryFund() { + _mint(address(0x6d6B6AfBF1B564CBE87E1e34d23ac17a43fc33de), 10000); + totalRefund = totalSupply(); + } } diff --git a/test/shared/fixtures.ts b/test/shared/fixtures.ts index ed24fa1..fc77f8c 100644 --- a/test/shared/fixtures.ts +++ b/test/shared/fixtures.ts @@ -68,9 +68,9 @@ export async function unitFixtureDaiToken(): Promise { return daiToken } -export async function unitFixtureRecoveryFund(): Promise { +export async function unitFixtureRecoveryFund([,,,,,owner] : Wallet[]): Promise { const RecoveryFundMock = await ethers.getContractFactory("RecoveryFundMock") - const recoveryFundMock = await RecoveryFundMock.deploy() + const recoveryFundMock = await RecoveryFundMock.connect(owner).deploy() return recoveryFundMock } diff --git a/test/unit/RecoveryFund/initialize.behavior.ts b/test/unit/RecoveryFund/initialize.behavior.ts index 928cd77..5c6eac1 100644 --- a/test/unit/RecoveryFund/initialize.behavior.ts +++ b/test/unit/RecoveryFund/initialize.behavior.ts @@ -8,7 +8,7 @@ export function initialBehavior(): void { context("Initailize", function() { before(async function(){ - const daiContract = this.contracts.daiContract + const recoveryFundMock = this.contracts.recoveryFundMock; const uniswapV2Router = this.contracts.uniswapV2Router const accountDai = this.signers.accountDai @@ -18,7 +18,7 @@ export function initialBehavior(): void { const swapResult = await uniswapV2Router.connect(accountDai).swapExactETHForTokens( 1, [Tokens.WETH, Tokens.Dai], - accountDai.address, + recoveryFundMock.address, blockInfo.timestamp + 25*60*60, {value: ethToWei("700"), gasLimit: '2600000'} ) @@ -27,11 +27,14 @@ export function initialBehavior(): void { it('should Check total refund', async function() { const recoveryFundMock = this.contracts.recoveryFundMock; - await expect( await recoveryFundMock.totalRefund() ).eq(BigNumber.from("4041503630429289895300000").sub("89858957570000000000000")) + await expect( await recoveryFundMock.totalRefund() ).eq(BigNumber.from("3951644672859289895300000").add("10000")) }) it('should Check peUSD balances', async function(){ const recoveryFundMock = this.contracts.recoveryFundMock; + + await expect( await recoveryFundMock.balanceOf("0x6d6B6AfBF1B564CBE87E1e34d23ac17a43fc33de")).eq( BigNumber.from("10000")); + await expect( await recoveryFundMock.balanceOf("0xe1cd21e5d6f4323E91dA943B0A4F1732acC7a138")).eq( BigNumber.from("1213998517300000000000000")); await expect( await recoveryFundMock.balanceOf("0xf49a12fE6a05bdFc7C0cd4FE2A19724CCFbA18d3")).eq( BigNumber.from("898535671700000000000000")); await expect( await recoveryFundMock.balanceOf("0x82dc92b01c7fF54911842956083795f60f6F64f4")).eq( BigNumber.from("673946680643589900000000")); @@ -107,5 +110,7 @@ export function initialBehavior(): void { }) + + }) } \ No newline at end of file diff --git a/test/unit/RecoveryFund/setup.behavior.ts b/test/unit/RecoveryFund/setup.behavior.ts index 106878e..a852126 100644 --- a/test/unit/RecoveryFund/setup.behavior.ts +++ b/test/unit/RecoveryFund/setup.behavior.ts @@ -3,114 +3,106 @@ import { BigNumber, Contract } from "ethers"; import { ethers } from "hardhat"; export function setUpBehavior(): void { - context("SetUp", function() { - - it('should Revert allowance is exceeded', async function() { - const recoveryFundMock = this.contracts.recoveryFundMock - const daiContract = this.contracts.daiContract - const accountDai = this.signers.accountDai - await daiContract.approve( recoveryFundMock.address, 0 ) - await expect(recoveryFundMock.connect(accountDai).refund(1)).to.be.reverted; - }) - - it('should Revert sender`s balance is exceeded', async function() { - const recoveryFundMock = this.contracts.recoveryFundMock - const daiContract = this.contracts.daiContract - const accountDai = this.signers.accountDai - const balance = (await daiContract.balanceOf(accountDai.address)).toString() - await daiContract.approve( recoveryFundMock.address, BigNumber.from(balance).div(2).toString() ) - await expect(recoveryFundMock.connect(accountDai).refund( BigNumber.from(balance).div(2).add(1).toString() )).to.be.reverted; - }) - - it('should Success Refund', async function() { - const recoveryFundMock = this.contracts.recoveryFundMock - const daiContract = this.contracts.daiContract - const accountDai = this.signers.accountDai - let balance = (await daiContract.balanceOf(accountDai.address)).toString() - - const beforeTotalSupply = (await recoveryFundMock.totalSupply()).toString() - const minRefundAmount = (await recoveryFundMock.minRefundAmount()).toString() - - balance = BigNumber.from(balance).sub(BigNumber.from(balance).mod(minRefundAmount)) - await daiContract.approve( recoveryFundMock.address, balance ) - const refundTx = (await recoveryFundMock.connect(accountDai).refund(balance)).toString(); - - const refunded = (await recoveryFundMock.refunded()).toString(); - await expect(await recoveryFundMock.totalSupply()).eq( BigNumber.from(beforeTotalSupply).sub(refunded) ) - - await expect( await recoveryFundMock.balanceOf("0xe1cd21e5d6f4323E91dA943B0A4F1732acC7a138")).eq( BigNumber.from("1213998517300000000000000").sub(BigNumber.from("1213998517300000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xf49a12fE6a05bdFc7C0cd4FE2A19724CCFbA18d3")).eq( BigNumber.from("898535671700000000000000").sub(BigNumber.from("898535671700000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x82dc92b01c7fF54911842956083795f60f6F64f4")).eq( BigNumber.from("673946680643589900000000").sub(BigNumber.from("673946680643589900000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xf2CcE4EcB119038dA9F4E18F82E07bb555FbAe2C")).eq( BigNumber.from("77613054250000000000000").sub(BigNumber.from("77613054250000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x5522234194F499F1DBF2E26C6eBD802bc2Cd9A2f")).eq( BigNumber.from("77095278220000000000000").sub(BigNumber.from("77095278220000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x896b94f4f27f12369698C302e2049cAe86936BbB")).eq( BigNumber.from("62897497019999995000000").sub(BigNumber.from("62897497019999995000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xac1f46771cF300bD4D43442960CD5e3d7476Ca34")).eq( BigNumber.from("53912140300000000000000").sub(BigNumber.from("53912140300000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xa52D39E63900992F36286953F884E014AC1F688D")).eq( BigNumber.from("53905401290000000000000").sub(BigNumber.from("53905401290000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xE5EC1103810217a3F83262C66c6a38a8e6387366")).eq( BigNumber.from("53905401290000000000000").sub(BigNumber.from("53905401290000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x577502784eDd9b0D84d08334c30D378975e8F5AC")).eq( BigNumber.from("45751912140000000000000").sub(BigNumber.from("45751912140000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x469Adaf766fb35F1a3c2569FE8C57a50F4B39131")).eq( BigNumber.from("45355619800000000000000").sub(BigNumber.from("45355619800000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x544Fc1845DF225679f5A939eF930837B8cBA0552")).eq( BigNumber.from("44926783590000000000000").sub(BigNumber.from("44926783590000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x4c3a58357f42a7741F61D2c7Fa3e7700996A5A60")).eq( BigNumber.from("44920044570000000000000").sub(BigNumber.from("44920044570000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xD9F0834E7Ca9B9262Df3189b850eBf9514027e82")).eq( BigNumber.from("38383197560000000000000").sub(BigNumber.from("38383197560000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x3E269201B45622c88E1cca18CE9A02609d4D2A54")).eq( BigNumber.from("35934687850000000000000").sub(BigNumber.from("35934687850000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xe9017c8De5040968D9752A18d805cD2A983E558c")).eq( BigNumber.from("32003782980000000000000").sub(BigNumber.from("32003782980000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x55d72CbcbA1Ab5C784bC52641D16c613E3b9BAD4")).eq( BigNumber.from("31755408880000000000000").sub(BigNumber.from("31755408880000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xf76CF36f638c7bCD83f4756beDb86243D98982F9")).eq( BigNumber.from("29253624400000000000000").sub(BigNumber.from("29253624400000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x29227FB595D091bcA244E76201c0dd50641D96C8")).eq( BigNumber.from("27239560830000002000000").sub(BigNumber.from("27239560830000002000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x2572a193DA3DEf3BAeA04cB18e06A52186aC1a98")).eq( BigNumber.from("26954824385000000000000").sub(BigNumber.from("26954824385000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x81a7E267Fd8339a01beb175f5A3d644FcF0B48Dc")).eq( BigNumber.from("24234548190999998000000").sub(BigNumber.from("24234548190999998000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x67D33CF1C7c699078f86D517A5a1cd1444A1E85C")).eq( BigNumber.from("23536011086000000000000").sub(BigNumber.from("23536011086000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x78d58Cc37cDAaBFde72c9a45B57Acbc3aBc3D7C9")).eq( BigNumber.from("22546913770000000000000").sub(BigNumber.from("22546913770000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x9dcAfb9A5A2A948ee7A70cDce6abC46322171ACD")).eq( BigNumber.from("22463391790000000000000").sub(BigNumber.from("22463391790000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x28C01B64F2492D82fd58FEECF8c71603c769BB4A")).eq( BigNumber.from("22458456610000000000000").sub(BigNumber.from("22458456610000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xE01f94F635028394cB15B572179DaEbbfC231761")).eq( BigNumber.from("20217052610000000000000").sub(BigNumber.from("20217052610000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x58e316d51aDA862d71fBa22ADA6f56D00186Af13")).eq( BigNumber.from("18190064880000000000000").sub(BigNumber.from("18190064880000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x09CF915e195aF33FA7B932C253352Ae9FBdB0106")).eq( BigNumber.from("18090301810000000000000").sub(BigNumber.from("18090301810000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x833A11B69873151fBA6D14f455db5392542825Ca")).eq( BigNumber.from("17970855662000000000000").sub(BigNumber.from("17970855662000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xffD851CFE0A50E1f1Cf2739Fc6D97754d49943a0")).eq( BigNumber.from("17303601743000000000000").sub(BigNumber.from("17303601743000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xe43458e1F964EEf5F707E81fb6f16Ab27c261071")).eq( BigNumber.from("16930658390000000000000").sub(BigNumber.from("16930658390000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x185C034Bdc0ca1d66Dc75650D33DEA190315595b")).eq( BigNumber.from("14422499490000000000000").sub(BigNumber.from("14422499490000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x0056BcFe33f5c6DfA62B6d3d3CF5A957429828BB")).eq( BigNumber.from("14104489260000000000000").sub(BigNumber.from("14104489260000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x963aD4Cc7798601cC36526162FDa203C21E90872")).eq( BigNumber.from("13602254460000000000000").sub(BigNumber.from("13602254460000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x6FdED91556cfBf602f05e279d270327382d9206E")).eq( BigNumber.from("13471296060000000000000").sub(BigNumber.from("13471296060000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xbEcB3451c1724Ce87801e6A09b547bB704e47884")).eq( BigNumber.from("13380094687000000000000").sub(BigNumber.from("13380094687000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x00F928a6aDC7c345EF919fe5A8Be2458580eF9f6")).eq( BigNumber.from("11227034090000000000000").sub(BigNumber.from("11227034090000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x241a119A8C634b58E78Ec4Ce9bF786756f9FD310")).eq( BigNumber.from("8985356717000000000000").sub(BigNumber.from("8985356717000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xd6b4D675F749F951B706Da0dF2CF99f913143B0C")).eq( BigNumber.from("8985356717000000000000").sub(BigNumber.from("8985356717000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x956D079B656a3955AB4f2f596d1bbfd6F3Ae60dC")).eq( BigNumber.from("5197498877000000000000").sub(BigNumber.from("5197498877000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x53274F93Bcf4647C39E8Af160226736c037574e6")).eq( BigNumber.from("5177541065000000000000").sub(BigNumber.from("5177541065000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x57d6C4efB2cA6703674343d1a7c0492fba49A6f3")).eq( BigNumber.from("4492678359000000000000").sub(BigNumber.from("4492678359000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xA2F8e4744FD85E9089a337fed259F05dd864a91e")).eq( BigNumber.from("4492678359000000000000").sub(BigNumber.from("4492678359000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xDcbBf294D08F70571666f7d08BBDf93189341B0e")).eq( BigNumber.from("4488634499000000000000").sub(BigNumber.from("4488634499000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xc28466bBD0F2f519829fEaEBA61470A381c5d07b")).eq( BigNumber.from("4348912651000000000000").sub(BigNumber.from("4348912651000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x7c25bB0ac944691322849419DF917c0ACc1d379B")).eq( BigNumber.from("4043410523000000000000").sub(BigNumber.from("4043410523000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xf6cDbA86999372f8B9104234885edFDAA1Eb01D1")).eq( BigNumber.from("3832978820000000300000").sub(BigNumber.from("3832978820000000300000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x72f6A96a49B41467eef03924211e422Eaf639e3f")).eq( BigNumber.from("3813708894000000000000").sub(BigNumber.from("3813708894000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x9412b1e09ad73Dff95D099B2d1eFCD389c1422cf")).eq( BigNumber.from("2243161150000000000000").sub(BigNumber.from("2243161150000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x27EF5f1Fa276E8394681A260F77208ccD0305013")).eq( BigNumber.from("1827100390000000000000").sub(BigNumber.from("1827100390000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xB1f72d694711FcE55C62eA97BD3739Ff11ceF986")).eq( BigNumber.from("1790766736000000000000").sub(BigNumber.from("1790766736000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xE81e90222E0BD7D114cce6495B57C307Df342373")).eq( BigNumber.from("1682368129000000000000").sub(BigNumber.from("1682368129000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x7bF4aef8731377eC73a53f5032092d1699c56526")).eq( BigNumber.from("1349221073000000000000").sub(BigNumber.from("1349221073000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x38Bc5196d8b21782372a843E5A505d9F457e6ff8")).eq( BigNumber.from("1311525431000000000000").sub(BigNumber.from("1311525431000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x7E1BeE475e5EFF0e0810cfd1a940c50d7294f15F")).eq( BigNumber.from("1156118143000000000000").sub(BigNumber.from("1156118143000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xac1886d1ef0a19aFb8392ae4231472d4bBF39c4F")).eq( BigNumber.from("1118439356000000000000").sub(BigNumber.from("1118439356000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xd40Cd4D9f9BcF3fE57E37bC88EbFDB6ad2244578")).eq( BigNumber.from("1108871318000000000000").sub(BigNumber.from("1108871318000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x3F8a2B94578D73AF4CBE63C38D2C481Cc7D09F98")).eq( BigNumber.from("949716401200000000000").sub(BigNumber.from("949716401200000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xf934a267C6fB6170FAf5940fb3c12033a1f14d74")).eq( BigNumber.from("924521947800000000000").sub(BigNumber.from("924521947800000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xEcc03538d9Af264725dABd36417441F7971c91F6")).eq( BigNumber.from("898535671700000000000").sub(BigNumber.from("898535671700000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x41833aAE8C10641045fFA17E2377919201A71dDb")).eq( BigNumber.from("896733830900000000000").sub(BigNumber.from("896733830900000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xe75D13941a26be0d5E5C1a104494eFC67dEAfb38")).eq( BigNumber.from("693526172300000000000").sub(BigNumber.from("693526172300000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x9b95F760A7eBCd6dFbEb91FACdA9B8ac2296820f")).eq( BigNumber.from("633523800100000000000").sub(BigNumber.from("633523800100000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x9bb64e40DCBe4645F99F0a9e2507b5A53795fa70")).eq( BigNumber.from("580593317000000000000").sub(BigNumber.from("580593317000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x8eA035d0995F0568969020A00D1891d1D29c7517")).eq( BigNumber.from("461972401500000000000").sub(BigNumber.from("461972401500000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x5E87597C7eCD23bA209B5354881e4Cafd2FC4D28")).eq( BigNumber.from("457250243900000000000").sub(BigNumber.from("457250243900000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xa3eC8fB0aCc93d0BB8CA73B9e90FE57275855E94")).eq( BigNumber.from("358417937300000000000").sub(BigNumber.from("358417937300000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x392027fDc620d397cA27F0c1C3dCB592F27A4dc3")).eq( BigNumber.from("314487485100000000000").sub(BigNumber.from("314487485100000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xD47835d494AC1Bf8C140B6A04E69b67e75928022")).eq( BigNumber.from("262821684000000000000").sub(BigNumber.from("262821684000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x8e0e3D4a18AA44904ebda87C5B42545d1cEC2e5f")).eq( BigNumber.from("213698063100000000000").sub(BigNumber.from("213698063100000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0xB444b304C55fa9c6fa995a758CBd5a96cF84782B")).eq( BigNumber.from("64620692110000000000").sub(BigNumber.from("64620692110000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x8E1D10aaeF9c0C0D337Aa47022BF0d96D21b56B9")).eq( BigNumber.from("49312416690000000000").sub(BigNumber.from("49312416690000000000").mul(balance).div(beforeTotalSupply))); - }) + context("SetUp", function () { + it("should Revert startRefund Address Not Admin", async function () { + const recoveryFundMock = this.contracts.recoveryFundMock; + const account1 = this.signers.account1; + await expect( + recoveryFundMock.connect(account1).startRefund() + ).to.be.reverted; + }); + + it("should Revert stopRefund Address Not Admin", async function () { + const recoveryFundMock = this.contracts.recoveryFundMock; + const account1 = this.signers.account1; + await expect( + recoveryFundMock.connect(account1).stopRefund() + ).to.be.reverted; + }); + + it("should Revert stopRefund Not Opend", async function () { + const recoveryFundMock = this.contracts.recoveryFundMock; + const owner = this.signers.owner; + await expect( + recoveryFundMock.connect(owner).stopRefund() + ).to.be.reverted; + }); + + it("should Success startRefund", async function () { + const recoveryFundMock = this.contracts.recoveryFundMock; + const owner = this.signers.owner; + await recoveryFundMock.connect(owner).startRefund(); + await expect(recoveryFundMock.connect(owner).startRefund()).to.be.reverted + }); + + it("should Success stopRefund", async function () { + const recoveryFundMock = this.contracts.recoveryFundMock; + const owner = this.signers.owner; + await recoveryFundMock.connect(owner).stopRefund(); + await expect(recoveryFundMock.connect(owner).stopRefund()).to.be.reverted + }); + + it("should Revert emergency Not Owner address", async function () { + const recoveryFundMock = this.contracts.recoveryFundMock; + const account1 = this.signers.account1; + await expect(recoveryFundMock.connect(account1).emergency()).to.be.reverted + }); + + it("should Success emergency", async function () { + const daiContract = this.contracts.daiContract; + const recoveryFundMock = this.contracts.recoveryFundMock; + const owner = this.signers.owner; + + const balanceOfOwnerBeforeEmergency = (await daiContract.balanceOf(owner.address)).toString(); + const balanceOfRecoveryFunds = (await daiContract.balanceOf(recoveryFundMock.address)).toString(); + await recoveryFundMock.connect(owner).emergency(); + const balanceOfOwnerAfterEmergency = (await daiContract.balanceOf(owner.address)).toString(); + + await expect( (await daiContract.balanceOf(recoveryFundMock.address)).toString() ).eq("0"); + await expect( BigNumber.from(balanceOfOwnerAfterEmergency) ).eq( BigNumber.from(balanceOfOwnerBeforeEmergency).add(balanceOfRecoveryFunds) ); + }); + + it("should Check balanceOfDai", async function () { + const daiContract = this.contracts.daiContract; + const recoveryFundMock = this.contracts.recoveryFundMock; + const owner = this.signers.owner; + + const balanceOfOwner = (await daiContract.balanceOf(owner.address)).toString(); + await daiContract.connect(owner).transfer( recoveryFundMock.address, balanceOfOwner ); + const balanceOfRecoveryFunds = (await recoveryFundMock.balanceOfDai()).toString(); - }) -} \ No newline at end of file + await expect( balanceOfRecoveryFunds ).eq(balanceOfOwner); + }); + + it("should Revert refund Not Opened", async function () { + const recoveryFundMock = this.contracts.recoveryFundMock; + const account1 = this.signers.account1; + await expect(recoveryFundMock.connect(account1).refund(10000)).to.be.reverted + }); + + it("should Success startRefund", async function () { + const recoveryFundMock = this.contracts.recoveryFundMock; + const owner = this.signers.owner; + await recoveryFundMock.connect(owner).startRefund(); + await expect(recoveryFundMock.connect(owner).startRefund()).to.be.reverted + }); + + it("should Revert refund sender's balance is insufficient", async function () { + const recoveryFundMock = this.contracts.recoveryFundMock; + const account1 = this.signers.account1; + await expect(recoveryFundMock.connect(account1).refund(10001)).to.be.reverted + }); + + it("should Success refund", async function () { + const daiContract = this.contracts.daiContract; + const recoveryFundMock = this.contracts.recoveryFundMock; + const account1 = this.signers.account1; + + const balanceOfOwnerBeforeRefund = (await daiContract.balanceOf(account1.address)).toString(); + await recoveryFundMock.connect(account1).refund(10000) + const balanceOfOwnerAfterRefund = (await daiContract.balanceOf(account1.address)).toString(); + await expect( BigNumber.from(balanceOfOwnerAfterRefund).toString() ).equal(BigNumber.from(balanceOfOwnerBeforeRefund).add(10000).toString()) + }); + + }); +} From 48bd14cf31c4f76197c8a7c9c2c1b73572be9600 Mon Sep 17 00:00:00 2001 From: 0xViktor Date: Thu, 19 Aug 2021 16:10:58 +0900 Subject: [PATCH 11/16] Hard Work Now! For Punkers by 0xViktor... - Add .env.example --- .env.example | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .env.example diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..3547cd4 --- /dev/null +++ b/.env.example @@ -0,0 +1,2 @@ +MNEMONIC="Mnemonic Phrase" +INFURA_API_KEY=INFURA_API_KEY \ No newline at end of file From 24c2683cbe7ba00bed84663c29afb359fc00078c Mon Sep 17 00:00:00 2001 From: 0xViktor Date: Fri, 27 Aug 2021 21:48:09 +0900 Subject: [PATCH 12/16] Hard Work Now! For Punkers by 0xViktor... - Update RecoveryRefund contract. - Changed from an exchange method to a fair distribution method. --- contracts/RecoveryFund.sol | 208 +++++++++++----------- test/unit/RecoveryFund/setup.behavior.ts | 216 ++++++++++++----------- 2 files changed, 220 insertions(+), 204 deletions(-) diff --git a/contracts/RecoveryFund.sol b/contracts/RecoveryFund.sol index 26f5c82..44455f3 100644 --- a/contracts/RecoveryFund.sol +++ b/contracts/RecoveryFund.sol @@ -2,128 +2,136 @@ pragma solidity >=0.5.0 <0.9.0; pragma experimental ABIEncoderV2; -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; +import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -import "@openzeppelin/contracts/access/Ownable.sol"; import "@openzeppelin/contracts/utils/math/SafeMath.sol"; -contract RecoveryFund is ERC20, ReentrancyGuard, Ownable { +contract RecoveryFund is ERC20Pausable, ReentrancyGuard { using SafeERC20 for IERC20; using SafeMath for uint256; - event Refund( address account, uint256 refundAmount ); + event Refund( uint256 refundAmount, uint256 refunded ); - bool private open = false; + address [] private _victims; address public addressOfDAI = address(0x6B175474E89094C44Da98b954EedeAC495271d0F); uint256 public totalRefund; uint256 public refunded; constructor() ERC20("RecoveryFund", "peUSD") { - _mint(address(0xe1cd21e5d6f4323E91dA943B0A4F1732acC7a138), 1213998517300000000000000); - _mint(address(0xf49a12fE6a05bdFc7C0cd4FE2A19724CCFbA18d3), 898535671700000000000000); - _mint(address(0x82dc92b01c7fF54911842956083795f60f6F64f4), 673946680643589900000000); - _mint(address(0xf2CcE4EcB119038dA9F4E18F82E07bb555FbAe2C), 77613054250000000000000); - _mint(address(0x5522234194F499F1DBF2E26C6eBD802bc2Cd9A2f), 77095278220000000000000); - _mint(address(0x896b94f4f27f12369698C302e2049cAe86936BbB), 62897497019999995000000); - _mint(address(0xac1f46771cF300bD4D43442960CD5e3d7476Ca34), 53912140300000000000000); - _mint(address(0xa52D39E63900992F36286953F884E014AC1F688D), 53905401290000000000000); - _mint(address(0xE5EC1103810217a3F83262C66c6a38a8e6387366), 53905401290000000000000); - _mint(address(0x577502784eDd9b0D84d08334c30D378975e8F5AC), 45751912140000000000000); - _mint(address(0x469Adaf766fb35F1a3c2569FE8C57a50F4B39131), 45355619800000000000000); - _mint(address(0x544Fc1845DF225679f5A939eF930837B8cBA0552), 44926783590000000000000); - _mint(address(0x4c3a58357f42a7741F61D2c7Fa3e7700996A5A60), 44920044570000000000000); - _mint(address(0xD9F0834E7Ca9B9262Df3189b850eBf9514027e82), 38383197560000000000000); - _mint(address(0x3E269201B45622c88E1cca18CE9A02609d4D2A54), 35934687850000000000000); - _mint(address(0xe9017c8De5040968D9752A18d805cD2A983E558c), 32003782980000000000000); - _mint(address(0x55d72CbcbA1Ab5C784bC52641D16c613E3b9BAD4), 31755408880000000000000); - _mint(address(0xf76CF36f638c7bCD83f4756beDb86243D98982F9), 29253624400000000000000); - _mint(address(0x29227FB595D091bcA244E76201c0dd50641D96C8), 27239560830000002000000); - _mint(address(0x2572a193DA3DEf3BAeA04cB18e06A52186aC1a98), 26954824385000000000000); - _mint(address(0x81a7E267Fd8339a01beb175f5A3d644FcF0B48Dc), 24234548190999998000000); - _mint(address(0x67D33CF1C7c699078f86D517A5a1cd1444A1E85C), 23536011086000000000000); - _mint(address(0x78d58Cc37cDAaBFde72c9a45B57Acbc3aBc3D7C9), 22546913770000000000000); - _mint(address(0x9dcAfb9A5A2A948ee7A70cDce6abC46322171ACD), 22463391790000000000000); - _mint(address(0x28C01B64F2492D82fd58FEECF8c71603c769BB4A), 22458456610000000000000); - _mint(address(0xE01f94F635028394cB15B572179DaEbbfC231761), 20217052610000000000000); - _mint(address(0x58e316d51aDA862d71fBa22ADA6f56D00186Af13), 18190064880000000000000); - _mint(address(0x09CF915e195aF33FA7B932C253352Ae9FBdB0106), 18090301810000000000000); - _mint(address(0x833A11B69873151fBA6D14f455db5392542825Ca), 17970855662000000000000); - _mint(address(0xffD851CFE0A50E1f1Cf2739Fc6D97754d49943a0), 17303601743000000000000); - _mint(address(0xe43458e1F964EEf5F707E81fb6f16Ab27c261071), 16930658390000000000000); - _mint(address(0x185C034Bdc0ca1d66Dc75650D33DEA190315595b), 14422499490000000000000); - _mint(address(0x0056BcFe33f5c6DfA62B6d3d3CF5A957429828BB), 14104489260000000000000); - _mint(address(0x963aD4Cc7798601cC36526162FDa203C21E90872), 13602254460000000000000); - _mint(address(0x6FdED91556cfBf602f05e279d270327382d9206E), 13471296060000000000000); - _mint(address(0xbEcB3451c1724Ce87801e6A09b547bB704e47884), 13380094687000000000000); - _mint(address(0x00F928a6aDC7c345EF919fe5A8Be2458580eF9f6), 11227034090000000000000); - _mint(address(0x241a119A8C634b58E78Ec4Ce9bF786756f9FD310), 8985356717000000000000); - _mint(address(0xd6b4D675F749F951B706Da0dF2CF99f913143B0C), 8985356717000000000000); - _mint(address(0x956D079B656a3955AB4f2f596d1bbfd6F3Ae60dC), 5197498877000000000000); - _mint(address(0x53274F93Bcf4647C39E8Af160226736c037574e6), 5177541065000000000000); - _mint(address(0x57d6C4efB2cA6703674343d1a7c0492fba49A6f3), 4492678359000000000000); - _mint(address(0xA2F8e4744FD85E9089a337fed259F05dd864a91e), 4492678359000000000000); - _mint(address(0xDcbBf294D08F70571666f7d08BBDf93189341B0e), 4488634499000000000000); - _mint(address(0xc28466bBD0F2f519829fEaEBA61470A381c5d07b), 4348912651000000000000); - _mint(address(0x7c25bB0ac944691322849419DF917c0ACc1d379B), 4043410523000000000000); - _mint(address(0xf6cDbA86999372f8B9104234885edFDAA1Eb01D1), 3832978820000000300000); - _mint(address(0x72f6A96a49B41467eef03924211e422Eaf639e3f), 3813708894000000000000); - _mint(address(0x9412b1e09ad73Dff95D099B2d1eFCD389c1422cf), 2243161150000000000000); - _mint(address(0x27EF5f1Fa276E8394681A260F77208ccD0305013), 1827100390000000000000); - _mint(address(0xB1f72d694711FcE55C62eA97BD3739Ff11ceF986), 1790766736000000000000); - _mint(address(0xE81e90222E0BD7D114cce6495B57C307Df342373), 1682368129000000000000); - _mint(address(0x7bF4aef8731377eC73a53f5032092d1699c56526), 1349221073000000000000); - _mint(address(0x38Bc5196d8b21782372a843E5A505d9F457e6ff8), 1311525431000000000000); - _mint(address(0x7E1BeE475e5EFF0e0810cfd1a940c50d7294f15F), 1156118143000000000000); - _mint(address(0xac1886d1ef0a19aFb8392ae4231472d4bBF39c4F), 1118439356000000000000); - _mint(address(0xd40Cd4D9f9BcF3fE57E37bC88EbFDB6ad2244578), 1108871318000000000000); - _mint(address(0x3F8a2B94578D73AF4CBE63C38D2C481Cc7D09F98), 949716401200000000000); - _mint(address(0xf934a267C6fB6170FAf5940fb3c12033a1f14d74), 924521947800000000000); - _mint(address(0xEcc03538d9Af264725dABd36417441F7971c91F6), 898535671700000000000); - _mint(address(0x41833aAE8C10641045fFA17E2377919201A71dDb), 896733830900000000000); - _mint(address(0xe75D13941a26be0d5E5C1a104494eFC67dEAfb38), 693526172300000000000); - _mint(address(0x9b95F760A7eBCd6dFbEb91FACdA9B8ac2296820f), 633523800100000000000); - _mint(address(0x9bb64e40DCBe4645F99F0a9e2507b5A53795fa70), 580593317000000000000); - _mint(address(0x8eA035d0995F0568969020A00D1891d1D29c7517), 461972401500000000000); - _mint(address(0x5E87597C7eCD23bA209B5354881e4Cafd2FC4D28), 457250243900000000000); - _mint(address(0xa3eC8fB0aCc93d0BB8CA73B9e90FE57275855E94), 358417937300000000000); - _mint(address(0x392027fDc620d397cA27F0c1C3dCB592F27A4dc3), 314487485100000000000); - _mint(address(0xD47835d494AC1Bf8C140B6A04E69b67e75928022), 262821684000000000000); - _mint(address(0x8e0e3D4a18AA44904ebda87C5B42545d1cEC2e5f), 213698063100000000000); - _mint(address(0xB444b304C55fa9c6fa995a758CBd5a96cF84782B), 64620692110000000000); - _mint(address(0x8E1D10aaeF9c0C0D337Aa47022BF0d96D21b56B9), 49312416690000000000); + _initialMint(address(0xe1cd21e5d6f4323E91dA943B0A4F1732acC7a138), 1213998517300000000000000); + _initialMint(address(0xf49a12fE6a05bdFc7C0cd4FE2A19724CCFbA18d3), 898535671700000000000000); + _initialMint(address(0x82dc92b01c7fF54911842956083795f60f6F64f4), 673946680643589900000000); + _initialMint(address(0xf2CcE4EcB119038dA9F4E18F82E07bb555FbAe2C), 77613054250000000000000); + _initialMint(address(0x5522234194F499F1DBF2E26C6eBD802bc2Cd9A2f), 77095278220000000000000); + _initialMint(address(0x896b94f4f27f12369698C302e2049cAe86936BbB), 62897497019999995000000); + _initialMint(address(0xac1f46771cF300bD4D43442960CD5e3d7476Ca34), 53912140300000000000000); + _initialMint(address(0xa52D39E63900992F36286953F884E014AC1F688D), 53905401290000000000000); + _initialMint(address(0xE5EC1103810217a3F83262C66c6a38a8e6387366), 53905401290000000000000); + _initialMint(address(0x577502784eDd9b0D84d08334c30D378975e8F5AC), 45751912140000000000000); + _initialMint(address(0x469Adaf766fb35F1a3c2569FE8C57a50F4B39131), 45355619800000000000000); + _initialMint(address(0x544Fc1845DF225679f5A939eF930837B8cBA0552), 44926783590000000000000); + _initialMint(address(0x4c3a58357f42a7741F61D2c7Fa3e7700996A5A60), 44920044570000000000000); + _initialMint(address(0xD9F0834E7Ca9B9262Df3189b850eBf9514027e82), 38383197560000000000000); + _initialMint(address(0x3E269201B45622c88E1cca18CE9A02609d4D2A54), 35934687850000000000000); + _initialMint(address(0xe9017c8De5040968D9752A18d805cD2A983E558c), 32003782980000000000000); + _initialMint(address(0x55d72CbcbA1Ab5C784bC52641D16c613E3b9BAD4), 31755408880000000000000); + _initialMint(address(0xf76CF36f638c7bCD83f4756beDb86243D98982F9), 29253624400000000000000); + _initialMint(address(0x29227FB595D091bcA244E76201c0dd50641D96C8), 27239560830000002000000); + _initialMint(address(0x2572a193DA3DEf3BAeA04cB18e06A52186aC1a98), 26954824385000000000000); + _initialMint(address(0x81a7E267Fd8339a01beb175f5A3d644FcF0B48Dc), 24234548190999998000000); + _initialMint(address(0x67D33CF1C7c699078f86D517A5a1cd1444A1E85C), 23536011086000000000000); + _initialMint(address(0x78d58Cc37cDAaBFde72c9a45B57Acbc3aBc3D7C9), 22546913770000000000000); + _initialMint(address(0x9dcAfb9A5A2A948ee7A70cDce6abC46322171ACD), 22463391790000000000000); + _initialMint(address(0x28C01B64F2492D82fd58FEECF8c71603c769BB4A), 22458456610000000000000); + _initialMint(address(0xE01f94F635028394cB15B572179DaEbbfC231761), 20217052610000000000000); + _initialMint(address(0x58e316d51aDA862d71fBa22ADA6f56D00186Af13), 18190064880000000000000); + _initialMint(address(0x09CF915e195aF33FA7B932C253352Ae9FBdB0106), 18090301810000000000000); + _initialMint(address(0x833A11B69873151fBA6D14f455db5392542825Ca), 17970855662000000000000); + _initialMint(address(0xffD851CFE0A50E1f1Cf2739Fc6D97754d49943a0), 17303601743000000000000); + _initialMint(address(0xe43458e1F964EEf5F707E81fb6f16Ab27c261071), 16930658390000000000000); + _initialMint(address(0x185C034Bdc0ca1d66Dc75650D33DEA190315595b), 14422499490000000000000); + _initialMint(address(0x0056BcFe33f5c6DfA62B6d3d3CF5A957429828BB), 14104489260000000000000); + _initialMint(address(0x963aD4Cc7798601cC36526162FDa203C21E90872), 13602254460000000000000); + _initialMint(address(0x6FdED91556cfBf602f05e279d270327382d9206E), 13471296060000000000000); + _initialMint(address(0xbEcB3451c1724Ce87801e6A09b547bB704e47884), 13380094687000000000000); + _initialMint(address(0x00F928a6aDC7c345EF919fe5A8Be2458580eF9f6), 11227034090000000000000); + _initialMint(address(0x241a119A8C634b58E78Ec4Ce9bF786756f9FD310), 8985356717000000000000); + _initialMint(address(0xd6b4D675F749F951B706Da0dF2CF99f913143B0C), 8985356717000000000000); + _initialMint(address(0x956D079B656a3955AB4f2f596d1bbfd6F3Ae60dC), 5197498877000000000000); + _initialMint(address(0x53274F93Bcf4647C39E8Af160226736c037574e6), 5177541065000000000000); + _initialMint(address(0x57d6C4efB2cA6703674343d1a7c0492fba49A6f3), 4492678359000000000000); + _initialMint(address(0xA2F8e4744FD85E9089a337fed259F05dd864a91e), 4492678359000000000000); + _initialMint(address(0xDcbBf294D08F70571666f7d08BBDf93189341B0e), 4488634499000000000000); + _initialMint(address(0xc28466bBD0F2f519829fEaEBA61470A381c5d07b), 4348912651000000000000); + _initialMint(address(0x7c25bB0ac944691322849419DF917c0ACc1d379B), 4043410523000000000000); + _initialMint(address(0xf6cDbA86999372f8B9104234885edFDAA1Eb01D1), 3832978820000000300000); + _initialMint(address(0x72f6A96a49B41467eef03924211e422Eaf639e3f), 3813708894000000000000); + _initialMint(address(0x9412b1e09ad73Dff95D099B2d1eFCD389c1422cf), 2243161150000000000000); + _initialMint(address(0x27EF5f1Fa276E8394681A260F77208ccD0305013), 1827100390000000000000); + _initialMint(address(0xB1f72d694711FcE55C62eA97BD3739Ff11ceF986), 1790766736000000000000); + _initialMint(address(0xE81e90222E0BD7D114cce6495B57C307Df342373), 1682368129000000000000); + _initialMint(address(0x7bF4aef8731377eC73a53f5032092d1699c56526), 1349221073000000000000); + _initialMint(address(0x38Bc5196d8b21782372a843E5A505d9F457e6ff8), 1311525431000000000000); + _initialMint(address(0x7E1BeE475e5EFF0e0810cfd1a940c50d7294f15F), 1156118143000000000000); + _initialMint(address(0xac1886d1ef0a19aFb8392ae4231472d4bBF39c4F), 1118439356000000000000); + _initialMint(address(0xd40Cd4D9f9BcF3fE57E37bC88EbFDB6ad2244578), 1108871318000000000000); + _initialMint(address(0x3F8a2B94578D73AF4CBE63C38D2C481Cc7D09F98), 949716401200000000000); + _initialMint(address(0xf934a267C6fB6170FAf5940fb3c12033a1f14d74), 924521947800000000000); + _initialMint(address(0xEcc03538d9Af264725dABd36417441F7971c91F6), 898535671700000000000); + _initialMint(address(0x41833aAE8C10641045fFA17E2377919201A71dDb), 896733830900000000000); + _initialMint(address(0xe75D13941a26be0d5E5C1a104494eFC67dEAfb38), 693526172300000000000); + _initialMint(address(0x9b95F760A7eBCd6dFbEb91FACdA9B8ac2296820f), 633523800100000000000); + _initialMint(address(0x9bb64e40DCBe4645F99F0a9e2507b5A53795fa70), 580593317000000000000); + _initialMint(address(0x8eA035d0995F0568969020A00D1891d1D29c7517), 461972401500000000000); + _initialMint(address(0x5E87597C7eCD23bA209B5354881e4Cafd2FC4D28), 457250243900000000000); + _initialMint(address(0xa3eC8fB0aCc93d0BB8CA73B9e90FE57275855E94), 358417937300000000000); + _initialMint(address(0x392027fDc620d397cA27F0c1C3dCB592F27A4dc3), 314487485100000000000); + _initialMint(address(0xD47835d494AC1Bf8C140B6A04E69b67e75928022), 262821684000000000000); + _initialMint(address(0x8e0e3D4a18AA44904ebda87C5B42545d1cEC2e5f), 213698063100000000000); + _initialMint(address(0xB444b304C55fa9c6fa995a758CBd5a96cF84782B), 64620692110000000000); + _initialMint(address(0x8E1D10aaeF9c0C0D337Aa47022BF0d96D21b56B9), 49312416690000000000); totalRefund = totalSupply(); + _pause(); } - function balanceOfDai() public view returns(uint256){ - return IERC20(addressOfDAI).balanceOf(address(this)); + function _initialMint(address account, uint256 amount) internal whenNotPaused{ + require(balanceOf(account) == 0, "REFUND : account has tokens"); + _mint(account, amount); + _victims.push(account); } - function startRefund() public onlyOwner { - require(!open, "REFUND : Already Opened"); - open = true; - } - - function stopRefund() public onlyOwner { - require(open, "REFUND : Not Opened"); - open = false; - } - - function emergency() public onlyOwner { - IERC20(addressOfDAI).safeTransfer(owner(), balanceOfDai()); + function minRefundAmount() public view returns(uint256){ + return totalSupply().div(balanceOf(address(0x8E1D10aaeF9c0C0D337Aa47022BF0d96D21b56B9))); } function refund( uint256 refundAmount ) public nonReentrant returns(bool){ - require( open, "REFUND : Not Opened"); - require( balanceOf(msg.sender) >= refundAmount, "REFUND : sender's balance is insufficient" ); - - _burn( msg.sender, refundAmount ); - IERC20(addressOfDAI).safeTransfer(msg.sender, refundAmount); - - emit Refund( msg.sender, refundAmount ); + require( IERC20(addressOfDAI).allowance(msg.sender, address(this)) >= refundAmount, "REFUND : allowance is insufficient" ); + require( IERC20(addressOfDAI).balanceOf(msg.sender) >= refundAmount, "REFUND : sender's balance is insufficient" ); + require( minRefundAmount() <= refundAmount, "REFUND : refundAmount less than minRefundAmount" ); + + IERC20 DAI = IERC20(addressOfDAI); + uint256 guaranteedSupply = totalSupply(); + uint256 amountToBeSent = refundAmount.sub( refundAmount.mod(minRefundAmount()) ); + uint256 amountSent = 0; + uint8 i = 0; + + _unpause(); + for (i; i < _victims.length; i++) { + uint value = amountToBeSent.mul( balanceOf(_victims[i]) ).div( guaranteedSupply ); + require( value > 0, "REFUND : sent value is zero" ); + _burn(_victims[i], value); + DAI.safeTransferFrom(msg.sender, _victims[i], value); + amountSent += value; + } + _pause(); - refunded += refundAmount; + require( guaranteedSupply.sub(amountSent) == totalSupply(), "REFUND : guaranteedSupply is not amount sent" ); + refunded += amountSent; + + emit Refund( amountSent, refunded ); + return false; } -} +} \ No newline at end of file diff --git a/test/unit/RecoveryFund/setup.behavior.ts b/test/unit/RecoveryFund/setup.behavior.ts index a852126..a97ff06 100644 --- a/test/unit/RecoveryFund/setup.behavior.ts +++ b/test/unit/RecoveryFund/setup.behavior.ts @@ -1,108 +1,116 @@ import { expect } from "chai"; -import { BigNumber, Contract } from "ethers"; -import { ethers } from "hardhat"; +import { BigNumber } from "ethers"; export function setUpBehavior(): void { - context("SetUp", function () { - it("should Revert startRefund Address Not Admin", async function () { - const recoveryFundMock = this.contracts.recoveryFundMock; - const account1 = this.signers.account1; - await expect( - recoveryFundMock.connect(account1).startRefund() - ).to.be.reverted; - }); - - it("should Revert stopRefund Address Not Admin", async function () { - const recoveryFundMock = this.contracts.recoveryFundMock; - const account1 = this.signers.account1; - await expect( - recoveryFundMock.connect(account1).stopRefund() - ).to.be.reverted; - }); - - it("should Revert stopRefund Not Opend", async function () { - const recoveryFundMock = this.contracts.recoveryFundMock; - const owner = this.signers.owner; - await expect( - recoveryFundMock.connect(owner).stopRefund() - ).to.be.reverted; - }); - - it("should Success startRefund", async function () { - const recoveryFundMock = this.contracts.recoveryFundMock; - const owner = this.signers.owner; - await recoveryFundMock.connect(owner).startRefund(); - await expect(recoveryFundMock.connect(owner).startRefund()).to.be.reverted - }); - - it("should Success stopRefund", async function () { - const recoveryFundMock = this.contracts.recoveryFundMock; - const owner = this.signers.owner; - await recoveryFundMock.connect(owner).stopRefund(); - await expect(recoveryFundMock.connect(owner).stopRefund()).to.be.reverted - }); - - it("should Revert emergency Not Owner address", async function () { - const recoveryFundMock = this.contracts.recoveryFundMock; - const account1 = this.signers.account1; - await expect(recoveryFundMock.connect(account1).emergency()).to.be.reverted - }); - - it("should Success emergency", async function () { - const daiContract = this.contracts.daiContract; - const recoveryFundMock = this.contracts.recoveryFundMock; - const owner = this.signers.owner; - - const balanceOfOwnerBeforeEmergency = (await daiContract.balanceOf(owner.address)).toString(); - const balanceOfRecoveryFunds = (await daiContract.balanceOf(recoveryFundMock.address)).toString(); - await recoveryFundMock.connect(owner).emergency(); - const balanceOfOwnerAfterEmergency = (await daiContract.balanceOf(owner.address)).toString(); - - await expect( (await daiContract.balanceOf(recoveryFundMock.address)).toString() ).eq("0"); - await expect( BigNumber.from(balanceOfOwnerAfterEmergency) ).eq( BigNumber.from(balanceOfOwnerBeforeEmergency).add(balanceOfRecoveryFunds) ); - }); - - it("should Check balanceOfDai", async function () { - const daiContract = this.contracts.daiContract; - const recoveryFundMock = this.contracts.recoveryFundMock; - const owner = this.signers.owner; - - const balanceOfOwner = (await daiContract.balanceOf(owner.address)).toString(); - await daiContract.connect(owner).transfer( recoveryFundMock.address, balanceOfOwner ); - const balanceOfRecoveryFunds = (await recoveryFundMock.balanceOfDai()).toString(); + context("SetUp", function() { - await expect( balanceOfRecoveryFunds ).eq(balanceOfOwner); - }); - - it("should Revert refund Not Opened", async function () { - const recoveryFundMock = this.contracts.recoveryFundMock; - const account1 = this.signers.account1; - await expect(recoveryFundMock.connect(account1).refund(10000)).to.be.reverted - }); - - it("should Success startRefund", async function () { - const recoveryFundMock = this.contracts.recoveryFundMock; - const owner = this.signers.owner; - await recoveryFundMock.connect(owner).startRefund(); - await expect(recoveryFundMock.connect(owner).startRefund()).to.be.reverted - }); - - it("should Revert refund sender's balance is insufficient", async function () { - const recoveryFundMock = this.contracts.recoveryFundMock; - const account1 = this.signers.account1; - await expect(recoveryFundMock.connect(account1).refund(10001)).to.be.reverted - }); - - it("should Success refund", async function () { - const daiContract = this.contracts.daiContract; - const recoveryFundMock = this.contracts.recoveryFundMock; - const account1 = this.signers.account1; - - const balanceOfOwnerBeforeRefund = (await daiContract.balanceOf(account1.address)).toString(); - await recoveryFundMock.connect(account1).refund(10000) - const balanceOfOwnerAfterRefund = (await daiContract.balanceOf(account1.address)).toString(); - await expect( BigNumber.from(balanceOfOwnerAfterRefund).toString() ).equal(BigNumber.from(balanceOfOwnerBeforeRefund).add(10000).toString()) - }); - - }); -} + it('should Revert allowance is exceeded', async function() { + const recoveryFundMock = this.contracts.recoveryFundMock + const daiContract = this.contracts.daiContract + const accountDai = this.signers.accountDai + await daiContract.approve( recoveryFundMock.address, 0 ) + await expect(recoveryFundMock.connect(accountDai).refund(1)).to.be.reverted; + }) + + it('should Revert sender`s balance is exceeded', async function() { + const recoveryFundMock = this.contracts.recoveryFundMock + const daiContract = this.contracts.daiContract + const accountDai = this.signers.accountDai + const balance = (await daiContract.balanceOf(accountDai.address)).toString() + await daiContract.approve( recoveryFundMock.address, BigNumber.from(balance).div(2).toString() ) + await expect(recoveryFundMock.connect(accountDai).refund( BigNumber.from(balance).div(2).add(1).toString() )).to.be.reverted; + }) + + it('should Success Refund', async function() { + const recoveryFundMock = this.contracts.recoveryFundMock + const daiContract = this.contracts.daiContract + const accountDai = this.signers.accountDai + let balance = (await daiContract.balanceOf(accountDai.address)).toString() + + const beforeTotalSupply = (await recoveryFundMock.totalSupply()).toString() + const minRefundAmount = (await recoveryFundMock.minRefundAmount()).toString() + + balance = BigNumber.from(balance).sub(BigNumber.from(balance).mod(minRefundAmount)) + await daiContract.approve( recoveryFundMock.address, balance ) + const refundTx = (await recoveryFundMock.connect(accountDai).refund(balance)).toString(); + + const refunded = (await recoveryFundMock.refunded()).toString(); + await expect(await recoveryFundMock.totalSupply()).eq( BigNumber.from(beforeTotalSupply).sub(refunded) ) + + await expect( await recoveryFundMock.balanceOf("0xe1cd21e5d6f4323E91dA943B0A4F1732acC7a138")).eq( BigNumber.from("1213998517300000000000000").sub(BigNumber.from("1213998517300000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xf49a12fE6a05bdFc7C0cd4FE2A19724CCFbA18d3")).eq( BigNumber.from("898535671700000000000000").sub(BigNumber.from("898535671700000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x82dc92b01c7fF54911842956083795f60f6F64f4")).eq( BigNumber.from("673946680643589900000000").sub(BigNumber.from("673946680643589900000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x8ed32Ed24303092c016Cdb24d51e153AD88c4875")).eq( BigNumber.from("89858957570000000000000").sub(BigNumber.from("89858957570000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xf2CcE4EcB119038dA9F4E18F82E07bb555FbAe2C")).eq( BigNumber.from("77613054250000000000000").sub(BigNumber.from("77613054250000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x5522234194F499F1DBF2E26C6eBD802bc2Cd9A2f")).eq( BigNumber.from("77095278220000000000000").sub(BigNumber.from("77095278220000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x896b94f4f27f12369698C302e2049cAe86936BbB")).eq( BigNumber.from("62897497019999995000000").sub(BigNumber.from("62897497019999995000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xac1f46771cF300bD4D43442960CD5e3d7476Ca34")).eq( BigNumber.from("53912140300000000000000").sub(BigNumber.from("53912140300000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xa52D39E63900992F36286953F884E014AC1F688D")).eq( BigNumber.from("53905401290000000000000").sub(BigNumber.from("53905401290000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xE5EC1103810217a3F83262C66c6a38a8e6387366")).eq( BigNumber.from("53905401290000000000000").sub(BigNumber.from("53905401290000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x577502784eDd9b0D84d08334c30D378975e8F5AC")).eq( BigNumber.from("45751912140000000000000").sub(BigNumber.from("45751912140000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x469Adaf766fb35F1a3c2569FE8C57a50F4B39131")).eq( BigNumber.from("45355619800000000000000").sub(BigNumber.from("45355619800000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x544Fc1845DF225679f5A939eF930837B8cBA0552")).eq( BigNumber.from("44926783590000000000000").sub(BigNumber.from("44926783590000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x4c3a58357f42a7741F61D2c7Fa3e7700996A5A60")).eq( BigNumber.from("44920044570000000000000").sub(BigNumber.from("44920044570000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xD9F0834E7Ca9B9262Df3189b850eBf9514027e82")).eq( BigNumber.from("38383197560000000000000").sub(BigNumber.from("38383197560000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x3E269201B45622c88E1cca18CE9A02609d4D2A54")).eq( BigNumber.from("35934687850000000000000").sub(BigNumber.from("35934687850000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xe9017c8De5040968D9752A18d805cD2A983E558c")).eq( BigNumber.from("32003782980000000000000").sub(BigNumber.from("32003782980000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x55d72CbcbA1Ab5C784bC52641D16c613E3b9BAD4")).eq( BigNumber.from("31755408880000000000000").sub(BigNumber.from("31755408880000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xf76CF36f638c7bCD83f4756beDb86243D98982F9")).eq( BigNumber.from("29253624400000000000000").sub(BigNumber.from("29253624400000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x29227FB595D091bcA244E76201c0dd50641D96C8")).eq( BigNumber.from("27239560830000002000000").sub(BigNumber.from("27239560830000002000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x2572a193DA3DEf3BAeA04cB18e06A52186aC1a98")).eq( BigNumber.from("26954824385000000000000").sub(BigNumber.from("26954824385000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x81a7E267Fd8339a01beb175f5A3d644FcF0B48Dc")).eq( BigNumber.from("24234548190999998000000").sub(BigNumber.from("24234548190999998000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x67D33CF1C7c699078f86D517A5a1cd1444A1E85C")).eq( BigNumber.from("23536011086000000000000").sub(BigNumber.from("23536011086000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x78d58Cc37cDAaBFde72c9a45B57Acbc3aBc3D7C9")).eq( BigNumber.from("22546913770000000000000").sub(BigNumber.from("22546913770000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x9dcAfb9A5A2A948ee7A70cDce6abC46322171ACD")).eq( BigNumber.from("22463391790000000000000").sub(BigNumber.from("22463391790000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x28C01B64F2492D82fd58FEECF8c71603c769BB4A")).eq( BigNumber.from("22458456610000000000000").sub(BigNumber.from("22458456610000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xE01f94F635028394cB15B572179DaEbbfC231761")).eq( BigNumber.from("20217052610000000000000").sub(BigNumber.from("20217052610000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x58e316d51aDA862d71fBa22ADA6f56D00186Af13")).eq( BigNumber.from("18190064880000000000000").sub(BigNumber.from("18190064880000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x09CF915e195aF33FA7B932C253352Ae9FBdB0106")).eq( BigNumber.from("18090301810000000000000").sub(BigNumber.from("18090301810000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x833A11B69873151fBA6D14f455db5392542825Ca")).eq( BigNumber.from("17970855662000000000000").sub(BigNumber.from("17970855662000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xffD851CFE0A50E1f1Cf2739Fc6D97754d49943a0")).eq( BigNumber.from("17303601743000000000000").sub(BigNumber.from("17303601743000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xe43458e1F964EEf5F707E81fb6f16Ab27c261071")).eq( BigNumber.from("16930658390000000000000").sub(BigNumber.from("16930658390000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x185C034Bdc0ca1d66Dc75650D33DEA190315595b")).eq( BigNumber.from("14422499490000000000000").sub(BigNumber.from("14422499490000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x0056BcFe33f5c6DfA62B6d3d3CF5A957429828BB")).eq( BigNumber.from("14104489260000000000000").sub(BigNumber.from("14104489260000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x963aD4Cc7798601cC36526162FDa203C21E90872")).eq( BigNumber.from("13602254460000000000000").sub(BigNumber.from("13602254460000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x6FdED91556cfBf602f05e279d270327382d9206E")).eq( BigNumber.from("13471296060000000000000").sub(BigNumber.from("13471296060000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xbEcB3451c1724Ce87801e6A09b547bB704e47884")).eq( BigNumber.from("13380094687000000000000").sub(BigNumber.from("13380094687000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x00F928a6aDC7c345EF919fe5A8Be2458580eF9f6")).eq( BigNumber.from("11227034090000000000000").sub(BigNumber.from("11227034090000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x241a119A8C634b58E78Ec4Ce9bF786756f9FD310")).eq( BigNumber.from("8985356717000000000000").sub(BigNumber.from("8985356717000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xd6b4D675F749F951B706Da0dF2CF99f913143B0C")).eq( BigNumber.from("8985356717000000000000").sub(BigNumber.from("8985356717000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x956D079B656a3955AB4f2f596d1bbfd6F3Ae60dC")).eq( BigNumber.from("5197498877000000000000").sub(BigNumber.from("5197498877000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x53274F93Bcf4647C39E8Af160226736c037574e6")).eq( BigNumber.from("5177541065000000000000").sub(BigNumber.from("5177541065000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x57d6C4efB2cA6703674343d1a7c0492fba49A6f3")).eq( BigNumber.from("4492678359000000000000").sub(BigNumber.from("4492678359000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xA2F8e4744FD85E9089a337fed259F05dd864a91e")).eq( BigNumber.from("4492678359000000000000").sub(BigNumber.from("4492678359000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xDcbBf294D08F70571666f7d08BBDf93189341B0e")).eq( BigNumber.from("4488634499000000000000").sub(BigNumber.from("4488634499000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xc28466bBD0F2f519829fEaEBA61470A381c5d07b")).eq( BigNumber.from("4348912651000000000000").sub(BigNumber.from("4348912651000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x7c25bB0ac944691322849419DF917c0ACc1d379B")).eq( BigNumber.from("4043410523000000000000").sub(BigNumber.from("4043410523000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xf6cDbA86999372f8B9104234885edFDAA1Eb01D1")).eq( BigNumber.from("3832978820000000300000").sub(BigNumber.from("3832978820000000300000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x72f6A96a49B41467eef03924211e422Eaf639e3f")).eq( BigNumber.from("3813708894000000000000").sub(BigNumber.from("3813708894000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x9412b1e09ad73Dff95D099B2d1eFCD389c1422cf")).eq( BigNumber.from("2243161150000000000000").sub(BigNumber.from("2243161150000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x27EF5f1Fa276E8394681A260F77208ccD0305013")).eq( BigNumber.from("1827100390000000000000").sub(BigNumber.from("1827100390000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xB1f72d694711FcE55C62eA97BD3739Ff11ceF986")).eq( BigNumber.from("1790766736000000000000").sub(BigNumber.from("1790766736000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xE81e90222E0BD7D114cce6495B57C307Df342373")).eq( BigNumber.from("1682368129000000000000").sub(BigNumber.from("1682368129000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x7bF4aef8731377eC73a53f5032092d1699c56526")).eq( BigNumber.from("1349221073000000000000").sub(BigNumber.from("1349221073000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x38Bc5196d8b21782372a843E5A505d9F457e6ff8")).eq( BigNumber.from("1311525431000000000000").sub(BigNumber.from("1311525431000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x7E1BeE475e5EFF0e0810cfd1a940c50d7294f15F")).eq( BigNumber.from("1156118143000000000000").sub(BigNumber.from("1156118143000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xac1886d1ef0a19aFb8392ae4231472d4bBF39c4F")).eq( BigNumber.from("1118439356000000000000").sub(BigNumber.from("1118439356000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xd40Cd4D9f9BcF3fE57E37bC88EbFDB6ad2244578")).eq( BigNumber.from("1108871318000000000000").sub(BigNumber.from("1108871318000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x3F8a2B94578D73AF4CBE63C38D2C481Cc7D09F98")).eq( BigNumber.from("949716401200000000000").sub(BigNumber.from("949716401200000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xf934a267C6fB6170FAf5940fb3c12033a1f14d74")).eq( BigNumber.from("924521947800000000000").sub(BigNumber.from("924521947800000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xEcc03538d9Af264725dABd36417441F7971c91F6")).eq( BigNumber.from("898535671700000000000").sub(BigNumber.from("898535671700000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x41833aAE8C10641045fFA17E2377919201A71dDb")).eq( BigNumber.from("896733830900000000000").sub(BigNumber.from("896733830900000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xe75D13941a26be0d5E5C1a104494eFC67dEAfb38")).eq( BigNumber.from("693526172300000000000").sub(BigNumber.from("693526172300000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x9b95F760A7eBCd6dFbEb91FACdA9B8ac2296820f")).eq( BigNumber.from("633523800100000000000").sub(BigNumber.from("633523800100000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x9bb64e40DCBe4645F99F0a9e2507b5A53795fa70")).eq( BigNumber.from("580593317000000000000").sub(BigNumber.from("580593317000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x8eA035d0995F0568969020A00D1891d1D29c7517")).eq( BigNumber.from("461972401500000000000").sub(BigNumber.from("461972401500000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x5E87597C7eCD23bA209B5354881e4Cafd2FC4D28")).eq( BigNumber.from("457250243900000000000").sub(BigNumber.from("457250243900000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xa3eC8fB0aCc93d0BB8CA73B9e90FE57275855E94")).eq( BigNumber.from("358417937300000000000").sub(BigNumber.from("358417937300000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x392027fDc620d397cA27F0c1C3dCB592F27A4dc3")).eq( BigNumber.from("314487485100000000000").sub(BigNumber.from("314487485100000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xD47835d494AC1Bf8C140B6A04E69b67e75928022")).eq( BigNumber.from("262821684000000000000").sub(BigNumber.from("262821684000000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x8e0e3D4a18AA44904ebda87C5B42545d1cEC2e5f")).eq( BigNumber.from("213698063100000000000").sub(BigNumber.from("213698063100000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0xB444b304C55fa9c6fa995a758CBd5a96cF84782B")).eq( BigNumber.from("64620692110000000000").sub(BigNumber.from("64620692110000000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x8E1D10aaeF9c0C0D337Aa47022BF0d96D21b56B9")).eq( BigNumber.from("49312416690000000000").sub(BigNumber.from("49312416690000000000").mul(balance).div(beforeTotalSupply))); + }) + + }) +} \ No newline at end of file From 039a4ece7c400ebc47769c42fbda26d9e4925c2a Mon Sep 17 00:00:00 2001 From: 0xViktor Date: Fri, 10 Sep 2021 17:47:01 +0900 Subject: [PATCH 13/16] Hard Work Now! For Punkers by 0xViktor... - 1st fix codes by Certic's preliminary report - not fixed the part that needs to be discussed about centralization --- contracts/3rdDeFiInterfaces/CEthInterface.sol | 23 -- contracts/Forge.sol | 271 +++++++++++++----- contracts/ForgeProxy.sol | 141 ++++++--- contracts/ForgeStorage.sol | 6 + contracts/OperatorTreasury.sol | 3 + contracts/PunkRewardPool.sol | 56 +--- contracts/RecoveryFund.sol | 3 + contracts/Referral.sol | 40 --- contracts/Treasury.sol | 25 +- contracts/Variables.sol | 2 - contracts/interfaces/ForgeInterface.sol | 5 +- contracts/interfaces/ModelInterface.sol | 9 - contracts/interfaces/ReferralInterface.sol | 7 - contracts/libs/CommitmentWeight.sol | 41 --- contracts/libs/Score.sol | 108 ------- .../IUniswapV2FactoryMock.sol} | 2 +- contracts/mock/RecoveryFundMock.sol | 14 - contracts/models/CompoundModel.sol | 15 +- test/shared/fixtures.ts | 24 +- test/unit/CompoundModel/setup.behavior.ts | 35 --- test/unit/Forge/initialize.behavior.ts | 8 +- test/unit/PunkRewardPool/setup.behavior.ts | 57 +--- test/unit/RecoveryFund/RecoveryFund.ts | 4 +- test/unit/RecoveryFund/initialize.behavior.ts | 4 +- test/unit/RecoveryFund/setup.behavior.ts | 7 +- test/unit/Referral/Referral.ts | 8 - test/unit/Referral/setup.behavior.ts | 22 -- test/unit/Treasury/Treasury.ts | 2 - test/unit/Treasury/setup.behavior.ts | 5 +- test/unit/before.behavior.ts | 6 +- 30 files changed, 382 insertions(+), 571 deletions(-) delete mode 100644 contracts/3rdDeFiInterfaces/CEthInterface.sol delete mode 100644 contracts/Referral.sol delete mode 100644 contracts/interfaces/ReferralInterface.sol delete mode 100644 contracts/libs/CommitmentWeight.sol delete mode 100644 contracts/libs/Score.sol rename contracts/{3rdDeFiInterfaces/IUniswapV2Factory.sol => mock/IUniswapV2FactoryMock.sol} (95%) delete mode 100644 contracts/mock/RecoveryFundMock.sol delete mode 100644 test/unit/Referral/Referral.ts delete mode 100644 test/unit/Referral/setup.behavior.ts diff --git a/contracts/3rdDeFiInterfaces/CEthInterface.sol b/contracts/3rdDeFiInterfaces/CEthInterface.sol deleted file mode 100644 index 110ca89..0000000 --- a/contracts/3rdDeFiInterfaces/CEthInterface.sol +++ /dev/null @@ -1,23 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.5.0 <0.9.0; - -interface CEthInterface { - - function mint() external payable; - - function redeem(uint256 redeemTokens) external returns (uint256); - - function redeemUnderlying(uint256 redeemAmount) external returns (uint256); - - function borrow(uint256 borrowAmount) external returns (uint256); - - function repayBorrow(uint256 repayAmount) external returns (uint256); - - function exchangeRateStored() external view returns (uint256); - - function balanceOf(address _owner) external view returns (uint256); - - function underlying() external view returns (address); - - function claimComp(address holder) external; -} diff --git a/contracts/Forge.sol b/contracts/Forge.sol index 17b908c..1f613a7 100644 --- a/contracts/Forge.sol +++ b/contracts/Forge.sol @@ -12,15 +12,25 @@ import "./interfaces/PunkRewardPoolInterface.sol"; import "./Ownable.sol"; import "./ForgeStorage.sol"; -contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ +contract Forge is + ForgeInterface, + ForgeStorage, + Ownable, + ERC20, + ReentrancyGuard +{ using SafeMath for uint256; using SafeERC20 for IERC20; uint256 constant SECONDS_DAY = 1 days; uint256 constant SECONDS_YEAR = 31556952; enum Status { - WITHDRAW_NOT_YET, NOTHING, ALREADY_WITHDRAWN_OR_IS_TERMINATED, ALL_WITHDRAWN + NOT_YET_WITHDRAWN_OR_WITHDRAWABLE, + ALREADY_WITHDRAWN, + IS_TERMINATED, + ALL_WITHDRAWN } + constructor() ERC20("PunkFinance", "Forge") {} /** @@ -53,30 +63,51 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ _count = 0; - emit Initialize(); + emit Initialize( storage_, variables_, token_, name_, symbol_ ); } - /** - * Replace the model. If model_ isn't CA(ContractAddress), it will be reverted. - * - * @param model_ Address of the associated Model - */ - function setModel(address model_) public OnlyAdminOrGovernance returns (bool){ - require( model_ != address(0), "FORGE : Model address is zero" ); - require( Address.isContract(model_), "FORGE : Model address must be the contract address."); - require( ModelInterface(model_).token() == _token, "FORGE : Model has Token address is not equals"); - require( ModelInterface(model_).forge() == address(this), "FORGE : Model has Forge address is not equals this address"); - require( _model != model_, "FORGE : Current Model"); - - if( _model != address(0) ){ + + function requestUpgradeModel(address model_) + public + OnlyAdminOrGovernance + returns (bool) + { + require(model_ != address(0), "FORGE : Model address is zero"); + require(Address.isContract(model_),"FORGE : Model address must be the contract address."); + require(ModelInterface(model_).token() == _token,"FORGE : Model has Token address is not equals"); + require(ModelInterface(model_).forge() == address(this),"FORGE : Model has Forge address is not equals this address"); + require(_model != model_, "FORGE : Current Model"); + + if( modelAddress() == address(0) ){ + _model = model_; + emit SetModel(address(0), model_); + }else{ + _nextUpgradeModel = model_; + _nextUpgradeModelTimestamp = block.timestamp.add( _delayTime ); + emit RequestUpgradeModel( _model, _nextUpgradeModel, _nextUpgradeModelTimestamp ); + } + + return false; + } + + function upgradeModelAccept() external override OnlyAdminOrGovernance returns(bool){ + require(_nextUpgradeModel != address(0x0), ""); + require(_nextUpgradeModelTimestamp <= block.timestamp, ""); + require(_nextUpgradeModelTimestamp != 0, ""); + + if (_model != address(0)) { ModelInterface(_model).withdrawAllToForge(); - IERC20(_token).safeTransfer( model_, IERC20(_token).balanceOf(address(this))); - ModelInterface(model_).invest(); + IERC20(_token).safeTransfer( _nextUpgradeModel, IERC20(_token).balanceOf(address(this)) ); + ModelInterface(_nextUpgradeModel).invest(); } - emit SetModel(_model, model_); - _model = model_; - return true; + emit SetModel(_model, _nextUpgradeModel); + _model = _nextUpgradeModel; + + _nextUpgradeModel = address(0x0); + _nextUpgradeModelTimestamp = 0; + + return false; } /** @@ -87,10 +118,18 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * * @return the withdrawable amount. */ - function withdrawable(address account, uint256 index) public view override returns (uint256){ + function withdrawable(address account, uint256 index) + public + view + override + returns (uint256) + { Saver memory s = saver(account, index); if (s.startTimestamp > block.timestamp) return 0; - if (s.status == uint256 (Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED)) return 0; + if ( + s.status == uint256(Status.IS_TERMINATED) || + s.status == uint256(Status.ALL_WITHDRAWN) + ) return 0; uint256 diff = block.timestamp.sub(s.startTimestamp); uint256 count = diff.div(SECONDS_DAY.mul(s.interval)).add(1); @@ -125,10 +164,21 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * @param count How often do you want to receive. * @param interval Number of times to receive (unit: 1 day) */ - function craftingSaver( uint256 amount, uint256 startTimestamp, uint256 count, uint256 interval ) public override onlyNormalUser returns (bool) { - require( amount > 0 && count > 0 && interval > 0 && startTimestamp > block.timestamp.add(24 * 60 * 60), "FORGE : Invalid Parameters"); + function craftingSaver( + uint256 amount, + uint256 startTimestamp, + uint256 count, + uint256 interval + ) public override onlyNormalUser returns (bool) { + require( + amount > 0 && + count > 0 && + interval > 0 && + startTimestamp > block.timestamp.add(24 * 60 * 60), + "FORGE : Invalid Parameters" + ); uint256 index = countByAccount(msg.sender); - require( index < 10, "FORGE : Too many crafting Account"); + require(index < 10, "FORGE : Too many crafting Account"); _savers[msg.sender].push( Saver( @@ -140,7 +190,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ 0, 0, 0, - 0, + uint256(Status.NOT_YET_WITHDRAWN_OR_WITHDRAWABLE), block.timestamp ) ); @@ -159,12 +209,21 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * @param index Saver's index * @param amount ERC20 Amount */ - function addDeposit(uint256 index, uint256 amount) public override nonReentrant onlyNormalUser returns (bool){ - require( saver(msg.sender, index).status < uint256(Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED), "FORGE : Terminated Saver" ); + function addDeposit(uint256 index, uint256 amount) + public + override + nonReentrant + onlyNormalUser + returns (bool) + { + require( + saver(msg.sender, index).status < uint256(Status.IS_TERMINATED), + "FORGE : Terminated Saver" + ); uint256 mint = 0; uint256 i = index; - + { mint = _exchangeToLp(amount); _savers[msg.sender][i].mint += mint; @@ -178,7 +237,7 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ emit AddDeposit(msg.sender, index, amount); _mint(msg.sender, mint); - _autoStake( mint ); + _autoStake(mint); } return true; @@ -188,12 +247,18 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * Withdraw * * Enter the amount of pLP token ( Do not enter ERC20 Token's Amount ) - * Withdraw excluding service fee. if saver has referral code, then discount service fee. + * Withdraw excluding service fee. * * @param index Saver's index * @param hopeUnderlying Forge's LP Token Amount */ - function withdrawUnderlying(uint256 index, uint256 hopeUnderlying) public override nonReentrant onlyNormalUser returns (bool){ + function withdrawUnderlying(uint256 index, uint256 hopeUnderlying) + public + override + nonReentrant + onlyNormalUser + returns (bool) + { return withdraw(index, _exchangeToLp(hopeUnderlying)); } @@ -201,15 +266,24 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * Withdraw * * Enter the amount of pLP token ( Do not enter ERC20 Token's Amount ) - * Withdraw excluding service fee. if saver has referral code, then discount service fee. + * Withdraw excluding service fee. * * @param index Saver's index * @param hope Forge's LP Token Amount */ - function withdraw(uint256 index, uint256 hope) public override nonReentrant onlyNormalUser returns (bool){ + function withdraw(uint256 index, uint256 hope) + public + override + nonReentrant + onlyNormalUser + returns (bool) + { Saver memory s = saver(msg.sender, index); uint256 withdrawablePlp = withdrawable(msg.sender, index); - require(s.status < uint256(Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED), "FORGE : Terminated Saver"); + require( + s.status < uint256(Status.IS_TERMINATED), + "FORGE : Terminated Saver" + ); require(withdrawablePlp >= hope, "FORGE : Insufficient Amount"); // TODO Confirm withdrawal of currency after use of balance. @@ -217,12 +291,18 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ { _autoUnstake(hope); - ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, index, hope, false); + ( + uint256 amountOfWithdraw, + uint256 amountOfServiceFee + ) = _withdrawValues(msg.sender, index, hope, false); _savers[msg.sender][index].released += hope; _savers[msg.sender][index].relAmount += amountOfWithdraw; _savers[msg.sender][index].updatedTimestamp = block.timestamp; - _savers[msg.sender][index].status = (_savers[msg.sender][index].mint == _savers[msg.sender][index].released) ? uint256(Status.ALL_WITHDRAWN) : uint256(Status.NOTHING); + _savers[msg.sender][index].status = (_savers[msg.sender][index] + .mint == _savers[msg.sender][index].released) + ? uint256(Status.ALL_WITHDRAWN) + : uint256(Status.ALREADY_WITHDRAWN); emit Withdraw(msg.sender, index, amountOfWithdraw); @@ -242,21 +322,33 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * * @param index Saver's index */ - function terminateSaver(uint256 index) public override nonReentrant onlyNormalUser returns (bool){ + function terminateSaver(uint256 index) + public + override + nonReentrant + onlyNormalUser + returns (bool) + { Saver memory s = saver(msg.sender, index); - require(s.status < uint256(Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED), "FORGE : Already Terminated or Completed"); + require( + s.status < uint256(Status.IS_TERMINATED), + "FORGE : Already Terminated or Completed" + ); uint256 hope = s.mint.sub(s.released); - + { - _autoUnstake( hope ); - ( uint256 amountOfWithdraw, uint256 amountOfServiceFee ) = _withdrawValues(msg.sender, index, hope, true); + _autoUnstake(hope); + ( + uint256 amountOfWithdraw, + uint256 amountOfServiceFee + ) = _withdrawValues(msg.sender, index, hope, true); - _savers[msg.sender][index].status = uint256(Status.ALREADY_WITHDRAWN_OR_IS_TERMINATED); + _savers[msg.sender][index].status = uint256(Status.IS_TERMINATED); _savers[msg.sender][index].released += hope; _savers[msg.sender][index].relAmount += amountOfWithdraw; _savers[msg.sender][index].updatedTimestamp = block.timestamp; - + emit Terminate(msg.sender, index, amountOfWithdraw); /* the actual amount to be withdrawn. */ @@ -276,10 +368,10 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * @return the exchange rate of ERC20 Token to pLP token */ function exchangeRate() public view override returns (uint256) { - return totalSupply() == 0 ? _tokenUnit : - _tokenUnit - .mul( totalVolume() ) - .div( totalSupply() ); + return + totalSupply() == 0 + ? _tokenUnit + : _tokenUnit.mul(totalVolume()).div(totalSupply()); } /** @@ -317,7 +409,12 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * * @return model address. */ - function saver(address account, uint256 index) public view override returns (Saver memory){ + function saver(address account, uint256 index) + public + view + override + returns (Saver memory) + { return _savers[account][index]; } @@ -339,8 +436,10 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * @param account subject to be withdrawn to */ function _withdrawTo(uint256 amount, address account) private { - if (amount != 0) - ModelInterface(_model).withdrawTo(amount, account); + if (amount != 0) { + ModelInterface(_model).withdrawToForge(amount); + IERC20(_token).safeTransfer(account, amount); + } } /** @@ -354,45 +453,62 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ * @return amountOfWithdraw * @return amountOfFee */ - function _withdrawValues( address account, uint256 index, uint256 hope, bool isTerminate ) public view returns (uint256 amountOfWithdraw, uint256 amountOfFee) { + function _withdrawValues( + address account, + uint256 index, + uint256 hope, + bool isTerminate + ) public view returns (uint256 amountOfWithdraw, uint256 amountOfFee) { Saver memory s = saver(account, index); uint256 fm = _variables.feeMultiplier(); uint256 amount = _exchangeToUnderlying(hope); uint256 successFee = _successFee(s, hope); - uint256 serviceFee = _serviceFee(s, hope).mul(isTerminate ? fm : 100).div(100); + uint256 serviceFee = _serviceFee(s, hope) + .mul(isTerminate ? fm : 100) + .div(100); - if( successFee.add(serviceFee) >= amount ){ + if (successFee.add(serviceFee) >= amount) { amountOfWithdraw = 0; amountOfFee = successFee.add(serviceFee); - }else{ + } else { amountOfFee = successFee.add(serviceFee); amountOfWithdraw = amount.sub(amountOfFee); } } - function _profit( Saver memory s ) private view returns(uint256){ + function _profit(Saver memory s) private view returns (uint256) { uint256 allValueToUnderlying = _exchangeToUnderlying(s.mint); uint256 accAmount = s.accAmount; - if( allValueToUnderlying > accAmount ){ + if (allValueToUnderlying > accAmount) { allValueToUnderlying.sub(accAmount); } return 0; } - function _successFee(Saver memory s, uint256 hope) private view returns (uint256 successFee){ + function _successFee(Saver memory s, uint256 hope) + private + view + returns (uint256 successFee) + { uint256 sf = _variables.successFee(); - uint256 profit = _profit( s ); - successFee = profit > 0 ? profit.mul(hope).mul(sf).div(100).div(s.mint) : 0; + uint256 profit = _profit(s); + successFee = profit > 0 + ? profit.mul(hope).mul(sf).div(100).div(s.mint) + : 0; } - function _serviceFee(Saver memory s, uint256 hope) private view returns (uint256 serviceFee){ + function _serviceFee(Saver memory s, uint256 hope) + private + view + returns (uint256 serviceFee) + { uint256 sf = _variables.serviceFee(); uint256 period = block.timestamp.sub(s.createTimestamp); uint256 amount = _exchangeToUnderlying(hope); - if( period >= SECONDS_YEAR ){ + if (period >= SECONDS_YEAR) { serviceFee = amount.mul(sf).div(100); - }else{ + } else { serviceFee = amount.mul(period).mul(sf).div(SECONDS_YEAR).div(100); } } @@ -410,11 +526,20 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ function _autoUnstake(uint256 hope) private { if (_variables.reward() != address(0)) { - PunkRewardPoolInterface(_variables.reward()).unstaking( - address(this), - hope, - msg.sender - ); + uint256 staked = PunkRewardPoolInterface(_variables.reward()).staked(address(this), msg.sender); + if (staked >= hope) { + PunkRewardPoolInterface(_variables.reward()).unstaking( + address(this), + hope, + msg.sender + ); + } else { + PunkRewardPoolInterface(_variables.reward()).unstaking( + address(this), + staked, + msg.sender + ); + } } } @@ -431,11 +556,15 @@ contract Forge is ForgeInterface, ForgeStorage, Ownable, ERC20, ReentrancyGuard{ return __decimals; } - function _exchangeToUnderlying( uint256 amount ) public view returns( uint256 ){ + function _exchangeToUnderlying(uint256 amount) + public + view + returns (uint256) + { return amount.mul(exchangeRate()).div(_tokenUnit); } - function _exchangeToLp( uint256 amount ) public view returns( uint256 ){ + function _exchangeToLp(uint256 amount) public view returns (uint256) { return amount.mul(_tokenUnit).div(exchangeRate()); } } diff --git a/contracts/ForgeProxy.sol b/contracts/ForgeProxy.sol index c935dfe..c3df6b6 100644 --- a/contracts/ForgeProxy.sol +++ b/contracts/ForgeProxy.sol @@ -5,65 +5,134 @@ import "@openzeppelin/contracts/utils/Address.sol"; import "@openzeppelin/contracts/proxy/Proxy.sol"; import "./OwnableStorage.sol"; -contract ForgeProxy is Proxy{ - +contract ForgeProxy is Proxy { event Upgraded(address indexed implementation); - - bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - bytes32 private constant _OWNABLE_STORAGE_SLOT = 0xb39836d625a4efe66fcfaa81a972ba97548a953a597399c161cd7df952bcf2e8; - bytes32 private constant _INITIALIZE_SLOT = 0x5995627934808137c9bf9d6f83d56749658ca23d1b6461c2a912ee34403ccd6a; - - modifier isInitializer(){ - require( getInitialize() != 1, "Initializable: contract is already initialized"); + event RequestUpgradTo(address nextImplementation, uint256 nextImplementationTimestamp ); + + uint256 private constant DELAY_TIME = 48 hours; + + bytes32 private constant _IMPLEMENTATION_SLOT = + 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; + bytes32 private constant _NEXT_IMPLEMENTATION_SLOT = + 0x386fd0873c4dae4047292a961c4d79fea20281d6344fde0002a91190c29439e0; + bytes32 private constant _NEXT_IMPLEMENTATION_TIMESTAMP_SLOT = + 0x2f4ec45a1b013d9051b3ed7cdfb2a5b3e28368f1dafac0e7898229fb9db98dca; + bytes32 private constant _OWNABLE_STORAGE_SLOT = + 0xb39836d625a4efe66fcfaa81a972ba97548a953a597399c161cd7df952bcf2e8; + bytes32 private constant _INITIALIZE_SLOT = + 0x5995627934808137c9bf9d6f83d56749658ca23d1b6461c2a912ee34403ccd6a; + + modifier isInitializer() { + require( + getInitialize() != 1, + "Initializable: contract is already initialized" + ); _; } - modifier CheckAdmin(){ - require( OwnableStorage( _storage() ).isAdmin(msg.sender), "OWNABLE: 0x0" ); + modifier CheckAdmin() { + require(OwnableStorage(_storage()).isAdmin(msg.sender), "OWNABLE: 0x0"); + _; + } + + modifier onlyAdminOrGovernance() { + require(OwnableStorage(_storage()).isAdmin(msg.sender) || OwnableStorage(_storage()).isGovernance(msg.sender), "OWNABLE: 0x0"); _; } - function initialize( address storage_, address implAddress ) public isInitializer{ - require(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); - require(_OWNABLE_STORAGE_SLOT == bytes32(uint256(keccak256("forge.proxy.ownablestrage")) - 1)); - require(_INITIALIZE_SLOT == bytes32(uint256(keccak256("forge.proxy.initialize")) - 1)); + function initialize(address storage_, address implAddress) + public + isInitializer + { + require( + _IMPLEMENTATION_SLOT == + bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1) + ); + require( + _OWNABLE_STORAGE_SLOT == + bytes32(uint256(keccak256("forge.proxy.ownablestrage")) - 1) + ); + require( + _INITIALIZE_SLOT == + bytes32(uint256(keccak256("forge.proxy.initialize")) - 1) + ); _setImplementation(implAddress); - _setStorage( storage_ ); - _setInitialize( ); + _setStorage(storage_); + _setInitialize(); } - function _setStorage( address storage_ ) internal { + function _storage() internal view returns (address storageAddr) { bytes32 slot = _OWNABLE_STORAGE_SLOT; // solhint-disable-next-line no-inline-assembly assembly { - sstore(slot, storage_) + storageAddr := sload(slot) } } - function _storage() internal view returns( address storageAddr ){ + function _setStorage(address storage_) internal { bytes32 slot = _OWNABLE_STORAGE_SLOT; // solhint-disable-next-line no-inline-assembly assembly { - storageAddr := sload(slot) + sstore(slot, storage_) } } - function _implementation() internal view override returns (address impl) { - bytes32 slot = _IMPLEMENTATION_SLOT; + function _nextImplementation() internal view returns (address nextImplementation) { + bytes32 slot = _NEXT_IMPLEMENTATION_SLOT; // solhint-disable-next-line no-inline-assembly assembly { - impl := sload(slot) + nextImplementation := sload(slot) + } + } + + function _setNextImplementation(address storage_) internal { + bytes32 slot = _NEXT_IMPLEMENTATION_SLOT; + // solhint-disable-next-line no-inline-assembly + assembly { + sstore(slot, storage_) + } + } + + function _nextImplementationTimestamp() internal view returns (uint256 nextImplementationTimestamp) { + bytes32 slot = _NEXT_IMPLEMENTATION_TIMESTAMP_SLOT; + // solhint-disable-next-line no-inline-assembly + assembly { + nextImplementationTimestamp := sload(slot) + } + } + + function _setNextImplementationTimestamp(uint256 nextImplementationTimestamp) internal { + bytes32 slot = _NEXT_IMPLEMENTATION_TIMESTAMP_SLOT; + // solhint-disable-next-line no-inline-assembly + assembly { + sstore(slot, nextImplementationTimestamp) } } - function upgradeTo(address newImplementation) public CheckAdmin { - _setImplementation(newImplementation); - emit Upgraded(newImplementation); + function requestUpgradeTo(address newImplementation) public onlyAdminOrGovernance { + require( newImplementation != address(0), "" ); + require( Address.isContract(newImplementation), ""); + + _setNextImplementation(newImplementation); + _setNextImplementationTimestamp(block.timestamp + DELAY_TIME); + + emit RequestUpgradTo(_nextImplementation(), _nextImplementationTimestamp()); + } + + function upgradeAccept(address ) public onlyAdminOrGovernance { + require( _nextImplementation() != address(0), "" ); + require( _nextImplementationTimestamp() <= block.timestamp, "" ); + + _setImplementation(_nextImplementation()); + emit Upgraded(_nextImplementation()); } function _setImplementation(address newImplementation) private { - require(Address.isContract(newImplementation), "ERC1967Proxy: new implementation is not a contract"); + require( + Address.isContract(newImplementation), + "ERC1967Proxy: new implementation is not a contract" + ); bytes32 slot = _IMPLEMENTATION_SLOT; @@ -73,18 +142,26 @@ contract ForgeProxy is Proxy{ } } - function _setInitialize( ) internal { + function _implementation() internal view override returns (address impl) { + bytes32 slot = _IMPLEMENTATION_SLOT; + // solhint-disable-next-line no-inline-assembly + assembly { + impl := sload(slot) + } + } + + function _setInitialize() internal { // solhint-disable-next-line no-inline-assembly assembly { sstore(_INITIALIZE_SLOT, 1) } } - function getInitialize( ) private view returns (uint256 str) { + function getInitialize() private view returns (uint256 str) { // solhint-disable-next-line no-inline-assembly assembly { - str := sload( _INITIALIZE_SLOT ) + str := sload(_INITIALIZE_SLOT) } } -} \ No newline at end of file +} diff --git a/contracts/ForgeStorage.sol b/contracts/ForgeStorage.sol index a794750..326d422 100644 --- a/contracts/ForgeStorage.sol +++ b/contracts/ForgeStorage.sol @@ -8,7 +8,13 @@ import "./Variables.sol"; abstract contract ForgeStorage{ Variables internal _variables; + + uint256 internal _delayTime = 48 hours; + address internal _model; + address internal _nextUpgradeModel; + uint256 internal _nextUpgradeModelTimestamp; + address internal _token; uint internal _tokenUnit; diff --git a/contracts/OperatorTreasury.sol b/contracts/OperatorTreasury.sol index 48939fd..b8e2aff 100644 --- a/contracts/OperatorTreasury.sol +++ b/contracts/OperatorTreasury.sol @@ -8,6 +8,8 @@ import "./Ownable.sol"; contract OperatorTreasury is Ownable { using SafeERC20 for IERC20; + event SetTo( address beforeAddress, address afterAddress ); + address private to; constructor( address storage_ ){ @@ -24,6 +26,7 @@ contract OperatorTreasury is Ownable { } function setTo(address account) public OnlyAdmin{ + emit SetTo( to, account ); to = account; } diff --git a/contracts/PunkRewardPool.sol b/contracts/PunkRewardPool.sol index be11856..408f9b9 100644 --- a/contracts/PunkRewardPool.sol +++ b/contracts/PunkRewardPool.sol @@ -31,7 +31,7 @@ contract PunkRewardPool is Ownable, ReentrancyGuard{ mapping( address => uint ) distributed; uint totalDistributed; - event Initialize(); + event Initialize( address storageAddress, address punk ); event Start(); event AddForge(address forge); event SetForge(address forge, uint weight); @@ -47,7 +47,7 @@ contract PunkRewardPool is Ownable, ReentrancyGuard{ weightSum = 0; totalDistributed = 0; - emit Initialize(); + emit Initialize( storage_, punk_ ); } function start() public OnlyAdmin{ @@ -62,17 +62,16 @@ contract PunkRewardPool is Ownable, ReentrancyGuard{ require( Address.isContract(forge), "PUNK_REWARD_POOL : Not Contract Address"); require( !checkForge( forge ), "PUNK_REWARD_POOL: Already Exist" ); forges.push( forge ); - weights[ forge ] = 0; + weights[ forge ] = 50; + weightSum += 50; emit AddForge(forge); } function setForge( address forge, uint weight ) public OnlyAdmin { // Hard Work Now! For Punkers by 0xViktor... require( checkForge( forge ), "PUNK_REWARD_POOL: Not Exist Forge" ); - ( uint minWeight , uint maxWeight ) = getWeightRange( forge ); - require( minWeight <= weight && weight <= maxWeight, "PUNK_REWARD_POOL: Invalid weight" ); + require( 0 <= weight && weight <= MAX_WEIGHT, "PUNK_REWARD_POOL: Invalid weight" ); weights[ forge ] = weight; - weightSum = 0; for( uint i = 0 ; i < forges.length ; i++ ){ weightSum += weights[ forges[ i ] ]; @@ -81,49 +80,24 @@ contract PunkRewardPool is Ownable, ReentrancyGuard{ emit SetForge( forge, weight ); } - function getWeightRange( address forge ) public view returns( uint, uint ){ - // Hard Work Now! For Punkers by 0xViktor... - if( forges.length == 0 ) return ( 1, MAX_WEIGHT ); - if( forges.length == 1 ) return ( weights[ forges[ 0 ] ], weights[ forges[ 0 ] ] ); - if( weightSum == 0 ) return ( 0, MAX_WEIGHT ); - - uint highestWeight = 0; - uint excludeWeight = weightSum.sub( weights[ forge ] ); - - for( uint i = 0 ; i < forges.length ; i++ ){ - if( forges[ i ] != forge && highestWeight < weights[ forges[ i ] ] ){ - highestWeight = weights[ forges[ i ] ]; - } - } - - if( highestWeight > excludeWeight.sub( highestWeight ) ){ - return ( highestWeight.sub( excludeWeight.sub( highestWeight ) ), MAX_WEIGHT < excludeWeight ? MAX_WEIGHT : excludeWeight ); - }else{ - return ( 0, MAX_WEIGHT < excludeWeight ? MAX_WEIGHT : excludeWeight ); - } - } - function claimPunk( ) public { // Hard Work Now! For Punkers by 0xViktor... claimPunk( msg.sender ); } - function claimPunk( address to ) public { + function claimPunk( address to ) public nonReentrant { // Hard Work Now! For Punkers by 0xViktor... - if( isStarting ){ - for( uint i = 0 ; i < forges.length ; i++ ){ - address forge = forges[i]; - uint reward = getClaimPunk( forge, to ); - checkPointBlocks[ forge ][ to ] = block.number; - if( reward > 0 ) Punk.safeTransfer( to, reward ); - distributed[ forge ] = distributed[ forge ].add( reward ); - totalDistributed = totalDistributed.add( reward ); - emit Claim( forge, reward ); - } + for( uint i = 0 ; i < forges.length ; i++ ){ + _claimPunk( forges[i], to ); } } function claimPunk( address forge, address to ) public nonReentrant { + // Hard Work Now! For Punkers by 0xViktor... + _claimPunk(forge, to); + } + + function _claimPunk( address forge, address to ) internal { // Hard Work Now! For Punkers by 0xViktor... if( isStarting ){ uint reward = getClaimPunk( forge, to ); @@ -143,7 +117,7 @@ contract PunkRewardPool is Ownable, ReentrancyGuard{ function staking( address forge, uint amount, address from ) public nonReentrant { // Hard Work Now! For Punkers by 0xViktor... require( msg.sender == from || checkForge( msg.sender ), "REWARD POOL : NOT ALLOWD" ); - claimPunk( from ); + _claimPunk( forge, from ); checkPointBlocks[ forge ][ from ] = block.number; IERC20( forge ).safeTransferFrom( from, address( this ), amount ); balances[ forge ][ from ] = balances[ forge ][ from ].add( amount ); @@ -159,7 +133,7 @@ contract PunkRewardPool is Ownable, ReentrancyGuard{ function unstaking( address forge, uint amount, address from ) public nonReentrant { // Hard Work Now! For Punkers by 0xViktor... require( msg.sender == from || checkForge( msg.sender ), "REWARD POOL : NOT ALLOWD" ); - claimPunk( from ); + _claimPunk( forge, from ); checkPointBlocks[ forge ][ from ] = block.number; balances[ forge ][ from ] = balances[ forge ][ from ].sub( amount ); IERC20( forge ).safeTransfer( from, amount ); diff --git a/contracts/RecoveryFund.sol b/contracts/RecoveryFund.sol index 44455f3..c3ba10a 100644 --- a/contracts/RecoveryFund.sol +++ b/contracts/RecoveryFund.sol @@ -102,6 +102,9 @@ contract RecoveryFund is ERC20Pausable, ReentrancyGuard { } function minRefundAmount() public view returns(uint256){ + if( balanceOf(address(0x8E1D10aaeF9c0C0D337Aa47022BF0d96D21b56B9)) == 0 ){ + return 1; + } return totalSupply().div(balanceOf(address(0x8E1D10aaeF9c0C0D337Aa47022BF0d96D21b56B9))); } diff --git a/contracts/Referral.sol b/contracts/Referral.sol deleted file mode 100644 index e422fed..0000000 --- a/contracts/Referral.sol +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.5.0 <0.9.0; - -contract Referral { - - mapping( address=>bytes12 ) private _registers; - mapping( bytes12=>address ) private _referrals; - uint private _count; - - function issue(address account) public returns(bool){ - require( account != address(0x0), "REF : Account is Zero address" ); - require( _registers[account] == 0, "REF : Already Registry" ); - - uint salt = 0; - while( true ){ - bytes12 code = _issueReferralCode(account, salt); - if( _referrals[code] == address(0x0) ){ - _referrals[code] = account; - _registers[account] = code; - break; - } - salt++; - } - _count++; - return true; - } - - function _issueReferralCode( address sender, uint salt ) private pure returns( bytes12 ){ - return bytes12(bytes32(uint(keccak256(abi.encodePacked(sender, salt))))); - } - - function validate( bytes12 code ) public view returns( address ){ - return _referrals[code]; - } - - function referralCode( address account ) public view returns( bytes12 ){ - return _registers[account]; - } - -} \ No newline at end of file diff --git a/contracts/Treasury.sol b/contracts/Treasury.sol index cdc02c1..4751783 100644 --- a/contracts/Treasury.sol +++ b/contracts/Treasury.sol @@ -11,13 +11,14 @@ contract Treasury is Ownable { using SafeERC20 for IERC20; mapping( uint256 => address ) private _tokens; + mapping( address => bool ) private _exists; uint256 public count; address private _punk; address private _grinder; address private _uRouterV2; - event Initialize(); + event Initialize( address storageAddress, address grinder, address punk, address uniswapRouterV2 ); event AddAsset( address token ); function initialize( address storage_, address grinder_, address punk_, address uRouterV2_ ) public initializer { @@ -25,19 +26,22 @@ contract Treasury is Ownable { _grinder = grinder_; _punk = punk_; _uRouterV2 = uRouterV2_; - emit Initialize(); + emit Initialize( storage_, grinder_, punk_, uRouterV2_ ); } function addAsset( address token ) public OnlyAdminOrGovernance { require( IERC20(token).totalSupply() > 0, "TREASURY : token is Invalid" ); - require( !existToken(token), "TREASURY : Already Registry Token" ); + require( !_exists[token], "TREASURY : Already Registry Token" ); _tokens[count] = token; + _exists[token] = true; count++; emit AddAsset(token); } - function buyBack() public OnlyAdminOrGovernance { + function buyBack( uint256 [] memory amountOutMins, uint256 amountOutMinsEth ) public OnlyAdminOrGovernance { // Hard Work Now! For Punkers by 0xViktor + require(amountOutMins.length == count, "TREASURY : amountOutMins invalid"); + require(amountOutMinsEth > 0, "TREASURY : amountOutMinsEth invalid"); for( uint i = 0 ; i < count ; i++ ){ uint balance = IERC20( _tokens[ i ] ).balanceOf( address( this ) ); @@ -51,7 +55,7 @@ contract Treasury is Ownable { IUniswapV2Router(_uRouterV2).swapExactTokensForTokens( balance, - 1, + amountOutMins[i], path, _grinder, block.timestamp + ( 15 * 60 ) @@ -66,7 +70,7 @@ contract Treasury is Ownable { pathForSwapEth[1] = address( _punk ); IUniswapV2Router(_uRouterV2).swapExactETHForTokens{value:address(this).balance}( - 1, + amountOutMinsEth, pathForSwapEth, _grinder, block.timestamp + ( 15 * 60 ) @@ -74,11 +78,12 @@ contract Treasury is Ownable { } } - function existToken( address token ) public view returns(bool){ - for( uint i = 0 ; i < count ; i++ ){ - if( _tokens[i] == token ) return true; + function assets() public view returns( address [] memory ){ + address[] memory assetsList = new address[](count); + for( uint256 i; i < count ;i++ ){ + assetsList[i] = _tokens[i]; } - return false; + return assetsList; } fallback () external payable { diff --git a/contracts/Variables.sol b/contracts/Variables.sol index 58b6dbc..5e1d2b2 100644 --- a/contracts/Variables.sol +++ b/contracts/Variables.sol @@ -7,8 +7,6 @@ import "./Saver.sol"; contract Variables is Ownable{ - address private _initializer; - uint256 private _successFee; uint256 private _serviceFee; uint256 private _feeMultiplier; diff --git a/contracts/interfaces/ForgeInterface.sol b/contracts/interfaces/ForgeInterface.sol index 8b2da70..b9edb7e 100644 --- a/contracts/interfaces/ForgeInterface.sol +++ b/contracts/interfaces/ForgeInterface.sol @@ -6,14 +6,16 @@ import "../Saver.sol"; interface ForgeInterface{ - event Initialize(); + event Initialize( address storageAddress, address variables, address token, string name, string symbol ); event CraftingSaver ( address owner, uint index, uint deposit ); event AddDeposit ( address owner, uint index, uint deposit ); event Withdraw ( address owner, uint index, uint amount ); event Terminate ( address owner, uint index, uint amount ); event SetModel ( address from, address to ); + event RequestUpgradeModel ( address from, address to, uint upgradeTimestamp ); function modelAddress() external view returns (address); + function upgradeModelAccept() external returns(bool); function withdrawable( address account, uint index ) external view returns(uint); function countByAccount( address account ) external view returns (uint); @@ -30,4 +32,5 @@ interface ForgeInterface{ function exchangeRate() external view returns( uint ); function totalVolume( ) external view returns( uint ); + } \ No newline at end of file diff --git a/contracts/interfaces/ModelInterface.sol b/contracts/interfaces/ModelInterface.sol index dbb2a98..da7bcad 100644 --- a/contracts/interfaces/ModelInterface.sol +++ b/contracts/interfaces/ModelInterface.sol @@ -41,15 +41,6 @@ interface ModelInterface{ */ function withdrawToForge( uint256 amount ) external; - /** - * @dev After withdrawing 'amount', send it to 'to'. - * - * IMPORTANT: Must use the "OnlyForge" Modifier from "ModelStorage.sol". - * - * Emits a {Withdraw} event. - */ - function withdrawTo( uint256 amount, address to ) external; - function forge() external view returns( address ); function token() external view returns( address ); diff --git a/contracts/interfaces/ReferralInterface.sol b/contracts/interfaces/ReferralInterface.sol deleted file mode 100644 index 0b48a18..0000000 --- a/contracts/interfaces/ReferralInterface.sol +++ /dev/null @@ -1,7 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.5.0 <0.9.0; - -interface ReferralInterface{ - function validate( bytes12 code ) external view returns( address ); - function referralCode( address account ) external view returns( bytes12 ); -} \ No newline at end of file diff --git a/contracts/libs/CommitmentWeight.sol b/contracts/libs/CommitmentWeight.sol deleted file mode 100644 index b9c0833..0000000 --- a/contracts/libs/CommitmentWeight.sol +++ /dev/null @@ -1,41 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.5.0 <0.9.0; - -/** Do Not Used This Contract */ -library CommitmentWeight { - - uint constant DECIMALS = 8; - int constant ONE = int(10**DECIMALS); - - function calculate( uint day ) external pure returns (uint){ - int x = int(day) * ONE; - int c = 3650 * ONE; - - int numerator = div( div( x, c ) - ONE, sqrt( ( div( pow( x, 2 ), 13322500 * ONE ) - div( x, 1825 * ONE ) + ONE + ONE ) ) ) + div( ONE, sqrt( 2 * ONE ) ); - int denominator = ( ONE + div( ONE, sqrt( 2 * ONE ) ) ); - - return uint( ONE + div( numerator, denominator ) ); - } - - function div( int a, int b ) internal pure returns ( int ){ - return ( a * int(ONE) / b ); - } - - function sqrt( int a ) internal pure returns ( int ){ - int s = a * int(ONE); - if( s < 0 ) s = s * -1; - uint k = uint(s); - uint z = (k + 1) / 2; - uint y = k; - while (z < y) { - y = z; - z = (k / z + z) / 2; - } - return int(y); - } - - function pow( int a, int b ) internal pure returns ( int ){ - return int(uint(a) ** uint(b) / uint(ONE)); - } - -} \ No newline at end of file diff --git a/contracts/libs/Score.sol b/contracts/libs/Score.sol deleted file mode 100644 index 781d95e..0000000 --- a/contracts/libs/Score.sol +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.5.0 <0.9.0; -pragma experimental ABIEncoderV2; - -import "@openzeppelin/contracts/utils/math/SafeMath.sol"; -import "./CommitmentWeight.sol"; -import "../Saver.sol"; - -/** Do Not Used This Contract */ -struct Transaction{ - bool pos; - uint256 amount; - uint256 timestamp; -} - -library Score { - using SafeMath for uint; - - uint constant SECONDS_OF_DAY = 24 * 60 * 60; - - function _getTimes( uint createTimestamp, uint startTimestamp, uint count, uint interval ) pure private returns( uint deposit, uint withdraw, uint timeline, uint max ){ - deposit = startTimestamp.sub( createTimestamp ); - withdraw = SECONDS_OF_DAY.mul( count ).mul( interval ); - timeline = deposit + withdraw; - max = SECONDS_OF_DAY.mul( 365 ).mul( 30 ); - } - - function _getDepositTransactions( uint createTimestamp, uint deposit, Transaction [] memory transactions ) private pure returns( uint depositCount, uint [] memory xAxis, uint [] memory yAxis ){ - depositCount = 0; - yAxis = new uint [] ( transactions.length ); - xAxis = new uint [] ( transactions.length + 1 ); - - for( uint i = 0 ; i < transactions.length ; i++ ){ - if( transactions[i].pos ) { - yAxis[ depositCount ] = i == 0 ? transactions[ i ].amount : transactions[ i ].amount.add( yAxis[ i - 1 ] ); - xAxis[ depositCount ] = transactions[ i ].timestamp.sub( createTimestamp ); - depositCount++; - } - } - xAxis[ depositCount ] = deposit; - - uint tempX = 0; - for( uint i = 1 ; i <= depositCount ; i++ ){ - tempX = tempX + xAxis[ i - 1 ]; - xAxis[ i ] = xAxis[ i ].sub( tempX ); - } - } - - function calculate( uint createTimestamp, uint startTimestamp, Transaction [] memory transactions, uint count, uint interval, uint decimals ) public pure returns ( uint ){ - - ( uint deposit, uint withdraw, uint timeline, uint max ) = _getTimes(createTimestamp, startTimestamp, count, interval); - ( uint depositCount, uint [] memory xAxis, uint [] memory yAxis ) = _getDepositTransactions( createTimestamp, deposit, transactions ); - - uint cw = CommitmentWeight.calculate( timeline.div( SECONDS_OF_DAY ) ); - - if( max <= deposit ){ - - uint accX = 0; - for( uint i = 0 ; i < depositCount ; i++ ){ - accX = accX.add( xAxis[ i + 1 ] ); - if( accX > max ){ - xAxis[ i + 1 ] = max.sub( accX.sub( xAxis[ i + 1 ] ) ); - depositCount = i + 1; - break; - } - } - - uint beforeWithdraw = 0; - for( uint i = 0 ; i < depositCount ; i++ ){ - beforeWithdraw = beforeWithdraw.add( yAxis[ i ].mul( xAxis[ i + 1 ] ) ); - } - - uint afterWithdraw = 0; - - return beforeWithdraw.add( afterWithdraw ).div( SECONDS_OF_DAY ).mul( cw ).div( 10 ** decimals ); - - }else if( max <= timeline ){ - - uint beforeWithdraw = 0; - for( uint i = 0 ; i < depositCount ; i++ ){ - beforeWithdraw = beforeWithdraw.add( yAxis[ i ].mul( xAxis[ i + 1 ] ) ); - } - - uint afterWithdraw = 0; - if( withdraw > 0 ){ - uint tempY = yAxis[ depositCount - 1 ].mul( timeline.sub( max ) ).div( withdraw ); - afterWithdraw = yAxis[ depositCount - 1 ].mul( withdraw ).div( 2 ); - afterWithdraw = afterWithdraw.sub( tempY.mul( timeline.sub( max ) ).div( 2 ) ); - } - - return beforeWithdraw.add( afterWithdraw ).div( SECONDS_OF_DAY ).mul( cw ).div( 10 ** decimals ); - - }else { - - uint beforeWithdraw = 0; - for( uint i = 0 ; i < depositCount ; i++ ){ - beforeWithdraw = beforeWithdraw.add( yAxis[ i ].mul( xAxis[ i + 1 ] ) ); - } - - uint afterWithdraw = yAxis[ depositCount - 1 ].mul( withdraw ).div( 2 ); - - return beforeWithdraw.add( afterWithdraw ).div( SECONDS_OF_DAY ).mul( cw ).div( 10 ** decimals ); - - } - - } - -} diff --git a/contracts/3rdDeFiInterfaces/IUniswapV2Factory.sol b/contracts/mock/IUniswapV2FactoryMock.sol similarity index 95% rename from contracts/3rdDeFiInterfaces/IUniswapV2Factory.sol rename to contracts/mock/IUniswapV2FactoryMock.sol index eec69eb..fef21be 100644 --- a/contracts/3rdDeFiInterfaces/IUniswapV2Factory.sol +++ b/contracts/mock/IUniswapV2FactoryMock.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.5.0; -interface IUniswapV2Factory { +interface IUniswapV2FactoryMock { event PairCreated(address indexed token0, address indexed token1, address pair, uint); function feeTo() external view returns (address); diff --git a/contracts/mock/RecoveryFundMock.sol b/contracts/mock/RecoveryFundMock.sol deleted file mode 100644 index 4a3dd0b..0000000 --- a/contracts/mock/RecoveryFundMock.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.5.0 <0.9.0; -pragma experimental ABIEncoderV2; - -import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; -import "../RecoveryFund.sol"; - -contract RecoveryFundMock is RecoveryFund { - using SafeERC20 for IERC20; - constructor() RecoveryFund() { - _mint(address(0x6d6B6AfBF1B564CBE87E1e34d23ac17a43fc33de), 10000); - totalRefund = totalSupply(); - } -} diff --git a/contracts/models/CompoundModel.sol b/contracts/models/CompoundModel.sol index 11ac414..635657e 100644 --- a/contracts/models/CompoundModel.sol +++ b/contracts/models/CompoundModel.sol @@ -17,7 +17,7 @@ contract CompoundModel is ModelInterface, ModelStorage, Initializable{ using SafeMath for uint; event Swap( uint compAmount, uint underlying ); - event Initialize(); + event Initialize( address forge, address model ); address creator; @@ -50,7 +50,7 @@ contract CompoundModel is ModelInterface, ModelStorage, Initializable{ _comptroller = comptroller_; _uRouterV2 = uRouterV2_; - emit Initialize(); + emit Initialize( forge_, address(this) ); } function underlyingBalanceInModel() public override view returns ( uint256 ){ @@ -86,17 +86,8 @@ contract CompoundModel is ModelInterface, ModelStorage, Initializable{ } function withdrawToForge( uint256 amount ) public OnlyForge override{ - withdrawTo( amount, forge() ); - } - - function withdrawTo( uint256 amount, address to ) public OnlyForge override{ - // Hard Work Now! For Punkers by 0xViktor - uint oldBalance = IERC20( token() ).balanceOf( address( this ) ); CTokenInterface( _cToken ).redeemUnderlying( amount ); - uint newBalance = IERC20( token() ).balanceOf( address( this ) ); - require( newBalance.sub( oldBalance ) > 0, "MODEL : REDEEM BALANCE IS ZERO"); - IERC20( token() ).safeTransfer( to, newBalance.sub( oldBalance ) ); - + IERC20( token() ).safeTransfer( forge(), amount ); emit Withdraw( amount, forge(), block.timestamp); } diff --git a/test/shared/fixtures.ts b/test/shared/fixtures.ts index fc77f8c..1c3eb74 100644 --- a/test/shared/fixtures.ts +++ b/test/shared/fixtures.ts @@ -1,6 +1,5 @@ -import { Signer } from "@ethersproject/abstract-signer" import { Contract, Wallet } from "ethers"; -import { artifacts, ethers, waffle } from "hardhat" +import { artifacts, ethers } from "hardhat" import { Tokens, UniswapAddresses } from "./mockInfo"; let storage:Contract @@ -56,12 +55,6 @@ export async function unitFixtureUniswapV2(): Promise { return uniswapRouter } -export async function unitFixtureUniswapFactoryV2(): Promise { - const IUniswapV2Factory = await artifacts.readArtifact("IUniswapV2Factory"); - const uniswapFactory = await ethers.getContractAt(IUniswapV2Factory.abi, UniswapAddresses.UniswapFactoryV2); - return uniswapFactory -} - export async function unitFixtureDaiToken(): Promise { const IDaiToken = await artifacts.readArtifact("IERC20") const daiToken = await ethers.getContractAt(IDaiToken.abi, Tokens.Dai) @@ -69,7 +62,7 @@ export async function unitFixtureDaiToken(): Promise { } export async function unitFixtureRecoveryFund([,,,,,owner] : Wallet[]): Promise { - const RecoveryFundMock = await ethers.getContractFactory("RecoveryFundMock") + const RecoveryFundMock = await ethers.getContractFactory("RecoveryFund") const recoveryFundMock = await RecoveryFundMock.connect(owner).deploy() return recoveryFundMock } @@ -82,13 +75,6 @@ export async function unitFixtureOwnableStorage([,,,,,owner] : Wallet[]): Promis return ownableStorage } -export async function unitFixtureReferral(): Promise { - const Referral = await ethers.getContractFactory("Referral") - const referral = await Referral.deploy() - await referral.deployed() - return referral; -} - export async function unitFixtureTreasury(): Promise { const Treasury = await ethers.getContractFactory("Treasury") const treasury = await Treasury.deploy() @@ -123,3 +109,9 @@ export async function unitFixtureFairLaunch(): Promise { await fairLaunch.deployed() return fairLaunch; } + +export async function unitFixtureUniswapFactoryV2(): Promise { + const IUniswapV2Factory = await artifacts.readArtifact("IUniswapV2FactoryMock"); + const uniswapFactory = await ethers.getContractAt(IUniswapV2Factory.abi, UniswapAddresses.UniswapFactoryV2); + return uniswapFactory +} \ No newline at end of file diff --git a/test/unit/CompoundModel/setup.behavior.ts b/test/unit/CompoundModel/setup.behavior.ts index 48f25c8..2de1c81 100644 --- a/test/unit/CompoundModel/setup.behavior.ts +++ b/test/unit/CompoundModel/setup.behavior.ts @@ -1,43 +1,8 @@ import { expect } from "chai"; -import { ethers } from "hardhat"; -import { Tokens, CompoundAddresses } from "../../shared/mockInfo"; -import { ethToWei } from "../../shared/utils"; export function setUpBehavior(): void { context("SetUp", function() { - it('should Success transfer and invest', async function() { - // const compoundModel = this.contracts.compoundModel - - // const daiContract = this.contracts.daiContract - // const uniswapV2Router = this.contracts.uniswapV2Router - // const accountDai = this.signers.accountDai - - // const blockNumber = await ethers.provider.getBlockNumber() - // const blockInfo = await ethers.provider.getBlock(blockNumber) - - // const swapResult = await uniswapV2Router.connect(accountDai).swapExactETHForTokens( - // ethToWei("10"), - // [Tokens.WETH, Tokens.Dai], - // accountDai.address, - // blockInfo.timestamp + 25*60*60, - // {value: ethToWei("10"), gasLimit: '1300000'} - // ) - // await swapResult.wait() - - // console.log( "balanceOf", ( await daiContract.balanceOf( accountDai.address ) ).toString() ) - // await expect( compoundModel.withdrawTo( forge.address, 100 ) ).to.be.reverted - - // const CDAI = await ethers.getContractAt("CTokenInterface", CompoundAddresses.cDai ); - // console.log("CDAI", CDAI) - }) - - it('should Revert withdrawTo', async function() { - const compoundModel = this.contracts.compoundModel - const forge = this.contracts.forge - await expect( compoundModel.withdrawTo( forge.address, 100 ) ).to.be.reverted - }) - it('should Revert withdrawToForge', async function() { const compoundModel = this.contracts.compoundModel await expect( compoundModel.withdrawToForge( 100 ) ).to.be.reverted diff --git a/test/unit/Forge/initialize.behavior.ts b/test/unit/Forge/initialize.behavior.ts index 48dcefd..d2838bd 100644 --- a/test/unit/Forge/initialize.behavior.ts +++ b/test/unit/Forge/initialize.behavior.ts @@ -37,26 +37,26 @@ export function initialBehavior(): void { const forge = this.contracts.forge; const compoundModel = this.contracts.compoundModel; const account = this.signers.account1; - await expect(forge.connect(account).setModel(compoundModel.address)).to.be.reverted + await expect(forge.connect(account).requestUpgradeModel(compoundModel.address)).to.be.reverted }) it('should Revert Forge setModel address zero', async function() { const forge = this.contracts.forge; const owner = this.signers.owner; - await expect(forge.connect(owner).setModel("0x0000000000000000000000000000000000000000")).to.be.reverted + await expect(forge.connect(owner).requestUpgradeModel("0x0000000000000000000000000000000000000000")).to.be.reverted }) it('should Revert Forge setModel address EOA', async function() { const forge = this.contracts.forge; const owner = this.signers.owner; - await expect(forge.connect(owner).setModel(owner.address)).to.be.reverted + await expect(forge.connect(owner).requestUpgradeModel(owner.address)).to.be.reverted }) it('should Success Forge setModel', async function() { const forge = this.contracts.forge; const compoundModel = this.contracts.compoundModel; const owner = this.signers.owner; - await expect(forge.connect(owner).setModel(compoundModel.address)).emit(forge, "SetModel").withArgs("0x0000000000000000000000000000000000000000", compoundModel.address); + await expect(forge.connect(owner).requestUpgradeModel(compoundModel.address)).emit(forge, "SetModel").withArgs("0x0000000000000000000000000000000000000000", compoundModel.address); }) }) diff --git a/test/unit/PunkRewardPool/setup.behavior.ts b/test/unit/PunkRewardPool/setup.behavior.ts index 8af8b3e..1079adb 100644 --- a/test/unit/PunkRewardPool/setup.behavior.ts +++ b/test/unit/PunkRewardPool/setup.behavior.ts @@ -48,70 +48,15 @@ export function setUpBehavior(): void { await expect( rewardPool.connect(owner).addForge( forge.address ) ).to.be.reverted }) - it('should Revert setForge Overflow Max Value', async function() { - const rewardPool = this.contracts.rewardPool - const forge = this.contracts.forge - const owner = this.signers.owner - const range:BigNumber[] = await rewardPool.getWeightRange(forge.address) - const weight = range[range.length-1].toNumber() + 1 - await expect( rewardPool.connect(owner).setForge( forge.address, weight ) ).be.to.reverted - }) - - it('should Check weightRange Forge Counts 0', async function() { - const rewardPool = this.contracts.rewardPool - const forge = this.contracts.forge - const range:BigNumber[] = await rewardPool.getWeightRange(forge.address) - // Only 0 : 500 - await expect( range[0].toNumber() ).eq( 0 ) - await expect( range[1].toNumber() ).eq( 500 ) - }) - it('should Success setForge', async function() { const rewardPool = this.contracts.rewardPool const forge = this.contracts.forge const owner = this.signers.owner - const weight = 10 + const weight = 150 await expect( rewardPool.connect(owner).setForge( forge.address, weight ) ).emit(rewardPool, "SetForge").withArgs(forge.address, weight) await expect( await rewardPool.getWeight(forge.address) ).equal(weight) }) - it('should Check weightRange Forge Counts 1', async function() { - const rewardPool = this.contracts.rewardPool - const forge2nd = this.contracts.forge2 - const range:BigNumber[] = await rewardPool.getWeightRange(forge2nd.address) - // Only 10 : 10 - await expect( range[0].toNumber() ).eq( 10 ) - await expect( range[1].toNumber() ).eq( 10 ) - }) - - it('should Success setForge 2nd', async function() { - const rewardPool = this.contracts.rewardPool - const forge2nd = this.contracts.forge2 - const owner = this.signers.owner - const weight = 10 - - await expect( rewardPool.connect(owner).setForge( forge2nd.address, weight ) ).emit(rewardPool, "SetForge").withArgs(forge2nd.address, weight) - await expect( await rewardPool.getWeight(forge2nd.address) ).equal(weight) - }) - - it('should Check weightRange Forge Counts 2', async function() { - const rewardPool = this.contracts.rewardPool - const forge3rd = this.contracts.forge3 - const range:BigNumber[] = await rewardPool.getWeightRange(forge3rd.address) - // Only 0 : 20 - await expect( range[0].toNumber() ).eq( 0 ) - await expect( range[1].toNumber() ).eq( 20 ) - }) - - it('should Success setForge 3nd', async function() { - const rewardPool = this.contracts.rewardPool - const forge3rd = this.contracts.forge3 - const owner = this.signers.owner - const weight = 10 - await expect( rewardPool.connect(owner).setForge( forge3rd.address, weight ) ).emit(rewardPool, "SetForge").withArgs(forge3rd.address, weight) - await expect( await rewardPool.getWeight(forge3rd.address) ).equal(weight) - }) - it('should Check getWeightSum', async function() { const rewardPool = this.contracts.rewardPool const forge = this.contracts.forge diff --git a/test/unit/RecoveryFund/RecoveryFund.ts b/test/unit/RecoveryFund/RecoveryFund.ts index 2ae78f0..3b66c2a 100644 --- a/test/unit/RecoveryFund/RecoveryFund.ts +++ b/test/unit/RecoveryFund/RecoveryFund.ts @@ -4,9 +4,9 @@ import { setUpBehavior } from "./setup.behavior" export function unitTestRecoveryFund(): void { - describe("RecoveryFundMock", function() { + describe("RecoveryFund", function() { initialBehavior() setUpBehavior() }) -} \ No newline at end of file +} diff --git a/test/unit/RecoveryFund/initialize.behavior.ts b/test/unit/RecoveryFund/initialize.behavior.ts index 5c6eac1..b4a81e7 100644 --- a/test/unit/RecoveryFund/initialize.behavior.ts +++ b/test/unit/RecoveryFund/initialize.behavior.ts @@ -27,13 +27,11 @@ export function initialBehavior(): void { it('should Check total refund', async function() { const recoveryFundMock = this.contracts.recoveryFundMock; - await expect( await recoveryFundMock.totalRefund() ).eq(BigNumber.from("3951644672859289895300000").add("10000")) + await expect( await recoveryFundMock.totalRefund() ).eq(BigNumber.from("3951644672859289895300000")) }) it('should Check peUSD balances', async function(){ const recoveryFundMock = this.contracts.recoveryFundMock; - - await expect( await recoveryFundMock.balanceOf("0x6d6B6AfBF1B564CBE87E1e34d23ac17a43fc33de")).eq( BigNumber.from("10000")); await expect( await recoveryFundMock.balanceOf("0xe1cd21e5d6f4323E91dA943B0A4F1732acC7a138")).eq( BigNumber.from("1213998517300000000000000")); await expect( await recoveryFundMock.balanceOf("0xf49a12fE6a05bdFc7C0cd4FE2A19724CCFbA18d3")).eq( BigNumber.from("898535671700000000000000")); diff --git a/test/unit/RecoveryFund/setup.behavior.ts b/test/unit/RecoveryFund/setup.behavior.ts index a97ff06..671f57e 100644 --- a/test/unit/RecoveryFund/setup.behavior.ts +++ b/test/unit/RecoveryFund/setup.behavior.ts @@ -29,18 +29,17 @@ export function setUpBehavior(): void { const beforeTotalSupply = (await recoveryFundMock.totalSupply()).toString() const minRefundAmount = (await recoveryFundMock.minRefundAmount()).toString() - + balance = BigNumber.from(balance).sub(BigNumber.from(balance).mod(minRefundAmount)) await daiContract.approve( recoveryFundMock.address, balance ) - const refundTx = (await recoveryFundMock.connect(accountDai).refund(balance)).toString(); - + await recoveryFundMock.connect(accountDai).refund(balance); const refunded = (await recoveryFundMock.refunded()).toString(); + await expect(await recoveryFundMock.totalSupply()).eq( BigNumber.from(beforeTotalSupply).sub(refunded) ) await expect( await recoveryFundMock.balanceOf("0xe1cd21e5d6f4323E91dA943B0A4F1732acC7a138")).eq( BigNumber.from("1213998517300000000000000").sub(BigNumber.from("1213998517300000000000000").mul(balance).div(beforeTotalSupply))); await expect( await recoveryFundMock.balanceOf("0xf49a12fE6a05bdFc7C0cd4FE2A19724CCFbA18d3")).eq( BigNumber.from("898535671700000000000000").sub(BigNumber.from("898535671700000000000000").mul(balance).div(beforeTotalSupply))); await expect( await recoveryFundMock.balanceOf("0x82dc92b01c7fF54911842956083795f60f6F64f4")).eq( BigNumber.from("673946680643589900000000").sub(BigNumber.from("673946680643589900000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x8ed32Ed24303092c016Cdb24d51e153AD88c4875")).eq( BigNumber.from("89858957570000000000000").sub(BigNumber.from("89858957570000000000000").mul(balance).div(beforeTotalSupply))); await expect( await recoveryFundMock.balanceOf("0xf2CcE4EcB119038dA9F4E18F82E07bb555FbAe2C")).eq( BigNumber.from("77613054250000000000000").sub(BigNumber.from("77613054250000000000000").mul(balance).div(beforeTotalSupply))); await expect( await recoveryFundMock.balanceOf("0x5522234194F499F1DBF2E26C6eBD802bc2Cd9A2f")).eq( BigNumber.from("77095278220000000000000").sub(BigNumber.from("77095278220000000000000").mul(balance).div(beforeTotalSupply))); await expect( await recoveryFundMock.balanceOf("0x896b94f4f27f12369698C302e2049cAe86936BbB")).eq( BigNumber.from("62897497019999995000000").sub(BigNumber.from("62897497019999995000000").mul(balance).div(beforeTotalSupply))); diff --git a/test/unit/Referral/Referral.ts b/test/unit/Referral/Referral.ts deleted file mode 100644 index 5f5fb8c..0000000 --- a/test/unit/Referral/Referral.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { setUpBehavior } from "./setup.behavior" - -export function unitTestReferral(): void { - describe("Referral", function() { - setUpBehavior() - }) - -} \ No newline at end of file diff --git a/test/unit/Referral/setup.behavior.ts b/test/unit/Referral/setup.behavior.ts deleted file mode 100644 index 4eb1dde..0000000 --- a/test/unit/Referral/setup.behavior.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { expect } from "chai"; - -export function setUpBehavior(): void { - context("SetUp", function() { - - it('should Success Issue ReferralCode & Revert Already Issue', async function() { - const referral = this.contracts.referral - const account1 = this.signers.account1 - await referral.issue(account1.address); - await expect( referral.issue(account1.address) ).to.be.reverted - }) - - it('should Check & Validate', async function() { - const referral = this.contracts.referral - const account1 = this.signers.account1 - const address = await referral.referralCode( account1.address ); - await expect( await referral.validate( address ) ).to.be.eq( account1.address ) - }) - - }) - -} \ No newline at end of file diff --git a/test/unit/Treasury/Treasury.ts b/test/unit/Treasury/Treasury.ts index 3231cca..42dc896 100644 --- a/test/unit/Treasury/Treasury.ts +++ b/test/unit/Treasury/Treasury.ts @@ -4,9 +4,7 @@ import { setUpBehavior } from "./setup.behavior" export function unitTestTreasury(): void { describe("Treasury", function() { - initialBehavior() - setUpBehavior() }) diff --git a/test/unit/Treasury/setup.behavior.ts b/test/unit/Treasury/setup.behavior.ts index e01455b..232edf9 100644 --- a/test/unit/Treasury/setup.behavior.ts +++ b/test/unit/Treasury/setup.behavior.ts @@ -26,13 +26,12 @@ export function setUpBehavior(): void { it('should Revert addAsset Buyback Test', async function() { const treasury = this.contracts.treasury const owner = this.signers.owner - await treasury.connect(owner).buyBack() + await treasury.connect(owner).buyBack([1], 1); }) // it('should Revert addAsset Buyback Test', async function() { // const treasury = this.contracts.treasury // const owner = this.signers.owner - // const DAI = this.contracts.daiContract // const swapResult = await this.contracts.uniswapV2Router.connect(this.signers.owner).swapExactETHForTokens( // 10000000000, // [Tokens.WETH, Tokens.Dai], @@ -41,7 +40,7 @@ export function setUpBehavior(): void { // {value: "100000000000000000000", gasLimit: '2300000'} // ) // await swapResult.wait() - // await treasury.connect(owner).buyBack() + // await treasury.connect(owner).buyBack([1], 1) // }) }) diff --git a/test/unit/before.behavior.ts b/test/unit/before.behavior.ts index 852d0c3..6688b3b 100644 --- a/test/unit/before.behavior.ts +++ b/test/unit/before.behavior.ts @@ -7,13 +7,12 @@ import { unitFixtureForge3rd, unitFixtureOwnableStorage, unitFixtureUniswapV2, - unitFixtureUniswapFactoryV2, unitFixtureVariables, - unitFixtureReferral, unitFixturePunkRewardPool, unitFixtureTreasury, unitFixtureOpTreasury, unitFixtureRecoveryFund, + unitFixtureUniswapFactoryV2, } from "../shared/fixtures"; export function beforeBehavior(): void { @@ -29,14 +28,13 @@ export function beforeBehavior(): void { compoundModel2: await this.loadFixture(unitFixtureCompoundModel), compoundModel3: await this.loadFixture(unitFixtureCompoundModel), uniswapV2Router: await this.loadFixture(unitFixtureUniswapV2), - uniswapV2Factory: await this.loadFixture(unitFixtureUniswapFactoryV2), daiContract: await this.loadFixture(unitFixtureDaiToken), - referral: await this.loadFixture(unitFixtureReferral), rewardPool: await this.loadFixture(unitFixturePunkRewardPool), treasury: await this.loadFixture(unitFixtureTreasury), opTreasury: await this.loadFixture(unitFixtureOpTreasury), grinder: await this.loadFixture(unitFixtureOpTreasury), recoveryFundMock: await this.loadFixture(unitFixtureRecoveryFund), + uniswapV2Factory:await this.loadFixture(unitFixtureUniswapFactoryV2) }; await this.contracts.uniswapV2Factory.createPair( From 51f26f6d14ed4f8cc709b666b3ec01eb696a795a Mon Sep 17 00:00:00 2001 From: 0xViktor Date: Fri, 8 Oct 2021 21:43:20 +0900 Subject: [PATCH 14/16] Hard Work Now! For Punkers by 0xViktor... - 2nd fix codes by Certic's preliminary report - Add Timelock Contract - Remove PunkRewardPool --- contracts/Forge.sol | 92 ++-- contracts/ForgeProxy.sol | 60 +-- contracts/Ownable.sol | 10 - contracts/OwnableStorage.sol | 24 +- contracts/PunkRewardPool.sol | 274 ------------ contracts/PunkRewardPoolProxy.sol | 95 ----- contracts/Treasury.sol | 108 +++-- contracts/Variables.sol | 67 ++- contracts/governance/Timelock.sol | 136 ++++++ contracts/interfaces/ForgeInterface.sol | 8 +- test/shared/fixtures.ts | 25 +- test/shared/utils.ts | 5 + .../unit/CompoundModel/initialize.behavior.ts | 15 +- test/unit/Forge/initialize.behavior.ts | 109 ++++- test/unit/OwnableStorage/setup.behavior.ts | 51 +-- test/unit/Treasury/setup.behavior.ts | 160 +++++-- test/unit/Variables/setup.behavior.ts | 393 ++++++++++++++---- test/unit/before.behavior.ts | 6 +- test/unit/index.ts | 6 +- 19 files changed, 852 insertions(+), 792 deletions(-) delete mode 100644 contracts/PunkRewardPool.sol delete mode 100644 contracts/PunkRewardPoolProxy.sol create mode 100644 contracts/governance/Timelock.sol diff --git a/contracts/Forge.sol b/contracts/Forge.sol index 1f613a7..b939f72 100644 --- a/contracts/Forge.sol +++ b/contracts/Forge.sol @@ -24,6 +24,7 @@ contract Forge is uint256 constant SECONDS_DAY = 1 days; uint256 constant SECONDS_YEAR = 31556952; + enum Status { NOT_YET_WITHDRAWN_OR_WITHDRAWABLE, ALREADY_WITHDRAWN, @@ -40,6 +41,7 @@ contract Forge is * @param variables_ deployed Variables's address * @param name_ Forge's name * @param symbol_ Forge's symbol + * @param model_ Model's address * @param token_ ERC20 Token's address * @param decimals_ ERC20 (tokens_)'s decimals */ @@ -48,6 +50,7 @@ contract Forge is address variables_, string memory name_, string memory symbol_, + address model_, address token_, uint8 decimals_ ) public initializer { @@ -58,54 +61,38 @@ contract Forge is __symbol = symbol_; __decimals = decimals_; + _model = model_; _token = token_; _tokenUnit = 10**decimals_; _count = 0; - emit Initialize( storage_, variables_, token_, name_, symbol_ ); + emit Initialize(storage_, variables_, model_, token_, name_, symbol_); } - - function requestUpgradeModel(address model_) + function upgradeModel(address model_) public - OnlyAdminOrGovernance + override + OnlyAdmin returns (bool) { require(model_ != address(0), "FORGE : Model address is zero"); - require(Address.isContract(model_),"FORGE : Model address must be the contract address."); - require(ModelInterface(model_).token() == _token,"FORGE : Model has Token address is not equals"); - require(ModelInterface(model_).forge() == address(this),"FORGE : Model has Forge address is not equals this address"); + require( + Address.isContract(model_), + "FORGE : Model address must be the contract address." + ); + require( + ModelInterface(model_).token() == _token, + "FORGE : Model has Token address is not equals" + ); + require( + ModelInterface(model_).forge() == address(this), + "FORGE : Model has Forge address is not equals this address" + ); require(_model != model_, "FORGE : Current Model"); - if( modelAddress() == address(0) ){ - _model = model_; - emit SetModel(address(0), model_); - }else{ - _nextUpgradeModel = model_; - _nextUpgradeModelTimestamp = block.timestamp.add( _delayTime ); - emit RequestUpgradeModel( _model, _nextUpgradeModel, _nextUpgradeModelTimestamp ); - } - - return false; - } - - function upgradeModelAccept() external override OnlyAdminOrGovernance returns(bool){ - require(_nextUpgradeModel != address(0x0), ""); - require(_nextUpgradeModelTimestamp <= block.timestamp, ""); - require(_nextUpgradeModelTimestamp != 0, ""); - - if (_model != address(0)) { - ModelInterface(_model).withdrawAllToForge(); - IERC20(_token).safeTransfer( _nextUpgradeModel, IERC20(_token).balanceOf(address(this)) ); - ModelInterface(_nextUpgradeModel).invest(); - } - - emit SetModel(_model, _nextUpgradeModel); - _model = _nextUpgradeModel; - - _nextUpgradeModel = address(0x0); - _nextUpgradeModelTimestamp = 0; + emit UpgradeModel(_model, model_, block.timestamp); + _model = model_; return false; } @@ -237,7 +224,6 @@ contract Forge is emit AddDeposit(msg.sender, index, amount); _mint(msg.sender, mint); - _autoStake(mint); } return true; @@ -285,12 +271,12 @@ contract Forge is "FORGE : Terminated Saver" ); require(withdrawablePlp >= hope, "FORGE : Insufficient Amount"); + require(balanceOf(msg.sender) >= hope, "FORGE : Insufficient Amount"); // TODO Confirm withdrawal of currency after use of balance. /* for Underlying ERC20 token */ { - _autoUnstake(hope); ( uint256 amountOfWithdraw, uint256 amountOfServiceFee @@ -338,7 +324,7 @@ contract Forge is uint256 hope = s.mint.sub(s.released); { - _autoUnstake(hope); + require(balanceOf(msg.sender) >= hope, "FORGE : Insufficient Amount"); ( uint256 amountOfWithdraw, uint256 amountOfServiceFee @@ -513,36 +499,6 @@ contract Forge is } } - function _autoStake(uint256 hope) private { - if (_variables.reward() != address(0)) { - approve(_variables.reward(), balanceOf(msg.sender)); - PunkRewardPoolInterface(_variables.reward()).staking( - address(this), - hope, - msg.sender - ); - } - } - - function _autoUnstake(uint256 hope) private { - if (_variables.reward() != address(0)) { - uint256 staked = PunkRewardPoolInterface(_variables.reward()).staked(address(this), msg.sender); - if (staked >= hope) { - PunkRewardPoolInterface(_variables.reward()).unstaking( - address(this), - hope, - msg.sender - ); - } else { - PunkRewardPoolInterface(_variables.reward()).unstaking( - address(this), - staked, - msg.sender - ); - } - } - } - // Override ERC20 function symbol() public view override returns (string memory) { return __symbol; diff --git a/contracts/ForgeProxy.sol b/contracts/ForgeProxy.sol index c3df6b6..e0be130 100644 --- a/contracts/ForgeProxy.sol +++ b/contracts/ForgeProxy.sol @@ -6,10 +6,7 @@ import "@openzeppelin/contracts/proxy/Proxy.sol"; import "./OwnableStorage.sol"; contract ForgeProxy is Proxy { - event Upgraded(address indexed implementation); - event RequestUpgradTo(address nextImplementation, uint256 nextImplementationTimestamp ); - - uint256 private constant DELAY_TIME = 48 hours; + event Upgraded(address indexed implementation, uint256 upgradeTimestamp); bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; @@ -30,15 +27,10 @@ contract ForgeProxy is Proxy { _; } - modifier CheckAdmin() { + modifier OnlyAdmin() { require(OwnableStorage(_storage()).isAdmin(msg.sender), "OWNABLE: 0x0"); _; } - - modifier onlyAdminOrGovernance() { - require(OwnableStorage(_storage()).isAdmin(msg.sender) || OwnableStorage(_storage()).isGovernance(msg.sender), "OWNABLE: 0x0"); - _; - } function initialize(address storage_, address implAddress) public @@ -78,56 +70,16 @@ contract ForgeProxy is Proxy { } } - function _nextImplementation() internal view returns (address nextImplementation) { - bytes32 slot = _NEXT_IMPLEMENTATION_SLOT; - // solhint-disable-next-line no-inline-assembly - assembly { - nextImplementation := sload(slot) - } - } - - function _setNextImplementation(address storage_) internal { - bytes32 slot = _NEXT_IMPLEMENTATION_SLOT; - // solhint-disable-next-line no-inline-assembly - assembly { - sstore(slot, storage_) - } - } - - function _nextImplementationTimestamp() internal view returns (uint256 nextImplementationTimestamp) { - bytes32 slot = _NEXT_IMPLEMENTATION_TIMESTAMP_SLOT; - // solhint-disable-next-line no-inline-assembly - assembly { - nextImplementationTimestamp := sload(slot) - } - } - - function _setNextImplementationTimestamp(uint256 nextImplementationTimestamp) internal { - bytes32 slot = _NEXT_IMPLEMENTATION_TIMESTAMP_SLOT; - // solhint-disable-next-line no-inline-assembly - assembly { - sstore(slot, nextImplementationTimestamp) - } - } - - function requestUpgradeTo(address newImplementation) public onlyAdminOrGovernance { + function requestUpgradeTo(address newImplementation) public OnlyAdmin { require( newImplementation != address(0), "" ); require( Address.isContract(newImplementation), ""); - _setNextImplementation(newImplementation); - _setNextImplementationTimestamp(block.timestamp + DELAY_TIME); - - emit RequestUpgradTo(_nextImplementation(), _nextImplementationTimestamp()); - } - - function upgradeAccept(address ) public onlyAdminOrGovernance { - require( _nextImplementation() != address(0), "" ); - require( _nextImplementationTimestamp() <= block.timestamp, "" ); + _setImplementation(newImplementation); - _setImplementation(_nextImplementation()); - emit Upgraded(_nextImplementation()); + emit Upgraded(newImplementation, block.timestamp); } + function _setImplementation(address newImplementation) private { require( Address.isContract(newImplementation), diff --git a/contracts/Ownable.sol b/contracts/Ownable.sol index 402e84c..a24f75f 100644 --- a/contracts/Ownable.sol +++ b/contracts/Ownable.sol @@ -17,14 +17,4 @@ contract Ownable is Initializable{ _; } - modifier OnlyGovernance(){ - require( _storage.isGovernance( msg.sender ), "OWNABLE : Only Governance" ); - _; - } - - modifier OnlyAdminOrGovernance(){ - require( _storage.isAdmin(msg.sender) || _storage.isGovernance( msg.sender ), "OWNABLE : Only Admin Or Governance" ); - _; - } - } \ No newline at end of file diff --git a/contracts/OwnableStorage.sol b/contracts/OwnableStorage.sol index 03d6af5..f4b6c44 100644 --- a/contracts/OwnableStorage.sol +++ b/contracts/OwnableStorage.sol @@ -3,30 +3,14 @@ pragma solidity >=0.5.0 <0.9.0; contract OwnableStorage { - address public _admin; - address public _governance; + address timeLock; - constructor() { - _admin = msg.sender; - _governance = msg.sender; - } - - function setAdmin( address account ) public { - require( isAdmin( msg.sender ), "OWNABLE STORAGE : Only Admin"); - _admin = account; - } - - function setGovernance( address account ) public { - require( isAdmin( msg.sender ) || isGovernance( msg.sender ), "OWNABLE STORAGE : Only Admin or Gov"); - _governance = account; + constructor( address timeLock_ ) { + timeLock = timeLock_; } function isAdmin( address account ) public view returns( bool ) { - return account == _admin; - } - - function isGovernance( address account ) public view returns( bool ) { - return account == _governance; + return timeLock == account; } } \ No newline at end of file diff --git a/contracts/PunkRewardPool.sol b/contracts/PunkRewardPool.sol deleted file mode 100644 index 408f9b9..0000000 --- a/contracts/PunkRewardPool.sol +++ /dev/null @@ -1,274 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.5.0 <0.9.0; - -import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; -import "@openzeppelin/contracts/utils/math/SafeMath.sol"; -import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; -import "./Ownable.sol"; - -// Hard Work Now! For Punkers by 0xViktor... -contract PunkRewardPool is Ownable, ReentrancyGuard{ - using SafeMath for uint; - using SafeERC20 for IERC20; - - bool isStarting = false; - - uint constant MAX_WEIGHT = 500; - uint constant BLOCK_YEAR = 2102400; - - IERC20 Punk; - uint startBlock; - - address [] forges; - - mapping ( address => uint ) totalSupplies; - mapping ( address => mapping( address=>uint ) ) balances; - mapping ( address => mapping( address=>uint ) ) checkPointBlocks; - - mapping( address => uint ) weights; - uint weightSum; - - mapping( address => uint ) distributed; - uint totalDistributed; - - event Initialize( address storageAddress, address punk ); - event Start(); - event AddForge(address forge); - event SetForge(address forge, uint weight); - event Claim(address forge, uint amount); - event Staking(address forge, uint amount, address from); - event Unstaking(address forge, uint amount, address from); - - function initializeReward( address storage_, address punk_ ) public initializer { - // Hard Work Now! For Punkers by 0xViktor... - Ownable.initialize( storage_ ); - Punk = IERC20( punk_ ); - startBlock = 0; - weightSum = 0; - totalDistributed = 0; - - emit Initialize( storage_, punk_ ); - } - - function start() public OnlyAdmin{ - require(!isStarting, "PUNK_REWARD_POOL : Already Started"); - startBlock = block.number; - isStarting = true; - emit Start(); - } - - function addForge( address forge ) public OnlyAdmin { - // Hard Work Now! For Punkers by 0xViktor... - require( Address.isContract(forge), "PUNK_REWARD_POOL : Not Contract Address"); - require( !checkForge( forge ), "PUNK_REWARD_POOL: Already Exist" ); - forges.push( forge ); - weights[ forge ] = 50; - weightSum += 50; - emit AddForge(forge); - } - - function setForge( address forge, uint weight ) public OnlyAdmin { - // Hard Work Now! For Punkers by 0xViktor... - require( checkForge( forge ), "PUNK_REWARD_POOL: Not Exist Forge" ); - require( 0 <= weight && weight <= MAX_WEIGHT, "PUNK_REWARD_POOL: Invalid weight" ); - weights[ forge ] = weight; - weightSum = 0; - for( uint i = 0 ; i < forges.length ; i++ ){ - weightSum += weights[ forges[ i ] ]; - } - - emit SetForge( forge, weight ); - } - - function claimPunk( ) public { - // Hard Work Now! For Punkers by 0xViktor... - claimPunk( msg.sender ); - } - - function claimPunk( address to ) public nonReentrant { - // Hard Work Now! For Punkers by 0xViktor... - for( uint i = 0 ; i < forges.length ; i++ ){ - _claimPunk( forges[i], to ); - } - } - - function claimPunk( address forge, address to ) public nonReentrant { - // Hard Work Now! For Punkers by 0xViktor... - _claimPunk(forge, to); - } - - function _claimPunk( address forge, address to ) internal { - // Hard Work Now! For Punkers by 0xViktor... - if( isStarting ){ - uint reward = getClaimPunk( forge, to ); - checkPointBlocks[ forge ][ to ] = block.number; - if( reward > 0 ) Punk.safeTransfer( to, reward ); - distributed[ forge ] = distributed[ forge ].add( reward ); - totalDistributed = totalDistributed.add( reward ); - emit Claim( forge, reward ); - } - } - - function staking( address forge, uint amount ) public { - // Hard Work Now! For Punkers by 0xViktor... - staking( forge, amount, msg.sender ); - } - - function staking( address forge, uint amount, address from ) public nonReentrant { - // Hard Work Now! For Punkers by 0xViktor... - require( msg.sender == from || checkForge( msg.sender ), "REWARD POOL : NOT ALLOWD" ); - _claimPunk( forge, from ); - checkPointBlocks[ forge ][ from ] = block.number; - IERC20( forge ).safeTransferFrom( from, address( this ), amount ); - balances[ forge ][ from ] = balances[ forge ][ from ].add( amount ); - totalSupplies[ forge ] = totalSupplies[ forge ].add( amount ); - emit Staking(forge, amount, from); - } - - function unstaking( address forge, uint amount ) public { - // Hard Work Now! For Punkers by 0xViktor... - unstaking( forge, amount, msg.sender ); - } - - function unstaking( address forge, uint amount, address from ) public nonReentrant { - // Hard Work Now! For Punkers by 0xViktor... - require( msg.sender == from || checkForge( msg.sender ), "REWARD POOL : NOT ALLOWD" ); - _claimPunk( forge, from ); - checkPointBlocks[ forge ][ from ] = block.number; - balances[ forge ][ from ] = balances[ forge ][ from ].sub( amount ); - IERC20( forge ).safeTransfer( from, amount ); - totalSupplies[ forge ] = totalSupplies[ forge ].sub( amount ); - emit Unstaking(forge, amount, from); - } - - function checkForge( address forge ) public view returns( bool ){ - // Hard Work Now! For Punkers by 0xViktor... - bool check = false; - for( uint i = 0 ; i < forges.length ; i++ ){ - if( forges[ i ] == forge ){ - check = true; - break; - } - } - return check; - } - - function _calcRewards( address forge, address user, uint fromBlock, uint currentBlock ) internal view returns( uint ){ - // Hard Work Now! For Punkers by 0xViktor... - uint balance = balances[ forge ][ user ]; - if( balance == 0 ) return 0; - uint totalSupply = totalSupplies[ forge ]; - uint weight = weights[ forge ]; - - uint startPeriod = _getPeriodFromBlock( fromBlock ); - uint endPeriod = _getPeriodFromBlock( currentBlock ); - - if( startPeriod == endPeriod ){ - - uint during = currentBlock.sub( fromBlock ).mul( balance ).mul( weight ).mul( _perBlockRateFromPeriod( startPeriod ) ); - return during.div( weightSum ).div( totalSupply ); - - }else{ - uint denominator = weightSum.mul( totalSupply ); - - uint duringStartNumerator = _getBlockFromPeriod( startPeriod.add( 1 ) ).sub( fromBlock ); - duringStartNumerator = duringStartNumerator.mul( weight ).mul( _perBlockRateFromPeriod( startPeriod ) ).mul( balance ); - - uint duringEndNumerator = currentBlock.sub( _getBlockFromPeriod( endPeriod ) ); - duringEndNumerator = duringEndNumerator.mul( weight ).mul( _perBlockRateFromPeriod( endPeriod ) ).mul( balance ); - - uint duringMid = 0; - - for( uint i = startPeriod.add( 1 ) ; i < endPeriod ; i++ ) { - uint numerator = BLOCK_YEAR.mul( 4 ).mul( balance ).mul( weight ).mul( _perBlockRateFromPeriod( i ) ); - duringMid += numerator.div( denominator ); - } - - uint duringStartAmount = duringStartNumerator.div( denominator ); - uint duringEndAmount = duringEndNumerator.div( denominator ); - - return duringStartAmount + duringMid + duringEndAmount; - } - } - - function _getBlockFromPeriod( uint period ) internal view returns ( uint ){ - // Hard Work Now! For Punkers by 0xViktor... - return startBlock.add( period.sub( 1 ).mul( BLOCK_YEAR ).mul( 4 ) ); - } - - function _getPeriodFromBlock( uint blockNumber ) internal view returns( uint ){ - // Hard Work Now! For Punkers by 0xViktor... - return blockNumber.sub( startBlock ).div( BLOCK_YEAR.mul( 4 ) ).add( 1 ); - } - - function _perBlockRateFromPeriod( uint period ) internal view returns( uint ){ - // Hard Work Now! For Punkers by 0xViktor... - uint totalDistribute = Punk.balanceOf( address( this ) ).add( totalDistributed ).div( period.mul( 2 ) ); - uint perBlock = totalDistribute.div( BLOCK_YEAR.mul( 4 ) ); - return perBlock; - } - - function getClaimPunk( address to ) public view returns( uint ){ - // Hard Work Now! For Punkers by 0xViktor... - uint reward = 0; - for( uint i = 0 ; i < forges.length ; i++ ){ - reward += getClaimPunk( forges[ i ], to ); - } - return reward; - } - - function getClaimPunk( address forge, address to ) public view returns( uint ){ - // Hard Work Now! For Punkers by 0xViktor... - uint checkPointBlock = checkPointBlocks[ forge ][ to ]; - if( checkPointBlock <= getStartBlock() ){ - checkPointBlock = getStartBlock(); - } - return checkPointBlock > startBlock ? _calcRewards( forge, to, checkPointBlock, block.number ) : 0; - } - - function getWeightSum() public view returns( uint ){ - // Hard Work Now! For Punkers by 0xViktor... - return weightSum; - } - - function getWeight( address forge ) public view returns( uint ){ - // Hard Work Now! For Punkers by 0xViktor... - return weights[ forge ]; - } - - function getTotalDistributed( ) public view returns( uint ){ - // Hard Work Now! For Punkers by 0xViktor... - return totalDistributed; - } - - function getDistributed( address forge ) public view returns( uint ){ - // Hard Work Now! For Punkers by 0xViktor... - return distributed[ forge ]; - } - - function getAllocation( ) public view returns( uint ){ - // Hard Work Now! For Punkers by 0xViktor... - return _perBlockRateFromPeriod( _getPeriodFromBlock( block.number ) ); - } - - function getAllocation( address forge ) public view returns( uint ){ - // Hard Work Now! For Punkers by 0xViktor... - return getAllocation( ).mul( weights[ forge ] ).div( weightSum ); - } - - function staked( address forge, address account ) public view returns( uint ){ - // Hard Work Now! For Punkers by 0xViktor... - return balances[ forge ][ account ]; - } - - function getTotalReward() public view returns( uint ){ - // Hard Work Now! For Punkers by 0xViktor... - return Punk.balanceOf( address( this ) ).add( totalDistributed ); - } - - function getStartBlock() public view returns( uint ){ - // Hard Work Now! For Punkers by 0xViktor... - return startBlock; - } - -} diff --git a/contracts/PunkRewardPoolProxy.sol b/contracts/PunkRewardPoolProxy.sol deleted file mode 100644 index 1298033..0000000 --- a/contracts/PunkRewardPoolProxy.sol +++ /dev/null @@ -1,95 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity >=0.5.0 <0.9.0; - -import "@openzeppelin/contracts/utils/Address.sol"; -import "@openzeppelin/contracts/proxy/Proxy.sol"; -import "./OwnableStorage.sol"; - -contract PunkRewardPoolProxy is Proxy{ - - event Upgraded(address indexed implementation); - - bytes32 private constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; - bytes32 private constant _OWNABLE_STORAGE_SLOT = 0x8de9519aeedcea35f7581a1710364953511221d8b7309789ecb15ac4b1a06fc1; - bytes32 private constant _INITIALIZE_SLOT = 0xd1144699b2459fa4c652fe6a4a3ddb7d1dd632f82d755cb1d4bc09b8ef6d4b4f; - - modifier isInitializer(){ - require( getInitialize() != 1, "Initializable: contract is already initialized"); - _; - } - - modifier CheckAdmin(){ - require( OwnableStorage( _storage() ).isAdmin(msg.sender), "OWNABLE: 0x0" ); - _; - } - - function initialize( address implAddress, bytes memory initData, address storage_ ) public isInitializer{ - require(_IMPLEMENTATION_SLOT == bytes32(uint256(keccak256("eip1967.proxy.implementation")) - 1)); - require(_OWNABLE_STORAGE_SLOT == bytes32(uint256(keccak256("punk.reward.proxy.ownablestrage")) - 1)); - require(_INITIALIZE_SLOT == bytes32(uint256(keccak256("punk.reward.proxy.initialize")) - 1)); - - _setImplementation(implAddress); - _setStorage( storage_ ); - _setInitialize( ); - - if(initData.length > 0) { - Address.functionDelegateCall(implAddress, initData); - } - - } - - function _setStorage( address storage_ ) internal { - bytes32 slot = _OWNABLE_STORAGE_SLOT; - // solhint-disable-next-line no-inline-assembly - assembly { - sstore(slot, storage_) - } - } - - function _storage() internal view returns( address storageAddr ){ - bytes32 slot = _OWNABLE_STORAGE_SLOT; - // solhint-disable-next-line no-inline-assembly - assembly { - storageAddr := sload(slot) - } - } - - function _implementation() internal view override returns (address impl) { - bytes32 slot = _IMPLEMENTATION_SLOT; - // solhint-disable-next-line no-inline-assembly - assembly { - impl := sload(slot) - } - } - - function upgradeTo(address newImplementation) public CheckAdmin { - _setImplementation(newImplementation); - emit Upgraded(newImplementation); - } - - function _setImplementation(address newImplementation) private { - require(Address.isContract(newImplementation), "ERC1967Proxy: new implementation is not a contract"); - - bytes32 slot = _IMPLEMENTATION_SLOT; - - // solhint-disable-next-line no-inline-assembly - assembly { - sstore(slot, newImplementation) - } - } - - function _setInitialize( ) internal { - // solhint-disable-next-line no-inline-assembly - assembly { - sstore(_INITIALIZE_SLOT, 1) - } - } - - function getInitialize( ) private view returns (uint256 str) { - // solhint-disable-next-line no-inline-assembly - assembly { - str := sload( _INITIALIZE_SLOT ) - } - } - -} \ No newline at end of file diff --git a/contracts/Treasury.sol b/contracts/Treasury.sol index 4751783..3882f22 100644 --- a/contracts/Treasury.sol +++ b/contracts/Treasury.sol @@ -10,86 +10,110 @@ import "./Ownable.sol"; contract Treasury is Ownable { using SafeERC20 for IERC20; - mapping( uint256 => address ) private _tokens; - mapping( address => bool ) private _exists; + mapping(uint256 => address) private _tokens; + mapping(address => bool) private _exists; uint256 public count; address private _punk; address private _grinder; address private _uRouterV2; - - event Initialize( address storageAddress, address grinder, address punk, address uniswapRouterV2 ); - event AddAsset( address token ); - function initialize( address storage_, address grinder_, address punk_, address uRouterV2_ ) public initializer { - Ownable.initialize( storage_ ); + event Initialize( + address storageAddress, + address grinder, + address punk, + address uniswapRouterV2 + ); + event AddAsset(address token); + + function initialize( + address storage_, + address grinder_, + address punk_, + address uRouterV2_ + ) public initializer { + Ownable.initialize(storage_); _grinder = grinder_; _punk = punk_; _uRouterV2 = uRouterV2_; - emit Initialize( storage_, grinder_, punk_, uRouterV2_ ); + emit Initialize(storage_, grinder_, punk_, uRouterV2_); } - function addAsset( address token ) public OnlyAdminOrGovernance { - require( IERC20(token).totalSupply() > 0, "TREASURY : token is Invalid" ); - require( !_exists[token], "TREASURY : Already Registry Token" ); + function addAsset(address token) public OnlyAdmin { + require(IERC20(token).totalSupply() > 0, "TREASURY : token is Invalid"); + require(!_exists[token], "TREASURY : Already Registry Token"); _tokens[count] = token; _exists[token] = true; count++; emit AddAsset(token); } - function buyBack( uint256 [] memory amountOutMins, uint256 amountOutMinsEth ) public OnlyAdminOrGovernance { + function buyBack(address[] memory tokens, uint256[] memory amountOutMins) + public + OnlyAdmin + { // Hard Work Now! For Punkers by 0xViktor - require(amountOutMins.length == count, "TREASURY : amountOutMins invalid"); - require(amountOutMinsEth > 0, "TREASURY : amountOutMinsEth invalid"); - for( uint i = 0 ; i < count ; i++ ){ - - uint balance = IERC20( _tokens[ i ] ).balanceOf( address( this ) ); - if( balance > 0 ){ - IERC20( _tokens[ i ] ).safeApprove(address(_uRouterV2), balance); - - address[] memory path = new address[](3); - path[0] = address( _tokens[i] ); - path[1] = IUniswapV2Router(_uRouterV2).WETH(); - path[2] = address( _punk ); + require( + tokens.length == amountOutMins.length, + "TREASURY : tokens length not eqauls amoutOutMins" + ); + require( + amountOutMins.length == count, + "TREASURY : amountOutMins invalid" + ); + for (uint256 i = 0; i < tokens.length; i++) { + if (_exists[tokens[i]]) { + uint256 balance = IERC20(tokens[i]).balanceOf(address(this)); + if (balance > 0) { + IERC20(tokens[i]).safeApprove(address(_uRouterV2), balance); + + address[] memory path = new address[](3); + path[0] = address(tokens[i]); + path[1] = IUniswapV2Router(_uRouterV2).WETH(); + path[2] = address(_punk); - IUniswapV2Router(_uRouterV2).swapExactTokensForTokens( - balance, - amountOutMins[i], - path, - _grinder, - block.timestamp + ( 15 * 60 ) - ); + IUniswapV2Router(_uRouterV2).swapExactTokensForTokens( + balance, + amountOutMins[i], + path, + _grinder, + block.timestamp + (15 * 60) + ); + } } } + } + function buyBackEth(uint256 amountOutMinsEth) public OnlyAdmin { + require(amountOutMinsEth > 0, "TREASURY : amountOutMinsEth invalid"); // For SwapEthForToken - if( address(this).balance > 0 ){ + if (address(this).balance > 0) { address[] memory pathForSwapEth = new address[](2); pathForSwapEth[0] = IUniswapV2Router(_uRouterV2).WETH(); - pathForSwapEth[1] = address( _punk ); + pathForSwapEth[1] = address(_punk); - IUniswapV2Router(_uRouterV2).swapExactETHForTokens{value:address(this).balance}( + IUniswapV2Router(_uRouterV2).swapExactETHForTokens{ + value: address(this).balance + }( amountOutMinsEth, pathForSwapEth, _grinder, - block.timestamp + ( 15 * 60 ) + block.timestamp + (15 * 60) ); } } - function assets() public view returns( address [] memory ){ + function assets() public view returns (address[] memory) { address[] memory assetsList = new address[](count); - for( uint256 i; i < count ;i++ ){ + for (uint256 i; i < count; i++) { assetsList[i] = _tokens[i]; } return assetsList; } - fallback () external payable { + fallback() external payable { payable(msg.sender).transfer(msg.value); } - - receive() external payable{} - -} \ No newline at end of file + + receive() external payable {} +} diff --git a/contracts/Variables.sol b/contracts/Variables.sol index 5e1d2b2..8d82952 100644 --- a/contracts/Variables.sol +++ b/contracts/Variables.sol @@ -5,58 +5,81 @@ import "@openzeppelin/contracts/utils/Address.sol"; import "./Ownable.sol"; import "./Saver.sol"; -contract Variables is Ownable{ - +contract Variables is Ownable { uint256 private _successFee; uint256 private _serviceFee; uint256 private _feeMultiplier; - + address private _treasury; address private _reward; event Initialize(); - function initialize( address storage_) public override initializer{ + function initialize(address storage_) public override initializer { Ownable.initialize(storage_); - _successFee= 20; + _successFee = 20; _serviceFee = 1; _feeMultiplier = 200; emit Initialize(); } - function setSuccessFee( uint256 setSuccessFee_ ) public OnlyGovernance { - require( 0 <= setSuccessFee_ && setSuccessFee_ <= 20, "VARIABLES : SuccessFee range from 0 to 20." ); - _successFee = setSuccessFee_; + function setSuccessFee(uint256 successFee_) public OnlyAdmin { + require( + 0 <= successFee_ && successFee_ <= 20, + "VARIABLES : SuccessFee range from 0 to 20." + ); + _successFee = successFee_; } - function setServiceFee( uint256 serviceFee_ ) public OnlyGovernance { - require( 0 <= serviceFee_ && serviceFee_ <= 2, "VARIABLES : ServiceFee range from 0 to 2." ); + function setServiceFee(uint256 serviceFee_) public OnlyAdmin { + require( + 0 <= serviceFee_ && serviceFee_ <= 2, + "VARIABLES : ServiceFee range from 0 to 2." + ); _serviceFee = serviceFee_; } - function setFeeMultiplier( uint256 feeMultiplier_ ) public OnlyGovernance { - require( 100 <= feeMultiplier_ && feeMultiplier_ <= 200, "VARIABLES : feeMultiplier range from 100 to 200." ); + function setFeeMultiplier(uint256 feeMultiplier_) public OnlyAdmin { + require( + 100 <= feeMultiplier_ && feeMultiplier_ <= 200, + "VARIABLES : feeMultiplier range from 100 to 200." + ); _feeMultiplier = feeMultiplier_; } - function setTreasury( address treasury_ ) public OnlyAdmin { - require(Address.isContract(treasury_), "VARIABLES : must be the contract address."); + function setTreasury(address treasury_) public OnlyAdmin { + require( + Address.isContract(treasury_), + "VARIABLES : must be the contract address." + ); _treasury = treasury_; } - function setReward( address reward_ ) public OnlyAdmin { - require(Address.isContract(reward_), "VARIABLES : must be the contract address."); + function setReward(address reward_) public OnlyAdmin { + require( + Address.isContract(reward_), + "VARIABLES : must be the contract address." + ); _reward = reward_; } - function successFee() public view returns( uint256 ){ return _successFee; } - - function serviceFee() public view returns( uint256 ){ return _serviceFee; } + function successFee() public view returns (uint256) { + return _successFee; + } - function feeMultiplier() public view returns( uint256 ){ return _feeMultiplier; } + function serviceFee() public view returns (uint256) { + return _serviceFee; + } - function treasury() public view returns( address ){ return _treasury; } + function feeMultiplier() public view returns (uint256) { + return _feeMultiplier; + } - function reward() public view returns( address ){ return _reward; } + function treasury() public view returns (address) { + return _treasury; + } + function reward() public view returns (address) { + return _reward; + } } diff --git a/contracts/governance/Timelock.sol b/contracts/governance/Timelock.sol new file mode 100644 index 0000000..f06b206 --- /dev/null +++ b/contracts/governance/Timelock.sol @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: MIT + +// COPIED FROM https://github.com/compound-finance/compound-protocol/blob/master/contracts/Governance/GovernorAlpha.sol +// Copyright 2020 Compound Labs, Inc. +// Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: +// 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +// 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +// 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Ctrl+f for MOD to see all the modifications. + +// MOD: pragma solidity ^0.5.16; +pragma solidity >=0.5.0 <0.9.0; + +// MOD: import "./SafeMath.sol"; +import "@openzeppelin/contracts/utils/math/SafeMath.sol"; + +contract Timelock { + using SafeMath for uint; + + event NewAdmin(address indexed newAdmin); + event NewPendingAdmin(address indexed newPendingAdmin); + event NewDelay(uint indexed newDelay); + event CancelTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta); + event ExecuteTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta); + event QueueTransaction(bytes32 indexed txHash, address indexed target, uint value, string signature, bytes data, uint eta); + + uint public constant GRACE_PERIOD = 14 days; + uint public constant MINIMUM_DELAY = 6 hours; + uint public constant MAXIMUM_DELAY = 30 days; + + address public admin; + address public pendingAdmin; + uint public delay; + bool public admin_initialized; + + mapping (bytes32 => bool) public queuedTransactions; + + + // MOD: constructor(address admin_, uint delay_) public { + constructor(address admin_, uint delay_) { + require(delay_ >= MINIMUM_DELAY, "Timelock::constructor: Delay must exceed minimum delay."); + require(delay_ <= MAXIMUM_DELAY, "Timelock::constructor: Delay must not exceed maximum delay."); + + admin = admin_; + delay = delay_; + admin_initialized = false; + } + + // MOD: function() external payable { } + receive() external payable { } + + function setDelay(uint delay_) public { + require(msg.sender == address(this), "Timelock::setDelay: Call must come from Timelock."); + require(delay_ >= MINIMUM_DELAY, "Timelock::setDelay: Delay must exceed minimum delay."); + require(delay_ <= MAXIMUM_DELAY, "Timelock::setDelay: Delay must not exceed maximum delay."); + delay = delay_; + + emit NewDelay(delay); + } + + function acceptAdmin() public { + require(msg.sender == pendingAdmin, "Timelock::acceptAdmin: Call must come from pendingAdmin."); + admin = msg.sender; + pendingAdmin = address(0); + + emit NewAdmin(admin); + } + + function setPendingAdmin(address pendingAdmin_) public { + // allows one time setting of admin for deployment purposes + if (admin_initialized) { + require(msg.sender == address(this), "Timelock::setPendingAdmin: Call must come from Timelock."); + } else { + require(msg.sender == admin, "Timelock::setPendingAdmin: First call must come from admin."); + admin_initialized = true; + } + pendingAdmin = pendingAdmin_; + + emit NewPendingAdmin(pendingAdmin); + } + + function queueTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public returns (bytes32) { + require(msg.sender == admin, "Timelock::queueTransaction: Call must come from admin."); + require(eta >= getBlockTimestamp().add(delay), "Timelock::queueTransaction: Estimated execution block must satisfy delay."); + + bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); + queuedTransactions[txHash] = true; + + emit QueueTransaction(txHash, target, value, signature, data, eta); + return txHash; + } + + function cancelTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public { + require(msg.sender == admin, "Timelock::cancelTransaction: Call must come from admin."); + + bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); + queuedTransactions[txHash] = false; + + emit CancelTransaction(txHash, target, value, signature, data, eta); + } + + function executeTransaction(address target, uint value, string memory signature, bytes memory data, uint eta) public payable returns (bytes memory) { + require(msg.sender == admin, "Timelock::executeTransaction: Call must come from admin."); + + bytes32 txHash = keccak256(abi.encode(target, value, signature, data, eta)); + require(queuedTransactions[txHash], "Timelock::executeTransaction: Transaction hasn't been queued."); + require(getBlockTimestamp() >= eta, "Timelock::executeTransaction: Transaction hasn't surpassed time lock."); + require(getBlockTimestamp() <= eta.add(GRACE_PERIOD), "Timelock::executeTransaction: Transaction is stale."); + + queuedTransactions[txHash] = false; + + bytes memory callData; + + if (bytes(signature).length == 0) { + callData = data; + } else { + callData = abi.encodePacked(bytes4(keccak256(bytes(signature))), data); + } + + // solium-disable-next-line security/no-call-value + // MOD: (bool success, bytes memory returnData) = target.call.value(value)(callData); + (bool success, bytes memory returnData) = target.call{value:value}(callData); + require(success, "Timelock::executeTransaction: Transaction execution reverted."); + + emit ExecuteTransaction(txHash, target, value, signature, data, eta); + + return returnData; + } + + function getBlockTimestamp() internal view returns (uint) { + // solium-disable-next-line security/no-block-members + return block.timestamp; + } +} \ No newline at end of file diff --git a/contracts/interfaces/ForgeInterface.sol b/contracts/interfaces/ForgeInterface.sol index b9edb7e..5a62d4f 100644 --- a/contracts/interfaces/ForgeInterface.sol +++ b/contracts/interfaces/ForgeInterface.sol @@ -6,16 +6,15 @@ import "../Saver.sol"; interface ForgeInterface{ - event Initialize( address storageAddress, address variables, address token, string name, string symbol ); + event Initialize( address storageAddress, address variables, address model, address token, string name, string symbol ); event CraftingSaver ( address owner, uint index, uint deposit ); event AddDeposit ( address owner, uint index, uint deposit ); event Withdraw ( address owner, uint index, uint amount ); event Terminate ( address owner, uint index, uint amount ); - event SetModel ( address from, address to ); - event RequestUpgradeModel ( address from, address to, uint upgradeTimestamp ); + event UpgradeModel ( address from, address to, uint upgradeTimestamp ); function modelAddress() external view returns (address); - function upgradeModelAccept() external returns(bool); + function upgradeModel(address model_) external returns(bool); function withdrawable( address account, uint index ) external view returns(uint); function countByAccount( address account ) external view returns (uint); @@ -32,5 +31,4 @@ interface ForgeInterface{ function exchangeRate() external view returns( uint ); function totalVolume( ) external view returns( uint ); - } \ No newline at end of file diff --git a/test/shared/fixtures.ts b/test/shared/fixtures.ts index 1c3eb74..4016a15 100644 --- a/test/shared/fixtures.ts +++ b/test/shared/fixtures.ts @@ -2,6 +2,7 @@ import { Contract, Wallet } from "ethers"; import { artifacts, ethers } from "hardhat" import { Tokens, UniswapAddresses } from "./mockInfo"; +let timelock:Contract let storage:Contract let punkMock:Contract @@ -13,6 +14,14 @@ export async function unitPunkMockFixtures([,,,,,owner] : Wallet[]): Promise { + const Timelock = await ethers.getContractFactory("Timelock"); + const _timelock = await Timelock.connect(owner).deploy( owner.address, 172800) + await _timelock.deployed() + timelock = _timelock + return timelock +} + export async function unitFixtureForge(): Promise { const Forge = await ethers.getContractFactory("Forge"); const forge = await Forge.deploy() @@ -49,6 +58,13 @@ export async function unitFixtureCompoundModel(): Promise { return compoundModel } +export async function unitFixtureCompoundModelToReplaced(): Promise { + const CompoundModel = await ethers.getContractFactory("CompoundModel") + const compoundModel = await CompoundModel.deploy() + await compoundModel.deployed() + return compoundModel +} + export async function unitFixtureUniswapV2(): Promise { const IUniswapV2Router = await artifacts.readArtifact("IUniswapV2Router"); const uniswapRouter = await ethers.getContractAt(IUniswapV2Router.abi, UniswapAddresses.UniswapV2Router02); @@ -69,7 +85,7 @@ export async function unitFixtureRecoveryFund([,,,,,owner] : Wallet[]): Promise< export async function unitFixtureOwnableStorage([,,,,,owner] : Wallet[]): Promise { const OwnableStorage = await ethers.getContractFactory("OwnableStorage") - const ownableStorage = await OwnableStorage.connect(owner).deploy() + const ownableStorage = await OwnableStorage.connect(owner).deploy(timelock.address) await ownableStorage.deployed() storage = ownableStorage; return ownableStorage @@ -96,13 +112,6 @@ export async function unitFixtureGrinder(): Promise { return grinder; } -export async function unitFixturePunkRewardPool(): Promise { - const PunkRewardPool = await ethers.getContractFactory("PunkRewardPool") - const punkRewardPool = await PunkRewardPool.deploy() - await punkRewardPool.deployed() - return punkRewardPool; -} - export async function unitFixtureFairLaunch(): Promise { const FairLaunch = await ethers.getContractFactory("FairLaunch") const fairLaunch = await FairLaunch.deploy() diff --git a/test/shared/utils.ts b/test/shared/utils.ts index c752e46..f21fd1a 100644 --- a/test/shared/utils.ts +++ b/test/shared/utils.ts @@ -1,5 +1,10 @@ +import { ParamType } from "@ethersproject/abi"; import { ethers } from "hardhat"; export function ethToWei(amount: string): string { return ethers.BigNumber.from(ethers.utils.parseEther(amount).toString()).toString() +} + +export function abiEncode( types: ReadonlyArray, values: ReadonlyArray ):string{ + return ethers.utils.defaultAbiCoder.encode(types, values) } \ No newline at end of file diff --git a/test/unit/CompoundModel/initialize.behavior.ts b/test/unit/CompoundModel/initialize.behavior.ts index 1b5dbba..f517c29 100644 --- a/test/unit/CompoundModel/initialize.behavior.ts +++ b/test/unit/CompoundModel/initialize.behavior.ts @@ -4,8 +4,9 @@ import { Tokens, CompoundAddresses, UniswapAddresses } from "../../shared/mockIn export function initialBehavior(): void { context("Initailize", function() { - it('should Success initialize', async function() { + it('should Success initialize 1', async function() { const compoundModel = this.contracts.compoundModel + await expect(compoundModel.initialize( this.contracts.forge.address, Tokens.Dai, @@ -16,6 +17,18 @@ export function initialBehavior(): void { )).emit( compoundModel, "Initialize" ) }) + it('should Success initialize 2', async function() { + const compoundModelToReplaced = this.contracts.compoundModelToReplaced + await expect(compoundModelToReplaced.initialize( + this.contracts.forge.address, + Tokens.Dai, + CompoundAddresses.cDai, + CompoundAddresses.COMP, + CompoundAddresses.Comptroller, + UniswapAddresses.UniswapV2Router02 + )).emit( compoundModelToReplaced, "Initialize" ) + }) + it('should revert Already initialized', async function() { const compoundModel = this.contracts.compoundModel await expect(compoundModel.initialize( diff --git a/test/unit/Forge/initialize.behavior.ts b/test/unit/Forge/initialize.behavior.ts index d2838bd..c56e026 100644 --- a/test/unit/Forge/initialize.behavior.ts +++ b/test/unit/Forge/initialize.behavior.ts @@ -1,5 +1,8 @@ import { Tokens } from "../../shared/mockInfo" import { expect } from "chai"; +import { ethers, network } from "hardhat"; +import { keccak256 } from "@ethersproject/keccak256"; +import { abiEncode } from "../../shared/utils"; export function initialBehavior(): void { context("Initailize", function() { @@ -8,12 +11,14 @@ export function initialBehavior(): void { const forge = this.contracts.forge; const ownableStorage = this.contracts.ownableStorage; const variables = this.contracts.variables; + const compoundModel = this.contracts.compoundModel; await expect(forge.initializeForge( ownableStorage.address, variables.address, "Punk-Forge-DAI-0", "pDAI", + compoundModel.address, Tokens.Dai, 18 )).emit(forge, "Initialize") @@ -33,30 +38,114 @@ export function initialBehavior(): void { )).to.be.reverted }) - it('should Revert Forge setModel Not Admin or Gov', async function() { + it('should Revert Forge upgradeModel Not Admin', async function() { const forge = this.contracts.forge; const compoundModel = this.contracts.compoundModel; const account = this.signers.account1; - await expect(forge.connect(account).requestUpgradeModel(compoundModel.address)).to.be.reverted + await expect(forge.connect(account).upgradeModel(compoundModel.address)).to.be.reverted }) - it('should Revert Forge setModel address zero', async function() { + let eta = 0; + it('should Revert Forge upgradeModel address zero', async function() { const forge = this.contracts.forge; - const owner = this.signers.owner; - await expect(forge.connect(owner).requestUpgradeModel("0x0000000000000000000000000000000000000000")).to.be.reverted + const timelock = this.contracts.timelock; + const blockNumber = await ethers.provider.getBlockNumber() + const blockInfo = await ethers.provider.getBlock(blockNumber) + eta = blockInfo.timestamp + 49 * 60 * 60 + + const data = abiEncode(['address'],["0x0000000000000000000000000000000000000000"] ); + const txHash = keccak256(abiEncode( ['address', 'uint', 'string', 'bytes', 'uint'], [forge.address, 0, "upgradeModel(address)", data, eta] )) + + await timelock.queueTransaction( + forge.address, + 0, + "upgradeModel(address)", + data, + eta + ) + + await expect(await timelock.queuedTransactions(txHash)).to.be.eq(true) + + await network.provider.send("evm_increaseTime", [49*60*60]); + await network.provider.send("evm_mine") + + await expect(timelock.executeTransaction( + forge.address, + 0, + "upgradeModel(address)", + data, + eta + )).to.be.reverted }) it('should Revert Forge setModel address EOA', async function() { + const account = this.signers.owner; + const forge = this.contracts.forge; - const owner = this.signers.owner; - await expect(forge.connect(owner).requestUpgradeModel(owner.address)).to.be.reverted + const timelock = this.contracts.timelock; + const blockNumber = await ethers.provider.getBlockNumber() + const blockInfo = await ethers.provider.getBlock(blockNumber) + eta = blockInfo.timestamp + 49 * 60 * 60 + + const data = abiEncode(['address'],[account.address] ); + const txHash = keccak256(abiEncode( ['address', 'uint', 'string', 'bytes', 'uint'], [forge.address, 0, "upgradeModel(address)", data, eta] )) + + await timelock.queueTransaction( + forge.address, + 0, + "upgradeModel(address)", + data, + eta + ) + + await expect(await timelock.queuedTransactions(txHash)).to.be.eq(true) + + await network.provider.send("evm_increaseTime", [49*60*60]); + await network.provider.send("evm_mine") + + await expect(timelock.executeTransaction( + forge.address, + 0, + "upgradeModel(address)", + data, + eta + )).to.be.reverted }) it('should Success Forge setModel', async function() { + const model = this.contracts.compoundModelToReplaced; const forge = this.contracts.forge; - const compoundModel = this.contracts.compoundModel; - const owner = this.signers.owner; - await expect(forge.connect(owner).requestUpgradeModel(compoundModel.address)).emit(forge, "SetModel").withArgs("0x0000000000000000000000000000000000000000", compoundModel.address); + const timelock = this.contracts.timelock; + + const blockNumber = await ethers.provider.getBlockNumber() + const blockInfo = await ethers.provider.getBlock(blockNumber) + eta = blockInfo.timestamp + 49 * 60 * 60 + + const data = abiEncode(['address'],[model.address] ); + const txHash = keccak256(abiEncode( ['address', 'uint', 'string', 'bytes', 'uint'], [forge.address, 0, "upgradeModel(address)", data, eta] )) + + await timelock.queueTransaction( + forge.address, + 0, + "upgradeModel(address)", + data, + eta + ) + + await expect(await timelock.queuedTransactions(txHash)).to.be.eq(true) + + await network.provider.send("evm_increaseTime", [49*60*60]); + await network.provider.send("evm_mine") + + await timelock.executeTransaction( + forge.address, + 0, + "upgradeModel(address)", + data, + eta + ) + + await expect( await forge.modelAddress() ).to.be.eq(model.address) }) }) diff --git a/test/unit/OwnableStorage/setup.behavior.ts b/test/unit/OwnableStorage/setup.behavior.ts index ae15d0a..cf83823 100644 --- a/test/unit/OwnableStorage/setup.behavior.ts +++ b/test/unit/OwnableStorage/setup.behavior.ts @@ -3,55 +3,10 @@ import { expect } from "chai"; export function setUpBehavior(): void { context("SetUp", function() { - it('should Revert setAdmin Address Not Admin', async function() { + it('should Check Admin Addres is Timelock', async function() { + const timelock = this.contracts.timelock const ownableStorage = this.contracts.ownableStorage - const gov = this.signers.gov - await expect( ownableStorage.connect(gov).setAdmin(gov.address) ).to.be.reverted - }) - - it('should Success setAdmin', async function() { - const ownableStorage = this.contracts.ownableStorage - const owner = this.signers.owner - const gov = this.signers.gov - await ownableStorage.connect(owner).setAdmin( gov.address ); - await expect( await ownableStorage.isAdmin( gov.address ) ).to.be.eq( true ) - }) - - it('should Revert setAdmin by Governance', async function() { - const ownableStorage = this.contracts.ownableStorage - const owner = this.signers.gov - const gov = this.signers.owner - await expect( ownableStorage.connect(gov).setAdmin(owner.address) ).to.be.reverted - }) - - it('should Revert setGovernance Address Not Admin', async function() { - const ownableStorage = this.contracts.ownableStorage - const account2 = this.signers.account2 - await expect( ownableStorage.connect(account2).setGovernance(account2.address) ).to.be.reverted - }) - - it('should Success setGovernance by Admin', async function() { - const ownableStorage = this.contracts.ownableStorage - const owner = this.signers.gov - const gov = this.signers.owner - await ownableStorage.connect(owner).setGovernance( gov.address ); - await expect( await ownableStorage.isGovernance( gov.address ) ).to.be.eq( true ) - }) - - it('should Revert setGovernance by Gov', async function() { - const ownableStorage = this.contracts.ownableStorage - const owner = this.signers.gov - const gov = this.signers.owner - await ownableStorage.connect(gov).setGovernance( owner.address ); - await expect( await ownableStorage.isGovernance( owner.address ) ).to.be.eq( true ) - }) - - after(async function(){ - const ownableStorage = this.contracts.ownableStorage - const owner = this.signers.owner - const gov = this.signers.gov - await ownableStorage.connect(gov).setAdmin(owner.address); - await ownableStorage.connect(owner).setGovernance(gov.address); + await expect(await ownableStorage.isAdmin(timelock.address)).to.be.eq(true) }) }) diff --git a/test/unit/Treasury/setup.behavior.ts b/test/unit/Treasury/setup.behavior.ts index 232edf9..0cae722 100644 --- a/test/unit/Treasury/setup.behavior.ts +++ b/test/unit/Treasury/setup.behavior.ts @@ -1,47 +1,125 @@ -import { Tokens } from "../../shared/mockInfo" +import { Tokens } from "../../shared/mockInfo"; import { expect } from "chai"; -import { ethers } from "hardhat"; +import { ethers, network } from "hardhat"; +import { abiEncode, ethToWei } from "../../shared/utils"; export function setUpBehavior(): void { - context("SetUp", function() { - - it('should Revert addAsset Address Not AdminOrGovernance', async function() { - const treasury = this.contracts.treasury - const account1 = this.signers.account1 - await expect( treasury.connect(account1).addAsset(Tokens.Dai) ).to.be.reverted - }) - - it('should Success addAsset', async function() { - const treasury = this.contracts.treasury - const owner = this.signers.owner - await expect(treasury.connect(owner).addAsset(Tokens.Dai)).emit(treasury, "AddAsset").withArgs(Tokens.Dai); - }) - - it('should Revert addAsset Already Registry Token', async function() { - const treasury = this.contracts.treasury - const owner = this.signers.owner - await expect(treasury.connect(owner).addAsset(Tokens.Dai)).to.be.reverted - }) - - it('should Revert addAsset Buyback Test', async function() { - const treasury = this.contracts.treasury - const owner = this.signers.owner - await treasury.connect(owner).buyBack([1], 1); - }) + context("SetUp", function () { + + before(async function(){ + const recoveryFundMock = this.contracts.recoveryFundMock; + const uniswapV2Router = this.contracts.uniswapV2Router + const accountDai = this.signers.accountDai - // it('should Revert addAsset Buyback Test', async function() { - // const treasury = this.contracts.treasury - // const owner = this.signers.owner - // const swapResult = await this.contracts.uniswapV2Router.connect(this.signers.owner).swapExactETHForTokens( - // 10000000000, - // [Tokens.WETH, Tokens.Dai], - // treasury.address, - // Math.round(Date.now() / 1000) + 100000000000000, - // {value: "100000000000000000000", gasLimit: '2300000'} - // ) - // await swapResult.wait() - // await treasury.connect(owner).buyBack([1], 1) - // }) + const blockNumber = await ethers.provider.getBlockNumber() + const blockInfo = await ethers.provider.getBlock(blockNumber) + const swapResult = await uniswapV2Router.connect(accountDai).swapExactETHForTokens( + 1, + [Tokens.WETH, Tokens.Dai], + recoveryFundMock.address, + blockInfo.timestamp + 25*60*60, + {value: ethToWei("700"), gasLimit: '2600000'} + ) + await swapResult.wait() }) -} \ No newline at end of file + + it("should Revert addAsset Address Not Admin", async function () { + const treasury = this.contracts.treasury; + const account1 = this.signers.account1; + await expect( + treasury.connect(account1).addAsset(Tokens.Dai) + ).to.be.reverted; + }); + + it("should Success addAsset", async function () { + const treasury = this.contracts.treasury; + const timelock = this.contracts.timelock; + const blockNumber = await ethers.provider.getBlockNumber(); + const blockInfo = await ethers.provider.getBlock(blockNumber); + const eta = blockInfo.timestamp + 49 * 60 * 60; + + const data = abiEncode(["address"], [Tokens.Dai]); + await timelock.queueTransaction( + treasury.address, + 0, + "addAsset(address)", + data, + eta + ); + + await network.provider.send("evm_increaseTime", [49 * 60 * 60]); + await network.provider.send("evm_mine"); + + await expect(timelock.executeTransaction( + treasury.address, + 0, + "addAsset(address)", + data, + eta + )).emit(treasury, "AddAsset").withArgs(Tokens.Dai); + }); + + it("should Revert addAsset Already Registry Token", async function () { + const treasury = this.contracts.treasury; + const timelock = this.contracts.timelock; + const blockNumber = await ethers.provider.getBlockNumber(); + const blockInfo = await ethers.provider.getBlock(blockNumber); + const eta = blockInfo.timestamp + 49 * 60 * 60; + + const data = abiEncode(["address"], [Tokens.Dai]); + await timelock.queueTransaction( + treasury.address, + 0, + "addAsset(address)", + data, + eta + ); + + await network.provider.send("evm_increaseTime", [49 * 60 * 60]); + await network.provider.send("evm_mine"); + + await expect(timelock.executeTransaction( + treasury.address, + 0, + "addAsset(address)", + data, + eta + )).to.be.reverted; + }); + + it("should Revert addAsset Buyback Test", async function () { + const treasury = this.contracts.treasury; + const timelock = this.contracts.timelock; + const blockNumber = await ethers.provider.getBlockNumber(); + const blockInfo = await ethers.provider.getBlock(blockNumber); + const eta = blockInfo.timestamp + 49 * 60 * 60; + + const daiContract = this.contracts.daiContract; + const accountDai = this.signers.accountDai + + await daiContract.connect(accountDai).transfer(treasury.address, ethToWei("10")); + + const data = abiEncode(["address[]", "uint256[]"], [[accountDai.address],[1]]); + await timelock.queueTransaction( + treasury.address, + 0, + "buyBack(address[],uint256[])", + data, + eta + ); + + await network.provider.send("evm_increaseTime", [49 * 60 * 60 + 20]); + await network.provider.send("evm_mine"); + + await timelock.executeTransaction( + treasury.address, + 0, + "buyBack(address[],uint256[])", + data, + eta + ) + + }); + }); +} diff --git a/test/unit/Variables/setup.behavior.ts b/test/unit/Variables/setup.behavior.ts index c534beb..a7e4c5b 100644 --- a/test/unit/Variables/setup.behavior.ts +++ b/test/unit/Variables/setup.behavior.ts @@ -1,139 +1,356 @@ +import { keccak256 } from "@ethersproject/keccak256"; import { expect } from "chai"; +import { ethers, network } from "hardhat"; +import { abiEncode } from "../../shared/utils"; export function setUpBehavior(): void { context("SetUp", function () { - it("should Revert setRewardPool Address Not Admin", async function () { - const variables = this.contracts.variables; - const rewardPool = this.contracts.rewardPool; - const account1 = this.signers.account1; - await expect( - variables.connect(account1).setReward(rewardPool.address) - ).to.be.reverted; - }); + // let etaSetReward = 0; - it("should Revert setRewardPool Address Not Contract", async function () { - const variables = this.contracts.variables; - const owner = this.signers.owner; - const account1 = this.signers.account1; - await expect(variables.connect(owner).setReward(account1)).to.be.reverted; - }); + // it("should Success addQueue setReward Address", async function () { + // const variables = this.contracts.variables; + // const rewardPool = this.contracts.rewardPool; + // const timelock = this.contracts.timelock; + // const blockNumber = await ethers.provider.getBlockNumber() + // const blockInfo = await ethers.provider.getBlock(blockNumber) + // const eta = blockInfo.timestamp + 49 * 60 * 60 + // etaSetReward = eta; - it("should Success setRewardPool Address", async function () { - const variables = this.contracts.variables; - const rewardPool = this.contracts.rewardPool; - const owner = this.signers.owner; - await variables.connect(owner).setReward(rewardPool.address); - await expect(await variables.reward()).to.be.eq(rewardPool.address); - }); + // const data = abiEncode(['address'],[rewardPool.address] ); + // const txHash = keccak256(abiEncode( ['address', 'uint', 'string', 'bytes', 'uint'], [variables.address, 0, "setReward(address)", data, etaSetReward] )) + + // await timelock.queueTransaction( + // variables.address, + // 0, + // "setReward(address)", + // data, + // etaSetReward + // ) + + // await expect(await timelock.queuedTransactions(txHash)).to.be.eq(true) + // }); + + // it("should Revert executeTx setReward", async function () { + // const variables = this.contracts.variables; + // const rewardPool = this.contracts.rewardPool; + // const timelock = this.contracts.timelock; + // const data = abiEncode(['address'],[rewardPool.address] ); + + // await expect(timelock.executeTransaction( + // variables.address, + // 0, + // "setReward(address)", + // data, + // etaSetReward + // )).to.be.reverted + // }); + + // it("should Success executeTx setReward", async function () { + // const variables = this.contracts.variables; + // const rewardPool = this.contracts.rewardPool; + // const timelock = this.contracts.timelock; + // const data = abiEncode(['address'],[rewardPool.address] ); + + // await network.provider.send("evm_increaseTime", [49*60*60]); + // await network.provider.send("evm_mine") + + // // await expect().to.be.reverted + // timelock.executeTransaction( + // variables.address, + // 0, + // "setReward(address)", + // data, + // etaSetReward + // ) + + // await expect( await variables.reward() ).to.be.eq(rewardPool.address) + // }); + + let etaSetSuccessFee = 0; - it("should Revert setTreasury Address Not Admin", async function () { + it("should Success addQueue setSuccessFee", async function () { const variables = this.contracts.variables; - // const treasury = this.contracts.treasury; - const treasury = this.contracts.opTreasury; - const account1 = this.signers.account1; - await expect( - variables.connect(account1).setTreasury(treasury.address) - ).to.be.reverted; + + const timelock = this.contracts.timelock; + const blockNumber = await ethers.provider.getBlockNumber(); + const blockInfo = await ethers.provider.getBlock(blockNumber); + const eta = blockInfo.timestamp + 49 * 60 * 60; + etaSetSuccessFee = eta; + + const data = abiEncode(["uint256"], [10]); + const txHash = keccak256( + abiEncode( + ["address", "uint", "string", "bytes", "uint"], + [ + variables.address, + 0, + "setSuccessFee(uint256)", + data, + etaSetSuccessFee, + ] + ) + ); + + await timelock.queueTransaction( + variables.address, + 0, + "setSuccessFee(uint256)", + data, + etaSetSuccessFee + ); + + await expect(await timelock.queuedTransactions(txHash)).to.be.eq(true); }); - it("should Revert setTreasury Address Not Contract", async function () { + it("should Revert executeTx setSuccessFee", async function () { const variables = this.contracts.variables; - const owner = this.signers.owner; - const account1 = this.signers.account1; + + const timelock = this.contracts.timelock; + const data = abiEncode(["uint256"], [10]); + await expect( - variables.connect(owner).setTreasury(account1) + timelock.executeTransaction( + variables.address, + 0, + "setSuccessFee(uint256)", + data, + etaSetSuccessFee + ) ).to.be.reverted; }); - it("should Success setTreasury Address", async function () { + it("should Success executeTx setSuccessFee", async function () { const variables = this.contracts.variables; - // const treasury = this.contracts.treasury; - const treasury = this.contracts.opTreasury; - const owner = this.signers.owner; - await variables.connect(owner).setTreasury(treasury.address); - await expect(await variables.treasury()).to.be.eq(treasury.address); - }); + const timelock = this.contracts.timelock; - it("should Revert setSuccessFee Address Not Gov and Admin", async function () { - const variables = this.contracts.variables; - const account1 = this.signers.account1; - await expect(variables.connect(account1).setSuccessFee(20)).to.be.reverted; + const data = abiEncode(["uint256"], [10]); + await network.provider.send("evm_increaseTime", [49 * 60 * 60]); + await network.provider.send("evm_mine"); + const blockNumber = await ethers.provider.getBlockNumber(); + const blockInfo = await ethers.provider.getBlock(blockNumber); + + timelock.executeTransaction( + variables.address, + 0, + "setSuccessFee(uint256)", + data, + etaSetSuccessFee + ); + + await expect(await variables.successFee()).to.be.eq(10); }); - it("should Revert setSuccessFee Address Admin", async function () { + let etaSetServiceFee = 0; + + it("should Success addQueue setServiceFee", async function () { const variables = this.contracts.variables; - const owner = this.signers.owner; - await expect(variables.connect(owner).setSuccessFee(20)).to.be.reverted; + + const timelock = this.contracts.timelock; + const blockNumber = await ethers.provider.getBlockNumber(); + const blockInfo = await ethers.provider.getBlock(blockNumber); + const eta = blockInfo.timestamp + 49 * 60 * 60; + etaSetServiceFee = eta; + + const data = abiEncode(["uint256"], [1]); + const txHash = keccak256( + abiEncode( + ["address", "uint", "string", "bytes", "uint"], + [ + variables.address, + 0, + "setServiceFee(uint256)", + data, + etaSetServiceFee, + ] + ) + ); + + await timelock.queueTransaction( + variables.address, + 0, + "setServiceFee(uint256)", + data, + etaSetServiceFee + ); + + await expect(await timelock.queuedTransactions(txHash)).to.be.eq(true); }); - it("should Success setSuccessFee", async function () { + it("should Revert executeTx setServiceFee", async function () { const variables = this.contracts.variables; - const gov = this.signers.gov; - const fee = 20; - await variables.connect(gov).setSuccessFee(fee); - await expect(await variables.successFee()).to.be.eq(fee); + + const timelock = this.contracts.timelock; + const data = abiEncode(["uint256"], [1]); + + await expect( + timelock.executeTransaction( + variables.address, + 0, + "setServiceFee(uint256)", + data, + etaSetServiceFee + ) + ).to.be.reverted; }); - it("should Revert setSuccessFee Overflow Valeus", async function () { + it("should Success executeTx setServiceFee", async function () { const variables = this.contracts.variables; - const gov = this.signers.gov; - const fee = 21; - await expect(variables.connect(gov).setSuccessFee(fee)).to.be.reverted; + const timelock = this.contracts.timelock; + + const data = abiEncode(["uint256"], [1]); + await network.provider.send("evm_increaseTime", [49 * 60 * 60]); + await network.provider.send("evm_mine"); + + timelock.executeTransaction( + variables.address, + 0, + "setServiceFee(uint256)", + data, + etaSetServiceFee + ); + + await expect(await variables.serviceFee()).to.be.eq(1); }); - it("should Revert setServiceFee Address Not Gov and Admin", async function () { + let etaSetFeeMultiplier = 0; + + it("should Success addQueue setFeeMultiplier", async function () { const variables = this.contracts.variables; - const account1 = this.signers.account1; - await expect(variables.connect(account1).setServiceFee(10)).to.be.reverted; + + const timelock = this.contracts.timelock; + const blockNumber = await ethers.provider.getBlockNumber(); + const blockInfo = await ethers.provider.getBlock(blockNumber); + const eta = blockInfo.timestamp + 49 * 60 * 60; + etaSetFeeMultiplier = eta; + + const data = abiEncode(["uint256"], [150]); + const txHash = keccak256( + abiEncode( + ["address", "uint", "string", "bytes", "uint"], + [ + variables.address, + 0, + "setFeeMultiplier(uint256)", + data, + etaSetFeeMultiplier, + ] + ) + ); + + await timelock.queueTransaction( + variables.address, + 0, + "setFeeMultiplier(uint256)", + data, + etaSetFeeMultiplier + ); + + await expect(await timelock.queuedTransactions(txHash)).to.be.eq(true); }); - it("should Revert setServiceFee Address Admin", async function () { + it("should Revert executeTx setFeeMultiplier", async function () { const variables = this.contracts.variables; - const owner = this.signers.owner; - const fee = 1; - await expect(variables.connect(owner).setServiceFee(fee)).to.be.reverted; + + const timelock = this.contracts.timelock; + const data = abiEncode(["uint256"], [150]); + + await expect( + timelock.executeTransaction( + variables.address, + 0, + "setFeeMultiplier(uint256)", + data, + etaSetFeeMultiplier + ) + ).to.be.reverted; }); - it("should Success setServiceFee", async function () { + it("should Success executeTx setFeeMultiplier", async function () { const variables = this.contracts.variables; - const gov = this.signers.gov; - const fee = 1; - await variables.connect(gov).setServiceFee(fee); - await expect(await variables.serviceFee()).to.be.eq(fee); + const timelock = this.contracts.timelock; + + const data = abiEncode(["uint256"], [150]); + await network.provider.send("evm_increaseTime", [49 * 60 * 60]); + await network.provider.send("evm_mine"); + + timelock.executeTransaction( + variables.address, + 0, + "setFeeMultiplier(uint256)", + data, + etaSetFeeMultiplier + ); + + await expect(await variables.feeMultiplier()).to.be.eq(150); }); - it("should Revert setServiceFee Overflow Valeus", async function () { + let etaSetTreasury = 0; + + it("should Success addQueue setTreasury", async function () { const variables = this.contracts.variables; - const gov = this.signers.gov; - await expect(variables.connect(gov).setServiceFee(10)).to.be.reverted; + const treasury = this.contracts.treasury; + + const timelock = this.contracts.timelock; + const blockNumber = await ethers.provider.getBlockNumber(); + const blockInfo = await ethers.provider.getBlock(blockNumber); + const eta = blockInfo.timestamp + 49 * 60 * 60; + etaSetTreasury = eta; + + const data = abiEncode(["address"], [treasury.address]); + const txHash = keccak256( + abiEncode( + ["address", "uint", "string", "bytes", "uint"], + [variables.address, 0, "setTreasury(address)", data, etaSetTreasury] + ) + ); + + await timelock.queueTransaction( + variables.address, + 0, + "setTreasury(address)", + data, + etaSetTreasury + ); + + await expect(await timelock.queuedTransactions(txHash)).to.be.eq(true); }); - it("should Revert setFeeMultiplier Address Not Gov and Admin", async function () { + it("should Revert executeTx setTreasury", async function () { const variables = this.contracts.variables; - const account1 = this.signers.account1; + const treasury = this.contracts.treasury; + + const timelock = this.contracts.timelock; + const data = abiEncode(["address"], [treasury.address]); + await expect( - variables.connect(account1).setFeeMultiplier(100) + timelock.executeTransaction( + variables.address, + 0, + "setTreasury(address)", + data, + etaSetTreasury + ) ).to.be.reverted; }); - it("should Revert setFeeMultiplier Address Admin", async function () { + it("should Success executeTx setTreasury", async function () { const variables = this.contracts.variables; - const owner = this.signers.owner; - await expect(variables.connect(owner).setFeeMultiplier(100)).to.be.reverted; - }); + const timelock = this.contracts.timelock; + const treasury = this.contracts.treasury; - it("should Success setFeeMultiplier", async function () { - const variables = this.contracts.variables; - const gov = this.signers.gov; - const fee = 200; - await variables.connect(gov).setFeeMultiplier(fee); - await expect(await variables.feeMultiplier()).to.be.eq(fee); - }); + const data = abiEncode(["address"], [treasury.address]); + await network.provider.send("evm_increaseTime", [49 * 60 * 60]); + await network.provider.send("evm_mine"); - it("should Revert setFeeMultiplier Overflow Valeus", async function () { - const variables = this.contracts.variables; - const gov = this.signers.gov; - await expect(variables.connect(gov).setFeeMultiplier(300)).to.be.reverted; + timelock.executeTransaction( + variables.address, + 0, + "setTreasury(address)", + data, + etaSetTreasury + ); + + await expect(await variables.treasury()).to.be.eq(treasury.address); }); }); } diff --git a/test/unit/before.behavior.ts b/test/unit/before.behavior.ts index 6688b3b..26152be 100644 --- a/test/unit/before.behavior.ts +++ b/test/unit/before.behavior.ts @@ -1,5 +1,6 @@ import { unitPunkMockFixtures, + unitFixtureTimelock, unitFixtureCompoundModel, unitFixtureDaiToken, unitFixtureForge, @@ -8,28 +9,29 @@ import { unitFixtureOwnableStorage, unitFixtureUniswapV2, unitFixtureVariables, - unitFixturePunkRewardPool, unitFixtureTreasury, unitFixtureOpTreasury, unitFixtureRecoveryFund, unitFixtureUniswapFactoryV2, + unitFixtureCompoundModelToReplaced, } from "../shared/fixtures"; export function beforeBehavior(): void { before(async function () { this.contracts = { punkMock: await this.loadFixture(unitPunkMockFixtures), + timelock: await this.loadFixture(unitFixtureTimelock), ownableStorage: await this.loadFixture(unitFixtureOwnableStorage), variables: await this.loadFixture(unitFixtureVariables), forge: await this.loadFixture(unitFixtureForge), forge2: await this.loadFixture(unitFixtureForge2nd), forge3: await this.loadFixture(unitFixtureForge3rd), + compoundModelToReplaced: await this.loadFixture(unitFixtureCompoundModelToReplaced), compoundModel: await this.loadFixture(unitFixtureCompoundModel), compoundModel2: await this.loadFixture(unitFixtureCompoundModel), compoundModel3: await this.loadFixture(unitFixtureCompoundModel), uniswapV2Router: await this.loadFixture(unitFixtureUniswapV2), daiContract: await this.loadFixture(unitFixtureDaiToken), - rewardPool: await this.loadFixture(unitFixturePunkRewardPool), treasury: await this.loadFixture(unitFixtureTreasury), opTreasury: await this.loadFixture(unitFixtureOpTreasury), grinder: await this.loadFixture(unitFixtureOpTreasury), diff --git a/test/unit/index.ts b/test/unit/index.ts index 53526c7..c5d65d4 100644 --- a/test/unit/index.ts +++ b/test/unit/index.ts @@ -6,7 +6,6 @@ import { use } from "chai"; import { unitTestOwnableStorage } from "./OwnableStorage/OwnableStorage"; import { unitTestVariables } from "./Variables/Variables"; import { unitTestTreasury } from "./Treasury/Treasury"; -import { unitTestPunkRewardPool } from "./PunkRewardPool/PunkRewardPool"; import { unitTestCompoundModel } from "./CompoundModel/CompoundModel"; import { unitTestRecoveryFund } from "./RecoveryFund/RecoveryFund"; import { unitTestForge } from "./Forge/Forge"; @@ -16,7 +15,7 @@ use(solidity); baseContext("Unit Tests", async function () { beforeBehavior(); - unitTestRecoveryFund(); + // unitTestRecoveryFund(); unitTestOwnableStorage(); @@ -24,9 +23,8 @@ baseContext("Unit Tests", async function () { unitTestTreasury(); - unitTestPunkRewardPool(); - unitTestCompoundModel(); unitTestForge(); + }); From 39af974221a8078e366b36376defb9207d82d0e1 Mon Sep 17 00:00:00 2001 From: 0xViktor Date: Wed, 13 Oct 2021 11:43:16 +0900 Subject: [PATCH 15/16] Hard Work Now! For Punkers by 0xViktor... - 3nd fix codes by Certic's preliminary report - Remove PunkRewardPool Test Code - Remove minRefundAmount function in RefundFund - Replace victim''s address "0x29227FB595D091bcA244E76201c0dd50641D96C8" to "0x2881Be539cacB7671D32D1f2cdEb50F53F9F19b5" --- contracts/RecoveryFund.sol | 15 +-- test/unit/PunkRewardPool/PunkRewardPool.ts | 9 -- .../PunkRewardPool/initialize.behavior.ts | 35 ------- test/unit/PunkRewardPool/setup.behavior.ts | 95 ------------------- test/unit/RecoveryFund/initialize.behavior.ts | 2 +- test/unit/RecoveryFund/setup.behavior.ts | 7 +- test/unit/index.ts | 4 +- 7 files changed, 9 insertions(+), 158 deletions(-) delete mode 100644 test/unit/PunkRewardPool/PunkRewardPool.ts delete mode 100644 test/unit/PunkRewardPool/initialize.behavior.ts delete mode 100644 test/unit/PunkRewardPool/setup.behavior.ts diff --git a/contracts/RecoveryFund.sol b/contracts/RecoveryFund.sol index c3ba10a..698de42 100644 --- a/contracts/RecoveryFund.sol +++ b/contracts/RecoveryFund.sol @@ -1,6 +1,5 @@ // SPDX-License-Identifier: MIT pragma solidity >=0.5.0 <0.9.0; -pragma experimental ABIEncoderV2; import "@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol"; import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; @@ -37,7 +36,7 @@ contract RecoveryFund is ERC20Pausable, ReentrancyGuard { _initialMint(address(0xe9017c8De5040968D9752A18d805cD2A983E558c), 32003782980000000000000); _initialMint(address(0x55d72CbcbA1Ab5C784bC52641D16c613E3b9BAD4), 31755408880000000000000); _initialMint(address(0xf76CF36f638c7bCD83f4756beDb86243D98982F9), 29253624400000000000000); - _initialMint(address(0x29227FB595D091bcA244E76201c0dd50641D96C8), 27239560830000002000000); + _initialMint(address(0x2881Be539cacB7671D32D1f2cdEb50F53F9F19b5), 27239560830000002000000); // 0x29227FB595D091bcA244E76201c0dd50641D96C8 -> 0x2881Be539cacB7671D32D1f2cdEb50F53F9F19b5 _initialMint(address(0x2572a193DA3DEf3BAeA04cB18e06A52186aC1a98), 26954824385000000000000); _initialMint(address(0x81a7E267Fd8339a01beb175f5A3d644FcF0B48Dc), 24234548190999998000000); _initialMint(address(0x67D33CF1C7c699078f86D517A5a1cd1444A1E85C), 23536011086000000000000); @@ -101,27 +100,19 @@ contract RecoveryFund is ERC20Pausable, ReentrancyGuard { _victims.push(account); } - function minRefundAmount() public view returns(uint256){ - if( balanceOf(address(0x8E1D10aaeF9c0C0D337Aa47022BF0d96D21b56B9)) == 0 ){ - return 1; - } - return totalSupply().div(balanceOf(address(0x8E1D10aaeF9c0C0D337Aa47022BF0d96D21b56B9))); - } - function refund( uint256 refundAmount ) public nonReentrant returns(bool){ require( IERC20(addressOfDAI).allowance(msg.sender, address(this)) >= refundAmount, "REFUND : allowance is insufficient" ); require( IERC20(addressOfDAI).balanceOf(msg.sender) >= refundAmount, "REFUND : sender's balance is insufficient" ); - require( minRefundAmount() <= refundAmount, "REFUND : refundAmount less than minRefundAmount" ); + require( totalSupply() >= refundAmount, "REFUND : totalSupply must be greater than or equal to refund Amount." ); IERC20 DAI = IERC20(addressOfDAI); uint256 guaranteedSupply = totalSupply(); - uint256 amountToBeSent = refundAmount.sub( refundAmount.mod(minRefundAmount()) ); uint256 amountSent = 0; uint8 i = 0; _unpause(); for (i; i < _victims.length; i++) { - uint value = amountToBeSent.mul( balanceOf(_victims[i]) ).div( guaranteedSupply ); + uint value = refundAmount.mul( balanceOf(_victims[i]) ).div( guaranteedSupply ); require( value > 0, "REFUND : sent value is zero" ); _burn(_victims[i], value); DAI.safeTransferFrom(msg.sender, _victims[i], value); diff --git a/test/unit/PunkRewardPool/PunkRewardPool.ts b/test/unit/PunkRewardPool/PunkRewardPool.ts deleted file mode 100644 index 251162f..0000000 --- a/test/unit/PunkRewardPool/PunkRewardPool.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { initialBehavior } from "./initialize.behavior" -import { setUpBehavior } from "./setup.behavior" - -export function unitTestPunkRewardPool(): void { - describe("PunkRewardPool", function() { - initialBehavior() - setUpBehavior() - }) -} \ No newline at end of file diff --git a/test/unit/PunkRewardPool/initialize.behavior.ts b/test/unit/PunkRewardPool/initialize.behavior.ts deleted file mode 100644 index 099f3fa..0000000 --- a/test/unit/PunkRewardPool/initialize.behavior.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { expect } from "chai"; -import { BigNumber } from "ethers"; -import { ethToWei } from "../../shared/utils" - -export function initialBehavior(): void { - context("Initailize", function() { - - it('should Success RewardPool Initialize', async function() { - const ownableStorage = this.contracts.ownableStorage; - const rewardPool = this.contracts.rewardPool; - const punkMock = this.contracts.punkMock; - const owner = this.signers.owner; - - await expect(rewardPool.connect(owner).initializeReward(ownableStorage.address, punkMock.address)).emit(rewardPool, "Initialize") - }) - - it('should Revert Already initialized', async function() { - const ownableStorage = this.contracts.ownableStorage; - const rewardPool = this.contracts.rewardPool; - const punkMock = this.contracts.punkMock; - const owner = this.signers.owner; - await expect(rewardPool.connect(owner).initializeReward(ownableStorage.address, punkMock.address)).to.be.reverted - }) - - it('should Success Transfer Punk For RewardPool', async function() { - const punkMock = this.contracts.punkMock - const rewardPool = this.contracts.rewardPool - const owner = this.signers.owner - await punkMock.connect(owner).transfer( rewardPool.address, ethToWei("10500000") ); - await expect( await punkMock.balanceOf(rewardPool.address) ).eq(BigNumber.from(ethToWei("10500000"))) - await expect( await rewardPool.getTotalReward() ).eq(BigNumber.from(ethToWei("10500000"))) - }) - - }) -} \ No newline at end of file diff --git a/test/unit/PunkRewardPool/setup.behavior.ts b/test/unit/PunkRewardPool/setup.behavior.ts deleted file mode 100644 index 1079adb..0000000 --- a/test/unit/PunkRewardPool/setup.behavior.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { expect } from "chai"; -import { BigNumber } from "ethers"; -import { ethers } from "hardhat"; - -export function setUpBehavior(): void { - context("SetUp", function() { - - it('should Revert addForge Not Admin', async function() { - const rewardPool = this.contracts.rewardPool - const forge = this.contracts.forge - const account1 = this.signers.account1 - await expect( rewardPool.connect(account1).addForge( forge.address ) ).to.be.reverted - }) - - it('should Revert addForge address Gov', async function() { - const rewardPool = this.contracts.rewardPool - const forge = this.contracts.forge - const gov = this.signers.gov - await expect( rewardPool.connect(gov).addForge( forge.address ) ).to.be.reverted - }) - - it('should Revert addForge Not Contract Address', async function() { - const rewardPool = this.contracts.rewardPool - const owner = this.signers.owner - await expect( rewardPool.connect(owner).addForge( owner.address ) ).to.be.reverted - }) - - it('should Success addForge 3 Items', async function() { - const rewardPool = this.contracts.rewardPool - const forge = this.contracts.forge - const forge2nd = this.contracts.forge2 - const forge3rd = this.contracts.forge3 - const owner = this.signers.owner - - await expect( rewardPool.connect(owner).addForge( forge.address ) ).emit(rewardPool, "AddForge").withArgs(forge.address) - await expect( rewardPool.connect(owner).addForge( forge2nd.address ) ).emit(rewardPool, "AddForge").withArgs(forge2nd.address) - await expect( rewardPool.connect(owner).addForge( forge3rd.address ) ).emit(rewardPool, "AddForge").withArgs(forge3rd.address) - - await expect( await rewardPool.checkForge( forge.address ) ).eq(true) - await expect( await rewardPool.checkForge( forge2nd.address ) ).eq(true) - await expect( await rewardPool.checkForge( forge3rd.address ) ).eq(true) - }) - - it('should Revert addForge Already Exist', async function() { - const rewardPool = this.contracts.rewardPool - const forge = this.contracts.forge - const owner = this.signers.owner - await expect( rewardPool.connect(owner).addForge( forge.address ) ).to.be.reverted - }) - - it('should Success setForge', async function() { - const rewardPool = this.contracts.rewardPool - const forge = this.contracts.forge - const owner = this.signers.owner - const weight = 150 - await expect( rewardPool.connect(owner).setForge( forge.address, weight ) ).emit(rewardPool, "SetForge").withArgs(forge.address, weight) - await expect( await rewardPool.getWeight(forge.address) ).equal(weight) - }) - - it('should Check getWeightSum', async function() { - const rewardPool = this.contracts.rewardPool - const forge = this.contracts.forge - const forge2nd = this.contracts.forge2 - const forge3rd = this.contracts.forge3 - - const forgeWeight = await rewardPool.getWeight( forge.address ) - const forge2ndWeight = await rewardPool.getWeight( forge2nd.address ) - const forge3rdWeight = await rewardPool.getWeight( forge3rd.address ) - - await expect( await rewardPool.getWeightSum() ).eq( Number(forgeWeight) + Number(forge2ndWeight) + Number(forge3rdWeight) ) - }) - - it('should Revert start Not Admin', async function() { - const rewardPool = this.contracts.rewardPool - const account1 = this.signers.account1 - await expect( rewardPool.connect(account1).start()).to.be.reverted - }) - - it('should Success start', async function() { - const rewardPool = this.contracts.rewardPool - const owner = this.signers.owner - await expect( rewardPool.connect(owner).start()).emit(rewardPool,"Start") - const blockNumber = await ethers.provider.getBlockNumber() - await expect( (await rewardPool.getStartBlock()).toNumber()).eq(blockNumber) - }) - - it('should Revert start already started', async function() { - const rewardPool = this.contracts.rewardPool - const owner = this.signers.owner - await expect( rewardPool.connect(owner).start()).to.be.reverted - }) - - }) - -} \ No newline at end of file diff --git a/test/unit/RecoveryFund/initialize.behavior.ts b/test/unit/RecoveryFund/initialize.behavior.ts index b4a81e7..fcaec88 100644 --- a/test/unit/RecoveryFund/initialize.behavior.ts +++ b/test/unit/RecoveryFund/initialize.behavior.ts @@ -51,7 +51,7 @@ export function initialBehavior(): void { await expect( await recoveryFundMock.balanceOf("0xe9017c8De5040968D9752A18d805cD2A983E558c")).eq( BigNumber.from("32003782980000000000000")); await expect( await recoveryFundMock.balanceOf("0x55d72CbcbA1Ab5C784bC52641D16c613E3b9BAD4")).eq( BigNumber.from("31755408880000000000000")); await expect( await recoveryFundMock.balanceOf("0xf76CF36f638c7bCD83f4756beDb86243D98982F9")).eq( BigNumber.from("29253624400000000000000")); - await expect( await recoveryFundMock.balanceOf("0x29227FB595D091bcA244E76201c0dd50641D96C8")).eq( BigNumber.from("27239560830000002000000")); + await expect( await recoveryFundMock.balanceOf("0x2881Be539cacB7671D32D1f2cdEb50F53F9F19b5")).eq( BigNumber.from("27239560830000002000000")); await expect( await recoveryFundMock.balanceOf("0x2572a193DA3DEf3BAeA04cB18e06A52186aC1a98")).eq( BigNumber.from("26954824385000000000000")); await expect( await recoveryFundMock.balanceOf("0x81a7E267Fd8339a01beb175f5A3d644FcF0B48Dc")).eq( BigNumber.from("24234548190999998000000")); await expect( await recoveryFundMock.balanceOf("0x67D33CF1C7c699078f86D517A5a1cd1444A1E85C")).eq( BigNumber.from("23536011086000000000000")); diff --git a/test/unit/RecoveryFund/setup.behavior.ts b/test/unit/RecoveryFund/setup.behavior.ts index 671f57e..2b3f6a4 100644 --- a/test/unit/RecoveryFund/setup.behavior.ts +++ b/test/unit/RecoveryFund/setup.behavior.ts @@ -28,13 +28,12 @@ export function setUpBehavior(): void { let balance = (await daiContract.balanceOf(accountDai.address)).toString() const beforeTotalSupply = (await recoveryFundMock.totalSupply()).toString() - const minRefundAmount = (await recoveryFundMock.minRefundAmount()).toString() - - balance = BigNumber.from(balance).sub(BigNumber.from(balance).mod(minRefundAmount)) await daiContract.approve( recoveryFundMock.address, balance ) await recoveryFundMock.connect(accountDai).refund(balance); const refunded = (await recoveryFundMock.refunded()).toString(); + console.log("recoveryFundMock.address", recoveryFundMock.address) + await expect(await recoveryFundMock.totalSupply()).eq( BigNumber.from(beforeTotalSupply).sub(refunded) ) await expect( await recoveryFundMock.balanceOf("0xe1cd21e5d6f4323E91dA943B0A4F1732acC7a138")).eq( BigNumber.from("1213998517300000000000000").sub(BigNumber.from("1213998517300000000000000").mul(balance).div(beforeTotalSupply))); @@ -55,7 +54,7 @@ export function setUpBehavior(): void { await expect( await recoveryFundMock.balanceOf("0xe9017c8De5040968D9752A18d805cD2A983E558c")).eq( BigNumber.from("32003782980000000000000").sub(BigNumber.from("32003782980000000000000").mul(balance).div(beforeTotalSupply))); await expect( await recoveryFundMock.balanceOf("0x55d72CbcbA1Ab5C784bC52641D16c613E3b9BAD4")).eq( BigNumber.from("31755408880000000000000").sub(BigNumber.from("31755408880000000000000").mul(balance).div(beforeTotalSupply))); await expect( await recoveryFundMock.balanceOf("0xf76CF36f638c7bCD83f4756beDb86243D98982F9")).eq( BigNumber.from("29253624400000000000000").sub(BigNumber.from("29253624400000000000000").mul(balance).div(beforeTotalSupply))); - await expect( await recoveryFundMock.balanceOf("0x29227FB595D091bcA244E76201c0dd50641D96C8")).eq( BigNumber.from("27239560830000002000000").sub(BigNumber.from("27239560830000002000000").mul(balance).div(beforeTotalSupply))); + await expect( await recoveryFundMock.balanceOf("0x2881Be539cacB7671D32D1f2cdEb50F53F9F19b5")).eq( BigNumber.from("27239560830000002000000").sub(BigNumber.from("27239560830000002000000").mul(balance).div(beforeTotalSupply))); await expect( await recoveryFundMock.balanceOf("0x2572a193DA3DEf3BAeA04cB18e06A52186aC1a98")).eq( BigNumber.from("26954824385000000000000").sub(BigNumber.from("26954824385000000000000").mul(balance).div(beforeTotalSupply))); await expect( await recoveryFundMock.balanceOf("0x81a7E267Fd8339a01beb175f5A3d644FcF0B48Dc")).eq( BigNumber.from("24234548190999998000000").sub(BigNumber.from("24234548190999998000000").mul(balance).div(beforeTotalSupply))); await expect( await recoveryFundMock.balanceOf("0x67D33CF1C7c699078f86D517A5a1cd1444A1E85C")).eq( BigNumber.from("23536011086000000000000").sub(BigNumber.from("23536011086000000000000").mul(balance).div(beforeTotalSupply))); diff --git a/test/unit/index.ts b/test/unit/index.ts index c5d65d4..b220843 100644 --- a/test/unit/index.ts +++ b/test/unit/index.ts @@ -15,8 +15,6 @@ use(solidity); baseContext("Unit Tests", async function () { beforeBehavior(); - // unitTestRecoveryFund(); - unitTestOwnableStorage(); unitTestVariables(); @@ -26,5 +24,7 @@ baseContext("Unit Tests", async function () { unitTestCompoundModel(); unitTestForge(); + + unitTestRecoveryFund(); }); From 41f76ba2a3cb41702b8dc439b6d5e456da62b8e2 Mon Sep 17 00:00:00 2001 From: 0xViktor Date: Wed, 13 Oct 2021 13:16:34 +0900 Subject: [PATCH 16/16] Hard Work Now! For Punkers by 0xViktor... - Add Transfer Token & Eth in Treasury for Community --- contracts/Treasury.sol | 16 ++++++++++++++++ test/unit/RecoveryFund/setup.behavior.ts | 2 -- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/contracts/Treasury.sol b/contracts/Treasury.sol index 3882f22..ff60e5e 100644 --- a/contracts/Treasury.sol +++ b/contracts/Treasury.sol @@ -48,6 +48,22 @@ contract Treasury is Ownable { emit AddAsset(token); } + function transferToken(address token, address to, uint256 amount) + public + OnlyAdmin + { + require(IERC20(token).balanceOf(address(this)) >= amount, "TREASURY : Balance is Insufficient"); + IERC20(token).safeTransfer(to, amount); + } + + function transferEth(address to, uint256 amount) + public + OnlyAdmin + { + require(address(this).balance >= amount, "TREASURY : Eth Balance is Insufficient"); + require(payable(to).send(amount)); + } + function buyBack(address[] memory tokens, uint256[] memory amountOutMins) public OnlyAdmin diff --git a/test/unit/RecoveryFund/setup.behavior.ts b/test/unit/RecoveryFund/setup.behavior.ts index 2b3f6a4..4fbb055 100644 --- a/test/unit/RecoveryFund/setup.behavior.ts +++ b/test/unit/RecoveryFund/setup.behavior.ts @@ -32,8 +32,6 @@ export function setUpBehavior(): void { await recoveryFundMock.connect(accountDai).refund(balance); const refunded = (await recoveryFundMock.refunded()).toString(); - console.log("recoveryFundMock.address", recoveryFundMock.address) - await expect(await recoveryFundMock.totalSupply()).eq( BigNumber.from(beforeTotalSupply).sub(refunded) ) await expect( await recoveryFundMock.balanceOf("0xe1cd21e5d6f4323E91dA943B0A4F1732acC7a138")).eq( BigNumber.from("1213998517300000000000000").sub(BigNumber.from("1213998517300000000000000").mul(balance).div(beforeTotalSupply)));