From f840b63f52bee4ec5ecbbd94a9a74cd1427ea309 Mon Sep 17 00:00:00 2001 From: andy Date: Mon, 25 Nov 2024 11:41:05 -0500 Subject: [PATCH] move token burn prep into the shares logic --- .../LenderCommitmentGroupShares.sol | 100 +++++++++++++++++- .../LenderCommitmentGroup_Smart.sol | 88 +++------------ .../interfaces/ILenderCommitmentGroup.sol | 2 +- .../LenderGroupMockInteract.sol | 4 +- .../LenderCommitmentGroup_Smart_Override.sol | 4 +- .../LenderCommitmentGroup_Smart_Test.sol | 6 +- 6 files changed, 124 insertions(+), 80 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroupShares.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroupShares.sol index a79efc38..17f450be 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroupShares.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroupShares.sol @@ -4,9 +4,25 @@ pragma solidity >=0.8.0 <0.9.0; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/Ownable.sol"; + + contract LenderCommitmentGroupShares is ERC20, Ownable { uint8 private immutable DECIMALS; + + mapping(address => uint256) public poolSharesPreparedToWithdrawForLender; + mapping(address => uint256) public poolSharesPreparedTimestamp; + + + event SharesPrepared( + address recipient, + uint256 sharesAmount, + uint256 preparedAt + + ); + + + constructor(string memory _name, string memory _symbol, uint8 _decimals) ERC20(_name, _symbol) Ownable() @@ -18,11 +34,93 @@ contract LenderCommitmentGroupShares is ERC20, Ownable { _mint(_recipient, _amount); } - function burn(address _burner, uint256 _amount) external onlyOwner { + function burn(address _burner, uint256 _amount, uint256 withdrawDelayTimeSeconds) external onlyOwner { + + + //require prepared + require(poolSharesPreparedToWithdrawForLender[_burner] >= _amount,"Shares not prepared for withdraw"); + require(poolSharesPreparedTimestamp[_burner] <= block.timestamp - withdrawDelayTimeSeconds,"Shares not prepared for withdraw"); + + + //reset prepared + poolSharesPreparedToWithdrawForLender[_burner] = 0; + poolSharesPreparedTimestamp[_burner] = block.timestamp; + + _burn(_burner, _amount); } function decimals() public view virtual override returns (uint8) { return DECIMALS; } + + + // ---- + + function _afterTokenTransfer( + address from, + address to, + uint256 amount + ) internal override { + + + //reset prepared + poolSharesPreparedToWithdrawForLender[from] = 0; + poolSharesPreparedTimestamp[from] = block.timestamp; + + } + + + // ---- + + + /** + * @notice Prepares shares for withdrawal, allowing the user to burn them later for principal tokens + accrued interest. + * @param _amountPoolSharesTokens Amount of pool shares to prepare for withdrawal. + * @return True if the preparation is successful. + */ + function prepareSharesForBurn( + address _recipient, + uint256 _amountPoolSharesTokens + ) external onlyOwner + returns (bool) { + + return _prepareSharesForBurn(_recipient, _amountPoolSharesTokens); + } + + + /** + * @notice Internal function to prepare shares for withdrawal using the required delay to mitigate sandwich attacks. + * @param _recipient Address of the user preparing their shares. + * @param _amountPoolSharesTokens Amount of pool shares to prepare for withdrawal. + * @return True if the preparation is successful. + */ + + function _prepareSharesForBurn( + address _recipient, + uint256 _amountPoolSharesTokens + ) internal returns (bool) { + + require( balanceOf(_recipient) >= _amountPoolSharesTokens ); + + poolSharesPreparedToWithdrawForLender[_recipient] = _amountPoolSharesTokens; + poolSharesPreparedTimestamp[_recipient] = block.timestamp; + + + emit SharesPrepared( + _recipient, + _amountPoolSharesTokens, + block.timestamp + + ); + + return true; + } + + + + + + + } diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index dacc9294..eb4a7b27 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -139,10 +139,8 @@ contract LenderCommitmentGroup_Smart is - mapping(address => uint256) public poolSharesPreparedToWithdrawForLender; - mapping(address => uint256) public poolSharesPreparedTimestamp; - uint256 immutable public DEFAULT_WITHDRAWL_DELAY_TIME_SECONDS = 300; - uint256 immutable public MAX_WITHDRAWL_DELAY_TIME = 86400; + uint256 immutable public DEFAULT_WITHDRAW_DELAY_TIME_SECONDS = 300; + uint256 immutable public MAX_WITHDRAW_DELAY_TIME = 86400; //mapping(address => uint256) public principalTokensCommittedByLender; mapping(uint256 => bool) public activeBids; @@ -152,7 +150,7 @@ contract LenderCommitmentGroup_Smart is int256 tokenDifferenceFromLiquidations; bool public firstDepositMade; - uint256 public withdrawlDelayTimeSeconds; + uint256 public withdrawDelayTimeSeconds; IUniswapPricingLibrary.PoolRouteConfig[] public poolOracleRoutes; @@ -218,12 +216,6 @@ contract LenderCommitmentGroup_Smart is uint256 totalInterestCollected ); - event PoolSharesPrepared( - address lender, - uint256 sharesAmount, - uint256 preparedAt - - ); event WithdrawFromEscrow( @@ -312,7 +304,7 @@ contract LenderCommitmentGroup_Smart is marketId = _commitmentGroupConfig.marketId; - withdrawlDelayTimeSeconds = DEFAULT_WITHDRAWL_DELAY_TIME_SECONDS; + withdrawDelayTimeSeconds = DEFAULT_WITHDRAW_DELAY_TIME_SECONDS; //in order for this to succeed, first, the SmartCommitmentForwarder needs to be a trusted forwarder for the market ITellerV2Context(TELLER_V2).approveMarketForwarder( @@ -362,12 +354,12 @@ contract LenderCommitmentGroup_Smart is * @notice Sets the delay time for withdrawing funds. Only Protocol Owner. * @param _seconds Delay time in seconds. */ - function setWithdrawlDelayTime(uint256 _seconds) + function setWithdrawDelayTime(uint256 _seconds) external onlyProtocolOwner { - require( _seconds < MAX_WITHDRAWL_DELAY_TIME ); + require( _seconds < MAX_WITHDRAW_DELAY_TIME ); - withdrawlDelayTimeSeconds = _seconds; + withdrawDelayTimeSeconds = _seconds; } @@ -476,22 +468,12 @@ contract LenderCommitmentGroup_Smart is require( principalTokenBalanceAfter == principalTokenBalanceBefore + _amount, "Token balance was not added properly" ); sharesAmount_ = _valueOfUnderlying(_amount, sharesExchangeRate()); - - - + totalPrincipalTokensCommitted += _amount; - - - //mint shares equal to _amount and give them to the shares recipient !!! + + //mint shares equal to _amount and give them to the shares recipient poolSharesToken.mint(_sharesRecipient, sharesAmount_); - - - - // prepare current balance - uint256 sharesBalance = poolSharesToken.balanceOf(address(_sharesRecipient)); - _prepareSharesForWithdraw(_sharesRecipient,sharesBalance); - - + emit LenderAddedPrincipal( msg.sender, @@ -609,48 +591,16 @@ contract LenderCommitmentGroup_Smart is } - /** - * @notice Prepares shares for withdrawal, allowing the user to burn them later for principal tokens + accrued interest. - * @param _amountPoolSharesTokens Amount of pool shares to prepare for withdrawal. - * @return True if the preparation is successful. - */ - function prepareSharesForWithdraw( + + function prepareSharesForBurn( uint256 _amountPoolSharesTokens ) external whenForwarderNotPaused whenNotPaused nonReentrant returns (bool) { - return _prepareSharesForWithdraw(msg.sender, _amountPoolSharesTokens); + return poolSharesToken.prepareSharesForBurn(msg.sender, _amountPoolSharesTokens); } - /** - * @notice Internal function to prepare shares for withdrawal using the required delay to mitigate sandwich attacks. - * @param _recipient Address of the user preparing their shares. - * @param _amountPoolSharesTokens Amount of pool shares to prepare for withdrawal. - * @return True if the preparation is successful. - */ - - function _prepareSharesForWithdraw( - address _recipient, - uint256 _amountPoolSharesTokens - ) internal returns (bool) { - - require( poolSharesToken.balanceOf(_recipient) >= _amountPoolSharesTokens ); - - poolSharesPreparedToWithdrawForLender[_recipient] = _amountPoolSharesTokens; - poolSharesPreparedTimestamp[_recipient] = block.timestamp; - - - emit PoolSharesPrepared( - - _recipient, - _amountPoolSharesTokens, - block.timestamp - - ); - - return true; - } /** @@ -668,13 +618,9 @@ contract LenderCommitmentGroup_Smart is ) external whenForwarderNotPaused whenNotPaused nonReentrant onlyOracleApprovedAllowEOA returns (uint256) { - require(poolSharesPreparedToWithdrawForLender[msg.sender] >= _amountPoolSharesTokens,"Shares not prepared for withdraw"); - require(poolSharesPreparedTimestamp[msg.sender] <= block.timestamp - withdrawlDelayTimeSeconds,"Shares not prepared for withdraw"); - + //require(poolSharesPreparedToWithdrawForLender[msg.sender] >= _amountPoolSharesTokens,"Shares not prepared for withdraw"); + // require(poolSharesPreparedTimestamp[msg.sender] <= block.timestamp - withdrawlDelayTimeSeconds,"Shares not prepared for withdraw"); - poolSharesPreparedToWithdrawForLender[msg.sender] = 0; - poolSharesPreparedTimestamp[msg.sender] = block.timestamp; - //this should compute BEFORE shares burn uint256 principalTokenValueToWithdraw = _valueOfUnderlying( @@ -682,7 +628,7 @@ contract LenderCommitmentGroup_Smart is sharesExchangeRateInverse() ); - poolSharesToken.burn(msg.sender, _amountPoolSharesTokens); + poolSharesToken.burn(msg.sender, _amountPoolSharesTokens, withdrawDelayTimeSeconds); totalPrincipalTokensWithdrawn += principalTokenValueToWithdraw; diff --git a/packages/contracts/contracts/interfaces/ILenderCommitmentGroup.sol b/packages/contracts/contracts/interfaces/ILenderCommitmentGroup.sol index e3eb242e..02219207 100644 --- a/packages/contracts/contracts/interfaces/ILenderCommitmentGroup.sol +++ b/packages/contracts/contracts/interfaces/ILenderCommitmentGroup.sol @@ -41,7 +41,7 @@ interface ILenderCommitmentGroup { - function prepareSharesForWithdraw( + function prepareSharesForBurn( uint256 _amountPoolSharesTokens ) external returns (bool); diff --git a/packages/contracts/contracts/penetrationtesting/LenderGroupMockInteract.sol b/packages/contracts/contracts/penetrationtesting/LenderGroupMockInteract.sol index 0b1b317c..a1c86ad6 100644 --- a/packages/contracts/contracts/penetrationtesting/LenderGroupMockInteract.sol +++ b/packages/contracts/contracts/penetrationtesting/LenderGroupMockInteract.sol @@ -46,11 +46,11 @@ contract LenderGroupMockInteract { * @param _target The LenderCommitmentGroup contract address. * @param _amountPoolSharesTokens The amount of share tokens to prepare for withdrawal. */ - function prepareSharesForWithdraw( + function prepareSharesForBurn( address _target, uint256 _amountPoolSharesTokens ) external { - ILenderCommitmentGroup(_target).prepareSharesForWithdraw( + ILenderCommitmentGroup(_target).prepareSharesForBurn( _amountPoolSharesTokens ); } diff --git a/packages/contracts/tests/SmartCommitmentForwarder/LenderCommitmentGroup_Smart_Override.sol b/packages/contracts/tests/SmartCommitmentForwarder/LenderCommitmentGroup_Smart_Override.sol index 9eafdd22..2024783e 100644 --- a/packages/contracts/tests/SmartCommitmentForwarder/LenderCommitmentGroup_Smart_Override.sol +++ b/packages/contracts/tests/SmartCommitmentForwarder/LenderCommitmentGroup_Smart_Override.sol @@ -43,13 +43,13 @@ contract LenderCommitmentGroup_Smart_Override is LenderCommitmentGroup_Smart { - function mock_prepareSharesForWithdraw( + /* function mock_prepareSharesForWithdraw( uint256 _amountPoolSharesTokens ) external { poolSharesPreparedToWithdrawForLender[msg.sender] = _amountPoolSharesTokens; poolSharesPreparedTimestamp[msg.sender] = block.timestamp; - } + } */ function getMinimumAmountDifferenceToCloseDefaultedLoan( diff --git a/packages/contracts/tests/SmartCommitmentForwarder/LenderCommitmentGroup_Smart_Test.sol b/packages/contracts/tests/SmartCommitmentForwarder/LenderCommitmentGroup_Smart_Test.sol index 8bfa8a2a..d7a66611 100644 --- a/packages/contracts/tests/SmartCommitmentForwarder/LenderCommitmentGroup_Smart_Test.sol +++ b/packages/contracts/tests/SmartCommitmentForwarder/LenderCommitmentGroup_Smart_Test.sol @@ -337,7 +337,7 @@ contract LenderCommitmentGroup_Smart_Test is Testable { vm.prank(address(lender)); - lenderCommitmentGroupSmart.prepareSharesForWithdraw(sharesAmount); + lenderCommitmentGroupSmart.prepareSharesForBurn(sharesAmount); vm.warp(1000); @@ -391,7 +391,7 @@ contract LenderCommitmentGroup_Smart_Test is Testable { vm.prank(address(lender)); - lenderCommitmentGroupSmart.prepareSharesForWithdraw(sharesAmount); + lenderCommitmentGroupSmart.prepareSharesForBurn(sharesAmount); vm.warp(1000); @@ -445,7 +445,7 @@ contract LenderCommitmentGroup_Smart_Test is Testable { vm.prank(address(lender)); - lenderCommitmentGroupSmart.prepareSharesForWithdraw(sharesAmount); + lenderCommitmentGroupSmart.prepareSharesForBurn(sharesAmount); vm.warp(1000);