Skip to content

Commit

Permalink
Merge pull request #424 from VenusProtocol/feat/move-debt-any-market
Browse files Browse the repository at this point in the history
[VEN-2274]: allow moving debts from users in non-deprecated markets
  • Loading branch information
kkirka authored Jan 15, 2024
2 parents 1dc85a5 + 822878a commit 167b402
Show file tree
Hide file tree
Showing 9 changed files with 3,026 additions and 36 deletions.
77 changes: 60 additions & 17 deletions contracts/DelegateBorrowers/MoveDebtDelegate.sol
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/
import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol";
import { ResilientOracleInterface } from "@venusprotocol/oracle/contracts/interfaces/OracleInterface.sol";
import { ensureNonzeroAddress } from "@venusprotocol/solidity-utilities/contracts/validators.sol";

import { approveOrRevert } from "../lib/approveOrRevert.sol";
import { IVBep20, IComptroller } from "../InterfacesV8.sol";
Expand All @@ -15,9 +16,8 @@ contract MoveDebtDelegate is Ownable2StepUpgradeable, ReentrancyGuardUpgradeable
/// @dev VToken return value signalling about successful execution
uint256 internal constant NO_ERROR = 0;

/// @notice VToken to repay the debt to
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
IVBep20 public immutable vTokenToRepay;
/// @notice A wildcard indicating that repayment is allowed for _any_ user in the market
address public constant ANY_USER = address(1);

/// @notice User to borrow on behalf of
/// @custom:oz-upgrades-unsafe-allow state-variable-immutable
Expand All @@ -26,15 +26,22 @@ contract MoveDebtDelegate is Ownable2StepUpgradeable, ReentrancyGuardUpgradeable
/// @notice Whether to allow borrowing from the corresponding vToken
mapping(address => bool) public borrowAllowed;

/// @notice Whether to allow repaying to the corresponding vToken on behalf of
/// a certain user. Use ANY_USER to check if repayment is allowed for any user.
mapping(address => mapping(address => bool)) public repaymentAllowed;

/// @notice Emitted when vToken is allowed or denied to be borrowed from
event BorrowAllowedSet(address indexed vTokenToBorrow, bool allowed);

/// @notice Emitted when vToken is allowed or denied to be borrowed from
event BorrowAllowedSet(address indexed vToken, bool allowed);
event RepaymentAllowedSet(address indexed vTokenToRepay, address indexed originalBorrower, bool allowed);

/// @notice Emitted if debt is swapped successfully
event DebtMoved(
address indexed originalBorrower,
address vTokenRepaid,
address indexed vTokenRepaid,
uint256 repaidAmount,
address indexed newBorrower,
address newBorrower,
address indexed vTokenBorrowed,
uint256 borrowedAmount
);
Expand All @@ -55,14 +62,15 @@ contract MoveDebtDelegate is Ownable2StepUpgradeable, ReentrancyGuardUpgradeable
/// @notice Thrown if borrowing from the corresponding vToken is not allowed
error BorrowNotAllowed(address vToken);

/// @notice Thrown if repaying the debts of the borrower to the corresponding vToken is not allowed
error RepaymentNotAllowed(address vToken, address borrower);

using SafeERC20Upgradeable for IERC20Upgradeable;

