diff --git a/contracts/DelegateBorrowers/MoveDebtDelegate.sol b/contracts/DelegateBorrowers/MoveDebtDelegate.sol index 01e02ab71..94eac5467 100644 --- a/contracts/DelegateBorrowers/MoveDebtDelegate.sol +++ b/contracts/DelegateBorrowers/MoveDebtDelegate.sol @@ -9,7 +9,8 @@ import { ResilientOracleInterface } from "@venusprotocol/oracle/contracts/interf import { ensureNonzeroAddress } from "@venusprotocol/solidity-utilities/contracts/validators.sol"; import { approveOrRevert } from "../lib/approveOrRevert.sol"; -import { IVBep20, IComptroller } from "../InterfacesV8.sol"; +import { IComptroller } from "../Comptroller/interfaces/IComptroller.sol"; +import { IVBep20 } from "../Tokens/VTokens/interfaces/IVBep20.sol"; contract MoveDebtDelegate is Ownable2StepUpgradeable, ReentrancyGuardUpgradeable { /// @dev VToken return value signalling about successful execution diff --git a/contracts/DelegateBorrowers/SwapDebtDelegate.sol b/contracts/DelegateBorrowers/SwapDebtDelegate.sol index f94f4e3e4..0d8151667 100644 --- a/contracts/DelegateBorrowers/SwapDebtDelegate.sol +++ b/contracts/DelegateBorrowers/SwapDebtDelegate.sol @@ -8,7 +8,8 @@ import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC import { ResilientOracleInterface } from "@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol"; import { approveOrRevert } from "../lib/approveOrRevert.sol"; -import { IVBep20, IComptroller } from "../InterfacesV8.sol"; +import { IComptroller } from "../Comptroller/interfaces/IComptroller.sol"; +import { IVBep20 } from "../Tokens/VTokens/interfaces/IVBep20.sol"; contract SwapDebtDelegate is Ownable2StepUpgradeable, ReentrancyGuardUpgradeable { /// @dev VToken return value signalling about successful execution diff --git a/contracts/Governance/TokenRedeemer.sol b/contracts/Governance/TokenRedeemer.sol index 386cae093..5638d1a2c 100644 --- a/contracts/Governance/TokenRedeemer.sol +++ b/contracts/Governance/TokenRedeemer.sol @@ -5,7 +5,10 @@ import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuar import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol"; import { ensureNonzeroAddress } from "@venusprotocol/solidity-utilities/contracts/validators.sol"; -import { IVAIController, IVToken, IVBep20, IVBNB } from "../InterfacesV8.sol"; +import { IVToken } from "../Tokens/VTokens/interfaces/IVToken.sol"; +import { IVBep20 } from "../Tokens/VTokens/interfaces/IVBep20.sol"; +import { IVBNB } from "../Tokens/VTokens/interfaces/IVBNB.sol"; +import { IVAIController } from "../Tokens/VAI/interfaces/IVAIController.sol"; import { Currency, CurrencyLibrary } from "../lib/Currency.sol"; contract TokenRedeemer is ReentrancyGuard, Ownable2Step { @@ -162,7 +165,7 @@ contract TokenRedeemer is ReentrancyGuard, Ownable2Step { return; } if (_isVBNB(vToken)) { - IVBNB(address(vToken)).repayBorrowBehalf{ value: amount }(borrower); + IVBNB(payable(address(vToken))).repayBorrowBehalf{ value: amount }(borrower); } else { uint256 err = IVBep20(address(vToken)).repayBorrowBehalf(borrower, amount); if (err != 0) { diff --git a/contracts/InterfacesV8.sol b/contracts/InterfacesV8.sol deleted file mode 100644 index 2a244b041..000000000 --- a/contracts/InterfacesV8.sol +++ /dev/null @@ -1,108 +0,0 @@ -// SPDX-License-Identifier: BSD-3-Clause -pragma solidity ^0.8.25; - -import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import { ResilientOracleInterface } from "@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol"; - -interface IVToken is IERC20Upgradeable { - function accrueInterest() external returns (uint256); - - function redeem(uint256 redeemTokens) external returns (uint256); - - function redeemUnderlying(uint256 redeemAmount) external returns (uint256); - - function borrowBalanceCurrent(address borrower) external returns (uint256); - - function balanceOfUnderlying(address owner) external returns (uint256); - - function comptroller() external view returns (IComptroller); - - function borrowBalanceStored(address account) external view returns (uint256); -} - -interface IVBep20 is IVToken { - function borrowBehalf(address borrower, uint256 borrowAmount) external returns (uint256); - - function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256); - - function liquidateBorrow( - address borrower, - uint256 repayAmount, - IVToken vTokenCollateral - ) external returns (uint256); - - function underlying() external view returns (address); -} - -interface IVBNB is IVToken { - function repayBorrowBehalf(address borrower) external payable; - - function liquidateBorrow(address borrower, IVToken vTokenCollateral) external payable; -} - -interface IVAIController { - function accrueVAIInterest() external; - - function liquidateVAI( - address borrower, - uint256 repayAmount, - IVToken vTokenCollateral - ) external returns (uint256, uint256); - - function repayVAIBehalf(address borrower, uint256 amount) external returns (uint256, uint256); - - function getVAIAddress() external view returns (address); - - function getVAIRepayAmount(address borrower) external view returns (uint256); -} - -interface IComptroller { - enum Action { - MINT, - REDEEM, - BORROW, - REPAY, - SEIZE, - LIQUIDATE, - TRANSFER, - ENTER_MARKET, - EXIT_MARKET - } - - function _setActionsPaused(address[] calldata markets_, Action[] calldata actions_, bool paused_) external; - - function liquidationIncentiveMantissa() external view returns (uint256); - - function vaiController() external view returns (IVAIController); - - function liquidatorContract() external view returns (address); - - function oracle() external view returns (ResilientOracleInterface); - - function actionPaused(address market, Action action) external view returns (bool); - - function markets(address) external view returns (bool, uint256, bool); - - function isForcedLiquidationEnabled(address) external view returns (bool); -} - -interface ILiquidator { - function restrictLiquidation(address borrower) external; - - function unrestrictLiquidation(address borrower) external; - - function addToAllowlist(address borrower, address liquidator) external; - - function removeFromAllowlist(address borrower, address liquidator) external; - - function liquidateBorrow( - address vToken, - address borrower, - uint256 repayAmount, - IVToken vTokenCollateral - ) external payable; - - function setTreasuryPercent(uint256 newTreasuryPercentMantissa) external; - - function treasuryPercentMantissa() external view returns (uint256); -} diff --git a/contracts/Liquidator/BUSDLiquidator.sol b/contracts/Liquidator/BUSDLiquidator.sol index 21fc2f25c..6a4a44bf1 100644 --- a/contracts/Liquidator/BUSDLiquidator.sol +++ b/contracts/Liquidator/BUSDLiquidator.sol @@ -1,15 +1,19 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.25; -import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import { Ownable2StepUpgradeable } from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol"; import { MANTISSA_ONE } from "@venusprotocol/solidity-utilities/contracts/constants.sol"; import { ensureNonzeroAddress } from "@venusprotocol/solidity-utilities/contracts/validators.sol"; import { approveOrRevert } from "../lib/approveOrRevert.sol"; -import { ILiquidator, IComptroller, IVToken, IVBep20, IVBNB, IVAIController } from "../InterfacesV8.sol"; +import { Action } from "../Comptroller/Diamond/interfaces/IFacetBase.sol"; +import { ILiquidator } from "./interfaces/ILiquidator.sol"; +import { IComptroller } from "../Comptroller/interfaces/IComptroller.sol"; +import { IVToken } from "../Tokens/VTokens/interfaces/IVToken.sol"; +import { IVBep20 } from "../Tokens/VTokens/interfaces/IVBep20.sol"; /** * @title BUSDLiquidator @@ -17,8 +21,8 @@ import { ILiquidator, IComptroller, IVToken, IVBep20, IVBNB, IVAIController } fr * @notice A custom contract for force-liquidating BUSD debts */ contract BUSDLiquidator is Ownable2StepUpgradeable, ReentrancyGuardUpgradeable { - using SafeERC20Upgradeable for IERC20Upgradeable; - using SafeERC20Upgradeable for IVToken; + using SafeERC20 for IERC20; + using SafeERC20 for IVToken; /// @custom:oz-upgrades-unsafe-allow state-variable-immutable IVBep20 public immutable vBUSD; @@ -95,7 +99,7 @@ contract BUSDLiquidator is Ownable2StepUpgradeable, ReentrancyGuardUpgradeable { /// @notice Allows to recover token accidentally sent to this contract by sending the entire balance to Governance /// @param token The address of the token to recover /// @custom:access Only Governance - function sweepToken(IERC20Upgradeable token) external onlyOwner { + function sweepToken(IERC20 token) external onlyOwner { token.safeTransfer(msg.sender, token.balanceOf(address(this))); } @@ -107,8 +111,8 @@ contract BUSDLiquidator is Ownable2StepUpgradeable, ReentrancyGuardUpgradeable { function _unpauseAndLiquidate(address borrower, uint256 repayAmount, IVToken vTokenCollateral) internal { address[] memory vTokens = new address[](1); vTokens[0] = address(vBUSD); - IComptroller.Action[] memory actions = new IComptroller.Action[](1); - actions[0] = IComptroller.Action.LIQUIDATE; + Action[] memory actions = new Action[](1); + actions[0] = Action.LIQUIDATE; comptroller._setActionsPaused(vTokens, actions, false); _liquidateBorrow(borrower, repayAmount, vTokenCollateral); @@ -122,7 +126,7 @@ contract BUSDLiquidator is Ownable2StepUpgradeable, ReentrancyGuardUpgradeable { /// @param vTokenCollateral The collateral to seize from the borrower function _liquidateBorrow(address borrower, uint256 repayAmount, IVToken vTokenCollateral) internal { ILiquidator liquidatorContract = ILiquidator(comptroller.liquidatorContract()); - IERC20Upgradeable busd = IERC20Upgradeable(vBUSD.underlying()); + IERC20 busd = IERC20(vBUSD.underlying()); uint256 actualRepayAmount = _transferIn(busd, msg.sender, repayAmount); approveOrRevert(busd, address(liquidatorContract), actualRepayAmount); @@ -141,7 +145,7 @@ contract BUSDLiquidator is Ownable2StepUpgradeable, ReentrancyGuardUpgradeable { /// @param from The account to transfer from /// @param amount The amount to transfer /// @return The actual amount transferred - function _transferIn(IERC20Upgradeable token, address from, uint256 amount) internal returns (uint256) { + function _transferIn(IERC20 token, address from, uint256 amount) internal returns (uint256) { uint256 prevBalance = token.balanceOf(address(this)); token.safeTransferFrom(from, address(this), amount); return token.balanceOf(address(this)) - prevBalance; diff --git a/contracts/Liquidator/Liquidator.sol b/contracts/Liquidator/Liquidator.sol index de8b16dae..62b3bc492 100644 --- a/contracts/Liquidator/Liquidator.sol +++ b/contracts/Liquidator/Liquidator.sol @@ -6,13 +6,25 @@ import { Ownable2StepUpgradeable } from "@openzeppelin/contracts-upgradeable/acc import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import { ensureNonzeroAddress } from "@venusprotocol/solidity-utilities/contracts/validators.sol"; -import "@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol"; +import { AccessControlledV8 } from "@venusprotocol/governance-contracts/contracts/Governance/AccessControlledV8.sol"; import { IProtocolShareReserve } from "../external/IProtocolShareReserve.sol"; import { IWBNB } from "../external/IWBNB.sol"; -import "./LiquidatorStorage.sol"; -import { IComptroller, IVToken, IVBep20, IVBNB, IVAIController } from "../InterfacesV8.sol"; - -contract Liquidator is Ownable2StepUpgradeable, ReentrancyGuardUpgradeable, LiquidatorStorage, AccessControlledV8 { +import { Action } from "../Comptroller/Diamond/interfaces/IFacetBase.sol"; +import { IComptroller } from "../Comptroller/interfaces/IComptroller.sol"; +import { IVToken } from "../Tokens/VTokens/interfaces/IVToken.sol"; +import { IVBep20 } from "../Tokens/VTokens/interfaces/IVBep20.sol"; +import { IVBNB } from "../Tokens/VTokens/interfaces/IVBNB.sol"; +import { IVAIController } from "../Tokens/VAI/interfaces/IVAIController.sol"; +import { ILiquidator } from "./interfaces/ILiquidator.sol"; +import { LiquidatorStorage } from "./LiquidatorStorage.sol"; + +contract Liquidator is + ILiquidator, + Ownable2StepUpgradeable, + ReentrancyGuardUpgradeable, + LiquidatorStorage, + AccessControlledV8 +{ /// @notice Address of vBNB contract. /// @custom:oz-upgrades-unsafe-allow state-variable-immutable IVBNB public immutable vBnb; @@ -468,7 +480,7 @@ contract Liquidator is Ownable2StepUpgradeable, ReentrancyGuardUpgradeable, Liqu /// @dev Checks liquidation action in comptroller and vaiDebt with minLiquidatableVAI threshold function _checkForceVAILiquidate(address vToken_, address borrower_) private view { uint256 _vaiDebt = vaiController.getVAIRepayAmount(borrower_); - bool _isVAILiquidationPaused = comptroller.actionPaused(address(vaiController), IComptroller.Action.LIQUIDATE); + bool _isVAILiquidationPaused = comptroller.actionPaused(address(vaiController), Action.LIQUIDATE); bool _isForcedLiquidationEnabled = comptroller.isForcedLiquidationEnabled(vToken_); if ( _isForcedLiquidationEnabled || diff --git a/contracts/Liquidator/LiquidatorStorage.sol b/contracts/Liquidator/LiquidatorStorage.sol index 744dc5278..da2ffcf2e 100644 --- a/contracts/Liquidator/LiquidatorStorage.sol +++ b/contracts/Liquidator/LiquidatorStorage.sol @@ -1,17 +1,19 @@ // SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.25; -contract LiquidatorStorage { +import { ILiquidatorStorage } from "./interfaces/ILiquidatorStorage.sol"; + +contract LiquidatorStorage is ILiquidatorStorage { /* State */ /// @notice Percent of seized amount that goes to treasury. uint256 public treasuryPercentMantissa; /// @notice Mapping of addresses allowed to liquidate an account if liquidationRestricted[borrower] == true - mapping(address => mapping(address => bool)) public allowedLiquidatorsByAccount; + mapping(address borrower => mapping(address liquidator => bool)) public allowedLiquidatorsByAccount; /// @notice Whether the liquidations are restricted to enabled allowedLiquidatorsByAccount addresses only - mapping(address => bool) public liquidationRestricted; + mapping(address borrower => bool) public liquidationRestricted; /// @notice minimum amount of VAI liquidation threshold uint256 public minLiquidatableVAI; diff --git a/contracts/Liquidator/interfaces/ILiquidator.sol b/contracts/Liquidator/interfaces/ILiquidator.sol new file mode 100644 index 000000000..c2b98be63 --- /dev/null +++ b/contracts/Liquidator/interfaces/ILiquidator.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity 0.8.25; + +import { IVToken } from "../../Tokens/VTokens/interfaces/IVToken.sol"; +import { ILiquidatorStorage } from "./ILiquidatorStorage.sol"; + +interface ILiquidator is ILiquidatorStorage { + /** + * @notice An admin function to restrict liquidations to allowed addresses only. + * @dev Use {addTo,removeFrom}AllowList to configure the allowed addresses. + * @param borrower The address of the borrower + */ + function restrictLiquidation(address borrower) external; + + /** + * @notice An admin function to remove restrictions for liquidations. + * @dev Does not impact the allowedLiquidatorsByAccount mapping for the borrower, just turns off the check. + * @param borrower The address of the borrower + */ + function unrestrictLiquidation(address borrower) external; + + /** + * @notice An admin function to add the liquidator to the allowedLiquidatorsByAccount mapping for a certain + * borrower. If the liquidations are restricted, only liquidators from the + * allowedLiquidatorsByAccount mapping can participate in liquidating the positions of this borrower. + * @param borrower The address of the borrower + * @param borrower The address of the liquidator + */ + function addToAllowlist(address borrower, address liquidator) external; + + /** + * @notice An admin function to remove the liquidator from the allowedLiquidatorsByAccount mapping for a certain + * borrower. + * @param borrower The address of the borrower + * @param liquidator The address of the liquidator + */ + function removeFromAllowlist(address borrower, address liquidator) external; + + /** + * @notice Liquidates a borrowers position. + * @param vToken The address of the vToken to liquidate + * @param borrower The address of the borrower + * @param repayAmount The amount of the underlying asset to repay + * @param vTokenCollateral The address of the vToken collateral + */ + function liquidateBorrow( + address vToken, + address borrower, + uint256 repayAmount, + IVToken vTokenCollateral + ) external payable; + + /** + * @notice Sets the percentage of the liquidation reward that is sent to the treasury. + * @param newTreasuryPercentMantissa The new treasury percent, scaled by 1e18 (e.g. 0.2 * 1e18 for 20%) + */ + function setTreasuryPercent(uint256 newTreasuryPercentMantissa) external; +} diff --git a/contracts/Liquidator/interfaces/ILiquidatorStorage.sol b/contracts/Liquidator/interfaces/ILiquidatorStorage.sol new file mode 100644 index 000000000..4f1f6c3af --- /dev/null +++ b/contracts/Liquidator/interfaces/ILiquidatorStorage.sol @@ -0,0 +1,50 @@ +// SPDX-License-Identifier: BSD-3-Clause +pragma solidity 0.8.25; + +interface ILiquidatorStorage { + /** + * @notice Percent of seized amount that goes to treasury. + * @return The treasury percent, scaled by 1e18 (e.g. 0.2 * 1e18 for 20%) + */ + function treasuryPercentMantissa() external view returns (uint256); + + /** + * @notice Mapping of addresses allowed to liquidate an account if liquidationRestricted[borrower] == true + * @param liquidator The address of the liquidator + * @param borrower The address of the borrower + * @return True if the liquidator is allowed to liquidate the borrower, false otherwise + */ + function allowedLiquidatorsByAccount(address liquidator, address borrower) external view returns (bool); + + /** + * @notice Whether the liquidations are restricted to enabled allowedLiquidatorsByAccount addresses only + * @param borrower The address of the borrower + * @return True if the liquidations are restricted to enabled allowedLiquidatorsByAccount addresses only, false otherwise + */ + function liquidationRestricted(address borrower) external view returns (bool); + + /** + * @notice minimum amount of VAI liquidation threshold + * @return The minimum amount of VAI liquidation threshold + */ + function minLiquidatableVAI() external view returns (uint256); + + /** + * @notice check for liquidation of VAI + * @return True if the liquidation of VAI is enabled, false otherwise + */ + function forceVAILiquidate() external view returns (bool); + + /** + * @notice assests whose redeem is pending to reduce reserves + * @param index The index of the pending redeem + * @return The address of the pending redeem + */ + function pendingRedeem(uint256 index) external view returns (address); + + /** + * @notice protocol share reserve contract address + * @return The address of the protocol share reserve contract + */ + function protocolShareReserve() external view returns (address); +} diff --git a/contracts/Tokens/VTokens/VToken.sol b/contracts/Tokens/VTokens/VToken.sol index 879bd172a..fadeea39e 100644 --- a/contracts/Tokens/VTokens/VToken.sol +++ b/contracts/Tokens/VTokens/VToken.sol @@ -271,6 +271,14 @@ abstract contract VToken is IVToken, VTokenStorage, Exponential, TokenErrorRepor return transferAllowances[owner][spender]; } + /** + * @notice Get the total supply of the vToken + * @return The total supply of the token + */ + function totalSupply() external view returns (uint256) { + return _totalSupply; + } + /** * @notice Get the token balance of the `owner` * @param owner The address of the account to query @@ -738,13 +746,13 @@ abstract contract VToken is IVToken, VTokenStorage, Exponential, TokenErrorRepor * totalSupplyNew = totalSupply + mintTokens * accountTokensNew = accountTokens[minter] + mintTokens */ - (vars.mathErr, vars.totalSupplyNew) = addUInt(totalSupply, vars.mintTokens); + (vars.mathErr, vars.totalSupplyNew) = addUInt(_totalSupply, vars.mintTokens); ensureNoMathError(vars.mathErr); (vars.mathErr, vars.accountTokensNew) = addUInt(accountTokens[minter], vars.mintTokens); ensureNoMathError(vars.mathErr); /* We write previously calculated values into storage */ - totalSupply = vars.totalSupplyNew; + _totalSupply = vars.totalSupplyNew; accountTokens[minter] = vars.accountTokensNew; /* We emit a Mint event, and a Transfer event */ @@ -832,14 +840,14 @@ abstract contract VToken is IVToken, VTokenStorage, Exponential, TokenErrorRepor * totalSupplyNew = totalSupply + mintTokens * accountTokensNew = accountTokens[receiver] + mintTokens */ - (vars.mathErr, vars.totalSupplyNew) = addUInt(totalSupply, vars.mintTokens); + (vars.mathErr, vars.totalSupplyNew) = addUInt(_totalSupply, vars.mintTokens); ensureNoMathError(vars.mathErr); (vars.mathErr, vars.accountTokensNew) = addUInt(accountTokens[receiver], vars.mintTokens); ensureNoMathError(vars.mathErr); /* We write previously calculated values into storage */ - totalSupply = vars.totalSupplyNew; + _totalSupply = vars.totalSupplyNew; accountTokens[receiver] = vars.accountTokensNew; /* We emit a MintBehalf event, and a Transfer event */ @@ -969,7 +977,7 @@ abstract contract VToken is IVToken, VTokenStorage, Exponential, TokenErrorRepor * totalSupplyNew = totalSupply - redeemTokens * accountTokensNew = accountTokens[redeemer] - redeemTokens */ - (vars.mathErr, vars.totalSupplyNew) = subUInt(totalSupply, vars.redeemTokens); + (vars.mathErr, vars.totalSupplyNew) = subUInt(_totalSupply, vars.redeemTokens); ensureNoMathError(vars.mathErr); (vars.mathErr, vars.accountTokensNew) = subUInt(accountTokens[redeemer], vars.redeemTokens); @@ -985,7 +993,7 @@ abstract contract VToken is IVToken, VTokenStorage, Exponential, TokenErrorRepor // (No safe failures beyond this point) /* We write previously calculated values into storage */ - totalSupply = vars.totalSupplyNew; + _totalSupply = vars.totalSupplyNew; accountTokens[redeemer] = vars.accountTokensNew; /* @@ -1650,8 +1658,8 @@ abstract contract VToken is IVToken, VTokenStorage, Exponential, TokenErrorRepor * @return Tuple of error code and calculated exchange rate scaled by 1e18 */ function exchangeRateStoredInternal() internal view virtual returns (MathError, uint) { - uint _totalSupply = totalSupply; - if (_totalSupply == 0) { + uint totalSupply_ = _totalSupply; + if (totalSupply_ == 0) { /* * If there are no tokens minted: * exchangeRate = initialExchangeRate @@ -1672,7 +1680,7 @@ abstract contract VToken is IVToken, VTokenStorage, Exponential, TokenErrorRepor return (mathErr, 0); } - (mathErr, exchangeRate) = getExp(cashPlusBorrowsMinusReserves, _totalSupply); + (mathErr, exchangeRate) = getExp(cashPlusBorrowsMinusReserves, totalSupply_); if (mathErr != MathError.NO_ERROR) { return (mathErr, 0); } diff --git a/contracts/Tokens/VTokens/VTokenStorage.sol b/contracts/Tokens/VTokens/VTokenStorage.sol index a5b6b1adc..2733519a4 100644 --- a/contracts/Tokens/VTokens/VTokenStorage.sol +++ b/contracts/Tokens/VTokens/VTokenStorage.sol @@ -89,8 +89,9 @@ contract VTokenStorage is IVTokenStorage { /** * @notice Total number of tokens in circulation + * @dev internal to avoid conflicts with IERC20.totalSupply() */ - uint public totalSupply; + uint internal _totalSupply; /** * @notice Official record of token balances for each account diff --git a/contracts/Tokens/VTokens/interfaces/IVBNB.sol b/contracts/Tokens/VTokens/interfaces/IVBNB.sol index 8ccba8795..0500afa74 100644 --- a/contracts/Tokens/VTokens/interfaces/IVBNB.sol +++ b/contracts/Tokens/VTokens/interfaces/IVBNB.sol @@ -17,36 +17,6 @@ interface IVBNB is IVToken { */ function mint() external payable; - /** - * @notice Sender redeems vTokens in exchange for the underlying asset - * @dev Accrues interest whether or not the operation succeeds, unless reverted - * @param redeemTokens The number of vTokens to redeem into underlying - * @return uint Returns 0 on success, otherwise returns a failure code (see ErrorReporter.sol for details). - * @custom:event Emits Redeem event on success - * @custom:event Emits Transfer event on success - * @custom:event Emits RedeemFee when fee is charged by the treasury - */ - function redeem(uint256 redeemTokens) external returns (uint256); - - /** - * @notice Sender redeems vTokens in exchange for a specified amount of underlying asset - * @dev Accrues interest whether or not the operation succeeds, unless reverted - * @param redeemAmount The amount of underlying to redeem - * @return uint Returns 0 on success, otherwise returns a failure code (see ErrorReporter.sol for details). - * @custom:event Emits Redeem event on success - * @custom:event Emits Transfer event on success - * @custom:event Emits RedeemFee when fee is charged by the treasury - */ - function redeemUnderlying(uint256 redeemAmount) external returns (uint256); - - /** - * @notice Sender borrows assets from the protocol to their own address - * @param borrowAmount The amount of the underlying asset to borrow - * @return uint Returns 0 on success, otherwise returns a failure code (see ErrorReporter.sol for details). - * @custom:event Emits Borrow event on success - */ - function borrow(uint256 borrowAmount) external returns (uint256); - /** * @notice Sender repays their own borrow * @dev Reverts upon any failure diff --git a/contracts/Tokens/VTokens/interfaces/IVBep20.sol b/contracts/Tokens/VTokens/interfaces/IVBep20.sol index 1c4e0a8c1..114b1fdad 100644 --- a/contracts/Tokens/VTokens/interfaces/IVBep20.sol +++ b/contracts/Tokens/VTokens/interfaces/IVBep20.sol @@ -4,14 +4,13 @@ pragma solidity 0.8.25; import { IVToken } from "./IVToken.sol"; interface IVBep20 is IVToken { - /*** User Interface ***/ /** * @notice Sender supplies assets into the market and receives vTokens in exchange * @dev Accrues interest whether or not the operation succeeds, unless reverted * @param mintAmount The amount of the underlying asset to supply * @return uint Returns 0 on success, otherwise returns a failure code */ - function mint(uint mintAmount) external returns (uint); + function mint(uint256 mintAmount) external returns (uint256); /** * @notice Sender supplies assets into the market on behalf of another account and receives vTokens in exchange @@ -20,31 +19,7 @@ interface IVBep20 is IVToken { * @param mintAmount The amount of the underlying asset to supply * @return uint Returns 0 on success, otherwise returns a failure code */ - function mintBehalf(address receiver, uint mintAmount) external returns (uint); - - /** - * @notice Sender redeems vTokens in exchange for the underlying asset - * @dev Accrues interest whether or not the operation succeeds, unless reverted - * @param redeemTokens The amount of vTokens to redeem - * @return uint Returns the underlying asset received - */ - function redeem(uint redeemTokens) external returns (uint); - - /** - * @notice Sender redeems vTokens in exchange for a specified amount of underlying asset - * @dev Accrues interest whether or not the operation succeeds, unless reverted - * @param redeemAmount The amount of underlying asset to receive - * @return uint Returns the vTokens redeemed - */ - function redeemUnderlying(uint redeemAmount) external returns (uint); - - /** - * @notice Sender borrows assets from the protocol to their own address - * @dev Accrues interest whether or not the operation succeeds, unless reverted - * @param borrowAmount The amount of the underlying asset to borrow - * @return uint Returns the amount successfully borrowed - */ - function borrow(uint borrowAmount) external returns (uint); + function mintBehalf(address receiver, uint256 mintAmount) external returns (uint256); /** * @notice Sender repays their own borrow @@ -52,7 +27,7 @@ interface IVBep20 is IVToken { * @param repayAmount The amount of the underlying asset to repay * @return uint Returns the remaining borrow amount after repayment */ - function repayBorrow(uint repayAmount) external returns (uint); + function repayBorrow(uint256 repayAmount) external returns (uint256); /** * @notice Sender repays a borrow on behalf of another account @@ -61,7 +36,17 @@ interface IVBep20 is IVToken { * @param repayAmount The amount of the underlying asset to repay * @return uint Returns the remaining borrow amount after repayment */ - function repayBorrowBehalf(address borrower, uint repayAmount) external returns (uint); + function repayBorrowBehalf(address borrower, uint256 repayAmount) external returns (uint256); + + /** + * @notice Sender borrows assets on behalf of some other address. This function is only available + * for senders, explicitly marked as delegates of the borrower using `comptroller.updateDelegate` + * @param borrower The borrower, on behalf of whom to borrow. + * @param borrowAmount The amount of the underlying asset to borrow + * @return uint Returns 0 on success, otherwise returns a failure code (see ErrorReporter.sol for details). + */ + // @custom:event Emits Borrow event on success + function borrowBehalf(address borrower, uint256 borrowAmount) external returns (uint256); /** * @notice Liquidates a borrowers position @@ -81,5 +66,5 @@ interface IVBep20 is IVToken { * @param addAmount The amount of the underlying asset to add * @return uint Returns the new total reserves */ - function _addReserves(uint addAmount) external returns (uint); + function _addReserves(uint256 addAmount) external returns (uint256); } diff --git a/contracts/Tokens/VTokens/interfaces/IVToken.sol b/contracts/Tokens/VTokens/interfaces/IVToken.sol index f8add6389..766aa36e7 100644 --- a/contracts/Tokens/VTokens/interfaces/IVToken.sol +++ b/contracts/Tokens/VTokens/interfaces/IVToken.sol @@ -1,6 +1,7 @@ // SPDX-License-Identifier: BSD-3-Clause pragma solidity 0.8.25; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IComptroller } from "../../../Comptroller/interfaces/IComptroller.sol"; import { InterestRateModelV8 } from "../../../InterestRateModels/InterestRateModelV8.sol"; import { IVTokenStorage } from "./IVTokenStorage.sol"; @@ -11,7 +12,7 @@ import { IVTokenStorage } from "./IVTokenStorage.sol"; * @notice Interface for interacting with Venus VTokens * @dev This interface defines the core functionality of Venus VTokens, which are interest-bearing tokens that represent deposits of underlying assets */ -interface IVToken is IVTokenStorage { +interface IVToken is IVTokenStorage, IERC20 { /// @notice Event emitted when interest is accrued event AccrueInterest(uint cashPrior, uint interestAccumulated, uint borrowIndex, uint totalBorrows); @@ -66,12 +67,6 @@ interface IVToken is IVTokenStorage { /// @notice Event emitted when the reserves are reduced event ReservesReduced(address protocolShareReserve, uint reduceAmount, uint newTotalReserves); - /// @notice EIP20 Transfer event - event Transfer(address indexed from, address indexed to, uint amount); - - /// @notice EIP20 Approval event - event Approval(address indexed owner, address indexed spender, uint amount); - /// @notice Event emitted when block delta for reduce reserves get updated event NewReduceReservesBlockDelta(uint256 oldReduceReservesBlockDelta, uint256 newReduceReservesBlockDelta); @@ -87,33 +82,34 @@ interface IVToken is IVTokenStorage { function isVToken() external pure returns (bool); /** - * @notice Transfer `amount` tokens from `msg.sender` to `dst` - * @param dst The address of the destination account - * @param amount The number of tokens to transfer - * @return Whether or not the transfer succeeded - * @custom:event Emits Transfer event + * @notice Sender redeems vTokens in exchange for the underlying asset + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @param redeemTokens The number of vTokens to redeem into underlying + * @return uint Returns 0 on success, otherwise returns a failure code (see ErrorReporter.sol for details). + * @custom:event Emits Redeem event on success + * @custom:event Emits Transfer event on success + * @custom:event Emits RedeemFee when fee is charged by the treasury */ - function transfer(address dst, uint amount) external returns (bool); + function redeem(uint256 redeemTokens) external returns (uint256); /** - * @notice Transfer `amount` tokens from `src` to `dst` - * @param src The address of the source account - * @param dst The address of the destination account - * @param amount The number of tokens to transfer - * @return Whether or not the transfer succeeded - * @custom:event Emits Transfer event + * @notice Sender redeems vTokens in exchange for a specified amount of underlying asset + * @dev Accrues interest whether or not the operation succeeds, unless reverted + * @param redeemAmount The amount of underlying to redeem + * @return uint Returns 0 on success, otherwise returns a failure code (see ErrorReporter.sol for details). + * @custom:event Emits Redeem event on success + * @custom:event Emits Transfer event on success + * @custom:event Emits RedeemFee when fee is charged by the treasury */ - function transferFrom(address src, address dst, uint amount) external returns (bool); + function redeemUnderlying(uint256 redeemAmount) external returns (uint256); /** - * @notice Approve `spender` to transfer up to `amount` from `msg.sender` - * @dev This will overwrite the approval amount for `spender` - * @param spender The address of the account which may transfer tokens - * @param amount The number of tokens that are approved - * @return Whether or not the approval succeeded - * @custom:event Emits Approval event + * @notice Sender borrows assets from the protocol to their own address + * @param borrowAmount The amount of the underlying asset to borrow + * @return uint Returns 0 on success, otherwise returns a failure code (see ErrorReporter.sol for details). + * @custom:event Emits Borrow event on success */ - function approve(address spender, uint amount) external returns (bool); + function borrow(uint256 borrowAmount) external returns (uint256); /** * @notice Get the underlying balance of the `owner` @@ -222,21 +218,6 @@ interface IVToken is IVTokenStorage { */ function _setInterestRateModel(InterestRateModelV8 newInterestRateModel) external returns (uint); - /** - * @notice Get the token balance of the `owner` - * @param owner The address of the account to query - * @return The number of tokens owned by `owner` - */ - function balanceOf(address owner) external view returns (uint); - - /** - * @notice Get the current allowance from `owner` for `spender` - * @param owner The address of the account which owns the tokens - * @param spender The address of the account which may transfer tokens - * @return The number of tokens allowed to be spent - */ - function allowance(address owner, address spender) external view returns (uint); - /** * @notice Get various information about an account in this market * @param account The address of the account to query diff --git a/contracts/Tokens/VTokens/interfaces/IVTokenStorage.sol b/contracts/Tokens/VTokens/interfaces/IVTokenStorage.sol index c9fdd0477..04d780358 100644 --- a/contracts/Tokens/VTokens/interfaces/IVTokenStorage.sol +++ b/contracts/Tokens/VTokens/interfaces/IVTokenStorage.sol @@ -75,11 +75,6 @@ interface IVTokenStorage { */ function totalReserves() external view returns (uint256); - /** - * @notice Total number of tokens in circulation - */ - function totalSupply() external view returns (uint256); - /** * @notice Underlying asset for this VToken */ diff --git a/contracts/lib/approveOrRevert.sol b/contracts/lib/approveOrRevert.sol index f64b833e4..b1265feb4 100644 --- a/contracts/lib/approveOrRevert.sol +++ b/contracts/lib/approveOrRevert.sol @@ -2,6 +2,7 @@ pragma solidity 0.8.25; import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /// @notice Thrown if a contract is unable to approve a transfer error ApproveFailed(); @@ -25,3 +26,16 @@ function approveOrRevert(IERC20Upgradeable token, address spender, uint256 amoun revert ApproveFailed(); } } + +/// @notice Approves a transfer, ensuring that it is successful. This function supports non-compliant +/// tokens like the ones that don't return a boolean value on success. Thus, such approve call supports +/// three different kinds of tokens: +/// * Compliant tokens that revert on failure +/// * Compliant tokens that return false on failure +/// * Non-compliant tokens that don't return a value +/// @param token The contract address of the token which will be transferred +/// @param spender The spender contract address +/// @param amount The value of the transfer +function approveOrRevert(IERC20 token, address spender, uint256 amount) { + approveOrRevert(IERC20Upgradeable(address(token)), spender, amount); +} diff --git a/contracts/test/EvilXDelegator.sol b/contracts/test/EvilXDelegator.sol index b2866367a..31d9198d5 100644 --- a/contracts/test/EvilXDelegator.sol +++ b/contracts/test/EvilXDelegator.sol @@ -149,6 +149,18 @@ contract EvilXDelegator is VTokenStorage, IVBep20, IVDelegator { return abi.decode(data, (uint256)); } + /** + * @notice Sender borrows assets from the protocol to their own address + * @param borrowAmount The amount of the underlying asset to borrow + * @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details) + */ + function borrowBehalf(address borrower, uint256 borrowAmount) external returns (uint256) { + bytes memory data = delegateToImplementation( + abi.encodeWithSignature("borrowBehalf(address,uint256)", borrower, borrowAmount) + ); + return abi.decode(data, (uint256)); + } + /** * @notice Sender repays their own borrow * @param repayAmount The amount to repay @@ -254,6 +266,15 @@ contract EvilXDelegator is VTokenStorage, IVBep20, IVDelegator { return abi.decode(data, (uint256)); } + /** + * @notice Get the total supply of the vToken + * @return The total supply of the token + */ + function totalSupply() external view override returns (uint256) { + bytes memory data = delegateToViewImplementation(abi.encodeWithSignature("totalSupply()")); + return abi.decode(data, (uint256)); + } + /** * @notice Get the underlying balance of the `owner` * @dev This also accrues interest in a transaction diff --git a/contracts/test/EvilXToken.sol b/contracts/test/EvilXToken.sol index 36f4c4875..75844832b 100644 --- a/contracts/test/EvilXToken.sol +++ b/contracts/test/EvilXToken.sol @@ -108,7 +108,7 @@ contract EvilXToken is VBep20Delegate { } function harnessSetTotalSupply(uint totalSupply_) public { - totalSupply = totalSupply_; + _totalSupply = totalSupply_; } function harnessSetTotalBorrows(uint totalBorrows_) public { @@ -124,7 +124,7 @@ contract EvilXToken is VBep20Delegate { } function harnessExchangeRateDetails(uint totalSupply_, uint totalBorrows_, uint totalReserves_) public { - totalSupply = totalSupply_; + _totalSupply = totalSupply_; totalBorrows = totalBorrows_; totalReserves = totalReserves_; } diff --git a/contracts/test/VBep20Harness.sol b/contracts/test/VBep20Harness.sol index 69e5f30f8..04642de81 100644 --- a/contracts/test/VBep20Harness.sol +++ b/contracts/test/VBep20Harness.sol @@ -72,7 +72,7 @@ contract VBep20Harness is VBep20Immutable { } function harnessSetTotalSupply(uint totalSupply_) public { - totalSupply = totalSupply_; + _totalSupply = totalSupply_; } function harnessSetTotalBorrows(uint totalBorrows_) public { @@ -84,7 +84,7 @@ contract VBep20Harness is VBep20Immutable { } function harnessExchangeRateDetails(uint totalSupply_, uint totalBorrows_, uint totalReserves_) public { - totalSupply = totalSupply_; + _totalSupply = totalSupply_; totalBorrows = totalBorrows_; totalReserves = totalReserves_; } @@ -317,7 +317,7 @@ contract VBep20DelegateHarness is VBep20Delegate { } function harnessSetTotalSupply(uint totalSupply_) public { - totalSupply = totalSupply_; + _totalSupply = totalSupply_; } function harnessSetTotalBorrows(uint totalBorrows_) public { @@ -333,7 +333,7 @@ contract VBep20DelegateHarness is VBep20Delegate { } function harnessExchangeRateDetails(uint totalSupply_, uint totalBorrows_, uint totalReserves_) public { - totalSupply = totalSupply_; + _totalSupply = totalSupply_; totalBorrows = totalBorrows_; totalReserves = totalReserves_; } diff --git a/contracts/test/VBep20MockDelegate.sol b/contracts/test/VBep20MockDelegate.sol index b530282c2..834b30110 100644 --- a/contracts/test/VBep20MockDelegate.sol +++ b/contracts/test/VBep20MockDelegate.sol @@ -134,6 +134,17 @@ contract VBep20MockDelegate is VToken, IVBep20, IVDelegate { return borrowInternal(msg.sender, payable(msg.sender), borrowAmount); } + /** + * @notice Sender borrows assets on behalf of some other address. This function is only available + * for senders, explicitly marked as delegates of the borrower using `comptroller.updateDelegate` + * @param borrower The borrower, on behalf of whom to borrow. + * @param borrowAmount The amount of the underlying asset to borrow + * @return uint Returns 0 on success, otherwise returns a failure code (see ErrorReporter.sol for details). + */ + function borrowBehalf(address borrower, uint256 borrowAmount) external returns (uint256) { + return borrowInternal(borrower, payable(msg.sender), borrowAmount); + } + /** * @notice Sender repays their own borrow * @param repayAmount The amount to repay