From daa3fc2216d8e964407900dde7b1fb308fcf0ec6 Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Wed, 18 Dec 2024 16:55:12 +0530 Subject: [PATCH 1/4] feat: simple flash loan contracts --- .../base/FlashloanSimpleReceiverBase.sol | 24 +++++++++++++++++++ .../interfaces/IFlashloanSimpleReceiver.sol | 23 ++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 contracts/Flashloan/base/FlashloanSimpleReceiverBase.sol create mode 100644 contracts/Flashloan/interfaces/IFlashloanSimpleReceiver.sol diff --git a/contracts/Flashloan/base/FlashloanSimpleReceiverBase.sol b/contracts/Flashloan/base/FlashloanSimpleReceiverBase.sol new file mode 100644 index 000000000..0ba5c8746 --- /dev/null +++ b/contracts/Flashloan/base/FlashloanSimpleReceiverBase.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity 0.8.25; + +import { IFlashloanSimpleReceiver } from "../interfaces/IFlashloanSimpleReceiver.sol"; +import { VTokenInterface } from "../../VTokenInterfaces.sol"; + +/** + * @title FlashloanSimpleReceiverBase + * @author Venus + * @notice Base contract to develop a flashloan-receiver contract. + * @dev This contract serves as a foundational contract for implementing custom flash loan receiver logic. + * Inheritors of this contract need to implement the `executeOperation` function defined in the `IFlashloanSimpleReceiver` interface. + */ +abstract contract FlashloanSimpleReceiverBase is IFlashloanSimpleReceiver { + /// @notice The VToken contract used to initiate and handle flash loan + /// @dev This is an immutable reference to the VTokenInterface, which enables the flash loan functionality. + VTokenInterface public immutable VTOKEN; + + /// @notice Initializes the base contract by setting the VToken address + /// @param vToken_ The address of the VToken contract that supports flash loan + constructor(VTokenInterface vToken_) { + VTOKEN = vToken_; + } +} diff --git a/contracts/Flashloan/interfaces/IFlashloanSimpleReceiver.sol b/contracts/Flashloan/interfaces/IFlashloanSimpleReceiver.sol new file mode 100644 index 000000000..21c1be1ad --- /dev/null +++ b/contracts/Flashloan/interfaces/IFlashloanSimpleReceiver.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity ^0.8.25; + +interface IFlashloanSimpleReceiver { + /** + * @notice Executes an operation after receiving the flash-borrowed asset + * @dev Ensure that the contract can return the debt + premium, e.g., has + * enough funds to repay and has to transfer the debt + premium to the VToken + * @param asset The address of the flash-borrowed asset + * @param amount The amount of the flash-borrowed asset + * @param premium The fee on flash-borrowed asset + * @param initiator The address of the flashloan initiator + * @param param The byte-encoded param passed when initiating the flashloan + * @return True if the execution of the operation succeeds, false otherwise + */ + function executeOperation( + address asset, + uint256 amount, + uint256 premium, + address initiator, + bytes calldata param + ) external returns (bool); +} From 32bd0be78163b8771b23c5c8b11a2c5f9e177237 Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Wed, 18 Dec 2024 16:55:47 +0530 Subject: [PATCH 2/4] feat: add simple flash loan functionality --- contracts/VToken.sol | 112 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 3 deletions(-) diff --git a/contracts/VToken.sol b/contracts/VToken.sol index 0e12b02f7..34a846b26 100644 --- a/contracts/VToken.sol +++ b/contracts/VToken.sol @@ -14,6 +14,7 @@ import { InterestRateModel } from "./InterestRateModel.sol"; import { ExponentialNoError } from "./ExponentialNoError.sol"; import { TimeManagerV8 } from "@venusprotocol/solidity-utilities/contracts/TimeManagerV8.sol"; import { ensureNonzeroAddress } from "./lib/validators.sol"; +import { IFlashloanSimpleReceiver } from "./Flashloan/interfaces/IFlashloanSimpleReceiver.sol"; /** * @title VToken @@ -106,6 +107,7 @@ contract VToken is * @param accessControlManager_ AccessControlManager contract address * @param riskManagement Addresses of risk & income related contracts * @param reserveFactorMantissa_ Percentage of borrow interest that goes to reserves (from 0 to 1e18) + * @param flashLoanEnabled_ Enable flashloan or not for this market * @custom:error ZeroAddressNotAllowed is thrown when admin address is zero * @custom:error ZeroAddressNotAllowed is thrown when shortfall contract address is zero * @custom:error ZeroAddressNotAllowed is thrown when protocol share reserve address is zero @@ -121,7 +123,9 @@ contract VToken is address admin_, address accessControlManager_, RiskManagementInit memory riskManagement, - uint256 reserveFactorMantissa_ + uint256 reserveFactorMantissa_, + bool flashLoanEnabled_, + uint256 flashloanFeeMantissa_ ) external initializer { ensureNonzeroAddress(admin_); @@ -137,7 +141,9 @@ contract VToken is admin_, accessControlManager_, riskManagement, - reserveFactorMantissa_ + reserveFactorMantissa_, + flashLoanEnabled_, + flashloanFeeMantissa_ ); } @@ -697,6 +703,101 @@ contract VToken is emit SweepToken(address(token)); } + /** + * @notice Transfers the underlying asset to the specified address. + * @dev Can only be called by the Comptroller contract. This function performs the actual transfer of the underlying + * asset by calling the `_doTransferOut` internal function. + * @param to The address to which the underlying asset is to be transferred. + * @param amount The amount of the underlying asset to transfer. + * @custom:requirements + * - The caller must be the Comptroller contract. + * @custom:reverts + * - Reverts with "Only Comptroller" if the caller is not the Comptroller. + * @custom:event Emits FlashloanAmountTransferred event on successful transfer of amount to receiver + */ + function transferUnderlying(address to, uint256 amount) external override nonReentrant { + if (msg.sender != address(comptroller)) { + revert InvalidComptroller(address(comptroller)); + } + + _doTransferOut(to, amount); + + emit FlashloanAmountTransferred(underlying, to, amount); + } + + /** + * @notice Executes a flashloan operation. + * @dev Transfers the amount to the receiver contract and ensures that the total repayment (amount + fee) + * is returned by the receiver contract after the operation. The function performs checks to ensure the validity + * of parameters, that flashloan is enabled for the given asset, and that the total repayment is sufficient. + * Reverts on invalid parameters, disabled flashloans, or insufficient repayment. + * @param receiver The address of the contract that will receive the flashloan and execute the operation. + * @param amount The amount of asset to be loaned. + * @custom:requirements + * - The `receiver` address must not be the zero address. + * - Flashloans must be enabled for the asset. + * - The `receiver` contract must repay the loan with the appropriate fee. + * @custom:reverts + * - Reverts with `FlashLoanNotEnabled(asset)` if flashloans are disabled for any of the requested assets. + * - Reverts with `ExecuteFlashloanFailed` if the receiver contract fails to execute the operation. + * - Reverts with `InsufficientReypaymentBalance(asset)` if the repayment (amount + fee) is insufficient after the operation. + * @custom:event Emits FlashloanExecuted event on success + */ + function executeFlashloan(address receiver, uint256 amount) external override nonReentrant returns (uint256) { + if (!isFlashloanEnabled) revert FlashLoanNotEnabled(address(this)); + ensureNonzeroAddress(receiver); + + IFlashloanSimpleReceiver receiverContract = IFlashloanSimpleReceiver(receiver); + + uint256 fee = (amount * flashloanFeeMantissa) / MANTISSA_ONE; + uint256 totalRepayment = amount + fee; + + // Transfer the underlying asset to the receiver. + _doTransferOut(receiver, amount); + + uint256 balanceBefore = _getCashPrior(); + + // Call the execute operation on receiver contract + if (!receiverContract.executeOperation(underlying, amount, fee, msg.sender, "")) { + revert ExecuteFlashloanFailed(); + } + + uint256 balanceAfter = _getCashPrior(); + + // balanceAfter should be greater than the fee calculated + if ((balanceAfter - balanceBefore) < totalRepayment) { + revert InsufficientReypaymentBalance(underlying); + } + + emit FlashloanExecuted(receiver, underlying, amount); + + return NO_ERROR; + } + + /** + * @notice Enable or disable flash loan for the market + * @custom:access Only Governance + * @custom:event Emits ToggleFlashloanEnabled event on success + */ + function toggleFlashloan() external override { + _checkAccessAllowed("toggleFlashloan()"); + isFlashloanEnabled = !isFlashloanEnabled; + + emit ToggleFlashloanEnabled(!isFlashloanEnabled, isFlashloanEnabled); + } + + /** + * @notice Update flashloan fee mantissa + * @custom:access Only Governance + * @custom:event Emits FLashloanFeeUpdated event on success + */ + function setFlashloanFeeMantissa(uint256 fee) external override { + _checkAccessAllowed("setFlashloanFeeMantissa(uint256)"); + + emit FLashloanFeeUpdated(flashloanFeeMantissa, fee); + flashloanFeeMantissa = fee; + } + /** * @notice A public function to set new threshold of slot(block or second) difference after which funds will be sent to the protocol share reserve * @param _newReduceReservesBlockOrTimestampDelta slot(block or second) difference value @@ -1536,6 +1637,7 @@ contract VToken is * @param accessControlManager_ AccessControlManager contract address * @param riskManagement Addresses of risk & income related contracts * @param reserveFactorMantissa_ Percentage of borrow interest that goes to reserves (from 0 to 1e18) + * @param flashLoanEnabled_ Enable flashloan or not for this market */ function _initialize( address underlying_, @@ -1548,7 +1650,9 @@ contract VToken is address admin_, address accessControlManager_, RiskManagementInit memory riskManagement, - uint256 reserveFactorMantissa_ + uint256 reserveFactorMantissa_, + bool flashLoanEnabled_, + uint256 flashloanFeeMantissa_ ) internal onlyInitializing { __Ownable2Step_init(); __AccessControlled_init_unchained(accessControlManager_); @@ -1575,6 +1679,8 @@ contract VToken is _setShortfallContract(riskManagement.shortfall); _setProtocolShareReserve(riskManagement.protocolShareReserve); protocolSeizeShareMantissa = DEFAULT_PROTOCOL_SEIZE_SHARE_MANTISSA; + isFlashloanEnabled = flashLoanEnabled_; + flashloanFeeMantissa = flashloanFeeMantissa_; // Set underlying and sanity check it underlying = underlying_; From a0c30a6b81117a8ddb053ce46b5af01fec38c817 Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Wed, 18 Dec 2024 17:44:12 +0530 Subject: [PATCH 3/4] feat: add flash loan contracts --- .../Flashloan/base/FlashloanReceiverBase.sol | 23 +++++++++++++++ .../interfaces/IFlashloanReceiver.sol | 28 +++++++++++++++++++ .../interfaces/IFlashloanSimpleReceiver.sol | 4 +-- 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 contracts/Flashloan/base/FlashloanReceiverBase.sol create mode 100644 contracts/Flashloan/interfaces/IFlashloanReceiver.sol diff --git a/contracts/Flashloan/base/FlashloanReceiverBase.sol b/contracts/Flashloan/base/FlashloanReceiverBase.sol new file mode 100644 index 000000000..b38a13819 --- /dev/null +++ b/contracts/Flashloan/base/FlashloanReceiverBase.sol @@ -0,0 +1,23 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity 0.8.25; + +import { IFlashloanReceiver } from "../interfaces/IFlashloanReceiver.sol"; +import { ComptrollerInterface } from "../../ComptrollerInterface.sol"; + +/// @title FlashloanReceiverBase +/// @notice A base contract for implementing flashloan receiver logic. +/// @dev This abstract contract provides the necessary structure for inheriting contracts to implement the `IFlashloanReceiver` interface. +/// It stores a reference to the Comptroller contract, which manages various aspects of the protocol. +abstract contract FlashloanReceiverBase is IFlashloanReceiver { + /// @notice The Comptroller contract that governs the protocol. + /// @dev This immutable variable stores the address of the Comptroller contract, which cannot be changed after deployment. + ComptrollerInterface public immutable COMPTROLLER; + + /** + * @notice Constructor to initialize the base contract with the Comptroller address. + * @param comptroller_ The address of the Comptroller contract that oversees the protocol. + */ + constructor(ComptrollerInterface comptroller_) { + COMPTROLLER = comptroller_; + } +} diff --git a/contracts/Flashloan/interfaces/IFlashloanReceiver.sol b/contracts/Flashloan/interfaces/IFlashloanReceiver.sol new file mode 100644 index 000000000..7c2942ccd --- /dev/null +++ b/contracts/Flashloan/interfaces/IFlashloanReceiver.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity ^0.8.25; + +import { VTokenInterface } from "../../VTokenInterfaces.sol"; + +/// @title IFlashloanReceiver +/// @notice Interface for flashloan receiver contract, which execute custom logic with flash-borrowed assets. +/// @dev This interface defines the method that must be implemented by any contract wishing to interact with the flashloan system. +/// Contracts must ensure they have the means to repay both the flashloaned amount and the associated premium (fee). +interface IFlashloanReceiver { + /** + * @notice Executes an operation after receiving the flash-borrowed assets. + * @dev Implementation of this function must ensure the borrowed amount plus the premium (fee) is repaid within the same transaction. + * @param assets The addresses of the assets that were flash-borrowed. + * @param amounts The amounts of each of the flash-borrowed assets. + * @param premiums The premiums (fees) associated with each flash-borrowed asset. + * @param initiator The address that initiated the flashloan operation. + * @param param Additional parameters encoded as bytes. These can be used to pass custom data to the receiver contract. + * @return True if the operation succeeds and the borrowed amount plus the premium is repaid, false otherwise. + */ + function executeOperation( + VTokenInterface[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata premiums, + address initiator, + bytes calldata param + ) external returns (bool); +} diff --git a/contracts/Flashloan/interfaces/IFlashloanSimpleReceiver.sol b/contracts/Flashloan/interfaces/IFlashloanSimpleReceiver.sol index 21c1be1ad..52d0d190a 100644 --- a/contracts/Flashloan/interfaces/IFlashloanSimpleReceiver.sol +++ b/contracts/Flashloan/interfaces/IFlashloanSimpleReceiver.sol @@ -8,8 +8,8 @@ interface IFlashloanSimpleReceiver { * enough funds to repay and has to transfer the debt + premium to the VToken * @param asset The address of the flash-borrowed asset * @param amount The amount of the flash-borrowed asset - * @param premium The fee on flash-borrowed asset - * @param initiator The address of the flashloan initiator + * @param premium The premium (fee) associated with flash-borrowed asset. + * @param initiator The address that initiated the flashloan operation * @param param The byte-encoded param passed when initiating the flashloan * @return True if the execution of the operation succeeds, false otherwise */ From eb28e0d4b32f09a4e31b2a0ebb679092d69d4a8a Mon Sep 17 00:00:00 2001 From: Debugger022 Date: Wed, 18 Dec 2024 17:46:59 +0530 Subject: [PATCH 4/4] feat: add flash loan functionality for multiple assets --- contracts/Comptroller.sol | 95 +++++++++++++++++++++++++++++- contracts/ComptrollerInterface.sol | 3 + contracts/ErrorReporter.sol | 5 ++ contracts/VTokenInterfaces.sol | 40 ++++++++++++- contracts/test/UpgradedVToken.sol | 8 ++- hardhat.config.ts | 1 + 6 files changed, 148 insertions(+), 4 deletions(-) diff --git a/contracts/Comptroller.sol b/contracts/Comptroller.sol index 8d25f5ad4..5a96cb6da 100644 --- a/contracts/Comptroller.sol +++ b/contracts/Comptroller.sol @@ -6,13 +6,14 @@ import { ResilientOracleInterface } from "@venusprotocol/oracle/contracts/interf import { AccessControlledV8 } from "@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol"; import { IPrime } from "@venusprotocol/venus-protocol/contracts/Tokens/Prime/Interfaces/IPrime.sol"; -import { ComptrollerInterface, Action } from "./ComptrollerInterface.sol"; +import { ComptrollerInterface, VTokenInterface, Action } from "./ComptrollerInterface.sol"; import { ComptrollerStorage } from "./ComptrollerStorage.sol"; import { ExponentialNoError } from "./ExponentialNoError.sol"; import { VToken } from "./VToken.sol"; import { RewardsDistributor } from "./Rewards/RewardsDistributor.sol"; import { MaxLoopsLimitHelper } from "./MaxLoopsLimitHelper.sol"; import { ensureNonzeroAddress } from "./lib/validators.sol"; +import { IFlashloanReceiver } from "./Flashloan/interfaces/IFlashloanReceiver.sol"; /** * @title Comptroller @@ -100,9 +101,13 @@ contract Comptroller is /// @notice Emitted when a market is unlisted event MarketUnlisted(address indexed vToken); + /// @notice Emitted when the borrowing or redeeming delegate rights are updated for an account event DelegateUpdated(address indexed approver, address indexed delegate, bool approved); + /// @notice Emitted When the flash loan is successfully executed + event FlashloanExecuted(address receiver, VTokenInterface[] assets, uint256[] amounts); + /// @notice Thrown when collateral factor exceeds the upper bound error InvalidCollateralFactor(); @@ -199,6 +204,18 @@ contract Comptroller is /// @notice Thrown if delegate approval status is already set to the requested value error DelegationStatusUnchanged(); + /// @notice Thrown if invalid flashloan params passed + error InvalidFlashloanParams(); + + ///@notice Thrown if the flashloan is not enabled for a particular market + error FlashLoanNotEnabled(address market); + + ///@notice Thrown if repayment amount is insufficient + error InsufficientReypaymentBalance(address tokenAddress); + + ///@notice Thrown if executeOperation failed + error ExecuteFlashloanFailed(); + /// @param poolRegistry_ Pool registry address /// @custom:oz-upgrades-unsafe-allow constructor /// @custom:error ZeroAddressNotAllowed is thrown when pool registry address is zero @@ -919,6 +936,82 @@ contract Comptroller is } } + /** + * @notice Executes a flashloan operation with the specified assets and amounts. + * @dev Transfer the specified assets to the receiver contract and ensures that the total repayment (amount + fee) + * is returned by the receiver contract after the operation for each asset. The function performs checks to ensure the validity + * of parameters, that flashloans are enabled for the given assets, and that the total repayment is sufficient. + * Reverts on invalid parameters, disabled flashloans, or insufficient repayment. + * @param receiver The address of the contract that will receive the flashloan and execute the operation. + * @param assets The addresses of the assets to be loaned. + * @param amounts The amounts of each asset to be loaned. + * @custom:requirements + * - `assets.length` must be equal to `amounts.length`. + * - `assets.length` and `amounts.length` must not be zero. + * - The `receiver` address must not be the zero address. + * - Flashloans must be enabled for each asset. + * - The `receiver` contract must repay the loan with the appropriate fee. + * @custom:reverts + * - Reverts with `InvalidFlashloanParams()` if parameter checks fail. + * - Reverts with `FlashLoanNotEnabled(asset)` if flashloans are disabled for any of the requested assets. + * - Reverts with `ExecuteFlashloanFailed` if the receiver contract fails to execute the operation. + * - Reverts with `InsufficientReypaymentBalance(asset)` if the repayment (amount + fee) is insufficient after the operation. + */ + function executeFlashloan( + address receiver, + VTokenInterface[] calldata assets, + uint256[] calldata amounts + ) external override { + // Asset and amount length must be equals and not be zero + if (assets.length != amounts.length || assets.length == 0 || receiver == address(0)) { + revert InvalidFlashloanParams(); + } + + IFlashloanReceiver receiverContract = IFlashloanReceiver(receiver); + uint256 len = assets.length; + uint256[] memory fees = new uint256[](len); + uint256[] memory balanceBefore = new uint256[](len); + + for (uint256 j; j < len; ) { + // Revert if flashloan is not enabled + if (!(assets[j]).isFlashloanEnabled()) { + revert FlashLoanNotEnabled(address(assets[j])); + } + + uint256 assetFlashloanFee = (assets[j]).flashloanFeeMantissa(); + fees[j] = (amounts[j] * assetFlashloanFee) / MANTISSA_ONE; + + // Transfer the asset + (assets[j]).transferUnderlying(receiver, amounts[j]); + + balanceBefore[j] = (assets[j]).getCash(); + + unchecked { + ++j; + } + } + + // Call the execute operation on receiver contract + if (!receiverContract.executeOperation(assets, amounts, fees, receiver, "")) { + revert ExecuteFlashloanFailed(); + } + + for (uint256 k; k < len; ) { + uint256 balanceAfter = (assets[k]).getCash(); + + // balanceAfter should be greater than the fee calculated + if ((balanceAfter - balanceBefore[k]) < (amounts[k] + fees[k])) { + revert InsufficientReypaymentBalance(address(assets[k])); + } + + unchecked { + ++k; + } + } + + emit FlashloanExecuted(receiver, assets, amounts); + } + /** * @notice Liquidates all borrows of the borrower. Callable only if the collateral is less than * a predefined threshold, and the account collateral can be seized to cover all borrows. If diff --git a/contracts/ComptrollerInterface.sol b/contracts/ComptrollerInterface.sol index 662a6aef9..786599ac4 100644 --- a/contracts/ComptrollerInterface.sol +++ b/contracts/ComptrollerInterface.sol @@ -5,6 +5,7 @@ import { ResilientOracleInterface } from "@venusprotocol/oracle/contracts/interf import { VToken } from "./VToken.sol"; import { RewardsDistributor } from "./Rewards/RewardsDistributor.sol"; +import { VTokenInterface } from "./VTokenInterfaces.sol"; enum Action { MINT, @@ -90,6 +91,8 @@ interface ComptrollerInterface { function preTransferHook(address vToken, address src, address dst, uint256 transferTokens) external; + function executeFlashloan(address receiver, VTokenInterface[] calldata assets, uint256[] calldata amounts) external; + function isComptroller() external view returns (bool); /*** Liquidity/Liquidation Calculations ***/ diff --git a/contracts/ErrorReporter.sol b/contracts/ErrorReporter.sol index 080c0b73c..e05ab34be 100644 --- a/contracts/ErrorReporter.sol +++ b/contracts/ErrorReporter.sol @@ -46,4 +46,9 @@ contract TokenErrorReporter { error ReduceReservesCashValidation(); error SetInterestRateModelFreshCheck(); + + error FlashLoanNotEnabled(address); + error ExecuteFlashloanFailed(); + error InvalidComptroller(address comptroller); + error InsufficientReypaymentBalance(address tokenAddress); } diff --git a/contracts/VTokenInterfaces.sol b/contracts/VTokenInterfaces.sol index 0f01e1f17..878496536 100644 --- a/contracts/VTokenInterfaces.sol +++ b/contracts/VTokenInterfaces.sol @@ -131,12 +131,22 @@ contract VTokenStorage { */ uint256 public reduceReservesBlockNumber; + /** + * @notice flashloan is enabled for this market or not + */ + bool public isFlashloanEnabled; + + /** + * @notice fee percentage collected by protocol on flashloan + */ + uint256 public flashloanFeeMantissa; + /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ - uint256[48] private __gap; + uint256[47] private __gap; } /** @@ -288,6 +298,26 @@ abstract contract VTokenInterface is VTokenStorage { */ event ProtocolSeize(address indexed from, address indexed to, uint256 amount); + /** + * @notice Event emitted when flashloanEnabled status is changed + */ + event ToggleFlashloanEnabled(bool oldEnabled, bool enabled); + + /** + * @notice Event emitted when flashloan is executed + */ + event FlashloanExecuted(address receiver, address underlying, uint256 amount); + + /** + * @notice Event emitted when asset is transferred to receiver + */ + event FlashloanAmountTransferred(address asset, address receiver, uint256 amount); + + /** + * @notice Event emitted when flashloan fee mantissa is updated + */ + event FLashloanFeeUpdated(uint256 oldFee, uint256 fee); + /*** User Interface ***/ function mint(uint256 mintAmount) external virtual returns (uint256); @@ -336,6 +366,10 @@ abstract contract VTokenInterface is VTokenStorage { function sweepToken(IERC20Upgradeable token) external virtual; + function transferUnderlying(address receiver, uint256 amount) external virtual; + + function executeFlashloan(address receiver, uint256 amount) external virtual returns (uint256); + /*** Admin Functions ***/ function setReserveFactor(uint256 newReserveFactorMantissa) external virtual; @@ -350,6 +384,10 @@ abstract contract VTokenInterface is VTokenStorage { function addReserves(uint256 addAmount) external virtual; + function toggleFlashloan() external virtual; + + function setFlashloanFeeMantissa(uint256 fee) external virtual; + function totalBorrowsCurrent() external virtual returns (uint256); function balanceOfUnderlying(address owner) external virtual returns (uint256); diff --git a/contracts/test/UpgradedVToken.sol b/contracts/test/UpgradedVToken.sol index efae5ac4f..a2480b932 100644 --- a/contracts/test/UpgradedVToken.sol +++ b/contracts/test/UpgradedVToken.sol @@ -57,7 +57,9 @@ contract UpgradedVToken is VToken { address payable admin_, address accessControlManager_, RiskManagementInit memory riskManagement, - uint256 reserveFactorMantissa_ + uint256 reserveFactorMantissa_, + bool isFlashloanEnabled_, + uint256 flashloanFeeMantissa_ ) public reinitializer(2) { super._initialize( underlying_, @@ -70,7 +72,9 @@ contract UpgradedVToken is VToken { admin_, accessControlManager_, riskManagement, - reserveFactorMantissa_ + reserveFactorMantissa_, + isFlashloanEnabled_, + flashloanFeeMantissa_ ); } diff --git a/hardhat.config.ts b/hardhat.config.ts index 011e59c8f..cef18c32b 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -171,6 +171,7 @@ const config: HardhatUserConfig = { yul: !process.env.CI, }, }, + viaIR: true, evmVersion: "paris", outputSelection: { "*": {