diff --git a/script/SetParameters.s.sol b/script/SetParameters.s.sol index bcc6bfa..25b718b 100644 --- a/script/SetParameters.s.sol +++ b/script/SetParameters.s.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.20; -import {ITroveManager} from "../src/core/interfaces/ITroveManager.sol"; +import { ITroveManager } from "../src/core/interfaces/ITroveManager.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { Script, console2 } from "forge-std/Script.sol"; @@ -14,7 +14,7 @@ uint256 constant MAX_BORROWING_FEE = 1e18 / 100 * 5; // (5%) uint256 constant INTEREST_RATE_IN_BPS = 0; // (0%) uint256 constant MAX_DEBT = 1e18 * 1_000_000_000; uint256 constant MCR = 170 * 1e16; // 110 * 1e16 -> 110% -uint128 constant REWARD_RATE = 0; +uint128 constant REWARD_RATE = 0; uint32 constant TM_CLAIM_START_TIME = 4_294_967_295; // max uint32 contract SetParametersScript is Script { diff --git a/script/priceFeed/DeployPriceFeedChainlink.s.sol b/script/priceFeed/DeployPriceFeedChainlink.s.sol index a9cc1d3..f798ef8 100644 --- a/script/priceFeed/DeployPriceFeedChainlink.s.sol +++ b/script/priceFeed/DeployPriceFeedChainlink.s.sol @@ -7,7 +7,7 @@ import { Script, console2 } from "forge-std/Script.sol"; //! Change when deploying address constant CHAINLINK_PRICE_FEED_SOURCE_ADDRESS = 0x31a36CdF4465ba61ce78F5CDbA26FDF8ec361803; -uint256 constant CHAINLINK_MAX_TIME_THRESHOLD = 21600 + 300; +uint256 constant CHAINLINK_MAX_TIME_THRESHOLD = 21_600 + 300; contract DeployPriceFeedChainlinkScript is Script { PriceFeedChainlink internal priceFeedChainlink; diff --git a/src/core/helpers/SatoshiPeriphery.sol b/src/core/helpers/SatoshiPeriphery.sol index aaf51ca..1af78f0 100644 --- a/src/core/helpers/SatoshiPeriphery.sol +++ b/src/core/helpers/SatoshiPeriphery.sol @@ -9,8 +9,11 @@ import { DebtTokenWithLz } from "../DebtTokenWithLz.sol"; import { IBorrowerOperationsFacet } from "../interfaces/IBorrowerOperationsFacet.sol"; import { ICoreFacet } from "../interfaces/ICoreFacet.sol"; import { ILiquidationFacet } from "../interfaces/ILiquidationFacet.sol"; + +import { IPriceFeedAggregatorFacet } from "../interfaces/IPriceFeedAggregatorFacet.sol"; import { ITroveManager } from "../interfaces/ITroveManager.sol"; +import { ISupraOraclePull } from "../../priceFeed/interfaces/ISupraOraclePull.sol"; import { IDebtToken } from "../interfaces/IDebtToken.sol"; import { ISatoshiPeriphery, LzSendParam } from "./interfaces/ISatoshiPeriphery.sol"; import { IWETH } from "./interfaces/IWETH.sol"; @@ -93,6 +96,38 @@ contract SatoshiPeriphery is ISatoshiPeriphery, UUPSUpgradeable, OwnableUpgradea _afterWithdrawDebt(userDebtAmount, _lzSendParam); } + function openTroveWithSupraPriceUpdate( + ITroveManager troveManager, + uint256 _maxFeePercentage, + uint256 _collAmount, + uint256 _debtAmount, + address _upperHint, + address _lowerHint, + LzSendParam calldata _lzSendParam, + bytes calldata _bytesProof + ) + external + payable + { + IERC20 collateralToken = troveManager.collateralToken(); + + _updateSupraPriceFeed(troveManager, _bytesProof); + + _beforeAddColl(collateralToken, _collAmount); + + uint256 debtTokenBalanceBefore = debtToken.balanceOf(address(this)); + + IBorrowerOperationsFacet(xApp).openTrove( + troveManager, msg.sender, _maxFeePercentage, _collAmount, _debtAmount, _upperHint, _lowerHint + ); + + uint256 debtTokenBalanceAfter = debtToken.balanceOf(address(this)); + uint256 userDebtAmount = debtTokenBalanceAfter - debtTokenBalanceBefore; + require(userDebtAmount == _debtAmount, "SatoshiPeriphery: Debt amount mismatch"); + + _afterWithdrawDebt(userDebtAmount, _lzSendParam); + } + /// @notice Add collateral to a active trove /// @param troveManager The TroveManager contract /// @param _collAmount The amount of additional collateral @@ -113,6 +148,24 @@ contract SatoshiPeriphery is ISatoshiPeriphery, UUPSUpgradeable, OwnableUpgradea IBorrowerOperationsFacet(xApp).addColl(troveManager, msg.sender, _collAmount, _upperHint, _lowerHint); } + function addCollWithSupraPriceUpdate( + ITroveManager troveManager, + uint256 _collAmount, + address _upperHint, + address _lowerHint, + bytes calldata _bytesProof + ) + external + { + IERC20 collateralToken = troveManager.collateralToken(); + + _updateSupraPriceFeed(troveManager, _bytesProof); + + _beforeAddColl(collateralToken, _collAmount); + + IBorrowerOperationsFacet(xApp).addColl(troveManager, msg.sender, _collAmount, _upperHint, _lowerHint); + } + /// @notice Withdraws _collWithdrawal of collateral from the caller’s Trove /// @dev Executes only if the user has an active Trove, /// @dev Withdrawal would not pull the user’s Trove below the MCR, and the resulting total collateralization ratio of the system is above 150%(TCR) @@ -139,6 +192,28 @@ contract SatoshiPeriphery is ISatoshiPeriphery, UUPSUpgradeable, OwnableUpgradea _afterWithdrawColl(collateralToken, userCollAmount); } + function withdrawColl( + ITroveManager troveManager, + uint256 _collWithdrawal, + address _upperHint, + address _lowerHint, + bytes calldata _bytesProof + ) + external + { + IERC20 collateralToken = troveManager.collateralToken(); + uint256 collTokenBalanceBefore = collateralToken.balanceOf(address(this)); + + _updateSupraPriceFeed(troveManager, _bytesProof); + + IBorrowerOperationsFacet(xApp).withdrawColl(troveManager, msg.sender, _collWithdrawal, _upperHint, _lowerHint); + + uint256 collTokenBalanceAfter = collateralToken.balanceOf(address(this)); + uint256 userCollAmount = collTokenBalanceAfter - collTokenBalanceBefore; + require(userCollAmount == _collWithdrawal, "SatoshiPeriphery: Collateral amount mismatch"); + _afterWithdrawColl(collateralToken, userCollAmount); + } + /// @notice Issues _debtAmount of Debt token from the caller’s Trove to the caller. /// @dev Executes only if the Trove's collateralization ratio would remain above the minimum /// @param troveManager The TroveManager contract @@ -168,6 +243,32 @@ contract SatoshiPeriphery is ISatoshiPeriphery, UUPSUpgradeable, OwnableUpgradea _afterWithdrawDebt(userDebtAmount, _lzSendParam); } + function withdrawDebtWithSupraPriceUpdate( + ITroveManager troveManager, + uint256 _maxFeePercentage, + uint256 _debtAmount, + address _upperHint, + address _lowerHint, + LzSendParam calldata _lzSendParam, + bytes calldata _bytesProof + ) + external + payable + { + uint256 debtTokenBalanceBefore = debtToken.balanceOf(address(this)); + + _updateSupraPriceFeed(troveManager, _bytesProof); + + IBorrowerOperationsFacet(xApp).withdrawDebt( + troveManager, msg.sender, _maxFeePercentage, _debtAmount, _upperHint, _lowerHint + ); + uint256 debtTokenBalanceAfter = debtToken.balanceOf(address(this)); + uint256 userDebtAmount = debtTokenBalanceAfter - debtTokenBalanceBefore; + require(userDebtAmount == _debtAmount, "SatoshiPeriphery: Debt amount mismatch"); + + _afterWithdrawDebt(userDebtAmount, _lzSendParam); + } + /// @notice Repays _debtAmount of Debt token to the caller’s Trove /// @param troveManager The TroveManager contract /// @param _debtAmount The amount of debt to repay @@ -179,13 +280,26 @@ contract SatoshiPeriphery is ISatoshiPeriphery, UUPSUpgradeable, OwnableUpgradea address _upperHint, address _lowerHint ) - external + public { _beforeRepayDebt(_debtAmount); IBorrowerOperationsFacet(xApp).repayDebt(troveManager, msg.sender, _debtAmount, _upperHint, _lowerHint); } + function repayDebtWithSupraPriceUpdate( + ITroveManager troveManager, + uint256 _debtAmount, + address _upperHint, + address _lowerHint, + bytes calldata _bytesProof + ) + external + { + _updateSupraPriceFeed(troveManager, _bytesProof); + repayDebt(troveManager, _debtAmount, _upperHint, _lowerHint); + } + /// @notice Enables a borrower to simultaneously change (decrease/increase) both their collateral and debt, function adjustTrove( ITroveManager troveManager, @@ -239,6 +353,60 @@ contract SatoshiPeriphery is ISatoshiPeriphery, UUPSUpgradeable, OwnableUpgradea } } + function adjustTroveWithSupraPriceUpdate( + ITroveManager troveManager, + uint256 _maxFeePercentage, + uint256 _collDeposit, + uint256 _collWithdrawal, + uint256 _debtChange, + bool _isDebtIncrease, + address _upperHint, + address _lowerHint, + LzSendParam calldata _lzSendParam, + bytes calldata _bytesProof + ) + external + payable + { + if (_collDeposit != 0 && _collWithdrawal != 0) revert CannotWithdrawAndAddColl(); + + IERC20 collateralToken = troveManager.collateralToken(); + _updateSupraPriceFeed(troveManager, _bytesProof); + _beforeAddColl(collateralToken, _collDeposit); + + uint256 debtTokenBalanceBefore = debtToken.balanceOf(address(this)); + + // repay debt + if (!_isDebtIncrease) { + _beforeRepayDebt(_debtChange); + } + + IBorrowerOperationsFacet(xApp).adjustTrove( + troveManager, + msg.sender, + _maxFeePercentage, + _collDeposit, + _collWithdrawal, + _debtChange, + _isDebtIncrease, + _upperHint, + _lowerHint + ); + + uint256 debtTokenBalanceAfter = debtToken.balanceOf(address(this)); + // withdraw collateral + _afterWithdrawColl(collateralToken, _collWithdrawal); + + // withdraw debt + if (_isDebtIncrease) { + require( + debtTokenBalanceAfter - debtTokenBalanceBefore == _debtChange, "SatoshiPeriphery: Debt amount mismatch" + ); + + _afterWithdrawDebt(_debtChange, _lzSendParam); + } + } + /// @notice Close the trove from the caller /// @param troveManager The TroveManager contract function closeTrove(ITroveManager troveManager) external { @@ -257,6 +425,23 @@ contract SatoshiPeriphery is ISatoshiPeriphery, UUPSUpgradeable, OwnableUpgradea _afterWithdrawColl(collateralToken, userCollAmount); } + function closeTroveWithSupraPriceUpdate(ITroveManager troveManager, bytes calldata _bytesProof) external { + (uint256 collAmount, uint256 debtAmount) = troveManager.getTroveCollAndDebt(msg.sender); + uint256 netDebtAmount = debtAmount - ICoreFacet(xApp).gasCompensation(); + _updateSupraPriceFeed(troveManager, _bytesProof); + _beforeRepayDebt(netDebtAmount); + + IERC20 collateralToken = troveManager.collateralToken(); + uint256 collTokenBalanceBefore = collateralToken.balanceOf(address(this)); + + IBorrowerOperationsFacet(xApp).closeTrove(troveManager, msg.sender); + + uint256 collTokenBalanceAfter = collateralToken.balanceOf(address(this)); + uint256 userCollAmount = collTokenBalanceAfter - collTokenBalanceBefore; + require(userCollAmount == collAmount, "SatoshiPeriphery: Collateral amount mismatch"); + _afterWithdrawColl(collateralToken, userCollAmount); + } + function liquidateTroves( ITroveManager troveManager, uint256 maxTrovesToLiquidate, @@ -281,6 +466,33 @@ contract SatoshiPeriphery is ISatoshiPeriphery, UUPSUpgradeable, OwnableUpgradea _afterWithdrawColl(troveManager.collateralToken(), userCollAmount); } + function liquidateTrovesWithSupraPriceUpdate( + ITroveManager troveManager, + uint256 maxTrovesToLiquidate, + uint256 maxICR, + LzSendParam calldata _lzSendParam, + bytes calldata _bytesProof + ) + external + payable + { + uint256 debtTokenBalanceBefore = debtToken.balanceOf(address(this)); + uint256 collTokenBalanceBefore = troveManager.collateralToken().balanceOf(address(this)); + + _updateSupraPriceFeed(troveManager, _bytesProof); + + ILiquidationFacet(xApp).liquidateTroves(troveManager, maxTrovesToLiquidate, maxICR); + + uint256 debtTokenBalanceAfter = debtToken.balanceOf(address(this)); + uint256 collTokenBalanceAfter = troveManager.collateralToken().balanceOf(address(this)); + + uint256 userDebtAmount = debtTokenBalanceAfter - debtTokenBalanceBefore; + uint256 userCollAmount = collTokenBalanceAfter - collTokenBalanceBefore; + + _afterWithdrawDebt(userDebtAmount, _lzSendParam); + _afterWithdrawColl(troveManager.collateralToken(), userCollAmount); + } + // INTERNAL FUNCTIONS // /// @dev Only support ERC20 token, not support native token @@ -342,6 +554,12 @@ contract SatoshiPeriphery is ISatoshiPeriphery, UUPSUpgradeable, OwnableUpgradea debtToken.safeTransferFrom(msg.sender, address(this), debtAmount); } + function _updateSupraPriceFeed(ITroveManager troveManager, bytes calldata _bytesProof) internal { + (IPriceFeed priceFeed,) = IPriceFeedAggregatorFacet(xApp).oracleRecords(troveManager.collateralToken()); + ISupraOraclePull supra_pull = ISupraOraclePull(priceFeed.source()); + supra_pull.verifyOracleProof(_bytesProof); + } + /// @notice Override the _authorizeUpgrade function inherited from UUPSUpgradeable contract // solhint-disable-next-line no-empty-blocks function _authorizeUpgrade(address newImplementation) internal view override onlyOwner {