From ea4417661c8c2a3d39eccba8fb6f11f0ab3f42cd Mon Sep 17 00:00:00 2001 From: Admazzola Date: Thu, 25 Apr 2024 11:11:08 -0400 Subject: [PATCH 01/53] test and fix --- .../LenderCommitmentGroup_Smart.sol | 4 +- .../LenderCommitmentGroup_Smart_Override.sol | 3 + .../LenderCommitmentGroup_Smart_Test.sol | 63 +++++++++++++++++++ 3 files changed, 68 insertions(+), 2 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..c33d8e39 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -451,7 +451,7 @@ contract LenderCommitmentGroup_Smart is tokenDifferenceFromLiquidations += int256(tokensToTakeFromSender); - totalPrincipalTokensRepaid += amountDue; + //totalPrincipalTokensRepaid += amountDue; } else { uint256 tokensToGiveToSender = abs(_tokenAmountDifference); @@ -464,7 +464,7 @@ contract LenderCommitmentGroup_Smart is tokenDifferenceFromLiquidations -= int256(tokensToGiveToSender); - totalPrincipalTokensRepaid += amountDue; + //totalPrincipalTokensRepaid += amountDue; } //this will give collateral to the caller diff --git a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Override.sol b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Override.sol index 57894db3..29b0dd8f 100644 --- a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Override.sol +++ b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Override.sol @@ -70,6 +70,9 @@ contract LenderCommitmentGroup_Smart_Override is LenderCommitmentGroup_Smart { } + function set_totalPrincipalTokensRepaid(uint256 _mockAmt) public { + totalPrincipalTokensRepaid = _mockAmt; + } function set_totalPrincipalTokensCommitted(uint256 _mockAmt) public { totalPrincipalTokensCommitted = _mockAmt; diff --git a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol index 8d468175..85f67127 100644 --- a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol +++ b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol @@ -599,6 +599,69 @@ contract LenderCommitmentGroup_Smart_Test is Testable { assertEq( _tellerV2.lenderCloseLoanWasCalled(), true, "lender close loan not called"); } + +function test_liquidateDefaultedLoanWithIncentive_does_not_double_count_repaid() public { + + + initialize_group_contract(); + + principalToken.transfer(address(liquidator), 1e18); + uint256 originalBalance = principalToken.balanceOf(address(liquidator)); + + uint256 amountOwed = 1000; + + + uint256 bidId = 0; + + + lenderCommitmentGroupSmart.set_mockAmountOwedForBid(amountOwed); + + + //time has advanced enough to now have a 50 percent discount s + vm.warp(1000); //loanDefaultedTimeStamp ? + + lenderCommitmentGroupSmart.set_mockBidAsActiveForGroup(bidId,true); + + vm.prank(address(liquidator)); + principalToken.approve(address(lenderCommitmentGroupSmart), 1e18); + + + lenderCommitmentGroupSmart.set_totalPrincipalTokensRepaid(0); + + lenderCommitmentGroupSmart.mock_setMinimumAmountDifferenceToCloseDefaultedLoan(-500); + + int256 tokenAmountDifference = -500; + vm.prank(address(liquidator)); + lenderCommitmentGroupSmart.liquidateDefaultedLoanWithIncentive( + bidId, + tokenAmountDifference + ); + + //simulate the repay loan callback as would happen in a liquidation + vm.prank(address(_tellerV2)); + lenderCommitmentGroupSmart.repayLoanCallback( + bidId, + address(this), + amountOwed, + 20 + ); + + uint256 updatedBalance = principalToken.balanceOf(address(liquidator)); + + uint256 totalPrincipalTokensRepaid = lenderCommitmentGroupSmart.totalPrincipalTokensRepaid(); + + + assertEq(totalPrincipalTokensRepaid, amountOwed, "unexpected totalPrincipalTokensRepaid"); + + + //make sure lenderCloseloan is called + assertEq( _tellerV2.lenderCloseLoanWasCalled(), true, "lender close loan not called"); + } + + + + + /* make sure we get expected data based on the vm warp */ From a5b89092958d99f0f8b9db9c8d5b2fd7b749574c Mon Sep 17 00:00:00 2001 From: Admazzola Date: Mon, 29 Apr 2024 12:11:30 -0400 Subject: [PATCH 02/53] add events --- .../LenderCommitmentGroup_Smart.sol | 353 ++++++++++++++---- 1 file changed, 272 insertions(+), 81 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..b3e2f406 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -41,19 +41,86 @@ import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; /* + + + +////---- + + +1. Use 50% forced max utilization ratio as initial game theory - have a global utilization limit and a user-signalled utilization limit (based on shares signalling) + +2. When pool shares are burned, give the lender : [ their pct shares * ( currentPrincipalTokens in contract, totalCollateralShares, totalInterestCollected) ] and later, they can burn the collateral shares for any collateral tokens that are in the contract. +3. use noahs TToken contract as reference for ratios -> amt of tokens to get when committing +4. Need price oracle bc we dont want to use maxPrincipalPerCollateral ratio as a static ideally +5. have an LTV ratio + +Every time a lender deposits tokens, we can mint an equal amt of RepresentationToken + + +// -- LIMITATIONS +1. neither the principal nor collateral token shall not have more than 18 decimals due to the way expansion is configured + + +// -- EXITING + +When exiting, a lender is burning X shares + + - We calculate the total equity value (Z) of the pool multiplies by their pct of shares (S%) (naive is just total committed princ tokens and interest , could maybe do better ) + - We are going to give the lender (Z * S%) value. The way we are going to give it to them is in a split of principal (P) and collateral tokens (C) which are in the pool right now. Similar to exiting a uni pool . C tokens will only be in the pool if bad defaults happened. + + NOTE: We will know the price of C in terms of P due to the ratio of total P used for loans and total C used for loans + + NOTE: if there are not enough P and C tokens in the pool to give the lender to equal a value of (Z * S%) then we revert . + +// --------- + + +// ISSUES + +1. for 'overall pool value' calculations (for shares math) an active loans value should be treated as "principal+interest" + aka the amount that will be paid to the pool optimistically. DONE +2. Redemption with ' the split' of principal and collateral is not ideal . What would be more ideal is a "conversion auction' or a 'swap auction'. + In this paradigm, any party can offer to give X principal tokens for the Y collateral tokens that are in the pool. the auction lasts (1 hour?) and this way it is always only principal tha is being withdrawn - far less risk of MEV attacker taking more C -- DONE +3. it is annoying that a bad default can cause a pool to have to totally exit and close ..this is a minor issue. maybe some form of Insurance can help resurrect a pool in this case, mayeb anyone can restore the health of the pool w a fn call. + a. fix this by changing the shares logic so you do get more shares in this event (i dont think its possible) + b. have a function that lets anyone donate principal tokens to make the pool whole again . (refill underwater pools w insurance fund??) + c. lets pools expire and get unwound and withdrawn completely , make a new pool + +4. build a function to do lender close loan + + + +TODO: +A. Make a mental map of these subsystems, attack vectors, mitigaions + +B. + + +// ----- + + + +// TODO + + + 2. consider adding PATHS to this for the oracle.. so the pair can be USDC to PNDC but use weth as intermediate + 4. tests - Each LenderCommitmentGroup SmartContract acts as its own Loan Commitment (for the SmartCommitmentForwarder) and acts as its own Lender in the Teller Protocol. +// ---- - Lender Users can deposit principal tokens in this contract and this will give them Share Tokens (LP tokens) representing their ownership in the liquidity pool of this contract. - Borrower Users can borrow principal token funds from this contract (via the SCF contract) by providing collateral tokens in the proper amount as specified by the rules of this smart contract. - These collateral tokens are then owned by this smart contract and are returned to the borrower via the Teller Protocol rules to the borrower if and only if the borrower repays principal and interest of the loan they took. - If the borrower defaults on a loan, for 24 hours a liquidation auction is automatically conducted by this smart contract in order to incentivize a liquidator to take the collateral tokens in exchange for principal tokens. + + +If a lender puts up 50,000 originally, im able to withdraw all my deposits. Everyone else is in the hole until a borrower repays a loan +If there isnt enough liquidity, you just cannot burn those shares. + +Consider implementing eip-4626 + */ @@ -72,14 +139,16 @@ contract LenderCommitmentGroup_Smart is uint256 public immutable UNISWAP_EXPANSION_FACTOR = 2**96; - uint256 public immutable EXCHANGE_RATE_EXPANSION_FACTOR = 1e36; + uint256 public immutable EXCHANGE_RATE_EXPANSION_FACTOR = 1e36; //consider making this dynamic /// @custom:oz-upgrades-unsafe-allow state-variable-immutable address public immutable TELLER_V2; address public immutable SMART_COMMITMENT_FORWARDER; address public immutable UNISWAP_V3_FACTORY; address public UNISWAP_V3_POOL; - + + // bool private _initialized; + LenderCommitmentGroupShares public poolSharesToken; IERC20 public principalToken; @@ -87,19 +156,18 @@ contract LenderCommitmentGroup_Smart is uint256 marketId; - - uint256 public totalPrincipalTokensCommitted; - uint256 public totalPrincipalTokensWithdrawn; + //this should be the net total principal tokens , lenderAdded - lenderWithdrawn + uint256 public totalPrincipalTokensCommitted; uint256 public totalPrincipalTokensLended; uint256 public totalPrincipalTokensRepaid; //subtract this and the above to find total principal tokens outstanding for loans - - + uint256 public totalCollateralTokensEscrowedForLoans; // we use this in conjunction with totalPrincipalTokensLended for a psuedo TWAP price oracle of C tokens, used for exiting . Nice bc it is averaged over all of our relevant loans, not the current price. + uint256 public totalInterestCollected; uint16 public liquidityThresholdPercent; //5000 is 50 pct // enforce max of 10000 - uint16 public collateralRatio; //the overcollateralization ratio, typically 80 pct + uint16 public loanToValuePercent; //the overcollateralization ratio, typically 80 pct uint32 public twapInterval; uint32 public maxLoanDuration; @@ -107,7 +175,7 @@ contract LenderCommitmentGroup_Smart is uint16 public interestRateUpperBound; - //mapping(address => uint256) public principalTokensCommittedByLender; + mapping(address => uint256) public principalTokensCommittedByLender; mapping(uint256 => bool) public activeBids; //this excludes interest @@ -139,6 +207,63 @@ contract LenderCommitmentGroup_Smart is _; } + + + event PoolInitialized( + address indexed principalTokenAddress, + address indexed collateralTokenAddress, + uint256 marketId, + uint32 maxLoanDuration, + uint16 interestRateLowerBound, + uint16 interestRateUpperBound, + uint16 liquidityThresholdPercent, + uint16 loanToValuePercent, + uint24 uniswapPoolFee, + uint32 twapInterval, + address poolSharesToken + ); + + event LenderAddedPrincipal( + address indexed lender, + uint256 amount, + uint256 sharesAmount, + address indexed sharesRecipient + ); + + event BorrowerAcceptedFunds( + address indexed borrower, + uint256 indexed bidId, + uint256 principalAmount, + uint256 collateralAmount, + uint32 loanDuration, + uint16 interestRate + ); + + event EarningsWithdrawn( + address indexed lender, + uint256 amountPoolSharesTokens, + uint256 principalTokensWithdrawn, + address indexed recipient + ); + + + event DefaultedLoanLiquidated( + uint256 indexed bidId, + address indexed liquidator, + uint256 amountDue, + int256 tokenAmountDifference + ); + + + event LoanRepaid( + uint256 indexed bidId, + address indexed repayer, + uint256 principalAmount, + uint256 interestAmount, + uint256 totalPrincipalRepaid, + uint256 totalInterestCollected + ); + /// @custom:oz-upgrades-unsafe-allow constructor constructor( address _tellerV2, @@ -163,7 +288,7 @@ contract LenderCommitmentGroup_Smart is uint16 _interestRateLowerBound, uint16 _interestRateUpperBound, uint16 _liquidityThresholdPercent, // When 100% , the entire pool can be drawn for lending. When 80%, only 80% of the pool can be drawn for lending. - uint16 _collateralRatio, //the required overcollateralization ratio. 10000 is 1:1 baseline , typically this is above 10000 + uint16 _loanToValuePercent, //the required overcollateralization ratio. 10000 is 1:1 baseline , typically this is above 10000 uint24 _uniswapPoolFee, uint32 _twapInterval ) external initializer returns (address poolSharesToken_) { @@ -187,7 +312,7 @@ contract LenderCommitmentGroup_Smart is //in order for this to succeed, first, that SmartCommitmentForwarder needs to be THE trusted forwarder for the market - + //approve this market as a forwarder ITellerV2Context(TELLER_V2).approveMarketForwarder( _marketId, SMART_COMMITMENT_FORWARDER @@ -199,17 +324,32 @@ contract LenderCommitmentGroup_Smart is - + require(interestRateUpperBound <= 10000, "invalid _interestRateUpperBound"); require(interestRateLowerBound <= interestRateUpperBound, "invalid _interestRateLowerBound"); require(_liquidityThresholdPercent <= 10000, "invalid _liquidityThresholdPercent"); liquidityThresholdPercent = _liquidityThresholdPercent; - collateralRatio = _collateralRatio; + loanToValuePercent = _loanToValuePercent; twapInterval = _twapInterval; poolSharesToken_ = _deployPoolSharesToken(); + + + emit PoolInitialized( + _principalTokenAddress, + _collateralTokenAddress, + _marketId, + _maxLoanDuration, + _interestRateLowerBound, + _interestRateUpperBound, + _liquidityThresholdPercent, + _loanToValuePercent, + _uniswapPoolFee, + _twapInterval, + poolSharesToken_ + ); } function _deployPoolSharesToken() @@ -217,61 +357,44 @@ contract LenderCommitmentGroup_Smart is onlyInitializing returns (address poolSharesToken_) { - + // uint256 principalTokenDecimals = principalToken.decimals(); + require( address(poolSharesToken) == address(0), "Pool shares already deployed" ); - - (string memory name, string memory symbol ) = _generateTokenNameAndSymbol( - address(principalToken), - address(collateralToken) - ); - poolSharesToken = new LenderCommitmentGroupShares( - name, - symbol, - 18 + "PoolShares", + "PSH", + 18 //may want this to equal the decimals of principal token !? ); return address(poolSharesToken); } - function _generateTokenNameAndSymbol(address principalToken, address collateralToken) - internal view - returns (string memory name, string memory symbol) { - // Read the symbol of the principal token - string memory principalSymbol = ERC20(principalToken).symbol(); - - // Read the symbol of the collateral token - string memory collateralSymbol = ERC20(collateralToken).symbol(); - - // Combine the symbols to create the name - name = string(abi.encodePacked("GroupShares-", principalSymbol, "-", collateralSymbol)); - - // Combine the symbols to create the symbol - symbol = string(abi.encodePacked("SHR-", principalSymbol, "-", collateralSymbol)); - } - /** * @notice This determines the number of shares you get for depositing principal tokens and the number of principal tokens you receive for burning shares * @return rate_ The current exchange rate, scaled by the EXCHANGE_RATE_FACTOR. */ function sharesExchangeRate() public view virtual returns (uint256 rate_) { - + /* + Should get slightly less shares than principal tokens put in !! diluted by ratio of pools actual equity + */ uint256 poolTotalEstimatedValue = getPoolTotalEstimatedValue(); + uint256 poolTotalEstimatedValuePlusInterest = poolTotalEstimatedValue + + totalInterestCollected; - if (poolSharesToken.totalSupply() == 0) { + if (poolTotalEstimatedValue == 0) { return EXCHANGE_RATE_EXPANSION_FACTOR; // 1 to 1 for first swap } rate_ = - (poolTotalEstimatedValue * + (poolTotalEstimatedValuePlusInterest * EXCHANGE_RATE_EXPANSION_FACTOR) / - poolSharesToken.totalSupply(); + poolTotalEstimatedValue; } function sharesExchangeRateInverse() @@ -286,14 +409,13 @@ contract LenderCommitmentGroup_Smart is } function getPoolTotalEstimatedValue() - public + internal view returns (uint256 poolTotalEstimatedValue_) { - - int256 poolTotalEstimatedValueSigned = int256(totalPrincipalTokensCommitted) - + int256(totalInterestCollected) + int256(tokenDifferenceFromLiquidations) - - int256(totalPrincipalTokensWithdrawn); + int256 poolTotalEstimatedValueSigned = int256( + totalPrincipalTokensCommitted + ) + tokenDifferenceFromLiquidations; //if the poolTotalEstimatedValue_ is less than 0, we treat it as 0. poolTotalEstimatedValue_ = poolTotalEstimatedValueSigned > int256(0) @@ -309,16 +431,25 @@ contract LenderCommitmentGroup_Smart is address _sharesRecipient ) external returns (uint256 sharesAmount_) { //transfers the primary principal token from msg.sender into this contract escrow - + //gives principalToken.transferFrom(msg.sender, address(this), _amount); sharesAmount_ = _valueOfUnderlying(_amount, sharesExchangeRate()); totalPrincipalTokensCommitted += _amount; - //principalTokensCommittedByLender[msg.sender] += _amount; + principalTokensCommittedByLender[msg.sender] += _amount; //mint shares equal to _amount and give them to the shares recipient !!! poolSharesToken.mint(_sharesRecipient, sharesAmount_); + + emit LenderAddedPrincipal( + + msg.sender, + _amount, + sharesAmount_, + _sharesRecipient + + ); } function _valueOfUnderlying(uint256 amount, uint256 rate) @@ -339,11 +470,11 @@ contract LenderCommitmentGroup_Smart is uint256 _principalAmount, uint256 _collateralAmount, address _collateralTokenAddress, - uint256 _collateralTokenId, + uint256 _collateralTokenId, //not used uint32 _loanDuration, uint16 _interestRate ) external onlySmartCommitmentForwarder whenNotPaused { - + //consider putting these into less readonly fn calls require( _collateralTokenAddress == address(collateralToken), "Mismatching collateral token" @@ -357,7 +488,8 @@ contract LenderCommitmentGroup_Smart is getPrincipalAmountAvailableToBorrow() >= _principalAmount, "Invalid loan max principal" ); - + + require(isAllowedToBorrow(_borrower), "unauthorized borrow"); //this is expanded by 10**18 uint256 requiredCollateral = getCollateralRequiredForPrincipalAmount( @@ -369,7 +501,8 @@ contract LenderCommitmentGroup_Smart is requiredCollateral, "Insufficient Borrower Collateral" ); - + + //consider changing how this works principalToken.approve(address(TELLER_V2), _principalAmount); //do not have to spoof/forward as this contract is the lender ! @@ -378,7 +511,19 @@ contract LenderCommitmentGroup_Smart is totalPrincipalTokensLended += _principalAmount; activeBids[_bidId] = true; //bool for now + + //emit event + //we know the principal token and collateral token addresses since they are hard set in this contract + + emit BorrowerAcceptedFunds( + _borrower, + _bidId, + _principalAmount, + _collateralAmount, + _loanDuration, + _interestRate + ); } function _acceptBidWithRepaymentListener(uint256 _bidId) internal { @@ -399,7 +544,7 @@ contract LenderCommitmentGroup_Smart is ) external returns (uint256) { - + //this reduces total supply poolSharesToken.burn(msg.sender, _amountPoolSharesTokens); uint256 principalTokenValueToWithdraw = _valueOfUnderlying( @@ -407,10 +552,17 @@ contract LenderCommitmentGroup_Smart is sharesExchangeRateInverse() ); - totalPrincipalTokensWithdrawn += principalTokenValueToWithdraw; + totalPrincipalTokensCommitted -= principalTokenValueToWithdraw; principalToken.transfer(_recipient, principalTokenValueToWithdraw); + emit EarningsWithdrawn( + msg.sender, + _amountPoolSharesTokens, + principalTokenValueToWithdraw, + _recipient + ); + return principalTokenValueToWithdraw; } @@ -438,6 +590,9 @@ contract LenderCommitmentGroup_Smart is "Insufficient tokenAmountDifference" ); + + + if (_tokenAmountDifference > 0) { //this is used when the collateral value is higher than the principal (rare) //the loan will be completely made whole and our contract gets extra funds too @@ -453,7 +608,8 @@ contract LenderCommitmentGroup_Smart is totalPrincipalTokensRepaid += amountDue; } else { - + //the loan will be not be made whole and will be short + uint256 tokensToGiveToSender = abs(_tokenAmountDifference); IERC20(principalToken).transferFrom( @@ -464,11 +620,19 @@ contract LenderCommitmentGroup_Smart is tokenDifferenceFromLiquidations -= int256(tokensToGiveToSender); - totalPrincipalTokensRepaid += amountDue; + totalPrincipalTokensRepaid += (amountDue - tokensToGiveToSender); } + //this will give collateral to the caller ITellerV2(TELLER_V2).lenderCloseLoanWithRecipient(_bidId, msg.sender); + + emit DefaultedLoanLiquidated( + _bidId, + msg.sender, + amountDue, + _tokenAmountDifference + ); } function getAmountOwedForBid(uint256 _bidId, bool _includeInterest) @@ -488,7 +652,8 @@ contract LenderCommitmentGroup_Smart is /* This function will calculate the incentive amount (using a uniswap bonus plus a timer) of principal tokens that will be given to incentivize liquidating a loan - + + Starts at 5000 and ticks down to -5000 */ function getMinimumAmountDifferenceToCloseDefaultedLoan( uint256 _amountOwed, @@ -505,8 +670,9 @@ contract LenderCommitmentGroup_Smart is uint256 secondsSinceDefaulted = block.timestamp - _loanDefaultedTimestamp; - - int256 incentiveMultiplier = int256(86400) - + + //make this 10000 be a param in the constructor + int256 incentiveMultiplier = int256(10000) - int256(secondsSinceDefaulted); if (incentiveMultiplier < -10000) { @@ -521,7 +687,8 @@ contract LenderCommitmentGroup_Smart is function abs(int x) private pure returns (uint) { return x >= 0 ? uint(x) : uint(-x); } - + + //this is expanded by 1e18 function getCollateralRequiredForPrincipalAmount(uint256 _principalAmount) public view @@ -532,10 +699,10 @@ contract LenderCommitmentGroup_Smart is ); //this is an amount of collateral - return baseAmount.percent(collateralRatio); + return baseAmount.percent(loanToValuePercent); } - //this result is expanded by UNISWAP_EXPANSION_FACTOR + //remember that this result is expanded by UNISWAP_EXPANSION_FACTOR function _getUniswapV3TokenPairPrice(uint32 _twapInterval) internal view @@ -549,13 +716,14 @@ contract LenderCommitmentGroup_Smart is return _getPriceFromSqrtX96(sqrtPriceX96); } - //this result is expanded by UNISWAP_EXPANSION_FACTOR + //remember that this result is expanded by UNISWAP_EXPANSION_FACTOR function _getPriceFromSqrtX96(uint160 _sqrtPriceX96) internal pure returns (uint256 price_) { - + // uint160 sqrtPrice = _sqrtPriceX96 ; + uint256 priceX96 = (uint256(_sqrtPriceX96) * uint256(_sqrtPriceX96)) / (2**96); @@ -706,9 +874,31 @@ contract LenderCommitmentGroup_Smart is //can use principal amt to increment amt paid back!! nice for math . totalPrincipalTokensRepaid += principalAmount; totalInterestCollected += interestAmount; + + + emit LoanRepaid( + _bidId, + repayer, + principalAmount, + interestAmount, + totalPrincipalTokensRepaid, + totalInterestCollected + ); + } + + function getAverageWeightedPriceForCollateralTokensPerPrincipalTokens() + public + view + returns (uint256) + { + if (totalPrincipalTokensLended <= 0) { + return 0; + } + + return + totalCollateralTokensEscrowedForLoans / totalPrincipalTokensLended; } - function getTotalPrincipalTokensOutstandingInActiveLoans() public view @@ -756,13 +946,11 @@ contract LenderCommitmentGroup_Smart is //this is always between 0 and 10000 function getPoolUtilizationRatio() public view returns (uint16) { - if (getPoolTotalEstimatedValue() == 0) { + if (totalPrincipalTokensCommitted == 0) { return 0; } - return uint16( Math.min( - getTotalPrincipalTokensOutstandingInActiveLoans() * 10000 / - getPoolTotalEstimatedValue() , 10000 )); + return uint16( Math.min( (totalPrincipalTokensLended - totalPrincipalTokensRepaid) * 10000 / totalPrincipalTokensCommitted , 10000 )); } @@ -774,17 +962,20 @@ contract LenderCommitmentGroup_Smart is return address(principalToken); } - + function isAllowedToBorrow(address borrower) public view returns (bool) { + return true; + } function getPrincipalAmountAvailableToBorrow() public view returns (uint256) - { + { + int256 amountAvailable = int256(totalPrincipalTokensCommitted) - + int256(getTotalPrincipalTokensOutstandingInActiveLoans()) + + tokenDifferenceFromLiquidations; - return ( uint256( getPoolTotalEstimatedValue() )).percent(liquidityThresholdPercent) - - getTotalPrincipalTokensOutstandingInActiveLoans() ; - + return uint256(amountAvailable).percent(liquidityThresholdPercent); } /** From d5273e0de260d0cc5c204f6af9bc3d913ab44d71 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Mon, 29 Apr 2024 12:22:51 -0400 Subject: [PATCH 03/53] event emits --- .../LenderCommitmentGroup_Smart.sol | 305 +++++++----------- 1 file changed, 116 insertions(+), 189 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index b3e2f406..d0028e6f 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -41,86 +41,19 @@ import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; /* - - - -////---- - - -1. Use 50% forced max utilization ratio as initial game theory - have a global utilization limit and a user-signalled utilization limit (based on shares signalling) - -2. When pool shares are burned, give the lender : [ their pct shares * ( currentPrincipalTokens in contract, totalCollateralShares, totalInterestCollected) ] and later, they can burn the collateral shares for any collateral tokens that are in the contract. -3. use noahs TToken contract as reference for ratios -> amt of tokens to get when committing -4. Need price oracle bc we dont want to use maxPrincipalPerCollateral ratio as a static ideally -5. have an LTV ratio - -Every time a lender deposits tokens, we can mint an equal amt of RepresentationToken - - -// -- LIMITATIONS -1. neither the principal nor collateral token shall not have more than 18 decimals due to the way expansion is configured - - -// -- EXITING - -When exiting, a lender is burning X shares - - - We calculate the total equity value (Z) of the pool multiplies by their pct of shares (S%) (naive is just total committed princ tokens and interest , could maybe do better ) - - We are going to give the lender (Z * S%) value. The way we are going to give it to them is in a split of principal (P) and collateral tokens (C) which are in the pool right now. Similar to exiting a uni pool . C tokens will only be in the pool if bad defaults happened. - - NOTE: We will know the price of C in terms of P due to the ratio of total P used for loans and total C used for loans - - NOTE: if there are not enough P and C tokens in the pool to give the lender to equal a value of (Z * S%) then we revert . - -// --------- - - -// ISSUES - -1. for 'overall pool value' calculations (for shares math) an active loans value should be treated as "principal+interest" - aka the amount that will be paid to the pool optimistically. DONE -2. Redemption with ' the split' of principal and collateral is not ideal . What would be more ideal is a "conversion auction' or a 'swap auction'. - In this paradigm, any party can offer to give X principal tokens for the Y collateral tokens that are in the pool. the auction lasts (1 hour?) and this way it is always only principal tha is being withdrawn - far less risk of MEV attacker taking more C -- DONE -3. it is annoying that a bad default can cause a pool to have to totally exit and close ..this is a minor issue. maybe some form of Insurance can help resurrect a pool in this case, mayeb anyone can restore the health of the pool w a fn call. - a. fix this by changing the shares logic so you do get more shares in this event (i dont think its possible) - b. have a function that lets anyone donate principal tokens to make the pool whole again . (refill underwater pools w insurance fund??) - c. lets pools expire and get unwound and withdrawn completely , make a new pool - -4. build a function to do lender close loan - - - -TODO: -A. Make a mental map of these subsystems, attack vectors, mitigaions - -B. - - -// ----- - - - -// TODO - - - 2. consider adding PATHS to this for the oracle.. so the pair can be USDC to PNDC but use weth as intermediate - 4. tests - -// ---- + Each LenderCommitmentGroup SmartContract acts as its own Loan Commitment (for the SmartCommitmentForwarder) and acts as its own Lender in the Teller Protocol. + Lender Users can deposit principal tokens in this contract and this will give them Share Tokens (LP tokens) representing their ownership in the liquidity pool of this contract. - + Borrower Users can borrow principal token funds from this contract (via the SCF contract) by providing collateral tokens in the proper amount as specified by the rules of this smart contract. + These collateral tokens are then owned by this smart contract and are returned to the borrower via the Teller Protocol rules to the borrower if and only if the borrower repays principal and interest of the loan they took. -If a lender puts up 50,000 originally, im able to withdraw all my deposits. Everyone else is in the hole until a borrower repays a loan -If there isnt enough liquidity, you just cannot burn those shares. + If the borrower defaults on a loan, for 24 hours a liquidation auction is automatically conducted by this smart contract in order to incentivize a liquidator to take the collateral tokens in exchange for principal tokens. - -Consider implementing eip-4626 - */ @@ -139,16 +72,14 @@ contract LenderCommitmentGroup_Smart is uint256 public immutable UNISWAP_EXPANSION_FACTOR = 2**96; - uint256 public immutable EXCHANGE_RATE_EXPANSION_FACTOR = 1e36; //consider making this dynamic + uint256 public immutable EXCHANGE_RATE_EXPANSION_FACTOR = 1e36; /// @custom:oz-upgrades-unsafe-allow state-variable-immutable address public immutable TELLER_V2; address public immutable SMART_COMMITMENT_FORWARDER; address public immutable UNISWAP_V3_FACTORY; address public UNISWAP_V3_POOL; - - // bool private _initialized; - + LenderCommitmentGroupShares public poolSharesToken; IERC20 public principalToken; @@ -156,18 +87,19 @@ contract LenderCommitmentGroup_Smart is uint256 marketId; - //this should be the net total principal tokens , lenderAdded - lenderWithdrawn - uint256 public totalPrincipalTokensCommitted; + + uint256 public totalPrincipalTokensCommitted; + uint256 public totalPrincipalTokensWithdrawn; uint256 public totalPrincipalTokensLended; uint256 public totalPrincipalTokensRepaid; //subtract this and the above to find total principal tokens outstanding for loans - uint256 public totalCollateralTokensEscrowedForLoans; // we use this in conjunction with totalPrincipalTokensLended for a psuedo TWAP price oracle of C tokens, used for exiting . Nice bc it is averaged over all of our relevant loans, not the current price. - + + uint256 public totalInterestCollected; uint16 public liquidityThresholdPercent; //5000 is 50 pct // enforce max of 10000 - uint16 public loanToValuePercent; //the overcollateralization ratio, typically 80 pct + uint16 public collateralRatio; //the overcollateralization ratio, typically 80 pct uint32 public twapInterval; uint32 public maxLoanDuration; @@ -175,7 +107,7 @@ contract LenderCommitmentGroup_Smart is uint16 public interestRateUpperBound; - mapping(address => uint256) public principalTokensCommittedByLender; + //mapping(address => uint256) public principalTokensCommittedByLender; mapping(uint256 => bool) public activeBids; //this excludes interest @@ -185,31 +117,7 @@ contract LenderCommitmentGroup_Smart is - modifier onlySmartCommitmentForwarder() { - require( - msg.sender == address(SMART_COMMITMENT_FORWARDER), - "Can only be called by Smart Commitment Forwarder" - ); - _; - } - - modifier onlyTellerV2() { - require( - msg.sender == address(TELLER_V2), - "Can only be called by TellerV2" - ); - _; - } - - modifier bidIsActiveForGroup(uint256 _bidId) { - require(activeBids[_bidId] == true, "Bid is not active for group"); - - _; - } - - - - event PoolInitialized( + event PoolInitialized( address indexed principalTokenAddress, address indexed collateralTokenAddress, uint256 marketId, @@ -264,6 +172,29 @@ contract LenderCommitmentGroup_Smart is uint256 totalInterestCollected ); + + modifier onlySmartCommitmentForwarder() { + require( + msg.sender == address(SMART_COMMITMENT_FORWARDER), + "Can only be called by Smart Commitment Forwarder" + ); + _; + } + + modifier onlyTellerV2() { + require( + msg.sender == address(TELLER_V2), + "Can only be called by TellerV2" + ); + _; + } + + modifier bidIsActiveForGroup(uint256 _bidId) { + require(activeBids[_bidId] == true, "Bid is not active for group"); + + _; + } + /// @custom:oz-upgrades-unsafe-allow constructor constructor( address _tellerV2, @@ -288,7 +219,7 @@ contract LenderCommitmentGroup_Smart is uint16 _interestRateLowerBound, uint16 _interestRateUpperBound, uint16 _liquidityThresholdPercent, // When 100% , the entire pool can be drawn for lending. When 80%, only 80% of the pool can be drawn for lending. - uint16 _loanToValuePercent, //the required overcollateralization ratio. 10000 is 1:1 baseline , typically this is above 10000 + uint16 _collateralRatio, //the required overcollateralization ratio. 10000 is 1:1 baseline , typically this is above 10000 uint24 _uniswapPoolFee, uint32 _twapInterval ) external initializer returns (address poolSharesToken_) { @@ -312,7 +243,7 @@ contract LenderCommitmentGroup_Smart is //in order for this to succeed, first, that SmartCommitmentForwarder needs to be THE trusted forwarder for the market - //approve this market as a forwarder + ITellerV2Context(TELLER_V2).approveMarketForwarder( _marketId, SMART_COMMITMENT_FORWARDER @@ -324,13 +255,13 @@ contract LenderCommitmentGroup_Smart is - require(interestRateUpperBound <= 10000, "invalid _interestRateUpperBound"); + require(interestRateLowerBound <= interestRateUpperBound, "invalid _interestRateLowerBound"); require(_liquidityThresholdPercent <= 10000, "invalid _liquidityThresholdPercent"); liquidityThresholdPercent = _liquidityThresholdPercent; - loanToValuePercent = _loanToValuePercent; + collateralRatio = _collateralRatio; twapInterval = _twapInterval; @@ -345,7 +276,7 @@ contract LenderCommitmentGroup_Smart is _interestRateLowerBound, _interestRateUpperBound, _liquidityThresholdPercent, - _loanToValuePercent, + _collateralRatio, _uniswapPoolFee, _twapInterval, poolSharesToken_ @@ -357,44 +288,61 @@ contract LenderCommitmentGroup_Smart is onlyInitializing returns (address poolSharesToken_) { - // uint256 principalTokenDecimals = principalToken.decimals(); - + require( address(poolSharesToken) == address(0), "Pool shares already deployed" ); + + (string memory name, string memory symbol ) = _generateTokenNameAndSymbol( + address(principalToken), + address(collateralToken) + ); + poolSharesToken = new LenderCommitmentGroupShares( - "PoolShares", - "PSH", - 18 //may want this to equal the decimals of principal token !? + name, + symbol, + 18 ); return address(poolSharesToken); } + function _generateTokenNameAndSymbol(address principalToken, address collateralToken) + internal view + returns (string memory name, string memory symbol) { + // Read the symbol of the principal token + string memory principalSymbol = ERC20(principalToken).symbol(); + + // Read the symbol of the collateral token + string memory collateralSymbol = ERC20(collateralToken).symbol(); + + // Combine the symbols to create the name + name = string(abi.encodePacked("GroupShares-", principalSymbol, "-", collateralSymbol)); + + // Combine the symbols to create the symbol + symbol = string(abi.encodePacked("SHR-", principalSymbol, "-", collateralSymbol)); + } + /** * @notice This determines the number of shares you get for depositing principal tokens and the number of principal tokens you receive for burning shares * @return rate_ The current exchange rate, scaled by the EXCHANGE_RATE_FACTOR. */ function sharesExchangeRate() public view virtual returns (uint256 rate_) { - /* - Should get slightly less shares than principal tokens put in !! diluted by ratio of pools actual equity - */ + uint256 poolTotalEstimatedValue = getPoolTotalEstimatedValue(); - uint256 poolTotalEstimatedValuePlusInterest = poolTotalEstimatedValue + - totalInterestCollected; - if (poolTotalEstimatedValue == 0) { + if (poolSharesToken.totalSupply() == 0) { return EXCHANGE_RATE_EXPANSION_FACTOR; // 1 to 1 for first swap } rate_ = - (poolTotalEstimatedValuePlusInterest * + (poolTotalEstimatedValue * EXCHANGE_RATE_EXPANSION_FACTOR) / - poolTotalEstimatedValue; + poolSharesToken.totalSupply(); } function sharesExchangeRateInverse() @@ -409,13 +357,14 @@ contract LenderCommitmentGroup_Smart is } function getPoolTotalEstimatedValue() - internal + public view returns (uint256 poolTotalEstimatedValue_) { - int256 poolTotalEstimatedValueSigned = int256( - totalPrincipalTokensCommitted - ) + tokenDifferenceFromLiquidations; + + int256 poolTotalEstimatedValueSigned = int256(totalPrincipalTokensCommitted) + + int256(totalInterestCollected) + int256(tokenDifferenceFromLiquidations) + - int256(totalPrincipalTokensWithdrawn); //if the poolTotalEstimatedValue_ is less than 0, we treat it as 0. poolTotalEstimatedValue_ = poolTotalEstimatedValueSigned > int256(0) @@ -431,13 +380,13 @@ contract LenderCommitmentGroup_Smart is address _sharesRecipient ) external returns (uint256 sharesAmount_) { //transfers the primary principal token from msg.sender into this contract escrow - //gives + principalToken.transferFrom(msg.sender, address(this), _amount); sharesAmount_ = _valueOfUnderlying(_amount, sharesExchangeRate()); totalPrincipalTokensCommitted += _amount; - principalTokensCommittedByLender[msg.sender] += _amount; + //principalTokensCommittedByLender[msg.sender] += _amount; //mint shares equal to _amount and give them to the shares recipient !!! poolSharesToken.mint(_sharesRecipient, sharesAmount_); @@ -470,11 +419,11 @@ contract LenderCommitmentGroup_Smart is uint256 _principalAmount, uint256 _collateralAmount, address _collateralTokenAddress, - uint256 _collateralTokenId, //not used + uint256 _collateralTokenId, uint32 _loanDuration, uint16 _interestRate ) external onlySmartCommitmentForwarder whenNotPaused { - //consider putting these into less readonly fn calls + require( _collateralTokenAddress == address(collateralToken), "Mismatching collateral token" @@ -488,8 +437,7 @@ contract LenderCommitmentGroup_Smart is getPrincipalAmountAvailableToBorrow() >= _principalAmount, "Invalid loan max principal" ); - - require(isAllowedToBorrow(_borrower), "unauthorized borrow"); + //this is expanded by 10**18 uint256 requiredCollateral = getCollateralRequiredForPrincipalAmount( @@ -501,8 +449,7 @@ contract LenderCommitmentGroup_Smart is requiredCollateral, "Insufficient Borrower Collateral" ); - - //consider changing how this works + principalToken.approve(address(TELLER_V2), _principalAmount); //do not have to spoof/forward as this contract is the lender ! @@ -511,11 +458,8 @@ contract LenderCommitmentGroup_Smart is totalPrincipalTokensLended += _principalAmount; activeBids[_bidId] = true; //bool for now - - - //emit event - //we know the principal token and collateral token addresses since they are hard set in this contract - + + emit BorrowerAcceptedFunds( _borrower, _bidId, @@ -533,6 +477,8 @@ contract LenderCommitmentGroup_Smart is _bidId, address(this) ); + + } /* @@ -544,7 +490,7 @@ contract LenderCommitmentGroup_Smart is ) external returns (uint256) { - //this reduces total supply + poolSharesToken.burn(msg.sender, _amountPoolSharesTokens); uint256 principalTokenValueToWithdraw = _valueOfUnderlying( @@ -552,10 +498,11 @@ contract LenderCommitmentGroup_Smart is sharesExchangeRateInverse() ); - totalPrincipalTokensCommitted -= principalTokenValueToWithdraw; + totalPrincipalTokensWithdrawn += principalTokenValueToWithdraw; principalToken.transfer(_recipient, principalTokenValueToWithdraw); + emit EarningsWithdrawn( msg.sender, _amountPoolSharesTokens, @@ -590,9 +537,6 @@ contract LenderCommitmentGroup_Smart is "Insufficient tokenAmountDifference" ); - - - if (_tokenAmountDifference > 0) { //this is used when the collateral value is higher than the principal (rare) //the loan will be completely made whole and our contract gets extra funds too @@ -608,8 +552,7 @@ contract LenderCommitmentGroup_Smart is totalPrincipalTokensRepaid += amountDue; } else { - //the loan will be not be made whole and will be short - + uint256 tokensToGiveToSender = abs(_tokenAmountDifference); IERC20(principalToken).transferFrom( @@ -620,13 +563,13 @@ contract LenderCommitmentGroup_Smart is tokenDifferenceFromLiquidations -= int256(tokensToGiveToSender); - totalPrincipalTokensRepaid += (amountDue - tokensToGiveToSender); + totalPrincipalTokensRepaid += amountDue; } - //this will give collateral to the caller ITellerV2(TELLER_V2).lenderCloseLoanWithRecipient(_bidId, msg.sender); - + + emit DefaultedLoanLiquidated( _bidId, msg.sender, @@ -647,13 +590,14 @@ contract LenderCommitmentGroup_Smart is amountOwed_ = _includeInterest ? amountOwedPayment.principal + amountOwedPayment.interest : amountOwedPayment.principal; + + } /* This function will calculate the incentive amount (using a uniswap bonus plus a timer) of principal tokens that will be given to incentivize liquidating a loan - - Starts at 5000 and ticks down to -5000 + */ function getMinimumAmountDifferenceToCloseDefaultedLoan( uint256 _amountOwed, @@ -670,9 +614,8 @@ contract LenderCommitmentGroup_Smart is uint256 secondsSinceDefaulted = block.timestamp - _loanDefaultedTimestamp; - - //make this 10000 be a param in the constructor - int256 incentiveMultiplier = int256(10000) - + + int256 incentiveMultiplier = int256(86400) - int256(secondsSinceDefaulted); if (incentiveMultiplier < -10000) { @@ -687,8 +630,7 @@ contract LenderCommitmentGroup_Smart is function abs(int x) private pure returns (uint) { return x >= 0 ? uint(x) : uint(-x); } - - //this is expanded by 1e18 + function getCollateralRequiredForPrincipalAmount(uint256 _principalAmount) public view @@ -699,10 +641,10 @@ contract LenderCommitmentGroup_Smart is ); //this is an amount of collateral - return baseAmount.percent(loanToValuePercent); + return baseAmount.percent(collateralRatio); } - //remember that this result is expanded by UNISWAP_EXPANSION_FACTOR + //this result is expanded by UNISWAP_EXPANSION_FACTOR function _getUniswapV3TokenPairPrice(uint32 _twapInterval) internal view @@ -716,14 +658,13 @@ contract LenderCommitmentGroup_Smart is return _getPriceFromSqrtX96(sqrtPriceX96); } - //remember that this result is expanded by UNISWAP_EXPANSION_FACTOR + //this result is expanded by UNISWAP_EXPANSION_FACTOR function _getPriceFromSqrtX96(uint160 _sqrtPriceX96) internal pure returns (uint256 price_) { - // uint160 sqrtPrice = _sqrtPriceX96 ; - + uint256 priceX96 = (uint256(_sqrtPriceX96) * uint256(_sqrtPriceX96)) / (2**96); @@ -875,8 +816,7 @@ contract LenderCommitmentGroup_Smart is totalPrincipalTokensRepaid += principalAmount; totalInterestCollected += interestAmount; - - emit LoanRepaid( + emit LoanRepaid( _bidId, repayer, principalAmount, @@ -886,19 +826,7 @@ contract LenderCommitmentGroup_Smart is ); } - function getAverageWeightedPriceForCollateralTokensPerPrincipalTokens() - public - view - returns (uint256) - { - if (totalPrincipalTokensLended <= 0) { - return 0; - } - - return - totalCollateralTokensEscrowedForLoans / totalPrincipalTokensLended; - } - + function getTotalPrincipalTokensOutstandingInActiveLoans() public view @@ -946,11 +874,13 @@ contract LenderCommitmentGroup_Smart is //this is always between 0 and 10000 function getPoolUtilizationRatio() public view returns (uint16) { - if (totalPrincipalTokensCommitted == 0) { + if (getPoolTotalEstimatedValue() == 0) { return 0; } - return uint16( Math.min( (totalPrincipalTokensLended - totalPrincipalTokensRepaid) * 10000 / totalPrincipalTokensCommitted , 10000 )); + return uint16( Math.min( + getTotalPrincipalTokensOutstandingInActiveLoans() * 10000 / + getPoolTotalEstimatedValue() , 10000 )); } @@ -962,20 +892,17 @@ contract LenderCommitmentGroup_Smart is return address(principalToken); } - function isAllowedToBorrow(address borrower) public view returns (bool) { - return true; - } + function getPrincipalAmountAvailableToBorrow() public view returns (uint256) - { - int256 amountAvailable = int256(totalPrincipalTokensCommitted) - - int256(getTotalPrincipalTokensOutstandingInActiveLoans()) + - tokenDifferenceFromLiquidations; + { - return uint256(amountAvailable).percent(liquidityThresholdPercent); + return ( uint256( getPoolTotalEstimatedValue() )).percent(liquidityThresholdPercent) - + getTotalPrincipalTokensOutstandingInActiveLoans() ; + } /** From 08bbbffd4513b35fa6b3756624ed732c956c0417 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Mon, 6 May 2024 11:00:31 -0400 Subject: [PATCH 04/53] add fix --- packages/contracts/contracts/TellerV2.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/contracts/TellerV2.sol b/packages/contracts/contracts/TellerV2.sol index 2e452d3d..44dc1568 100644 --- a/packages/contracts/contracts/TellerV2.sol +++ b/packages/contracts/contracts/TellerV2.sol @@ -584,7 +584,7 @@ contract TellerV2 is Bid storage bid = bids[_bidId]; address sender = _msgSenderForMarket(bid.marketplaceId); - require(sender == bid.lender, "only lender can claim NFT"); + require(sender == getLoanLender(_bidId), "only lender can claim NFT"); // set lender address to the lender manager so we know to check the owner of the NFT for the true lender bid.lender = address(USING_LENDER_MANAGER); From 16cf4b0fe9818617ef48b6edbc090a5741f8b522 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Mon, 6 May 2024 11:07:01 -0400 Subject: [PATCH 05/53] fix burnshares fn --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..9a0e80b2 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -398,15 +398,15 @@ contract LenderCommitmentGroup_Smart is address _recipient ) external returns (uint256) { - - - poolSharesToken.burn(msg.sender, _amountPoolSharesTokens); + uint256 principalTokenValueToWithdraw = _valueOfUnderlying( _amountPoolSharesTokens, sharesExchangeRateInverse() ); + poolSharesToken.burn(msg.sender, _amountPoolSharesTokens); + totalPrincipalTokensWithdrawn += principalTokenValueToWithdraw; principalToken.transfer(_recipient, principalTokenValueToWithdraw); From 586bfd76fca6e9ac5e353ae0c86f95b026656650 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Mon, 6 May 2024 11:20:54 -0400 Subject: [PATCH 06/53] add gas requirement --- packages/contracts/contracts/TellerV2.sol | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/contracts/contracts/TellerV2.sol b/packages/contracts/contracts/TellerV2.sol index 2e452d3d..a580e770 100644 --- a/packages/contracts/contracts/TellerV2.sol +++ b/packages/contracts/contracts/TellerV2.sol @@ -949,9 +949,10 @@ contract TellerV2 is address loanRepaymentListener = repaymentListenerForBid[_bidId]; if (loanRepaymentListener != address(0)) { + require(gasleft() >= 40000, "Insufficient gas"); //fixes the 63/64 remaining issue try ILoanRepaymentListener(loanRepaymentListener).repayLoanCallback{ - gas: 80000 + gas: 40000 }( //limit gas costs to prevent lender griefing repayments _bidId, _msgSenderForMarket(bid.marketplaceId), From edf4ba95ae00de9eb4b4f99d7f9a66ac2b169185 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Mon, 6 May 2024 12:02:05 -0400 Subject: [PATCH 07/53] add ownable init --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..21805fa4 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -167,9 +167,8 @@ contract LenderCommitmentGroup_Smart is uint24 _uniswapPoolFee, uint32 _twapInterval ) external initializer returns (address poolSharesToken_) { - // require(!_initialized,"already initialized"); - // _initialized = true; - + + __Ownable_init(); __Pausable_init(); principalToken = IERC20(_principalTokenAddress); From c200666f09369df7255dd54e7a1e1ed85a18c323 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Mon, 6 May 2024 12:05:33 -0400 Subject: [PATCH 08/53] use muldiv --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..d416842a 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -269,9 +269,9 @@ contract LenderCommitmentGroup_Smart is } rate_ = - (poolTotalEstimatedValue * - EXCHANGE_RATE_EXPANSION_FACTOR) / - poolSharesToken.totalSupply(); + MathUpgradeable.mulDiv(poolTotalEstimatedValue , + EXCHANGE_RATE_EXPANSION_FACTOR , + poolSharesToken.totalSupply() ); } function sharesExchangeRateInverse() @@ -330,7 +330,7 @@ contract LenderCommitmentGroup_Smart is return 0; } - value_ = (amount * EXCHANGE_RATE_EXPANSION_FACTOR) / rate; + value_ = MathUpgradeable.mulDiv(amount , EXCHANGE_RATE_EXPANSION_FACTOR , rate ) ; } function acceptFundsForAcceptBid( From 8a40b95c7fdf8a05b20ee71abbdf70c0a8128a3c Mon Sep 17 00:00:00 2001 From: Admazzola Date: Mon, 6 May 2024 14:20:22 -0400 Subject: [PATCH 09/53] fix --- .../LenderCommitmentForwarder_G2.sol | 2 +- .../LenderCommitmentForwarder_Unit_Test.sol | 73 +++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/LenderCommitmentForwarder_G2.sol b/packages/contracts/contracts/LenderCommitmentForwarder/LenderCommitmentForwarder_G2.sol index 57bdfbc0..c500484d 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/LenderCommitmentForwarder_G2.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/LenderCommitmentForwarder_G2.sol @@ -477,7 +477,7 @@ contract LenderCommitmentForwarder_G2 is ); require( - commitmentPrincipalAccepted[bidId] <= commitment.maxPrincipal, + commitmentPrincipalAccepted[_commitmentId] <= commitment.maxPrincipal, "Invalid loan max principal" ); diff --git a/packages/contracts/tests/LenderCommitmentForwarder/LenderCommitmentForwarder_Unit_Test.sol b/packages/contracts/tests/LenderCommitmentForwarder/LenderCommitmentForwarder_Unit_Test.sol index 0baec2f0..a50b87ad 100644 --- a/packages/contracts/tests/LenderCommitmentForwarder/LenderCommitmentForwarder_Unit_Test.sol +++ b/packages/contracts/tests/LenderCommitmentForwarder/LenderCommitmentForwarder_Unit_Test.sol @@ -1355,6 +1355,79 @@ contract LenderCommitmentForwarder_Test is Testable { ///assertEq(uint16(cType), uint16(CollateralType.NONE), "unexpected collateral type"); } + + function test_acceptCommitment_logic_error_check() public { + ILenderCommitmentForwarder.Commitment + memory c = ILenderCommitmentForwarder.Commitment({ + maxPrincipal: maxPrincipal, + expiration: expiration, + maxDuration: maxDuration, + minInterestRate: minInterestRate, + collateralTokenAddress: address(collateralToken), + collateralTokenId: collateralTokenId, + maxPrincipalPerCollateralAmount: maxPrincipalPerCollateralAmount, + collateralTokenType: collateralTokenType, + lender: address(lender), + marketId: marketId, + principalTokenAddress: address(principalToken) + }); + + //uint256 commitmentId = 0; + + // lenderCommitmentForwarder.setCommitment(commitmentId, c); + uint256 commitmentId = lender._createCommitment(c, emptyArray); + + uint256 principalAmount = maxPrincipal; + uint256 collateralAmount = 1000; + uint16 interestRate = minInterestRate; + uint32 loanDuration = maxDuration; + + // vm.expectRevert("collateral token mismatch"); + lenderCommitmentForwarder.acceptCommitment( + commitmentId, + principalAmount, + collateralAmount, + collateralTokenId, + address(collateralToken), + interestRate, + loanDuration + ); + + assertEq( + lenderCommitmentForwarder.getCommitmentMaxPrincipal(commitmentId), + maxPrincipal, + "Max principal changed" + ); + + ILenderCommitmentForwarder.Commitment + memory c2 = ILenderCommitmentForwarder.Commitment({ + maxPrincipal: maxPrincipal - 100, + expiration: expiration, + maxDuration: maxDuration, + minInterestRate: minInterestRate, + collateralTokenAddress: address(collateralToken), + collateralTokenId: collateralTokenId, + maxPrincipalPerCollateralAmount: maxPrincipalPerCollateralAmount, + collateralTokenType: collateralTokenType, + lender: address(lender), + marketId: marketId, + principalTokenAddress: address(principalToken) + }); + + principalAmount = maxPrincipal - 100; + //commitmentId = 1 + commitmentId = lender._createCommitment(c2, emptyArray); + lenderCommitmentForwarder.acceptCommitment( + commitmentId, + principalAmount, + collateralAmount, + collateralTokenId, + address(collateralToken), + interestRate, + loanDuration + ); + } + /* Overrider methods for exercise */ From af2b60318dbe46675d9f10207139fae439b9adf1 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Mon, 6 May 2024 14:38:41 -0400 Subject: [PATCH 10/53] update collateral mgr --- .../contracts/contracts/CollateralManager.sol | 18 ++++++++++++++++++ packages/contracts/contracts/TellerV2.sol | 17 ++--------------- .../interfaces/ICollateralManager.sol | 11 +++++++++++ .../contracts/mock/CollateralManagerMock.sol | 3 +++ 4 files changed, 34 insertions(+), 15 deletions(-) diff --git a/packages/contracts/contracts/CollateralManager.sol b/packages/contracts/contracts/CollateralManager.sol index 67af5ff1..3e33ab76 100644 --- a/packages/contracts/contracts/CollateralManager.sol +++ b/packages/contracts/contracts/CollateralManager.sol @@ -282,6 +282,24 @@ contract CollateralManager is OwnableUpgradeable, ICollateralManager { } } + /** + * @notice Withdraws deposited collateral from the created escrow of a bid that has been CLOSED after being defaulted. + * @param _bidId The id of the bid to withdraw collateral for. + */ + function lenderClaimCollateralWithRecipient(uint256 _bidId, address _collateralRecipient) external onlyTellerV2 { + if (isBidCollateralBacked(_bidId)) { + BidState bidState = tellerV2.getBidState(_bidId); + + require( + bidState == BidState.CLOSED, + "Loan has not been liquidated" + ); + + _withdraw(_bidId, _collateralRecipient); + emit CollateralClaimed(_bidId); + } + } + /** * @notice Sends the deposited collateral to a liquidator of a bid. * @notice Can only be called by the protocol. diff --git a/packages/contracts/contracts/TellerV2.sol b/packages/contracts/contracts/TellerV2.sol index 2e452d3d..8ab76be1 100644 --- a/packages/contracts/contracts/TellerV2.sol +++ b/packages/contracts/contracts/TellerV2.sol @@ -754,21 +754,8 @@ contract TellerV2 is address sender = _msgSenderForMarket(bid.marketplaceId); require(sender == bid.lender, "only lender can close loan"); - /* - - - address collateralManagerForBid = address(_getCollateralManagerForBid(_bidId)); - - if( collateralManagerForBid == address(collateralManagerV2) ){ - ICollateralManagerV2(collateralManagerForBid).lenderClaimCollateral(_bidId,_collateralRecipient); - }else{ - require( _collateralRecipient == address(bid.lender)); - ICollateralManager(collateralManagerForBid).lenderClaimCollateral(_bidId ); - } - - */ - - collateralManager.lenderClaimCollateral(_bidId); + + collateralManager.lenderClaimCollateralWithRecipient(_bidId, _collateralRecipient); emit LoanClosed(_bidId); } diff --git a/packages/contracts/contracts/interfaces/ICollateralManager.sol b/packages/contracts/contracts/interfaces/ICollateralManager.sol index 7c57e41c..fa047fac 100644 --- a/packages/contracts/contracts/interfaces/ICollateralManager.sol +++ b/packages/contracts/contracts/interfaces/ICollateralManager.sol @@ -79,6 +79,17 @@ interface ICollateralManager { */ function lenderClaimCollateral(uint256 _bidId) external; + + + /** + * @notice Sends the deposited collateral to a lender of a bid. + * @notice Can only be called by the protocol. + * @param _bidId The id of the liquidated bid. + * @param _collateralRecipient the address that will receive the collateral + */ + function lenderClaimCollateralWithRecipient(uint256 _bidId, address _collateralRecipient) external; + + /** * @notice Sends the deposited collateral to a liquidator of a bid. * @notice Can only be called by the protocol. diff --git a/packages/contracts/contracts/mock/CollateralManagerMock.sol b/packages/contracts/contracts/mock/CollateralManagerMock.sol index e0c707bc..b02d6d8a 100644 --- a/packages/contracts/contracts/mock/CollateralManagerMock.sol +++ b/packages/contracts/contracts/mock/CollateralManagerMock.sol @@ -84,6 +84,9 @@ contract CollateralManagerMock is ICollateralManager { function lenderClaimCollateral(uint256 _bidId) external {} + function lenderClaimCollateralWithRecipient(uint256 _bidId, address _collateralRecipient) external {} + + /** * @notice Sends the deposited collateral to a liquidator of a bid. * @notice Can only be called by the protocol. From 3e0e864d9e4faaa2ec85d363553a5d69313fb5dd Mon Sep 17 00:00:00 2001 From: Admazzola Date: Mon, 6 May 2024 15:00:38 -0400 Subject: [PATCH 11/53] incorporate liquidity threshold in get pool util ratio --- .../LenderCommitmentGroup_Smart.sol | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..6fddb9c9 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -759,10 +759,15 @@ contract LenderCommitmentGroup_Smart is if (getPoolTotalEstimatedValue() == 0) { return 0; } + + + return uint16(Math.min( + + MathUpgradeable.mulDiv( getTotalPrincipalTokensOutstandingInActiveLoans() , 10000 , + (getPoolTotalEstimatedValue().percent(liquidityThresholdPercent) ) ) + + , 10000)); - return uint16( Math.min( - getTotalPrincipalTokensOutstandingInActiveLoans() * 10000 / - getPoolTotalEstimatedValue() , 10000 )); } From 84ce533fb9ffb56e302f9b70629e841437918e0a Mon Sep 17 00:00:00 2001 From: Admazzola Date: Mon, 6 May 2024 15:09:33 -0400 Subject: [PATCH 12/53] remove unnecessary expansion --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..aae220f3 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -358,14 +358,13 @@ contract LenderCommitmentGroup_Smart is "Invalid loan max principal" ); - - //this is expanded by 10**18 + uint256 requiredCollateral = getCollateralRequiredForPrincipalAmount( _principalAmount ); - require( - (_collateralAmount * STANDARD_EXPANSION_FACTOR) >= + require( + _collateralAmount >= requiredCollateral, "Insufficient Borrower Collateral" ); From c798d0390d99f5d359569d60410afe25edc4d211 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Mon, 6 May 2024 15:46:49 -0400 Subject: [PATCH 13/53] adding public escrwo withdraw --- .../LenderCommitmentGroup_Smart.sol | 15 +++++++++++++++ packages/contracts/contracts/TellerV2.sol | 4 ++++ .../contracts/interfaces/IEscrowVault.sol | 2 ++ .../contracts/contracts/interfaces/ITellerV2.sol | 3 +++ .../contracts/contracts/mock/TellerV2SolMock.sol | 5 +++++ 5 files changed, 29 insertions(+) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..e3342c5f 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -33,6 +33,7 @@ import { ILoanRepaymentListener } from "../../../interfaces/ILoanRepaymentListen import { ILoanRepaymentCallbacks } from "../../../interfaces/ILoanRepaymentCallbacks.sol"; +import { IEscrowVault } from "../../../interfaces/IEscrowVault.sol"; import { ILenderCommitmentGroup } from "../../../interfaces/ILenderCommitmentGroup.sol"; import { Payment } from "../../../TellerV2Storage.sol"; @@ -708,6 +709,20 @@ contract LenderCommitmentGroup_Smart is totalInterestCollected += interestAmount; } + + /* + If principaltokens get stuck in the escrow vault for any reason, anyone may + call this function to move them from that vault in to this contract + */ + function withdrawFromEscrowVault ( uint256 _amount ) public { + + + address _escrowVault = ITellerV2(TELLER_V2).getEscrowVault(); + + IEscrowVault(_escrowVault).withdraw(address(principalToken), _amount ); + + } + function getTotalPrincipalTokensOutstandingInActiveLoans() public diff --git a/packages/contracts/contracts/TellerV2.sol b/packages/contracts/contracts/TellerV2.sol index 2e452d3d..ae951339 100644 --- a/packages/contracts/contracts/TellerV2.sol +++ b/packages/contracts/contracts/TellerV2.sol @@ -1104,6 +1104,10 @@ contract TellerV2 is dueDate + defaultDuration + _additionalDelay; } + function getEscrowVault() external view returns(address){ + return address(escrowVault); + } + function getBidState(uint256 _bidId) external view diff --git a/packages/contracts/contracts/interfaces/IEscrowVault.sol b/packages/contracts/contracts/interfaces/IEscrowVault.sol index 499a5fa6..0d20e978 100644 --- a/packages/contracts/contracts/interfaces/IEscrowVault.sol +++ b/packages/contracts/contracts/interfaces/IEscrowVault.sol @@ -9,4 +9,6 @@ interface IEscrowVault { * @param amount The amount to increase the balance */ function deposit(address account, address token, uint256 amount) external; + + function withdraw(address token, uint256 amount) external ; } diff --git a/packages/contracts/contracts/interfaces/ITellerV2.sol b/packages/contracts/contracts/interfaces/ITellerV2.sol index 6cb923d2..9837d6da 100644 --- a/packages/contracts/contracts/interfaces/ITellerV2.sol +++ b/packages/contracts/contracts/interfaces/ITellerV2.sol @@ -168,4 +168,7 @@ interface ITellerV2 { external view returns (uint256); + + + function getEscrowVault() external view returns(address); } diff --git a/packages/contracts/contracts/mock/TellerV2SolMock.sol b/packages/contracts/contracts/mock/TellerV2SolMock.sol index 6a3538a4..bea8c4ef 100644 --- a/packages/contracts/contracts/mock/TellerV2SolMock.sol +++ b/packages/contracts/contracts/mock/TellerV2SolMock.sol @@ -40,6 +40,11 @@ contract TellerV2SolMock is ITellerV2, IProtocolFee, TellerV2Storage , ILoanRepa } + function getEscrowVault() external view returns(address){ + return address(0); + } + + function approveMarketForwarder(uint256 _marketId, address _forwarder) external { From 568cb8801ee8c955ba134a534fd5b38794148b4f Mon Sep 17 00:00:00 2001 From: Admazzola Date: Tue, 7 May 2024 09:22:28 -0400 Subject: [PATCH 14/53] add requirement to prep for withdrawl --- .../LenderCommitmentGroup_Smart.sol | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..f8bb0b7e 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -107,6 +107,13 @@ contract LenderCommitmentGroup_Smart is uint16 public interestRateUpperBound; + + + mapping(address => uint256) public poolSharesPreparedToWithdrawForLender; + mapping(address => uint256) public poolSharesPreparedTimestamp; + uint256 immutable public WITHDRAW_DELAY_TIME_SECONDS = 300; + + //mapping(address => uint256) public principalTokensCommittedByLender; mapping(uint256 => bool) public activeBids; @@ -390,6 +397,18 @@ contract LenderCommitmentGroup_Smart is ); } + function prepareSharesForWithdraw( + uint256 _amountPoolSharesTokens + ) external returns (bool) { + require( poolSharesToken.balanceOf(msg.sender) >= _amountPoolSharesTokens ); + + poolSharesPreparedToWithdrawForLender[msg.sender] = _amountPoolSharesTokens; + poolSharesPreparedTimestamp[msg.sender] = block.timestamp; + + return true; + } + + /* */ @@ -398,7 +417,8 @@ contract LenderCommitmentGroup_Smart is address _recipient ) external returns (uint256) { - + require(poolSharesPreparedToWithdrawForLender[msg.sender] >= _amountPoolSharesTokens,"Shares not prepared for withdraw"); + require(poolSharesPreparedTimestamp[msg.sender] <= block.timestamp - WITHDRAW_DELAY_TIME_SECONDS,"Shares not prepared for withdraw"); poolSharesToken.burn(msg.sender, _amountPoolSharesTokens); From a887e74d27a7366bc0243b871f34e1556b0b984d Mon Sep 17 00:00:00 2001 From: Admazzola Date: Tue, 7 May 2024 09:30:42 -0400 Subject: [PATCH 15/53] fix --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 6 +++--- .../SmartCommitments/LenderCommitmentGroup_Smart_Test.sol | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..77b52c59 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -438,10 +438,10 @@ contract LenderCommitmentGroup_Smart is "Insufficient tokenAmountDifference" ); - if (_tokenAmountDifference > 0) { + if (minAmountDifference > 0) { //this is used when the collateral value is higher than the principal (rare) //the loan will be completely made whole and our contract gets extra funds too - uint256 tokensToTakeFromSender = abs(_tokenAmountDifference); + uint256 tokensToTakeFromSender = abs(minAmountDifference); IERC20(principalToken).transferFrom( msg.sender, @@ -454,7 +454,7 @@ contract LenderCommitmentGroup_Smart is totalPrincipalTokensRepaid += amountDue; } else { - uint256 tokensToGiveToSender = abs(_tokenAmountDifference); + uint256 tokensToGiveToSender = abs(minAmountDifference); IERC20(principalToken).transferFrom( msg.sender, diff --git a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol index 8d468175..1bf265e6 100644 --- a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol +++ b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol @@ -529,7 +529,9 @@ contract LenderCommitmentGroup_Smart_Test is Testable { vm.prank(address(liquidator)); principalToken.approve(address(lenderCommitmentGroupSmart), 1e18); - lenderCommitmentGroupSmart.mock_setMinimumAmountDifferenceToCloseDefaultedLoan(2000); + int256 minAmountDifference = 2000; + + lenderCommitmentGroupSmart.mock_setMinimumAmountDifferenceToCloseDefaultedLoan(minAmountDifference); int256 tokenAmountDifference = 4000; vm.prank(address(liquidator)); @@ -540,7 +542,7 @@ contract LenderCommitmentGroup_Smart_Test is Testable { uint256 updatedBalance = principalToken.balanceOf(address(liquidator)); - int256 expectedDifference = int256(amountOwed) + tokenAmountDifference; + int256 expectedDifference = int256(amountOwed) + minAmountDifference; assertEq(originalBalance - updatedBalance , uint256(expectedDifference), "unexpected tokenDifferenceFromLiquidations"); From f357316c22c1d3d4ee7c40776ceb355f2ba99521 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Tue, 7 May 2024 09:40:04 -0400 Subject: [PATCH 16/53] add tests --- .../LenderCommitmentGroup_Smart.sol | 12 ++++++--- .../interfaces/ILenderCommitmentGroup.sol | 3 ++- .../LenderCommitmentGroup_Smart_Test.sol | 27 ++++++++++++++----- 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..a9d1e42b 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -306,7 +306,8 @@ contract LenderCommitmentGroup_Smart is */ function addPrincipalToCommitmentGroup( uint256 _amount, - address _sharesRecipient + address _sharesRecipient, + uint256 _minSharesAmountOut ) external returns (uint256 sharesAmount_) { //transfers the primary principal token from msg.sender into this contract escrow @@ -314,6 +315,8 @@ contract LenderCommitmentGroup_Smart is sharesAmount_ = _valueOfUnderlying(_amount, sharesExchangeRate()); + require( sharesAmount_ >= _minSharesAmountOut, "Invalid: Min Shares AmountOut" ); + totalPrincipalTokensCommitted += _amount; //principalTokensCommittedByLender[msg.sender] += _amount; @@ -395,10 +398,9 @@ contract LenderCommitmentGroup_Smart is */ function burnSharesToWithdrawEarnings( uint256 _amountPoolSharesTokens, - address _recipient + address _recipient, + uint256 _minAmountOut ) external returns (uint256) { - - poolSharesToken.burn(msg.sender, _amountPoolSharesTokens); @@ -411,6 +413,8 @@ contract LenderCommitmentGroup_Smart is principalToken.transfer(_recipient, principalTokenValueToWithdraw); + require( principalTokenValueToWithdraw >= _minAmountOut ,"Invalid: Min Amount Out"); + return principalTokenValueToWithdraw; } diff --git a/packages/contracts/contracts/interfaces/ILenderCommitmentGroup.sol b/packages/contracts/contracts/interfaces/ILenderCommitmentGroup.sol index 7b0c0287..bc1e283a 100644 --- a/packages/contracts/contracts/interfaces/ILenderCommitmentGroup.sol +++ b/packages/contracts/contracts/interfaces/ILenderCommitmentGroup.sol @@ -25,6 +25,7 @@ interface ILenderCommitmentGroup { function addPrincipalToCommitmentGroup( uint256 _amount, - address _sharesRecipient + address _sharesRecipient, + uint256 _minAmountOut ) external returns (uint256 sharesAmount_); } diff --git a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol index 8d468175..204b6b74 100644 --- a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol +++ b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol @@ -149,9 +149,11 @@ contract LenderCommitmentGroup_Smart_Test is Testable { vm.prank(address(lender)); principalToken.approve(address(lenderCommitmentGroupSmart), 1000000); + uint256 minAmountOut = 1000000; + vm.prank(address(lender)); uint256 sharesAmount_ = lenderCommitmentGroupSmart - .addPrincipalToCommitmentGroup(1000000, address(borrower)); + .addPrincipalToCommitmentGroup(1000000, address(borrower), minAmountOut); uint256 expectedSharesAmount = 1000000; @@ -176,12 +178,15 @@ contract LenderCommitmentGroup_Smart_Test is Testable { //lenderCommitmentGroupSmart.set_totalPrincipalTokensCommitted(1000000); //lenderCommitmentGroupSmart.set_totalInterestCollected(2000000); + uint256 minAmountOut = 500000; + + vm.prank(address(lender)); principalToken.approve(address(lenderCommitmentGroupSmart), 1000000); vm.prank(address(lender)); uint256 sharesAmount_ = lenderCommitmentGroupSmart - .addPrincipalToCommitmentGroup(1000000, address(borrower)); + .addPrincipalToCommitmentGroup(1000000, address(borrower),minAmountOut); uint256 expectedSharesAmount = 500000; @@ -204,14 +209,14 @@ contract LenderCommitmentGroup_Smart_Test is Testable { lenderCommitmentGroupSmart.set_totalInterestCollected(1e6 * 1); lenderCommitmentGroupSmart.set_totalPrincipalTokensCommitted(1e6 * 1); - + uint256 minAmountOut = 500000; vm.prank(address(lender)); principalToken.approve(address(lenderCommitmentGroupSmart), 1000000); vm.prank(address(lender)); uint256 sharesAmount_ = lenderCommitmentGroupSmart - .addPrincipalToCommitmentGroup(1000000, address(borrower)); + .addPrincipalToCommitmentGroup(1000000, address(borrower), minAmountOut); uint256 expectedSharesAmount = 1000000; @@ -253,11 +258,14 @@ contract LenderCommitmentGroup_Smart_Test is Testable { ); vm.prank(address(lender)); + + uint256 minAmountOut = 1000000; uint256 receivedPrincipalTokens = lenderCommitmentGroupSmart.burnSharesToWithdrawEarnings( sharesAmount, - address(lender) + address(lender), + minAmountOut ); uint256 expectedReceivedPrincipalTokens = 1000000; // the orig amt ! @@ -297,12 +305,14 @@ contract LenderCommitmentGroup_Smart_Test is Testable { sharesAmount ); + uint256 minAmountOut = 900000; vm.prank(address(lender)); uint256 receivedPrincipalTokens = lenderCommitmentGroupSmart.burnSharesToWithdrawEarnings( sharesAmount, - address(lender) + address(lender), + minAmountOut ); uint256 expectedReceivedPrincipalTokens = 1000000; // the orig amt ! @@ -332,6 +342,8 @@ contract LenderCommitmentGroup_Smart_Test is Testable { vm.prank(address(lender)); + uint256 minAmountOut = 500000; + uint256 sharesAmount = 500000; //should have all of the shares at this point lenderCommitmentGroupSmart.mock_mintShares( @@ -344,7 +356,8 @@ contract LenderCommitmentGroup_Smart_Test is Testable { uint256 receivedPrincipalTokens = lenderCommitmentGroupSmart.burnSharesToWithdrawEarnings( sharesAmount, - address(lender) + address(lender), + minAmountOut ); uint256 expectedReceivedPrincipalTokens = 500000; // the orig amt ! From 8b52a09ca9c080c625f5d764df767b06283b1f84 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Tue, 7 May 2024 09:49:56 -0400 Subject: [PATCH 17/53] apr calc fix --- .../LenderCommitmentGroup_Smart.sol | 12 +++++++----- .../contracts/interfaces/ISmartCommitment.sol | 2 +- .../LenderCommitmentGroup_Smart_Test.sol | 4 ++-- 3 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..42a0e366 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -349,7 +349,7 @@ contract LenderCommitmentGroup_Smart is "Mismatching collateral token" ); //the interest rate must be at least as high has the commitment demands. The borrower can use a higher interest rate although that would not be beneficial to the borrower. - require(_interestRate >= getMinInterestRate(), "Invalid interest rate"); + require(_interestRate >= getMinInterestRate(_principalAmount), "Invalid interest rate"); //the loan duration must be less than the commitment max loan duration. The lender who made the commitment expects the money to be returned before this window. require(_loanDuration <= maxLoanDuration, "Invalid loan max duration"); @@ -754,21 +754,23 @@ contract LenderCommitmentGroup_Smart is } //this is always between 0 and 10000 - function getPoolUtilizationRatio() public view returns (uint16) { + function getPoolUtilizationRatio(uint256 activeLoansAmountDelta ) public view returns (uint16) { if (getPoolTotalEstimatedValue() == 0) { return 0; } return uint16( Math.min( - getTotalPrincipalTokensOutstandingInActiveLoans() * 10000 / + (getTotalPrincipalTokensOutstandingInActiveLoans() + activeLoansAmountDelta) * 10000 / getPoolTotalEstimatedValue() , 10000 )); } - function getMinInterestRate() public view returns (uint16) { - return interestRateLowerBound + uint16( uint256(interestRateUpperBound-interestRateLowerBound).percent(getPoolUtilizationRatio()) ); + function getMinInterestRate(uint256 amountDelta) public view returns (uint16) { + return interestRateLowerBound + uint16( uint256(interestRateUpperBound-interestRateLowerBound).percent(getPoolUtilizationRatio(amountDelta )) ); } + + function getPrincipalTokenAddress() external view returns (address) { return address(principalToken); diff --git a/packages/contracts/contracts/interfaces/ISmartCommitment.sol b/packages/contracts/contracts/interfaces/ISmartCommitment.sol index 57b9637d..73068dfd 100644 --- a/packages/contracts/contracts/interfaces/ISmartCommitment.sol +++ b/packages/contracts/contracts/interfaces/ISmartCommitment.sol @@ -26,7 +26,7 @@ interface ISmartCommitment { function getCollateralTokenId() external view returns (uint256); - function getMinInterestRate() external view returns (uint16); + function getMinInterestRate(uint256 _delta) external view returns (uint16); function getMaxLoanDuration() external view returns (uint32); diff --git a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol index 8d468175..652c618d 100644 --- a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol +++ b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol @@ -702,7 +702,7 @@ contract LenderCommitmentGroup_Smart_Test is Testable { uint16 poolUtilizationRatio = lenderCommitmentGroupSmart.getPoolUtilizationRatio( - + 0 ); @@ -710,7 +710,7 @@ contract LenderCommitmentGroup_Smart_Test is Testable { // submit bid uint16 minInterestRate = lenderCommitmentGroupSmart.getMinInterestRate( - + 0 ); From 0186a1c7d4d33fb6539633e0f6f6e1f02dea4da7 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Tue, 7 May 2024 12:28:51 -0400 Subject: [PATCH 18/53] flip min max --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..0ac5d4bc 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -638,8 +638,8 @@ contract LenderCommitmentGroup_Smart is bool principalTokenIsToken0 ) internal pure returns (uint256 collateralTokensAmountToMatchValue) { if (principalTokenIsToken0) { - //token 1 to token 0 ? - uint256 worstCasePairPrice = Math.min( + + uint256 worstCasePairPrice = Math.max( pairPriceWithTwap, pairPriceImmediate ); @@ -649,8 +649,8 @@ contract LenderCommitmentGroup_Smart is worstCasePairPrice //if this is lower, collateral tokens amt will be higher ); } else { - //token 0 to token 1 ? - uint256 worstCasePairPrice = Math.max( + + uint256 worstCasePairPrice = Math.min( pairPriceWithTwap, pairPriceImmediate ); From 6a62895c5d7b7e586ed8b9abb2199813311c71de Mon Sep 17 00:00:00 2001 From: Admazzola Date: Tue, 7 May 2024 12:35:45 -0400 Subject: [PATCH 19/53] add a way for protocol owner to extract non-loan-related tokens from collateral --- .../contracts/contracts/CollateralManager.sol | 22 +++++++++++++++++++ .../contracts/escrow/CollateralEscrowV1.sol | 19 ++++++++++++++++ .../interfaces/escrow/ICollateralEscrowV1.sol | 6 +++++ 3 files changed, 47 insertions(+) diff --git a/packages/contracts/contracts/CollateralManager.sol b/packages/contracts/contracts/CollateralManager.sol index 67af5ff1..8d80237e 100644 --- a/packages/contracts/contracts/CollateralManager.sol +++ b/packages/contracts/contracts/CollateralManager.sol @@ -70,6 +70,14 @@ contract CollateralManager is OwnableUpgradeable, ICollateralManager { _; } + modifier onlyProtocolOwner() { + + address protocolOwner = OwnableUpgradeable(address(tellerV2)).owner(); + + require(_msgSender() == protocolOwner, "Sender not authorized"); + _; + } + /* External Functions */ /** @@ -264,6 +272,20 @@ contract CollateralManager is OwnableUpgradeable, ICollateralManager { emit CollateralClaimed(_bidId); } + function withdrawDustTokens( + uint256 _bidId, + address _tokenAddress, + uint256 _amount, + address _recipientAddress + ) external onlyProtocolOwner { + + ICollateralEscrowV1(_escrows[_bidId]).withdrawDustTokens( + _tokenAddress, + _amount, + _recipientAddress + ); + } + /** * @notice Withdraws deposited collateral from the created escrow of a bid that has been CLOSED after being defaulted. * @param _bidId The id of the bid to withdraw collateral for. diff --git a/packages/contracts/contracts/escrow/CollateralEscrowV1.sol b/packages/contracts/contracts/escrow/CollateralEscrowV1.sol index 9194df81..bffa660c 100644 --- a/packages/contracts/contracts/escrow/CollateralEscrowV1.sol +++ b/packages/contracts/contracts/escrow/CollateralEscrowV1.sol @@ -102,6 +102,25 @@ contract CollateralEscrowV1 is OwnableUpgradeable, ICollateralEscrowV1 { emit CollateralWithdrawn(_collateralAddress, _amount, _recipient); } + + function withdrawDustTokens( + address tokenAddress, + uint256 amount, + address recipient + ) external virtual onlyOwner { //the owner should be collateral manager + + require(tokenAddress != address(0), "Invalid token address"); + + Collateral storage collateral = collateralBalances[tokenAddress]; + require( + collateral._amount == 0, + "Asset not allowed to be withdrawn as dust" + ); + + IERC20Upgradeable(tokenAddress).transfer(recipient, amount); + } + + /** * @notice Internal function for transferring collateral assets into this contract. * @param _collateralAddress The address of the collateral contract. diff --git a/packages/contracts/contracts/interfaces/escrow/ICollateralEscrowV1.sol b/packages/contracts/contracts/interfaces/escrow/ICollateralEscrowV1.sol index 6b2194ce..0969452a 100644 --- a/packages/contracts/contracts/interfaces/escrow/ICollateralEscrowV1.sol +++ b/packages/contracts/contracts/interfaces/escrow/ICollateralEscrowV1.sol @@ -40,6 +40,12 @@ interface ICollateralEscrowV1 { address _recipient ) external; + function withdrawDustTokens( + address _tokenAddress, + uint256 _amount, + address _recipient + ) external; + function getBid() external view returns (uint256); function initialize(uint256 _bidId) external; From f5c153bc6560926633dcc36d023c334fd31fe735 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Tue, 7 May 2024 12:45:26 -0400 Subject: [PATCH 20/53] simple fix --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..05c85615 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -70,6 +70,8 @@ contract LenderCommitmentGroup_Smart is uint256 public immutable STANDARD_EXPANSION_FACTOR = 1e18; + uint256 public immutable MIN_TWAP_INTERVAL = 3; + uint256 public immutable UNISWAP_EXPANSION_FACTOR = 2**96; uint256 public immutable EXCHANGE_RATE_EXPANSION_FACTOR = 1e36; @@ -181,6 +183,7 @@ contract LenderCommitmentGroup_Smart is _uniswapPoolFee ); + require(_twapInterval > MIN_TWAP_INTERVAL, "Invalid TWAP Interval"); require(UNISWAP_V3_POOL != address(0), "Invalid uniswap pool address"); marketId = _marketId; From 3c56ee26a32e9b1def87ff1c519b4aec747c4caf Mon Sep 17 00:00:00 2001 From: Admazzola Date: Tue, 7 May 2024 12:54:51 -0400 Subject: [PATCH 21/53] mean tick --- .../LenderCommitmentGroup_Smart.sol | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..9a25893f 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -584,13 +584,16 @@ contract LenderCommitmentGroup_Smart is (int56[] memory tickCumulatives, ) = IUniswapV3Pool(UNISWAP_V3_POOL) .observe(secondsAgos); - // tick(imprecise as it's an integer) to price - sqrtPriceX96 = TickMath.getSqrtRatioAtTick( - int24( - (tickCumulatives[1] - tickCumulatives[0]) / - int32(twapInterval) - ) - ); + + + int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0]; + int24 arithmeticMeanTick = int24(tickCumulativesDelta / int32(twapInterval)); + //// Always round to negative infinity + if (tickCumulativesDelta < 0 && (tickCumulativesDelta % int32(twapInterval) != 0)) arithmeticMeanTick--; + + sqrtPriceX96 = TickMath.getSqrtRatioAtTick(arithmeticMeanTick); + + } } From 8135d3b9ffaab96af4741fcb36286b9312a31b85 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Tue, 7 May 2024 12:56:08 -0400 Subject: [PATCH 22/53] adding offset --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..a8806706 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -578,8 +578,8 @@ contract LenderCommitmentGroup_Smart is .slot0(); } else { uint32[] memory secondsAgos = new uint32[](2); - secondsAgos[0] = twapInterval; // from (before) - secondsAgos[1] = 0; // to (now) + secondsAgos[0] = twapInterval+1; // from (before) + secondsAgos[1] = 1; // to (now) (int56[] memory tickCumulatives, ) = IUniswapV3Pool(UNISWAP_V3_POOL) .observe(secondsAgos); From b675d0967ab7fd85fd152f30fd5afaa101c68650 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Tue, 7 May 2024 13:02:23 -0400 Subject: [PATCH 23/53] use safe xfer --- .../LenderCommitmentGroup_Smart.sol | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..e7ad9405 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -39,7 +39,7 @@ import { Payment } from "../../../TellerV2Storage.sol"; import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; import "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; - +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; /* @@ -74,6 +74,8 @@ contract LenderCommitmentGroup_Smart is uint256 public immutable EXCHANGE_RATE_EXPANSION_FACTOR = 1e36; + using SafeERC20 for IERC20; + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable address public immutable TELLER_V2; address public immutable SMART_COMMITMENT_FORWARDER; @@ -310,7 +312,7 @@ contract LenderCommitmentGroup_Smart is ) external returns (uint256 sharesAmount_) { //transfers the primary principal token from msg.sender into this contract escrow - principalToken.transferFrom(msg.sender, address(this), _amount); + principalToken.safeTransferFrom(msg.sender, address(this), _amount); sharesAmount_ = _valueOfUnderlying(_amount, sharesExchangeRate()); @@ -409,7 +411,7 @@ contract LenderCommitmentGroup_Smart is totalPrincipalTokensWithdrawn += principalTokenValueToWithdraw; - principalToken.transfer(_recipient, principalTokenValueToWithdraw); + principalToken.safeTransfer(_recipient, principalTokenValueToWithdraw); return principalTokenValueToWithdraw; } @@ -443,7 +445,7 @@ contract LenderCommitmentGroup_Smart is //the loan will be completely made whole and our contract gets extra funds too uint256 tokensToTakeFromSender = abs(_tokenAmountDifference); - IERC20(principalToken).transferFrom( + IERC20(principalToken).safeTransferFrom( msg.sender, address(this), amountDue + tokensToTakeFromSender @@ -456,7 +458,7 @@ contract LenderCommitmentGroup_Smart is uint256 tokensToGiveToSender = abs(_tokenAmountDifference); - IERC20(principalToken).transferFrom( + IERC20(principalToken).safeTransferFrom( msg.sender, address(this), amountDue - tokensToGiveToSender From c7359d81729c44ac0adeec9fee63494e345c5857 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Tue, 7 May 2024 13:43:05 -0400 Subject: [PATCH 24/53] reset approval to 0 once finished --- .../extensions/FlashRolloverLoan_G5.sol | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/FlashRolloverLoan_G5.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/FlashRolloverLoan_G5.sol index 7156068f..04507b2c 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/FlashRolloverLoan_G5.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/FlashRolloverLoan_G5.sol @@ -208,6 +208,11 @@ contract FlashRolloverLoan_G5 is IFlashLoanSimpleReceiver, IFlashRolloverLoan_G4 ); } + IERC20Upgradeable(_flashToken).approve( + address(POOL()), + 0 + ); + emit RolloverLoanComplete( _rolloverArgs.borrower, _rolloverArgs.loanId, @@ -246,6 +251,11 @@ contract FlashRolloverLoan_G5 is IFlashLoanSimpleReceiver, IFlashRolloverLoan_G4 ); TELLER_V2.repayLoanFull(_bidId); + IERC20Upgradeable(_principalToken).approve( + address(TELLER_V2), + 0 + ); + uint256 fundsAfterRepayment = IERC20Upgradeable(_principalToken) .balanceOf(address(this)); From d81af4b3113fa360d6190e4667cd28058fa5d848 Mon Sep 17 00:00:00 2001 From: andy Date: Tue, 7 May 2024 14:11:52 -0400 Subject: [PATCH 25/53] fix SCF --- .../LenderCommitmentForwarder/SmartCommitmentForwarder.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/SmartCommitmentForwarder.sol b/packages/contracts/contracts/LenderCommitmentForwarder/SmartCommitmentForwarder.sol index 6ba9b0c8..8ddbb11a 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/SmartCommitmentForwarder.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/SmartCommitmentForwarder.sol @@ -4,11 +4,12 @@ pragma solidity ^0.8.0; import "../TellerV2MarketForwarder_G3.sol"; import "../interfaces/ILenderCommitmentForwarder.sol"; +import "../interfaces/ISmartCommitmentForwarder.sol"; import "./LenderCommitmentForwarder_G1.sol"; import { CommitmentCollateralType, ISmartCommitment } from "../interfaces/ISmartCommitment.sol"; -contract SmartCommitmentForwarder is TellerV2MarketForwarder_G3 { +contract SmartCommitmentForwarder is TellerV2MarketForwarder_G3, ISmartCommitmentForwarder { event ExercisedSmartCommitment( address indexed smartCommitmentAddress, address borrower, @@ -35,7 +36,7 @@ contract SmartCommitmentForwarder is TellerV2MarketForwarder_G3 { * @param _loanDuration The overall duration for the loan. Must be longer than market payment cycle duration. * @return bidId The ID of the loan that was created on TellerV2 */ - function acceptCommitmentWithRecipient( + function acceptSmartCommitmentWithRecipient( address _smartCommitmentAddress, uint256 _principalAmount, uint256 _collateralAmount, From eb76c6d2fb652d764e1183549d70097a94742a6d Mon Sep 17 00:00:00 2001 From: andy Date: Wed, 8 May 2024 11:18:00 -0400 Subject: [PATCH 26/53] require interest in liquidation --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..0e7388e4 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -423,7 +423,7 @@ contract LenderCommitmentGroup_Smart is uint256 _bidId, int256 _tokenAmountDifference ) public bidIsActiveForGroup(_bidId) { - uint256 amountDue = getAmountOwedForBid(_bidId, false); + uint256 amountDue = getAmountOwedForBid(_bidId, true); uint256 loanDefaultedTimeStamp = ITellerV2(TELLER_V2) .getLoanDefaultTimestamp(_bidId); @@ -451,7 +451,7 @@ contract LenderCommitmentGroup_Smart is tokenDifferenceFromLiquidations += int256(tokensToTakeFromSender); - totalPrincipalTokensRepaid += amountDue; + } else { uint256 tokensToGiveToSender = abs(_tokenAmountDifference); @@ -464,7 +464,7 @@ contract LenderCommitmentGroup_Smart is tokenDifferenceFromLiquidations -= int256(tokensToGiveToSender); - totalPrincipalTokensRepaid += amountDue; + } //this will give collateral to the caller From c1147896ce741e0d3f7a993dd61f09b70185c1ab Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 8 May 2024 14:24:41 -0400 Subject: [PATCH 27/53] add extensions to SCF --- .../LenderCommitmentForwarder/SmartCommitmentForwarder.sol | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/SmartCommitmentForwarder.sol b/packages/contracts/contracts/LenderCommitmentForwarder/SmartCommitmentForwarder.sol index 6ba9b0c8..504b2cb5 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/SmartCommitmentForwarder.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/SmartCommitmentForwarder.sol @@ -2,13 +2,16 @@ pragma solidity ^0.8.0; import "../TellerV2MarketForwarder_G3.sol"; - +import "./extensions/ExtensionsContextUpgradeable.sol"; import "../interfaces/ILenderCommitmentForwarder.sol"; import "./LenderCommitmentForwarder_G1.sol"; import { CommitmentCollateralType, ISmartCommitment } from "../interfaces/ISmartCommitment.sol"; -contract SmartCommitmentForwarder is TellerV2MarketForwarder_G3 { +contract SmartCommitmentForwarder is + ExtensionsContextUpgradeable, //this should always be first for upgradeability + TellerV2MarketForwarder_G3 + { event ExercisedSmartCommitment( address indexed smartCommitmentAddress, address borrower, From 599e32b70b507d04a22ae2d5a5b22b9ea98f2768 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Thu, 9 May 2024 10:47:41 -0400 Subject: [PATCH 28/53] non dynamic name for token --- .../LenderCommitmentGroup_Smart.sol | 29 +++---------------- 1 file changed, 4 insertions(+), 25 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..82460fc3 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -222,37 +222,16 @@ contract LenderCommitmentGroup_Smart is address(poolSharesToken) == address(0), "Pool shares already deployed" ); - - - (string memory name, string memory symbol ) = _generateTokenNameAndSymbol( - address(principalToken), - address(collateralToken) - ); - + poolSharesToken = new LenderCommitmentGroupShares( - name, - symbol, + "LenderGroupShares", + "SHR", 18 ); return address(poolSharesToken); - } + } - function _generateTokenNameAndSymbol(address principalToken, address collateralToken) - internal view - returns (string memory name, string memory symbol) { - // Read the symbol of the principal token - string memory principalSymbol = ERC20(principalToken).symbol(); - - // Read the symbol of the collateral token - string memory collateralSymbol = ERC20(collateralToken).symbol(); - - // Combine the symbols to create the name - name = string(abi.encodePacked("GroupShares-", principalSymbol, "-", collateralSymbol)); - - // Combine the symbols to create the symbol - symbol = string(abi.encodePacked("SHR-", principalSymbol, "-", collateralSymbol)); - } /** * @notice This determines the number of shares you get for depositing principal tokens and the number of principal tokens you receive for burning shares From a4bfe5ca3c9bc62cb142a94644c276dafebc779e Mon Sep 17 00:00:00 2001 From: Admazzola Date: Thu, 9 May 2024 14:20:32 -0400 Subject: [PATCH 29/53] prevent use of tokens with fee on xfer --- .../LenderCommitmentGroup_Smart.sol | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..f1219259 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -310,12 +310,22 @@ contract LenderCommitmentGroup_Smart is ) external returns (uint256 sharesAmount_) { //transfers the primary principal token from msg.sender into this contract escrow + uint256 principalTokenBalanceBefore = principalToken.balanceOf(address(this)); + principalToken.transferFrom(msg.sender, address(this), _amount); + + uint256 principalTokenBalanceAfter = principalToken.balanceOf(address(this)); + + require( principalTokenBalanceAfter == principalTokenBalanceBefore + _amount, "Token balance was not added properly" ); + + sharesAmount_ = _valueOfUnderlying(_amount, sharesExchangeRate()); + + totalPrincipalTokensCommitted += _amount; - //principalTokensCommittedByLender[msg.sender] += _amount; + //mint shares equal to _amount and give them to the shares recipient !!! poolSharesToken.mint(_sharesRecipient, sharesAmount_); From 9cf624a4298f2d75fd383aa5ef386c97f279fe43 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Thu, 9 May 2024 14:25:12 -0400 Subject: [PATCH 30/53] add max market fee --- packages/contracts/contracts/MarketRegistry.sol | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/packages/contracts/contracts/MarketRegistry.sol b/packages/contracts/contracts/MarketRegistry.sol index bb599389..71f19122 100644 --- a/packages/contracts/contracts/MarketRegistry.sol +++ b/packages/contracts/contracts/MarketRegistry.sol @@ -27,7 +27,7 @@ contract MarketRegistry is /** Constant Variables **/ uint256 public constant CURRENT_CODE_VERSION = 8; - + uint256 public constant MAX_MARKET_FEE_PERCENT = 1000; /* Storage Variables */ struct Marketplace { @@ -637,7 +637,11 @@ contract MarketRegistry is public ownsMarket(_marketId) { - require(_newPercent >= 0 && _newPercent <= 10000, "invalid percent"); + require( + _newPercent >= 0 && _newPercent <= MAX_MARKET_FEE_PERCENT, + "invalid fee percent" + ); + if (_newPercent != markets[_marketId].marketplaceFeePercent) { markets[_marketId].marketplaceFeePercent = _newPercent; emit SetMarketFee(_marketId, _newPercent); From eef86accbba01ff374d4d3f6bde553961bc8d965 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 15 May 2024 15:02:29 -0400 Subject: [PATCH 31/53] include interest in liq --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..8939210a 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -423,7 +423,7 @@ contract LenderCommitmentGroup_Smart is uint256 _bidId, int256 _tokenAmountDifference ) public bidIsActiveForGroup(_bidId) { - uint256 amountDue = getAmountOwedForBid(_bidId, false); + uint256 amountDue = getAmountOwedForBid(_bidId, true); uint256 loanDefaultedTimeStamp = ITellerV2(TELLER_V2) .getLoanDefaultTimestamp(_bidId); @@ -451,7 +451,7 @@ contract LenderCommitmentGroup_Smart is tokenDifferenceFromLiquidations += int256(tokensToTakeFromSender); - totalPrincipalTokensRepaid += amountDue; + //totalPrincipalTokensRepaid += amountDue; } else { uint256 tokensToGiveToSender = abs(_tokenAmountDifference); @@ -464,7 +464,7 @@ contract LenderCommitmentGroup_Smart is tokenDifferenceFromLiquidations -= int256(tokensToGiveToSender); - totalPrincipalTokensRepaid += amountDue; + //totalPrincipalTokensRepaid += amountDue; } //this will give collateral to the caller From f1d2069ac6154be0514aae4e96a8498abd43c824 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 15 May 2024 15:20:14 -0400 Subject: [PATCH 32/53] use 1x instead of 8x --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..950331d4 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -506,7 +506,7 @@ contract LenderCommitmentGroup_Smart is uint256 secondsSinceDefaulted = block.timestamp - _loanDefaultedTimestamp; - int256 incentiveMultiplier = int256(86400) - + int256 incentiveMultiplier = int256(10000) - int256(secondsSinceDefaulted); if (incentiveMultiplier < -10000) { From afe9c9ba211a44989a3aef0a2e13af414de33295 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 15 May 2024 15:34:33 -0400 Subject: [PATCH 33/53] add --- .../contracts/libraries/V2Calculations.sol | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/packages/contracts/contracts/libraries/V2Calculations.sol b/packages/contracts/contracts/libraries/V2Calculations.sol index 4a01c336..375ea3c1 100644 --- a/packages/contracts/contracts/libraries/V2Calculations.sol +++ b/packages/contracts/contracts/libraries/V2Calculations.sol @@ -86,13 +86,18 @@ library V2Calculations { _bid.loanDetails.principal - _bid.loanDetails.totalRepaid.principal; - uint256 daysInYear = _paymentCycleType == PaymentCycleType.Monthly + uint256 owedTime = _timestamp - uint256(_lastRepaidTimestamp); + + { + uint256 daysInYear = _paymentCycleType == PaymentCycleType.Monthly ? 360 days : 365 days; - uint256 interestOwedInAYear = owedPrincipal_.percent(_bid.terms.APR); - uint256 owedTime = _timestamp - uint256(_lastRepaidTimestamp); - interest_ = (interestOwedInAYear * owedTime) / daysInYear; + uint256 interestOwedInAYear = owedPrincipal_.percent(_bid.terms.APR); + + interest_ = (interestOwedInAYear * owedTime) / daysInYear; + } + bool isLastPaymentCycle; { @@ -121,10 +126,13 @@ library V2Calculations { // Max payable amount in a cycle // NOTE: the last cycle could have less than the calculated payment amount + //the amount owed for the cycle should never exceed the current payment cycle amount so we use min here + uint256 owedAmountForCycle = Math.min( ((_bid.terms.paymentCycleAmount * owedTime)+interest_ ) / + _paymentCycleDuration , _bid.terms.paymentCycleAmount+interest_ ) ; + uint256 owedAmount = isLastPaymentCycle ? owedPrincipal_ + interest_ - : (_bid.terms.paymentCycleAmount * owedTime) / - _paymentCycleDuration; + : owedAmountForCycle ; duePrincipal_ = Math.min(owedAmount - interest_, owedPrincipal_); } From 04d4afac29f1d4711e92e8db1fb497bb86746d2d Mon Sep 17 00:00:00 2001 From: "gmryan.eth" <45776981+rbcp18@users.noreply.github.com> Date: Wed, 15 May 2024 23:15:47 +0100 Subject: [PATCH 34/53] switch fix to retrieve original loan principal amount --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 950331d4..4b31cc51 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -480,9 +480,7 @@ contract LenderCommitmentGroup_Smart is Payment memory amountOwedPayment = ITellerV2(TELLER_V2) .calculateAmountOwed(_bidId, block.timestamp); - amountOwed_ = _includeInterest - ? amountOwedPayment.principal + amountOwedPayment.interest - : amountOwedPayment.principal; + amountOwed_ = _bidId.loanDetails.principal; } /* @@ -506,7 +504,7 @@ contract LenderCommitmentGroup_Smart is uint256 secondsSinceDefaulted = block.timestamp - _loanDefaultedTimestamp; - int256 incentiveMultiplier = int256(10000) - + int256 incentiveMultiplier = int256(86400) - int256(secondsSinceDefaulted); if (incentiveMultiplier < -10000) { From e84f9738d745af442ccfdd8a58020253b28a2351 Mon Sep 17 00:00:00 2001 From: "gmryan.eth" <45776981+rbcp18@users.noreply.github.com> Date: Wed, 15 May 2024 23:24:49 +0100 Subject: [PATCH 35/53] simplify liquidate logic by moving up amount due variable fetch --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b31cc51..81825c6d 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -423,7 +423,7 @@ contract LenderCommitmentGroup_Smart is uint256 _bidId, int256 _tokenAmountDifference ) public bidIsActiveForGroup(_bidId) { - uint256 amountDue = getAmountOwedForBid(_bidId, false); + uint256 amountDue = _bidId.loanDetails.principal; uint256 loanDefaultedTimeStamp = ITellerV2(TELLER_V2) .getLoanDefaultTimestamp(_bidId); @@ -480,7 +480,9 @@ contract LenderCommitmentGroup_Smart is Payment memory amountOwedPayment = ITellerV2(TELLER_V2) .calculateAmountOwed(_bidId, block.timestamp); - amountOwed_ = _bidId.loanDetails.principal; + amountOwed_ = _includeInterest + ? amountOwedPayment.principal + amountOwedPayment.interest + : amountOwedPayment.principal; } /* From 58a4b674d09e5eb202b66fe89fdbc8d104bd0e62 Mon Sep 17 00:00:00 2001 From: andy Date: Thu, 16 May 2024 10:20:56 -0400 Subject: [PATCH 36/53] fix pr --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 81825c6d..a34add7b 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -423,7 +423,10 @@ contract LenderCommitmentGroup_Smart is uint256 _bidId, int256 _tokenAmountDifference ) public bidIsActiveForGroup(_bidId) { - uint256 amountDue = _bidId.loanDetails.principal; + + //use original principal amount as amountDue + (,,,, uint256 amountDue, , , ) + = ITellerV2(TELLER_V2).getLoanSummary(_bidId); uint256 loanDefaultedTimeStamp = ITellerV2(TELLER_V2) .getLoanDefaultTimestamp(_bidId); From fdc741755d43286957852df1933a3e2f7958dbb2 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Tue, 28 May 2024 15:32:38 -0400 Subject: [PATCH 37/53] add getter --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..049494be 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -84,6 +84,7 @@ contract LenderCommitmentGroup_Smart is IERC20 public principalToken; IERC20 public collateralToken; + uint24 public uniswapPoolFee; uint256 marketId; @@ -174,6 +175,7 @@ contract LenderCommitmentGroup_Smart is principalToken = IERC20(_principalTokenAddress); collateralToken = IERC20(_collateralTokenAddress); + uniswapPoolFee = _uniswapPoolFee; UNISWAP_V3_POOL = IUniswapV3Factory(UNISWAP_V3_FACTORY).getPool( _principalTokenAddress, @@ -717,6 +719,9 @@ contract LenderCommitmentGroup_Smart is return totalPrincipalTokensLended - totalPrincipalTokensRepaid; } + + + function getCollateralTokenAddress() external view returns (address) { return address(collateralToken); } @@ -765,7 +770,7 @@ contract LenderCommitmentGroup_Smart is getPoolTotalEstimatedValue() , 10000 )); } - + function getMinInterestRate() public view returns (uint16) { return interestRateLowerBound + uint16( uint256(interestRateUpperBound-interestRateLowerBound).percent(getPoolUtilizationRatio()) ); } From b0fd0169c327c9e1b5b7c6639b761f7d7421ee72 Mon Sep 17 00:00:00 2001 From: andy Date: Tue, 28 May 2024 16:22:37 -0400 Subject: [PATCH 38/53] add fix --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 5 +++-- packages/contracts/hardhat.config.ts | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..13c49b6b 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -556,8 +556,9 @@ contract LenderCommitmentGroup_Smart is returns (uint256 price_) { - uint256 priceX96 = (uint256(_sqrtPriceX96) * uint256(_sqrtPriceX96)) / - (2**96); + + + uint256 priceX96 = FullMath.mulDiv(uint256(_sqrtPriceX96), uint256(_sqrtPriceX96), (2**96) ); // sqrtPrice is in X96 format so we scale it down to get the price // Also note that this price is a relative price between the two tokens in the pool diff --git a/packages/contracts/hardhat.config.ts b/packages/contracts/hardhat.config.ts index e406e4c2..6e139ea8 100644 --- a/packages/contracts/hardhat.config.ts +++ b/packages/contracts/hardhat.config.ts @@ -37,7 +37,8 @@ import semver from 'semver' // import { logger as tenderlyLogger } from 'tenderly/utils/logger' const NODE_VERSION = 'v16' -if (!semver.satisfies(process.version, NODE_VERSION)) +const ALT_NODE_VERSION = 'v18' +if (!semver.satisfies(process.version, NODE_VERSION) && !semver.satisfies(process.version, ALT_NODE_VERSION)) throw new Error( `Incorrect NodeJS version being used (${process.version}). Expected: ${NODE_VERSION}` ) From 2af9c0d58775d972f7850031e70da7a53ddf6a91 Mon Sep 17 00:00:00 2001 From: andy Date: Tue, 28 May 2024 16:42:10 -0400 Subject: [PATCH 39/53] commitmentid fix check --- .../LenderCommitmentForwarder/LenderCommitmentForwarder_U1.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/LenderCommitmentForwarder_U1.sol b/packages/contracts/contracts/LenderCommitmentForwarder/LenderCommitmentForwarder_U1.sol index 8c401074..0f9a9a93 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/LenderCommitmentForwarder_U1.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/LenderCommitmentForwarder_U1.sol @@ -521,7 +521,7 @@ contract LenderCommitmentForwarder_U1 is ); require( - commitmentPrincipalAccepted[bidId] <= commitment.maxPrincipal, + commitmentPrincipalAccepted[_commitmentId] <= commitment.maxPrincipal, "Invalid loan max principal" ); From 3c5493afa42a3413a200a893154ef6a1ace8c700 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 29 May 2024 11:37:00 -0400 Subject: [PATCH 40/53] merge 109 --- .../LenderCommitmentGroup_Smart_Test.sol | 35 +++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol index d7a2e402..300a80f3 100644 --- a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol +++ b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol @@ -946,7 +946,7 @@ function test_liquidateDefaultedLoanWithIncentive_does_not_double_count_repaid() ); - uint256 expectedAmount = 4500; + uint256 expectedAmount = 9000; assertEq( amountCollateral, @@ -974,7 +974,7 @@ function test_liquidateDefaultedLoanWithIncentive_does_not_double_count_repaid() ); - uint256 expectedAmount = 9000; + uint256 expectedAmount = 18000; assertEq( amountCollateral, @@ -1002,7 +1002,36 @@ function test_liquidateDefaultedLoanWithIncentive_does_not_double_count_repaid() ); - uint256 expectedAmount = 1; + uint256 expectedAmount = 9000; + + assertEq( + amountCollateral, + expectedAmount, + "Unexpected getCollateralTokensPricePerPrincipalTokens" + ); + } + + + function test_getCollateralTokensAmountEquivalentToPrincipalTokens_scenarioD() public { + + initialize_group_contract(); + + uint256 principalTokenAmountValue = 9000; + uint256 pairPriceWithTwap = 60000 * 2**96; + uint256 pairPriceImmediate = 2**96; + bool principalTokenIsToken0 = false; + + + uint256 amountCollateral = lenderCommitmentGroupSmart + .super_getCollateralTokensAmountEquivalentToPrincipalTokens( + principalTokenAmountValue, + pairPriceWithTwap, + pairPriceImmediate, + principalTokenIsToken0 + ); + + + uint256 expectedAmount = 9000; assertEq( amountCollateral, From 1bb746510833ace529512d8348b7212db1e6bde4 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 29 May 2024 11:56:21 -0400 Subject: [PATCH 41/53] merge 32 --- .../extensions/FlashRolloverLoan_G5.sol | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/FlashRolloverLoan_G5.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/FlashRolloverLoan_G5.sol index 04507b2c..b6b7cf73 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/FlashRolloverLoan_G5.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/FlashRolloverLoan_G5.sol @@ -115,6 +115,7 @@ contract FlashRolloverLoan_G5 is IFlashLoanSimpleReceiver, IFlashRolloverLoan_G4 ); } + // Call 'Flash' on the vault to borrow funds and call tellerV2FlashCallback // This ultimately calls executeOperation IPool(POOL()).flashLoanSimple( @@ -132,6 +133,14 @@ contract FlashRolloverLoan_G5 is IFlashLoanSimpleReceiver, IFlashRolloverLoan_G4 ), 0 //referral code ); + + + ///have to set approval to zero AFTER we repay the flash loan to aave + IERC20Upgradeable(lendingToken).approve( + address(POOL()), + 0 + ); + } /** @@ -208,11 +217,6 @@ contract FlashRolloverLoan_G5 is IFlashLoanSimpleReceiver, IFlashRolloverLoan_G4 ); } - IERC20Upgradeable(_flashToken).approve( - address(POOL()), - 0 - ); - emit RolloverLoanComplete( _rolloverArgs.borrower, _rolloverArgs.loanId, From 0d1ead33675c9e9f35134ab45736993b83e5052b Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 29 May 2024 12:29:58 -0400 Subject: [PATCH 42/53] special rules for first deposit --- .../LenderCommitmentGroup_Smart.sol | 15 ++++++++++++++- .../LenderCommitmentGroup_Smart_Override.sol | 3 +++ .../LenderCommitmentGroup_Smart_Test.sol | 2 ++ 3 files changed, 19 insertions(+), 1 deletion(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 4b363d99..e3911830 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -114,6 +114,8 @@ contract LenderCommitmentGroup_Smart is // maybe it is possible to get rid of this storage slot and calculate it from totalPrincipalTokensRepaid, totalPrincipalTokensLended int256 tokenDifferenceFromLiquidations; + bool public firstDepositMade; + @@ -169,7 +171,7 @@ contract LenderCommitmentGroup_Smart is ) external initializer returns (address poolSharesToken_) { // require(!_initialized,"already initialized"); // _initialized = true; - + __Ownable_init(); __Pausable_init(); principalToken = IERC20(_principalTokenAddress); @@ -309,6 +311,9 @@ contract LenderCommitmentGroup_Smart is address _sharesRecipient ) external returns (uint256 sharesAmount_) { //transfers the primary principal token from msg.sender into this contract escrow + + + principalToken.transferFrom(msg.sender, address(this), _amount); @@ -319,6 +324,14 @@ contract LenderCommitmentGroup_Smart is //mint shares equal to _amount and give them to the shares recipient !!! poolSharesToken.mint(_sharesRecipient, sharesAmount_); + + + if(!firstDepositMade){ + require(msg.sender == owner(), "Owner must initialize the pool with a deposit first."); + require( sharesAmount_>= 1e6, "Initial shares amount must be atleast 1e6" ); + + firstDepositMade = true; + } } function _valueOfUnderlying(uint256 amount, uint256 rate) diff --git a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Override.sol b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Override.sol index 57894db3..fc73702a 100644 --- a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Override.sol +++ b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Override.sol @@ -98,7 +98,10 @@ contract LenderCommitmentGroup_Smart_Override is LenderCommitmentGroup_Smart { mockMaxPrincipalPerCollateralAmount = amt; } + function mock_setFirstDepositMade(bool made) public { + firstDepositMade = made; + } function sharesExchangeRate() public override view returns (uint256 rate_) { diff --git a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol index 8d468175..59ed3d09 100644 --- a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol +++ b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Test.sol @@ -107,6 +107,8 @@ contract LenderCommitmentGroup_Smart_Test is Testable { _uniswapPoolFee, _twapInterval ); + + lenderCommitmentGroupSmart.mock_setFirstDepositMade(true); } function test_initialize() public { From d000cdb78137e8e9ac734a90c172a9787911e978 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 29 May 2024 12:40:47 -0400 Subject: [PATCH 43/53] fix tests --- .../LenderCommitmentGroup_Smart.sol | 22 +++++++++---------- .../LenderCommitmentGroup_Smart_Override.sol | 4 ++-- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index ab2662a8..32fd613e 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -566,8 +566,9 @@ contract LenderCommitmentGroup_Smart is ) public bidIsActiveForGroup(_bidId) { //use original principal amount as amountDue - (,,,, uint256 amountDue, , , ) - = ITellerV2(TELLER_V2).getLoanSummary(_bidId); + + uint256 amountDue = _getAmountOwedForBid(_bidId); + uint256 loanDefaultedTimeStamp = ITellerV2(TELLER_V2) .getLoanDefaultTimestamp(_bidId); @@ -623,21 +624,20 @@ contract LenderCommitmentGroup_Smart is ); } - function getAmountOwedForBid(uint256 _bidId, bool _includeInterest) - public + + + function _getAmountOwedForBid(uint256 _bidId ) + internal view virtual - returns (uint256 amountOwed_) + returns (uint256 amountDue) { - Payment memory amountOwedPayment = ITellerV2(TELLER_V2) - .calculateAmountOwed(_bidId, block.timestamp); - - amountOwed_ = _includeInterest - ? amountOwedPayment.principal + amountOwedPayment.interest - : amountOwedPayment.principal; + (,,,, amountDue, , , ) + = ITellerV2(TELLER_V2).getLoanSummary(_bidId); } + /* This function will calculate the incentive amount (using a uniswap bonus plus a timer) diff --git a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Override.sol b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Override.sol index 180dd5e0..be2065f1 100644 --- a/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Override.sol +++ b/packages/contracts/tests/LenderCommitmentForwarder/extensions/SmartCommitments/LenderCommitmentGroup_Smart_Override.sol @@ -69,8 +69,8 @@ contract LenderCommitmentGroup_Smart_Override is LenderCommitmentGroup_Smart { return super.getMinimumAmountDifferenceToCloseDefaultedLoan(_amountOwed,_loanDefaultedTimestamp); } - function getAmountOwedForBid(uint256 _bidId, bool _includeInterest) - public override view returns (uint256){ + function _getAmountOwedForBid(uint256 _bidId ) + internal override view returns (uint256){ return mockAmountOwed; } From 14afa3979e389a038c27a1a7eee5826b416c4f31 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 29 May 2024 12:42:54 -0400 Subject: [PATCH 44/53] revert semver check --- packages/contracts/hardhat.config.ts | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/packages/contracts/hardhat.config.ts b/packages/contracts/hardhat.config.ts index 6e139ea8..627ac1f6 100644 --- a/packages/contracts/hardhat.config.ts +++ b/packages/contracts/hardhat.config.ts @@ -37,11 +37,7 @@ import semver from 'semver' // import { logger as tenderlyLogger } from 'tenderly/utils/logger' const NODE_VERSION = 'v16' -const ALT_NODE_VERSION = 'v18' -if (!semver.satisfies(process.version, NODE_VERSION) && !semver.satisfies(process.version, ALT_NODE_VERSION)) - throw new Error( - `Incorrect NodeJS version being used (${process.version}). Expected: ${NODE_VERSION}` - ) +if (!semver.satisfies(process.version, NODE_VERSION)) config() From 7517c8a34c429b58643657c1f90a94ccc9ce3246 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 29 May 2024 12:44:29 -0400 Subject: [PATCH 45/53] fi xhardhat config --- packages/contracts/hardhat.config.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/contracts/hardhat.config.ts b/packages/contracts/hardhat.config.ts index 627ac1f6..e406e4c2 100644 --- a/packages/contracts/hardhat.config.ts +++ b/packages/contracts/hardhat.config.ts @@ -38,6 +38,9 @@ import semver from 'semver' const NODE_VERSION = 'v16' if (!semver.satisfies(process.version, NODE_VERSION)) + throw new Error( + `Incorrect NodeJS version being used (${process.version}). Expected: ${NODE_VERSION}` + ) config() From 8ac0e1037257130c71dc4fbf4829d56c5e343291 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Thu, 13 Jun 2024 14:52:52 -0400 Subject: [PATCH 46/53] remove interst dbl count --- packages/contracts/contracts/libraries/V2Calculations.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/contracts/libraries/V2Calculations.sol b/packages/contracts/contracts/libraries/V2Calculations.sol index 375ea3c1..f913616b 100644 --- a/packages/contracts/contracts/libraries/V2Calculations.sol +++ b/packages/contracts/contracts/libraries/V2Calculations.sol @@ -127,7 +127,7 @@ library V2Calculations { // NOTE: the last cycle could have less than the calculated payment amount //the amount owed for the cycle should never exceed the current payment cycle amount so we use min here - uint256 owedAmountForCycle = Math.min( ((_bid.terms.paymentCycleAmount * owedTime)+interest_ ) / + uint256 owedAmountForCycle = Math.min( ((_bid.terms.paymentCycleAmount * owedTime) ) / _paymentCycleDuration , _bid.terms.paymentCycleAmount+interest_ ) ; uint256 owedAmount = isLastPaymentCycle From 1e4b8efe5f846c4118a29e481a2bb881e9076fa0 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Thu, 13 Jun 2024 14:59:54 -0400 Subject: [PATCH 47/53] trying to add safexfer --- packages/contracts/contracts/escrow/CollateralEscrowV1.sol | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/contracts/contracts/escrow/CollateralEscrowV1.sol b/packages/contracts/contracts/escrow/CollateralEscrowV1.sol index bffa660c..cf3e24be 100644 --- a/packages/contracts/contracts/escrow/CollateralEscrowV1.sol +++ b/packages/contracts/contracts/escrow/CollateralEscrowV1.sol @@ -10,10 +10,12 @@ import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC1155/IERC1155Upgradeable.sol"; import "@openzeppelin/contracts-upgradeable/token/ERC721/IERC721ReceiverUpgradeable.sol"; +import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import "../interfaces/escrow/ICollateralEscrowV1.sol"; contract CollateralEscrowV1 is OwnableUpgradeable, ICollateralEscrowV1 { - uint256 public bidId; + using SafeERC20 for ERC20; + uint256 public bidId; /* Mappings */ mapping(address => Collateral) public collateralBalances; // collateral address -> collateral @@ -117,7 +119,7 @@ contract CollateralEscrowV1 is OwnableUpgradeable, ICollateralEscrowV1 { "Asset not allowed to be withdrawn as dust" ); - IERC20Upgradeable(tokenAddress).transfer(recipient, amount); + IERC20Upgradeable(tokenAddress).safeTransfer(recipient, amount); } From e090f594889307d84d904dd6d93db37e0f0691d4 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Mon, 17 Jun 2024 10:42:50 -0400 Subject: [PATCH 48/53] use GEQ --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index 05c85615..bc3f96af 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -183,7 +183,7 @@ contract LenderCommitmentGroup_Smart is _uniswapPoolFee ); - require(_twapInterval > MIN_TWAP_INTERVAL, "Invalid TWAP Interval"); + require(_twapInterval >= MIN_TWAP_INTERVAL, "Invalid TWAP Interval"); require(UNISWAP_V3_POOL != address(0), "Invalid uniswap pool address"); marketId = _marketId; From 19f79d9982d8cdb4140ef2db32e4c663fa5f5e1f Mon Sep 17 00:00:00 2001 From: Admazzola Date: Mon, 17 Jun 2024 10:57:06 -0400 Subject: [PATCH 49/53] add more checks properly for bid lender --- packages/contracts/contracts/TellerV2.sol | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/contracts/contracts/TellerV2.sol b/packages/contracts/contracts/TellerV2.sol index 44dc1568..5e8ed414 100644 --- a/packages/contracts/contracts/TellerV2.sol +++ b/packages/contracts/contracts/TellerV2.sol @@ -726,7 +726,7 @@ contract TellerV2 is acceptedLoan(_bidId, "lenderClaimCollateral") { Bid storage bid = bids[_bidId]; - address _collateralRecipient = bid.lender; + address _collateralRecipient = getLoanLender(_bidId); _lenderCloseLoanWithRecipient(_bidId, _collateralRecipient); } @@ -752,7 +752,7 @@ contract TellerV2 is bid.state = BidState.CLOSED; address sender = _msgSenderForMarket(bid.marketplaceId); - require(sender == bid.lender, "only lender can close loan"); + require(sender == getLoanLender(_bidId), "only lender can close loan"); /* @@ -1251,7 +1251,7 @@ contract TellerV2 is address sender = _msgSenderForMarket(bids[_bidId].marketplaceId); require( - sender == bids[_bidId].lender, + sender == getLoanLender(_bidId), "Only bid lender may set repayment listener" ); From 752c5fa3dabbfe40c38cabccaf43522fd612aa21 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Mon, 17 Jun 2024 16:18:00 -0400 Subject: [PATCH 50/53] add fix --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index f8bb0b7e..eda6caef 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -420,6 +420,10 @@ contract LenderCommitmentGroup_Smart is require(poolSharesPreparedToWithdrawForLender[msg.sender] >= _amountPoolSharesTokens,"Shares not prepared for withdraw"); require(poolSharesPreparedTimestamp[msg.sender] <= block.timestamp - WITHDRAW_DELAY_TIME_SECONDS,"Shares not prepared for withdraw"); + poolSharesPreparedToWithdrawForLender[msg.sender] = 0; + poolSharesPreparedTimestamp[msg.sender] = 0; + + poolSharesToken.burn(msg.sender, _amountPoolSharesTokens); uint256 principalTokenValueToWithdraw = _valueOfUnderlying( From 8103f6614cdcc05c543641025e75ca96aa194db8 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Tue, 18 Jun 2024 16:03:14 -0400 Subject: [PATCH 51/53] add check for contract codesize --- packages/contracts/contracts/TellerV2.sol | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/packages/contracts/contracts/TellerV2.sol b/packages/contracts/contracts/TellerV2.sol index 2e452d3d..97934506 100644 --- a/packages/contracts/contracts/TellerV2.sol +++ b/packages/contracts/contracts/TellerV2.sol @@ -1244,19 +1244,22 @@ contract TellerV2 is return dueDate + defaultDuration; } - - function setRepaymentListenerForBid(uint256 _bidId, address _listener) - external - { + + function setRepaymentListenerForBid(uint256 _bidId, address _listener) external { + uint256 codeSize; + assembly { + codeSize := extcodesize(_listener) + } + require(codeSize > 0, "Listener must be a contract"); address sender = _msgSenderForMarket(bids[_bidId].marketplaceId); - require( - sender == bids[_bidId].lender, - "Only bid lender may set repayment listener" - ); + require(sender == bids[_bidId].lender, 'Only bid lender may set repayment listener'); repaymentListenerForBid[_bidId] = _listener; - } + } + + + function getRepaymentListenerForBid(uint256 _bidId) external From a82a64cde84e8b24acfe292dd04bf6743b66c098 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 26 Jun 2024 13:36:04 -0400 Subject: [PATCH 52/53] use basic check for claimloannft --- packages/contracts/contracts/TellerV2.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/contracts/contracts/TellerV2.sol b/packages/contracts/contracts/TellerV2.sol index 5e8ed414..5368cb86 100644 --- a/packages/contracts/contracts/TellerV2.sol +++ b/packages/contracts/contracts/TellerV2.sol @@ -584,7 +584,7 @@ contract TellerV2 is Bid storage bid = bids[_bidId]; address sender = _msgSenderForMarket(bid.marketplaceId); - require(sender == getLoanLender(_bidId), "only lender can claim NFT"); + require(sender == bid.lender, "only lender can claim NFT"); // set lender address to the lender manager so we know to check the owner of the NFT for the true lender bid.lender = address(USING_LENDER_MANAGER); From 769f1a3f85cc41dcd10bd681498c7a7f8af13394 Mon Sep 17 00:00:00 2001 From: Admazzola Date: Wed, 26 Jun 2024 13:42:45 -0400 Subject: [PATCH 53/53] fix merge issue --- .../LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol index cdea69e1..72481fbd 100644 --- a/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol +++ b/packages/contracts/contracts/LenderCommitmentForwarder/extensions/LenderCommitmentGroup/LenderCommitmentGroup_Smart.sol @@ -531,11 +531,8 @@ contract LenderCommitmentGroup_Smart is poolSharesPreparedToWithdrawForLender[msg.sender] = 0; poolSharesPreparedTimestamp[msg.sender] = 0; - - - poolSharesToken.burn(msg.sender, _amountPoolSharesTokens); - - + + //this should compute BEFORE shares burn uint256 principalTokenValueToWithdraw = _valueOfUnderlying( _amountPoolSharesTokens,