/// @notice Constructor for the implementation contract. Sets immutable variables.
/// @param vTokenToRepay_ VToken to repay the debt to
/// @param newBorrower_ User to borrow on behalf of
/// @custom:oz-upgrades-unsafe-allow constructor
constructor(IVBep20 vTokenToRepay_, address newBorrower_) {
vTokenToRepay = vTokenToRepay_;
constructor(address newBorrower_) {
newBorrower = newBorrower_;
_disableInitializers();
}
Expand All @@ -74,16 +82,29 @@ contract MoveDebtDelegate is Ownable2StepUpgradeable, ReentrancyGuardUpgradeable

/**
* @notice Repays originalBorrower's borrow in vTokenToRepay.underlying() and borrows
* vTokenToBorrow.underlying() on behalf of newBorrower
* vTokenToBorrow.underlying() on behalf of newBorrower.
*
* @param originalBorrower The address of the borrower, whose debt to repay
* @param vTokenToRepay VToken to repay to on behalf of originalBorrower
* @param repayAmount The amount to repay in terms of vTokenToRepay.underlying()
* @param vTokenToBorrow VToken to borrow from
*/
function moveDebt(address originalBorrower, uint256 repayAmount, IVBep20 vTokenToBorrow) external nonReentrant {
function moveDebt(
IVBep20 vTokenToRepay,
address originalBorrower,
uint256 repayAmount,
IVBep20 vTokenToBorrow
) external nonReentrant {
if (!borrowAllowed[address(vTokenToBorrow)]) {
revert BorrowNotAllowed(address(vTokenToBorrow));
}
uint256 actualRepaymentAmount = _repay(originalBorrower, repayAmount);

mapping(address => bool) storage repaymentAllowedFor = repaymentAllowed[address(vTokenToRepay)];
if (!repaymentAllowedFor[ANY_USER] && !repaymentAllowedFor[originalBorrower]) {
revert RepaymentNotAllowed(address(vTokenToRepay), originalBorrower);
}

uint256 actualRepaymentAmount = _repay(vTokenToRepay, originalBorrower, repayAmount);
uint256 amountToBorrow = _convert(vTokenToRepay, vTokenToBorrow, actualRepaymentAmount);
_borrow(vTokenToBorrow, amountToBorrow);
emit DebtMoved(
Expand All @@ -102,12 +123,29 @@ contract MoveDebtDelegate is Ownable2StepUpgradeable, ReentrancyGuardUpgradeable
* @param allow Whether to allow borrowing from the corresponding vToken
*/
function setBorrowAllowed(address vTokenToBorrow, bool allow) external onlyOwner {
ensureNonzeroAddress(vTokenToBorrow);
if (borrowAllowed[vTokenToBorrow] != allow) {
borrowAllowed[vTokenToBorrow] = allow;
emit BorrowAllowedSet(vTokenToBorrow, allow);
}
}

/**
* @notice Allows or denies repaying the debts of originalBorrower to the corresponding vToken
* @param vTokenToRepay VToken to repay to
* @param originalBorrower The address of the borrower, whose debt to repay (or ANY_USER to allow
* repayments for all users in the market, e.g. if the market is going to be deprecated soon)
* @param allow Whether to allow repaying to the corresponding vToken on behalf of originalBorrower
*/
function setRepaymentAllowed(address vTokenToRepay, address originalBorrower, bool allow) external onlyOwner {
ensureNonzeroAddress(vTokenToRepay);
ensureNonzeroAddress(originalBorrower);
if (repaymentAllowed[vTokenToRepay][originalBorrower] != allow) {
repaymentAllowed[vTokenToRepay][originalBorrower] = allow;
emit RepaymentAllowedSet(vTokenToRepay, originalBorrower, allow);
}
}

/**
* @notice Transfers tokens, accidentially sent to this contract, to the owner
* @param token ERC-20 token to sweep
Expand All @@ -120,10 +158,15 @@ contract MoveDebtDelegate is Ownable2StepUpgradeable, ReentrancyGuardUpgradeable

/**
* @dev Transfers the funds from the sender and repays a borrow in vToken on behalf of the borrower
* @param vTokenToRepay VToken to repay to
* @param borrower The address of the borrower, whose debt to repay
* @param repayAmount The amount to repay in terms of underlying
*/
function _repay(address borrower, uint256 repayAmount) internal returns (uint256 actualRepaymentAmount) {
function _repay(
IVBep20 vTokenToRepay,
address borrower,
uint256 repayAmount
) internal returns (uint256 actualRepaymentAmount) {
IERC20Upgradeable underlying = IERC20Upgradeable(vTokenToRepay.underlying());
uint256 balanceBefore = underlying.balanceOf(address(this));
underlying.safeTransferFrom(msg.sender, address(this), repayAmount);
Expand All @@ -143,13 +186,13 @@ contract MoveDebtDelegate is Ownable2StepUpgradeable, ReentrancyGuardUpgradeable

/**
* @dev Borrows in vToken on behalf of the borrower and transfers the funds to the sender
* @param vToken VToken to borrow from
* @param vTokenToBorrow VToken to borrow from
* @param borrowAmount The amount to borrow in terms of underlying
*/
function _borrow(IVBep20 vToken, uint256 borrowAmount) internal {
IERC20Upgradeable underlying = IERC20Upgradeable(vToken.underlying());
function _borrow(IVBep20 vTokenToBorrow, uint256 borrowAmount) internal {
IERC20Upgradeable underlying = IERC20Upgradeable(vTokenToBorrow.underlying());
uint256 balanceBefore = underlying.balanceOf(address(this));
uint256 err = vToken.borrowBehalf(newBorrower, borrowAmount);
uint256 err = vTokenToBorrow.borrowBehalf(newBorrower, borrowAmount);
if (err != NO_ERROR) {
revert BorrowFailed(err);
}
Expand Down
Loading

0 comments on commit 167b402

Please sign in to comment.