/
FacetBase.sol
198 lines (170 loc) · 7.62 KB
/
FacetBase.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
pragma solidity 0.5.16;
import "../../../Utils/ErrorReporter.sol";
import "../../../Tokens/VTokens/VToken.sol";
import "../../../Utils/ExponentialNoError.sol";
import "../../../Comptroller/ComptrollerStorage.sol";
import "../../../Governance/IAccessControlManager.sol";
import "../../../Utils/SafeBEP20.sol";
contract FacetBase is ComptrollerV12Storage, ExponentialNoError {
/// @notice Emitted when an account enters a market
event MarketEntered(VToken vToken, address account);
/// @notice Emitted when XVS is distributed to VAI Vault
event DistributedVAIVaultVenus(uint amount);
using SafeBEP20 for IBEP20;
/// @notice The initial Venus index for a market
uint224 public constant venusInitialIndex = 1e36;
// closeFactorMantissa must be strictly greater than this value
uint internal constant closeFactorMinMantissa = 0.05e18; // 0.05
// closeFactorMantissa must not exceed this value
uint internal constant closeFactorMaxMantissa = 0.9e18; // 0.9
// No collateralFactorMantissa may exceed this value
uint internal constant collateralFactorMaxMantissa = 0.9e18; // 0.9
/// @notice Reverts if the protocol is paused
function checkProtocolPauseState() internal view {
require(!protocolPaused, "protocol is paused");
}
/// @notice Reverts if a certain action is paused on a market
function checkActionPauseState(address market, Action action) internal view {
require(!actionPaused(market, action), "action is paused");
}
/// @notice Reverts if the caller is not admin
function ensureAdmin() internal view {
require(msg.sender == admin, "only admin can");
}
/// @notice Checks the passed address is nonzero
function ensureNonzeroAddress(address someone) internal pure {
require(someone != address(0), "can't be zero address");
}
/// @notice Reverts if the market is not listed
function ensureListed(Market storage market) internal view {
require(market.isListed, "market not listed");
}
/// @notice Reverts if the caller is neither admin nor the passed address
function ensureAdminOr(address privilegedAddress) internal view {
require(msg.sender == admin || msg.sender == privilegedAddress, "access denied");
}
function ensureAllowed(string memory functionSig) internal view {
require(IAccessControlManager(accessControl).isAllowedToCall(msg.sender, functionSig), "access denied");
}
/**
* @notice Checks if a certain action is paused on a market
* @param action Action id
* @param market vToken address
*/
function actionPaused(address market, Action action) public view returns (bool) {
return _actionPaused[market][uint(action)];
}
/**
* @notice Get the latest block number
*/
function getBlockNumber() public view returns (uint) {
return block.number;
}
/**
* @notice Transfer XVS to VAI Vault
*/
function releaseToVault() public {
if (releaseStartBlock == 0 || getBlockNumber() < releaseStartBlock) {
return;
}
uint256 xvsBalance = IBEP20(getXVSAddress()).balanceOf(address(this));
if (xvsBalance == 0) {
return;
}
uint256 actualAmount;
uint256 deltaBlocks = sub_(getBlockNumber(), releaseStartBlock);
// releaseAmount = venusVAIVaultRate * deltaBlocks
uint256 _releaseAmount = mul_(venusVAIVaultRate, deltaBlocks);
if (xvsBalance >= _releaseAmount) {
actualAmount = _releaseAmount;
} else {
actualAmount = xvsBalance;
}
if (actualAmount < minReleaseAmount) {
return;
}
releaseStartBlock = getBlockNumber();
IBEP20(getXVSAddress()).safeTransfer(vaiVaultAddress, actualAmount);
emit DistributedVAIVaultVenus(actualAmount);
IVAIVault(vaiVaultAddress).updatePendingRewards();
}
/**
* @notice Return the address of the XVS token
* @return The address of XVS
*/
function getXVSAddress() public pure returns (address) {
return 0xcF6BB5389c92Bdda8a3747Ddb454cB7a64626C63;
}
/**
* @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed
* @param vTokenModify The market to hypothetically redeem/borrow in
* @param account The account to determine liquidity for
* @param redeemTokens The number of tokens to hypothetically redeem
* @param borrowAmount The amount of underlying to hypothetically borrow
* @dev Note that we calculate the exchangeRateStored for each collateral vToken using stored data,
* without calculating accumulated interest.
* @return (possible error code,
hypothetical account liquidity in excess of collateral requirements,
* hypothetical account shortfall below collateral requirements)
*/
function getHypotheticalAccountLiquidityInternal(
address account,
VToken vTokenModify,
uint redeemTokens,
uint borrowAmount
) internal view returns (ComptrollerErrorReporter.Error, uint, uint) {
(uint err, uint liquidity, uint shortfall) = comptrollerLens.getHypotheticalAccountLiquidity(
address(this),
account,
vTokenModify,
redeemTokens,
borrowAmount
);
return (ComptrollerErrorReporter.Error(err), liquidity, shortfall);
}
/**
* @notice Add the market to the borrower's "assets in" for liquidity calculations
* @param vToken The market to enter
* @param borrower The address of the account to modify
* @return Success indicator for whether the market was entered
*/
function addToMarketInternal(VToken vToken, address borrower) internal returns (ComptrollerErrorReporter.Error) {
checkActionPauseState(address(vToken), Action.ENTER_MARKET);
Market storage marketToJoin = markets[address(vToken)];
ensureListed(marketToJoin);
if (marketToJoin.accountMembership[borrower]) {
// already joined
return ComptrollerErrorReporter.Error.NO_ERROR;
}
// survived the gauntlet, add to list
// NOTE: we store these somewhat redundantly as a significant optimization
// this avoids having to iterate through the list for the most common use cases
// that is, only when we need to perform liquidity checks
// and not whenever we want to check if an account is in a particular market
marketToJoin.accountMembership[borrower] = true;
accountAssets[borrower].push(vToken);
emit MarketEntered(vToken, borrower);
return ComptrollerErrorReporter.Error.NO_ERROR;
}
function redeemAllowedInternal(address vToken, address redeemer, uint redeemTokens) internal view returns (uint) {
ensureListed(markets[vToken]);
/* If the redeemer is not 'in' the market, then we can bypass the liquidity check */
if (!markets[vToken].accountMembership[redeemer]) {
return uint(ComptrollerErrorReporter.Error.NO_ERROR);
}
/* Otherwise, perform a hypothetical liquidity check to guard against shortfall */
(ComptrollerErrorReporter.Error err, , uint shortfall) = getHypotheticalAccountLiquidityInternal(
redeemer,
VToken(vToken),
redeemTokens,
0
);
if (err != ComptrollerErrorReporter.Error.NO_ERROR) {
return uint(err);
}
if (shortfall != 0) {
return uint(ComptrollerErrorReporter.Error.INSUFFICIENT_LIQUIDITY);
}
return uint(ComptrollerErrorReporter.Error.NO_ERROR);
}
}