From e27591f9f436c4be3430f045d2300e8f054fb785 Mon Sep 17 00:00:00 2001 From: Daniel Alcarraz Date: Tue, 10 Jun 2025 01:42:41 -0400 Subject: [PATCH] don't charge external swap fee to refType 3 loans --- .../VaultExternalLiquidationStrategy.sol | 51 +++++++++++++++++++ .../VaultExternalRebalanceStrategy.sol | 36 +++++++++++++ package.json | 2 +- 3 files changed, 88 insertions(+), 1 deletion(-) diff --git a/contracts/strategies/vault/liquidation/VaultExternalLiquidationStrategy.sol b/contracts/strategies/vault/liquidation/VaultExternalLiquidationStrategy.sol index 6b031bb..5737575 100644 --- a/contracts/strategies/vault/liquidation/VaultExternalLiquidationStrategy.sol +++ b/contracts/strategies/vault/liquidation/VaultExternalLiquidationStrategy.sol @@ -41,4 +41,55 @@ contract VaultExternalLiquidationStrategy is CPMMExternalLiquidationStrategy, Va override(BaseRepayStrategy,VaultBaseLiquidationStrategy) returns(uint256 lpTokenPrincipal, uint256 remainingLiquidity) { return super.payLoanLiquidity(liquidity, loanLiquidity, _loan); } + + /// @dev See {IExternalLiquidationStrategy-_liquidateExternally}. + function _liquidateExternally(uint256 tokenId, uint128[] calldata amounts, uint256 lpTokens, address to, bytes calldata data) external override lock beforeLiquidation virtual + returns(uint256, uint128[] memory) { + // Check can liquidate loan and get loan with updated loan liquidity and collateral + // No need to check if msg.sender has permission + LibStorage.Loan storage _loan = _getExistingLoan(tokenId); + + (LiquidatableLoan memory _liqLoan,) = getLiquidatableLoan(_loan, tokenId); + + uint256 liquiditySwapped; + (liquiditySwapped,) = externalSwap(_loan, s.cfmm, amounts, lpTokens, to, data); // of the CFMM LP Tokens that we pulled out, more have to come back + + for(uint256 i = 0; i < _liqLoan.tokensHeld.length;) { + if(_liqLoan.tokensHeld[i] > _loan.tokensHeld[i]) revert CollateralShortfall(); + unchecked { + ++i; + } + } + + uint256 loanLiquidity = _liqLoan.loanLiquidity; + if(liquiditySwapped > loanLiquidity && _loan.refType != 3) { + uint256 swapFee = calcExternalSwapFee(liquiditySwapped, loanLiquidity) / 2; + uint256 borrowedInvariant = s.BORROWED_INVARIANT + swapFee; + s.LP_TOKEN_BORROWED_PLUS_INTEREST = convertInvariantToLPRoundUp(borrowedInvariant, s.lastCFMMTotalSupply, s.lastCFMMInvariant); + s.BORROWED_INVARIANT = uint128(borrowedInvariant); + loanLiquidity += swapFee; + _loan.liquidity = uint128(loanLiquidity); + _liqLoan.payableInternalLiquidity += swapFee; + _liqLoan.loanLiquidity = loanLiquidity; + } + + _liqLoan.writeDownAmt = payLiquidatableLoan(_liqLoan, 0, true); + + uint128[] memory refund; + (refund, _liqLoan.tokensHeld) = refundLiquidator(_liqLoan.payableInternalLiquidityPlusFee, _liqLoan.internalCollateral, _liqLoan.tokensHeld); + + _loan.tokensHeld = _liqLoan.tokensHeld; // Clear loan collateral + + onLoanUpdate(_loan, tokenId); + + emit ExternalSwap(tokenId, amounts, lpTokens, uint128(liquiditySwapped), TX_TYPE.EXTERNAL_LIQUIDATION); + + emit Liquidation(tokenId, uint128(_liqLoan.collateral), uint128(loanLiquidity - _liqLoan.writeDownAmt), uint128(_liqLoan.writeDownAmt), uint128(_liqLoan.internalFee), TX_TYPE.EXTERNAL_LIQUIDATION); + + emit LoanUpdated(tokenId, _liqLoan.tokensHeld, 0, 0, 0, 0, TX_TYPE.EXTERNAL_LIQUIDATION); + + emit PoolUpdated(s.LP_TOKEN_BALANCE, s.LP_TOKEN_BORROWED, s.LAST_BLOCK_NUMBER, s.accFeeIndex, s.LP_TOKEN_BORROWED_PLUS_INTEREST, s.LP_INVARIANT, s.BORROWED_INVARIANT, s.CFMM_RESERVES, TX_TYPE.EXTERNAL_LIQUIDATION); + + return(loanLiquidity, refund); + } } diff --git a/contracts/strategies/vault/rebalance/VaultExternalRebalanceStrategy.sol b/contracts/strategies/vault/rebalance/VaultExternalRebalanceStrategy.sol index fc82bfc..4e33939 100644 --- a/contracts/strategies/vault/rebalance/VaultExternalRebalanceStrategy.sol +++ b/contracts/strategies/vault/rebalance/VaultExternalRebalanceStrategy.sol @@ -73,4 +73,40 @@ contract VaultExternalRebalanceStrategy is VaultBaseLongStrategy, ExternalRebala return lpTokens; } + + /// @dev See {IExternalLongStrategy-_rebalanceExternally}. + function _rebalanceExternally(uint256 tokenId, uint128[] calldata amounts, uint256 lpTokens, address to, bytes calldata data) external override lock virtual returns(uint256 loanLiquidity, uint128[] memory tokensHeld) { + // Get loan for tokenId, revert if not loan creator + LibStorage.Loan storage _loan = _getLoan(tokenId); + + // Update liquidity debt to include accrued interest since last update + loanLiquidity = updateLoan(_loan); + + address _cfmm = s.cfmm; + + uint256 liquiditySwapped; + // Calculate amounts to swap from deltas and available loan collateral + (liquiditySwapped, tokensHeld) = externalSwap(_loan, _cfmm, amounts, lpTokens, to, data); + _loan.tokensHeld = tokensHeld; + + if(liquiditySwapped > loanLiquidity && _loan.refType != 3) { + uint256 fee = calcExternalSwapFee(liquiditySwapped, loanLiquidity); + uint256 borrowedInvariant = s.BORROWED_INVARIANT + fee; + s.LP_TOKEN_BORROWED_PLUS_INTEREST = convertInvariantToLP(borrowedInvariant, s.lastCFMMTotalSupply, s.lastCFMMInvariant); + s.BORROWED_INVARIANT = uint128(borrowedInvariant); + loanLiquidity = loanLiquidity + fee; + _loan.liquidity = uint128(loanLiquidity); + } + + // Check that loan is not undercollateralized after external swap + uint256 collateral = calcInvariant(_cfmm, tokensHeld) + onLoanUpdate(_loan, tokenId); + checkMargin(collateral, loanLiquidity); + + emit ExternalSwap(tokenId, amounts, lpTokens, uint128(liquiditySwapped), TX_TYPE.EXTERNAL_REBALANCE); + + emit LoanUpdated(tokenId, tokensHeld, uint128(loanLiquidity), _loan.initLiquidity, _loan.lpTokens, _loan.rateIndex, TX_TYPE.EXTERNAL_REBALANCE); + + emit PoolUpdated(s.LP_TOKEN_BALANCE, s.LP_TOKEN_BORROWED, s.LAST_BLOCK_NUMBER, s.accFeeIndex, + s.LP_TOKEN_BORROWED_PLUS_INTEREST, s.LP_INVARIANT, s.BORROWED_INVARIANT, s.CFMM_RESERVES, TX_TYPE.EXTERNAL_REBALANCE); + } } diff --git a/package.json b/package.json index 6b56fcf..a423b6c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@gammaswap/v1-implementations", - "version": "1.2.17", + "version": "1.2.18", "description": "Pool and strategies implementation contracts for GammaSwap V1 protocol", "homepage": "https://gammaswap.com", "scripts": {