Skip to content

Commit

Permalink
Merge pull request #85 from VenusProtocol/fix/protocol-share
Browse files Browse the repository at this point in the history
[VEN-783] Fix: Protocol share reserve for each market and add access control for addMarket.
  • Loading branch information
coreyar authored Nov 14, 2022
2 parents 385d5c8 + deb035b commit 87bda9a
Show file tree
Hide file tree
Showing 12 changed files with 675 additions and 93 deletions.
1 change: 0 additions & 1 deletion contracts/Governance/AccessControlManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import "@openzeppelin/contracts/access/AccessControl.sol";
* within Venus Smart Contract Ecosystem
*/
contract AccessControlManager is AccessControl {

/// @notice Emitted when an account is given a permission to a certain contract function
/// NOTE: If contract address is 0x000..0 this means that the account is a default admin of this function and
/// can call any contract function with this signature
Expand Down
10 changes: 4 additions & 6 deletions contracts/Pool/PoolRegistry.sol
Original file line number Diff line number Diff line change
Expand Up @@ -345,7 +345,7 @@ contract PoolRegistry is OwnableUpgradeable {
/**
* @notice Add a market to an existing pool
*/
function addMarket(AddMarketInput memory input) external {
function addMarket(AddMarketInput memory input) external onlyOwner {
InterestRateModel rate;
if (input.rateModel == InterestRateModels.JumpRate) {
rate = InterestRateModel(
Expand All @@ -368,8 +368,8 @@ contract PoolRegistry is OwnableUpgradeable {

Comptroller comptroller = Comptroller(input.comptroller);

VTokenProxyFactory.VTokenArgs
memory initializeArgs = VTokenProxyFactory.VTokenArgs(
VTokenProxyFactory.VTokenArgs memory initializeArgs = VTokenProxyFactory
.VTokenArgs(
input.asset,
comptroller,
rate,
Expand All @@ -388,9 +388,7 @@ contract PoolRegistry is OwnableUpgradeable {
input.tokenImplementation_
);

VToken vToken = vTokenFactory.deployVTokenProxy(
initializeArgs
);
VToken vToken = vTokenFactory.deployVTokenProxy(initializeArgs);

comptroller._supportMarket(vToken);
comptroller._setCollateralFactor(
Expand Down
6 changes: 6 additions & 0 deletions contracts/RiskFund/IProtocolShareReserve.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.13;

interface IProtocolShareReserve {
function updateAssetsState(address comptroller, address asset) external;
}
4 changes: 3 additions & 1 deletion contracts/RiskFund/IRiskFund.sol
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.10;
pragma solidity 0.8.13;

interface IRiskFund {
function swapAllPoolsAssets() external returns (uint256);
Expand All @@ -12,4 +12,6 @@ interface IRiskFund {
function transferReserveForAuction(address comptroller, uint256 amount)
external
returns (uint256);

function updateAssetsState(address comptroller, address asset) external;
}
30 changes: 22 additions & 8 deletions contracts/RiskFund/ProtocolShareReserve.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@ import "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import "../ExponentialNoError.sol";
import "./IRiskFund.sol";
import "./ReserveHelpers.sol";

contract ProtocolShareReserve is OwnableUpgradeable, ExponentialNoError {
contract ProtocolShareReserve is
OwnableUpgradeable,
ExponentialNoError,
ReserveHelpers
{
using SafeERC20 for IERC20;

address private liquidatedShares;
Expand Down Expand Up @@ -42,19 +48,23 @@ contract ProtocolShareReserve is OwnableUpgradeable, ExponentialNoError {
* @param amount Amount to release.
* @return Number of total released tokens.
*/
function releaseFunds(address asset, uint256 amount)
external
onlyOwner
returns (uint256)
{
function releaseFunds(
address comptroller,
address asset,
uint256 amount
) external onlyOwner returns (uint256) {
require(
asset != address(0),
"Liquidated shares Reserves: Asset address invalid"
);
require(
amount <= IERC20(asset).balanceOf(address(this)),
"Liquidated shares Reserves: Insufficient balance"
amount <= poolsAssetsReserves[comptroller][asset],
"Liquidated shares Reserves: Insufficient pool balance"
);

assetsReserves[asset] -= amount;
poolsAssetsReserves[comptroller][asset] -= amount;

IERC20(asset).safeTransfer(
liquidatedShares,
mul_(
Expand All @@ -69,6 +79,10 @@ contract ProtocolShareReserve is OwnableUpgradeable, ExponentialNoError {
div_(Exp({mantissa: 30 * expScale}), 100)
).mantissa
);

// Update the pool asset's state in the risk fund for the above transfer.
IRiskFund(riskFund).updateAssetsState(comptroller, asset);

return amount;
}
}
66 changes: 66 additions & 0 deletions contracts/RiskFund/ReserveHelpers.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity 0.8.13;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import "../ComptrollerInterface.sol";

contract ReserveHelpers {
using SafeERC20 for IERC20;

// Store the previous state for the asset transferred to ProtocolShareReserve combined(for all pools).
mapping(address => uint256) internal assetsReserves;

// Store the asset's reserve per pool in the ProtocolShareReserve.
// Comptroller(pool) -> Asset -> amount
mapping(address => mapping(address => uint256))
internal poolsAssetsReserves;

/**
* @dev Update the reserve of the asset for the specific pool after transferring to risk fund.
* @param comptroller Comptroller address(pool).
* @param asset Asset address.
*/
function updateAssetsState(address comptroller, address asset) external {
require(
ComptrollerInterface(comptroller).isComptroller(),
"Liquidated shares Reserves: Comptroller address invalid"
);
require(
asset != address(0),
"Liquidated shares Reserves: Asset address invalid"
);
uint256 currentBalance = IERC20(asset).balanceOf(address(this));
uint256 assetReserve = assetsReserves[asset];
if (currentBalance > assetReserve) {
uint256 balanceDifference;
unchecked {
balanceDifference = currentBalance - assetReserve;
}
assetsReserves[asset] += balanceDifference;
poolsAssetsReserves[comptroller][asset] += balanceDifference;
}
}

/**
* @dev Get the Amount of the asset in the risk fund for the specific pool.
* @param comptroller Comptroller address(pool).
* @param asset Asset address.
* @return Asset's reserve in risk fund.
*/
function getPoolAssetReserve(address comptroller, address asset)
external
view
returns (uint256)
{
require(
ComptrollerInterface(comptroller).isComptroller(),
"Liquidated shares Reserves: Comptroller address invalid"
);
require(
asset != address(0),
"Liquidated shares Reserves: Asset address invalid"
);
return poolsAssetsReserves[comptroller][asset];
}
}
97 changes: 62 additions & 35 deletions contracts/RiskFund/RiskFund.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,44 +9,47 @@ import "../Pool/PoolRegistry.sol";
import "../Pool/PoolRegistryInterface.sol";
import "../IPancakeswapV2Router.sol";
import "../Pool/PoolRegistry.sol";
import "./ReserveHelpers.sol";

/**
* @dev This contract does not support BNB.
*/
contract RiskFund is OwnableUpgradeable, ExponentialNoError {
contract RiskFund is OwnableUpgradeable, ExponentialNoError, ReserveHelpers {
using SafeERC20 for IERC20;

address private poolRegistry;
address private pancakeSwapRouter;
uint256 private minAmountToConvert;
uint256 private amountOutMin;
address private convertableBUSDAddress;
address private convertableBaseAsset;
address private auctionContractAddress;
address private accessControl;

// Store base asset's reserve for specific pool.
mapping(address => uint256) private poolReserves;

/**
* @dev Initializes the deployer to owner.
* @param _pancakeSwapRouter Address of the pancake swap router.
* @param _amountOutMin Min amount out for the pancake swap.
* @param _minAmountToConvert Asset should be worth of min amount to convert to BUSD
* @param _convertableBUSDAddress Address of the BUSD
* @param _minAmountToConvert Asset should be worth of min amount to convert into base asset
* @param _convertableBaseAsset Address of the base asset
* @param _accessControl Address of the access control contract.
*/
function initialize(
address _pancakeSwapRouter,
uint256 _amountOutMin,
uint256 _minAmountToConvert,
address _convertableBUSDAddress,
address _convertableBaseAsset,
address _accessControl
) public initializer {
require(
_pancakeSwapRouter != address(0),
"Risk Fund: Pancake swap address invalid"
);
require(
_convertableBUSDAddress != address(0),
"Risk Fund: BUSD address invalid"
_convertableBaseAsset != address(0),
"Risk Fund: Base asset address invalid"
);
require(
_minAmountToConvert > 0,
Expand All @@ -58,7 +61,7 @@ contract RiskFund is OwnableUpgradeable, ExponentialNoError {
pancakeSwapRouter = _pancakeSwapRouter;
amountOutMin = _amountOutMin;
minAmountToConvert = _minAmountToConvert;
convertableBUSDAddress = _convertableBUSDAddress;
convertableBaseAsset = _convertableBaseAsset;
accessControl = _accessControl;
}

Expand All @@ -74,6 +77,21 @@ contract RiskFund is OwnableUpgradeable, ExponentialNoError {
poolRegistry = _poolRegistry;
}

/**
* @dev convertable base asset setter
* @param _convertableBaseAsset Address of the asset.
*/
function setConvertableBaseAsset(address _convertableBaseAsset)
external
onlyOwner
{
require(
_convertableBaseAsset != address(0),
"Risk Fund: Asset address invalid"
);
convertableBaseAsset = _convertableBaseAsset;
}

/**
* @dev Auction contract address setter
* @param _auctionContractAddress Address of the auction contract.
Expand Down Expand Up @@ -129,10 +147,10 @@ contract RiskFund is OwnableUpgradeable, ExponentialNoError {
}

/**
* @dev Swap single asset to BUSD.
* @dev Swap single asset to Base asset.
* @param vToken VToken
* @param comptroller comptorller address
* @return Number of BUSD tokens.
* @return Number of swapped tokens.
*/
function swapAsset(VToken vToken, address comptroller)
internal
Expand All @@ -141,9 +159,9 @@ contract RiskFund is OwnableUpgradeable, ExponentialNoError {
uint256 totalAmount;

address underlyingAsset = VTokenInterface(address(vToken)).underlying();
uint256 balanceOfUnderlyingAsset = IERC20(underlyingAsset).balanceOf(
address(this)
);
uint256 balanceOfUnderlyingAsset = poolsAssetsReserves[comptroller][
underlyingAsset
];

ComptrollerViewInterface(comptroller).oracle().updatePrice(
address(vToken)
Expand All @@ -161,32 +179,41 @@ contract RiskFund is OwnableUpgradeable, ExponentialNoError {
);

if (amountInUsd >= minAmountToConvert) {
address[] memory path = new address[](2);
path[0] = underlyingAsset;
path[1] = convertableBUSDAddress;
IERC20(underlyingAsset).safeApprove(
pancakeSwapRouter,
balanceOfUnderlyingAsset
);
uint256[] memory amounts = IPancakeswapV2Router(
pancakeSwapRouter
).swapExactTokensForTokens(
balanceOfUnderlyingAsset,
amountOutMin,
path,
address(this),
block.timestamp
assetsReserves[underlyingAsset] -= balanceOfUnderlyingAsset;
poolsAssetsReserves[comptroller][
underlyingAsset
] -= balanceOfUnderlyingAsset;

if (underlyingAsset != convertableBaseAsset) {
address[] memory path = new address[](2);
path[0] = underlyingAsset;
path[1] = convertableBaseAsset;
IERC20(underlyingAsset).safeApprove(
pancakeSwapRouter,
balanceOfUnderlyingAsset
);
totalAmount = amounts[1];
uint256[] memory amounts = IPancakeswapV2Router(
pancakeSwapRouter
).swapExactTokensForTokens(
balanceOfUnderlyingAsset,
amountOutMin,
path,
address(this),
block.timestamp
);
totalAmount = amounts[1];
} else {
totalAmount = balanceOfUnderlyingAsset;
}
}
}
return totalAmount;
}

/**
* @dev Swap assets of selected pools into BUSD tokens.
* @param venusPools Array of Pools to swap for BUSD
* @return Number of BUSD tokens.
* @dev Swap assets of selected pools into base tokens.
* @param venusPools Array of Pools to swap
* @return Number of swapped tokens.
*/
function swapPoolsAssets(PoolRegistry.VenusPool[] memory venusPools)
public
Expand Down Expand Up @@ -214,8 +241,8 @@ contract RiskFund is OwnableUpgradeable, ExponentialNoError {
}

/**
* @dev Swap assets of all pools into BUSD tokens.
* @return Number of BUSD tokens.
* @dev Swap assets of all pools into base asset's tokens.
* @return Number of swapped tokens.
*/
function swapAllPoolsAssets() external returns (uint256) {
require(
Expand Down Expand Up @@ -276,7 +303,7 @@ contract RiskFund is OwnableUpgradeable, ExponentialNoError {
"Risk Fund: Insufficient pool reserve."
);
poolReserves[comptroller] = poolReserves[comptroller] - amount;
IERC20(convertableBUSDAddress).safeTransfer(
IERC20(convertableBaseAsset).safeTransfer(
auctionContractAddress,
amount
);
Expand Down
Loading

0 comments on commit 87bda9a

Please sign in to comment.