¡El código fuente de este contrato está verificado!
Metadatos del Contrato
Compilador
0.8.10+commit.fc410830
Idioma
Solidity
Código Fuente del Contrato
Archivo 1 de 45: Address.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)pragmasolidity ^0.8.1;/**
* @dev Collection of functions related to the address type
*/libraryAddress{
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/functionisContract(address account) internalviewreturns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0// for contracts in construction, since the code is only stored at the end// of the constructor execution.return account.code.length>0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/functionsendValue(addresspayable recipient, uint256 amount) internal{
require(address(this).balance>= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/functionfunctionCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value
) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value,
stringmemory errorMessage
) internalreturns (bytesmemory) {
require(address(this).balance>= value, "Address: insufficient balance for call");
(bool success, bytesmemory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target, bytesmemory data) internalviewreturns (bytesmemory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
(bool success, bytesmemory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/functionfunctionDelegateCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
(bool success, bytesmemory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/functionverifyCallResultFromTarget(address target,
bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
if (success) {
if (returndata.length==0) {
// only check isContract if the call was successful and the return data is empty// otherwise we already know that it was a contractrequire(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/functionverifyCallResult(bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalpurereturns (bytesmemory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function_revert(bytesmemory returndata, stringmemory errorMessage) privatepure{
// Look for revert reason and bubble it up if presentif (returndata.length>0) {
// The easiest way to bubble the revert reason is using memory via assembly/// @solidity memory-safe-assemblyassembly {
let returndata_size :=mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
Código Fuente del Contrato
Archivo 2 de 45: AddressUpgradeable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (utils/Address.sol)pragmasolidity ^0.8.1;/**
* @dev Collection of functions related to the address type
*/libraryAddressUpgradeable{
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/functionisContract(address account) internalviewreturns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0// for contracts in construction, since the code is only stored at the end// of the constructor execution.return account.code.length>0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.5.11/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/functionsendValue(addresspayable recipient, uint256 amount) internal{
require(address(this).balance>= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/functionfunctionCall(address target, bytesmemory data) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value
) internalreturns (bytesmemory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/functionfunctionCallWithValue(address target,
bytesmemory data,
uint256 value,
stringmemory errorMessage
) internalreturns (bytesmemory) {
require(address(this).balance>= value, "Address: insufficient balance for call");
(bool success, bytesmemory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target, bytesmemory data) internalviewreturns (bytesmemory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/functionfunctionStaticCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
(bool success, bytesmemory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/functionverifyCallResultFromTarget(address target,
bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalviewreturns (bytesmemory) {
if (success) {
if (returndata.length==0) {
// only check isContract if the call was successful and the return data is empty// otherwise we already know that it was a contractrequire(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/functionverifyCallResult(bool success,
bytesmemory returndata,
stringmemory errorMessage
) internalpurereturns (bytesmemory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function_revert(bytesmemory returndata, stringmemory errorMessage) privatepure{
// Look for revert reason and bubble it up if presentif (returndata.length>0) {
// The easiest way to bubble the revert reason is using memory via assembly/// @solidity memory-safe-assemblyassembly {
let returndata_size :=mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}
Código Fuente del Contrato
Archivo 3 de 45: AddressesProvider.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;import { SafeOwnableUpgradeable } from"../ionic/SafeOwnableUpgradeable.sol";
/**
* @title AddressesProvider
* @notice The Addresses Provider serves as a central storage of system internal and external
* contract addresses that change between deploys and across chains
* @author Veliko Minkov <veliko@midascapital.xyz>
*/contractAddressesProviderisSafeOwnableUpgradeable{
mapping(string=>address) private _addresses;
mapping(address=> Contract) public plugins;
mapping(address=> Contract) public flywheelRewards;
mapping(address=> RedemptionStrategy) public redemptionStrategiesConfig;
mapping(address=> FundingStrategy) public fundingStrategiesConfig;
JarvisPool[] public jarvisPoolsConfig;
CurveSwapPool[] public curveSwapPoolsConfig;
mapping(address=>mapping(address=>address)) public balancerPoolForTokens;
/// @dev Initializer to set the admin that can set and change contracts addressesfunctioninitialize(address owner) publicinitializer{
__SafeOwnable_init(owner);
}
/**
* @dev The contract address and a string that uniquely identifies the contract's interface
*/structContract {
address addr;
string contractInterface;
}
structRedemptionStrategy {
address addr;
string contractInterface;
address outputToken;
}
structFundingStrategy {
address addr;
string contractInterface;
address inputToken;
}
structJarvisPool {
address syntheticToken;
address collateralToken;
address liquidityPool;
uint256 expirationTime;
}
structCurveSwapPool {
address poolAddress;
address[] coins;
}
/**
* @dev sets the address and contract interface ID of the flywheel for the reward token
* @param rewardToken the reward token address
* @param flywheelRewardsModule the flywheel rewards module address
* @param contractInterface a string that uniquely identifies the contract's interface
*/functionsetFlywheelRewards(address rewardToken,
address flywheelRewardsModule,
stringcalldata contractInterface
) publiconlyOwner{
flywheelRewards[rewardToken] = Contract(flywheelRewardsModule, contractInterface);
}
/**
* @dev sets the address and contract interface ID of the ERC4626 plugin for the asset
* @param asset the asset address
* @param plugin the ERC4626 plugin address
* @param contractInterface a string that uniquely identifies the contract's interface
*/functionsetPlugin(address asset,
address plugin,
stringcalldata contractInterface
) publiconlyOwner{
plugins[asset] = Contract(plugin, contractInterface);
}
/**
* @dev sets the address and contract interface ID of the redemption strategy for the asset
* @param asset the asset address
* @param strategy redemption strategy address
* @param contractInterface a string that uniquely identifies the contract's interface
*/functionsetRedemptionStrategy(address asset,
address strategy,
stringcalldata contractInterface,
address outputToken
) publiconlyOwner{
redemptionStrategiesConfig[asset] = RedemptionStrategy(strategy, contractInterface, outputToken);
}
functiongetRedemptionStrategy(address asset) publicviewreturns (RedemptionStrategy memory) {
return redemptionStrategiesConfig[asset];
}
/**
* @dev sets the address and contract interface ID of the funding strategy for the asset
* @param asset the asset address
* @param strategy funding strategy address
* @param contractInterface a string that uniquely identifies the contract's interface
*/functionsetFundingStrategy(address asset,
address strategy,
stringcalldata contractInterface,
address inputToken
) publiconlyOwner{
fundingStrategiesConfig[asset] = FundingStrategy(strategy, contractInterface, inputToken);
}
functiongetFundingStrategy(address asset) publicviewreturns (FundingStrategy memory) {
return fundingStrategiesConfig[asset];
}
/**
* @dev configures the Jarvis pool of a Jarvis synthetic token
* @param syntheticToken the synthetic token address
* @param collateralToken the collateral token address
* @param liquidityPool the liquidity pool address
* @param expirationTime the operation expiration time
*/functionsetJarvisPool(address syntheticToken,
address collateralToken,
address liquidityPool,
uint256 expirationTime
) publiconlyOwner{
jarvisPoolsConfig.push(JarvisPool(syntheticToken, collateralToken, liquidityPool, expirationTime));
}
functionsetCurveSwapPool(address poolAddress, address[] calldata coins) publiconlyOwner{
curveSwapPoolsConfig.push(CurveSwapPool(poolAddress, coins));
}
/**
* @dev Sets an address for an id replacing the address saved in the addresses map
* @param id The id
* @param newAddress The address to set
*/functionsetAddress(stringcalldata id, address newAddress) externalonlyOwner{
_addresses[id] = newAddress;
}
/**
* @dev Returns an address by id
* @return The address
*/functiongetAddress(stringcalldata id) publicviewreturns (address) {
return _addresses[id];
}
functiongetCurveSwapPools() publicviewreturns (CurveSwapPool[] memory) {
return curveSwapPoolsConfig;
}
functiongetJarvisPools() publicviewreturns (JarvisPool[] memory) {
return jarvisPoolsConfig;
}
functionsetBalancerPoolForTokens(address inputToken,
address outputToken,
address pool
) externalonlyOwner{
balancerPoolForTokens[inputToken][outputToken] = pool;
}
functiongetBalancerPoolForTokens(address inputToken, address outputToken) externalviewreturns (address) {
return balancerPoolForTokens[inputToken][outputToken];
}
}
Código Fuente del Contrato
Archivo 4 de 45: Auth.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)abstractcontractAuth{
eventOwnerUpdated(addressindexed user, addressindexed newOwner);
eventAuthorityUpdated(addressindexed user, Authority indexed newAuthority);
addresspublic owner;
Authority public authority;
constructor(address _owner, Authority _authority) {
owner = _owner;
authority = _authority;
emit OwnerUpdated(msg.sender, _owner);
emit AuthorityUpdated(msg.sender, _authority);
}
modifierrequiresAuth() virtual{
require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");
_;
}
functionisAuthorized(address user, bytes4 functionSig) internalviewvirtualreturns (bool) {
Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.// Checking if the caller is the owner only after calling the authority saves gas in most cases, but be// aware that this makes protected functions uncallable even to the owner if the authority is out of order.return (address(auth) !=address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
}
functionsetAuthority(Authority newAuthority) publicvirtual{
// We check if the caller is the owner first because we want to ensure they can// always swap out the authority even if it's reverting or using up a lot of gas.require(msg.sender== owner || authority.canCall(msg.sender, address(this), msg.sig));
authority = newAuthority;
emit AuthorityUpdated(msg.sender, newAuthority);
}
functionsetOwner(address newOwner) publicvirtualrequiresAuth{
owner = newOwner;
emit OwnerUpdated(msg.sender, newOwner);
}
}
/// @notice A generic interface for a contract which provides authorization data to an Auth instance./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)interfaceAuthority{
functioncanCall(address user,
address target,
bytes4 functionSig
) externalviewreturns (bool);
}
Código Fuente del Contrato
Archivo 5 de 45: AuthoritiesRegistry.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity >=0.8.0;import { PoolRolesAuthority } from"../ionic/PoolRolesAuthority.sol";
import { SafeOwnableUpgradeable } from"../ionic/SafeOwnableUpgradeable.sol";
import { IonicComptroller } from"../compound/ComptrollerInterface.sol";
import { TransparentUpgradeableProxy } from"@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
contractAuthoritiesRegistryisSafeOwnableUpgradeable{
mapping(address=> PoolRolesAuthority) public poolsAuthorities;
PoolRolesAuthority public poolAuthLogic;
addresspublic leveredPositionsFactory;
boolpublic noAuthRequired;
functioninitialize(address _leveredPositionsFactory) publicinitializer{
__SafeOwnable_init(msg.sender);
leveredPositionsFactory = _leveredPositionsFactory;
poolAuthLogic =new PoolRolesAuthority();
}
functionreinitialize(address _leveredPositionsFactory) publiconlyOwnerOrAdmin{
leveredPositionsFactory = _leveredPositionsFactory;
poolAuthLogic =new PoolRolesAuthority();
// for Neon the auth is not required
noAuthRequired =block.chainid==245022934;
}
functioncreatePoolAuthority(address pool) publiconlyOwnerreturns (PoolRolesAuthority auth) {
require(address(poolsAuthorities[pool]) ==address(0), "already created");
TransparentUpgradeableProxy proxy =new TransparentUpgradeableProxy(address(poolAuthLogic), _getProxyAdmin(), "");
auth = PoolRolesAuthority(address(proxy));
auth.initialize(address(this));
poolsAuthorities[pool] = auth;
auth.openPoolSupplierCapabilities(IonicComptroller(pool));
auth.setUserRole(address(this), auth.REGISTRY_ROLE(), true);
// sets the registry owner as the auth owner
reconfigureAuthority(pool);
}
functionreconfigureAuthority(address poolAddress) public{
IonicComptroller pool = IonicComptroller(poolAddress);
PoolRolesAuthority auth = poolsAuthorities[address(pool)];
if (msg.sender!= poolAddress ||address(auth) !=address(0)) {
require(address(auth) !=address(0), "no such authority");
require(msg.sender== owner() ||msg.sender== poolAddress, "not owner or pool");
auth.configureRegistryCapabilities();
auth.configurePoolSupplierCapabilities(pool);
auth.configurePoolBorrowerCapabilities(pool);
// everyone can be a liquidator
auth.configureOpenPoolLiquidatorCapabilities(pool);
auth.configureLeveredPositionCapabilities(pool);
if (auth.owner() != owner()) {
auth.setOwner(owner());
}
}
}
functioncanCall(address pool,
address user,
address target,
bytes4 functionSig
) externalviewreturns (bool) {
PoolRolesAuthority authorityForPool = poolsAuthorities[pool];
if (address(authorityForPool) ==address(0)) {
return noAuthRequired;
} else {
// allow only if an auth exists and it allows the actionreturn authorityForPool.canCall(user, target, functionSig);
}
}
functionsetUserRole(address pool,
address user,
uint8 role,
bool enabled
) external{
PoolRolesAuthority poolAuth = poolsAuthorities[pool];
require(address(poolAuth) !=address(0), "auth does not exist");
require(msg.sender== owner() ||msg.sender== leveredPositionsFactory, "not owner or factory");
require(msg.sender!= leveredPositionsFactory || role == poolAuth.LEVERED_POSITION_ROLE(), "only lev pos role");
poolAuth.setUserRole(user, role, enabled);
}
}
Código Fuente del Contrato
Archivo 6 de 45: BasePriceOracle.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity >=0.8.0;import"../compound/CTokenInterfaces.sol";
/**
* @title BasePriceOracle
* @notice Returns prices of underlying tokens directly without the caller having to specify a cToken address.
* @dev Implements the `PriceOracle` interface.
* @author David Lucid <david@rari.capital> (https://github.com/davidlucid)
*/interfaceBasePriceOracle{
/**
* @notice Get the price of an underlying asset.
* @param underlying The underlying asset to get the price of.
* @return The underlying asset price in ETH as a mantissa (scaled by 1e18).
* Zero means the price is unavailable.
*/functionprice(address underlying) externalviewreturns (uint256);
/**
* @notice Get the underlying price of a cToken asset
* @param cToken The cToken to get the underlying price of
* @return The underlying asset price mantissa (scaled by 1e18).
* Zero means the price is unavailable.
*/functiongetUnderlyingPrice(ICErc20 cToken) externalviewreturns (uint256);
}
Código Fuente del Contrato
Archivo 7 de 45: CTokenInterfaces.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity >=0.8.0;import { IonicComptroller } from"./ComptrollerInterface.sol";
import { InterestRateModel } from"./InterestRateModel.sol";
import { ComptrollerV3Storage } from"./ComptrollerStorage.sol";
import { AddressesProvider } from"../ionic/AddressesProvider.sol";
abstractcontractCTokenAdminStorage{
/*
* Administrator for Ionic
*/addresspayablepublic ionicAdmin;
}
abstractcontractCErc20StorageisCTokenAdminStorage{
/**
* @dev Guard variable for re-entrancy checks
*/boolinternal _notEntered;
/**
* @notice EIP-20 token name for this token
*/stringpublic name;
/**
* @notice EIP-20 token symbol for this token
*/stringpublic symbol;
/**
* @notice EIP-20 token decimals for this token
*/uint8public decimals;
/*
* Maximum borrow rate that can ever be applied (.0005% / block)
*/uint256internalconstant borrowRateMaxMantissa =0.0005e16;
/*
* Maximum fraction of interest that can be set aside for reserves + fees
*/uint256internalconstant reserveFactorPlusFeesMaxMantissa =1e18;
/**
* @notice Contract which oversees inter-cToken operations
*/
IonicComptroller public comptroller;
/**
* @notice Model which tells what the current interest rate should be
*/
InterestRateModel public interestRateModel;
/*
* Initial exchange rate used when minting the first CTokens (used when totalSupply = 0)
*/uint256internal initialExchangeRateMantissa;
/**
* @notice Fraction of interest currently set aside for admin fees
*/uint256public adminFeeMantissa;
/**
* @notice Fraction of interest currently set aside for Ionic fees
*/uint256public ionicFeeMantissa;
/**
* @notice Fraction of interest currently set aside for reserves
*/uint256public reserveFactorMantissa;
/**
* @notice Block number that interest was last accrued at
*/uint256public accrualBlockNumber;
/**
* @notice Accumulator of the total earned interest rate since the opening of the market
*/uint256public borrowIndex;
/**
* @notice Total amount of outstanding borrows of the underlying in this market
*/uint256public totalBorrows;
/**
* @notice Total amount of reserves of the underlying held in this market
*/uint256public totalReserves;
/**
* @notice Total amount of admin fees of the underlying held in this market
*/uint256public totalAdminFees;
/**
* @notice Total amount of Ionic fees of the underlying held in this market
*/uint256public totalIonicFees;
/**
* @notice Total number of tokens in circulation
*/uint256public totalSupply;
/*
* Official record of token balances for each account
*/mapping(address=>uint256) internal accountTokens;
/*
* Approved token transfer amounts on behalf of others
*/mapping(address=>mapping(address=>uint256)) internal transferAllowances;
/**
* @notice Container for borrow balance information
* @member principal Total balance (with accrued interest), after applying the most recent balance-changing action
* @member interestIndex Global borrowIndex as of the most recent balance-changing action
*/structBorrowSnapshot {
uint256 principal;
uint256 interestIndex;
}
/*
* Mapping of account addresses to outstanding borrow balances
*/mapping(address=> BorrowSnapshot) internal accountBorrows;
/*
* Share of seized collateral that is added to reserves
*/uint256publicconstant protocolSeizeShareMantissa =2.8e16; //2.8%/*
* Share of seized collateral taken as fees
*/uint256publicconstant feeSeizeShareMantissa =1e17; //10%/**
* @notice Underlying asset for this CToken
*/addresspublic underlying;
/**
* @notice Addresses Provider
*/
AddressesProvider public ap;
}
abstractcontractCTokenBaseEvents{
/* ERC20 *//**
* @notice EIP20 Transfer event
*/eventTransfer(addressindexedfrom, addressindexed to, uint256 amount);
/*** Admin Events ***//**
* @notice Event emitted when interestRateModel is changed
*/eventNewMarketInterestRateModel(InterestRateModel oldInterestRateModel, InterestRateModel newInterestRateModel);
/**
* @notice Event emitted when the reserve factor is changed
*/eventNewReserveFactor(uint256 oldReserveFactorMantissa, uint256 newReserveFactorMantissa);
/**
* @notice Event emitted when the admin fee is changed
*/eventNewAdminFee(uint256 oldAdminFeeMantissa, uint256 newAdminFeeMantissa);
/**
* @notice Event emitted when the Ionic fee is changed
*/eventNewIonicFee(uint256 oldIonicFeeMantissa, uint256 newIonicFeeMantissa);
/**
* @notice EIP20 Approval event
*/eventApproval(addressindexed owner, addressindexed spender, uint256 amount);
/**
* @notice Event emitted when interest is accrued
*/eventAccrueInterest(uint256 cashPrior, uint256 interestAccumulated, uint256 borrowIndex, uint256 totalBorrows);
}
abstractcontractCTokenFirstExtensionEventsisCTokenBaseEvents{
eventFlash(address receiver, uint256 amount);
}
abstractcontractCTokenSecondExtensionEventsisCTokenBaseEvents{
/*** Market Events ***//**
* @notice Event emitted when tokens are minted
*/eventMint(address minter, uint256 mintAmount, uint256 mintTokens);
/**
* @notice Event emitted when tokens are redeemed
*/eventRedeem(address redeemer, uint256 redeemAmount, uint256 redeemTokens);
/**
* @notice Event emitted when underlying is borrowed
*/eventBorrow(address borrower, uint256 borrowAmount, uint256 accountBorrows, uint256 totalBorrows);
/**
* @notice Event emitted when a borrow is repaid
*/eventRepayBorrow(address payer, address borrower, uint256 repayAmount, uint256 accountBorrows, uint256 totalBorrows);
/**
* @notice Event emitted when a borrow is liquidated
*/eventLiquidateBorrow(address liquidator,
address borrower,
uint256 repayAmount,
address cTokenCollateral,
uint256 seizeTokens
);
/**
* @notice Event emitted when the reserves are added
*/eventReservesAdded(address benefactor, uint256 addAmount, uint256 newTotalReserves);
/**
* @notice Event emitted when the reserves are reduced
*/eventReservesReduced(address admin, uint256 reduceAmount, uint256 newTotalReserves);
}
interfaceCTokenFirstExtensionInterface{
/*** User Interface ***/functiontransfer(address dst, uint256 amount) externalreturns (bool);
functiontransferFrom(address src,
address dst,
uint256 amount
) externalreturns (bool);
functionapprove(address spender, uint256 amount) externalreturns (bool);
functionallowance(address owner, address spender) externalviewreturns (uint256);
functionbalanceOf(address owner) externalviewreturns (uint256);
/*** Admin Functions ***/function_setReserveFactor(uint256 newReserveFactorMantissa) externalreturns (uint256);
function_setAdminFee(uint256 newAdminFeeMantissa) externalreturns (uint256);
function_setInterestRateModel(InterestRateModel newInterestRateModel) externalreturns (uint256);
functiongetAccountSnapshot(address account)
externalviewreturns (uint256,
uint256,
uint256,
uint256);
functionborrowRatePerBlock() externalviewreturns (uint256);
functionsupplyRatePerBlock() externalviewreturns (uint256);
functionexchangeRateCurrent() externalviewreturns (uint256);
functionaccrueInterest() externalreturns (uint256);
functiontotalBorrowsCurrent() externalviewreturns (uint256);
functionborrowBalanceCurrent(address account) externalviewreturns (uint256);
functiongetTotalUnderlyingSupplied() externalviewreturns (uint256);
functionbalanceOfUnderlying(address owner) externalviewreturns (uint256);
functionmulticall(bytes[] calldata data) externalpayablereturns (bytes[] memory results);
functionflash(uint256 amount, bytescalldata data) external;
functionsupplyRatePerBlockAfterDeposit(uint256 mintAmount) externalviewreturns (uint256);
functionsupplyRatePerBlockAfterWithdraw(uint256 withdrawAmount) externalviewreturns (uint256);
functionborrowRatePerBlockAfterBorrow(uint256 borrowAmount) externalviewreturns (uint256);
functionregisterInSFS() externalreturns (uint256);
}
interfaceCTokenSecondExtensionInterface{
functionmint(uint256 mintAmount) externalreturns (uint256);
functionredeem(uint256 redeemTokens) externalreturns (uint256);
functionredeemUnderlying(uint256 redeemAmount) externalreturns (uint256);
functionborrow(uint256 borrowAmount) externalreturns (uint256);
functionrepayBorrow(uint256 repayAmount) externalreturns (uint256);
functionrepayBorrowBehalf(address borrower, uint256 repayAmount) externalreturns (uint256);
functionliquidateBorrow(address borrower,
uint256 repayAmount,
address cTokenCollateral
) externalreturns (uint256);
functiongetCash() externalviewreturns (uint256);
functionseize(address liquidator,
address borrower,
uint256 seizeTokens
) externalreturns (uint256);
/*** Admin Functions ***/function_withdrawAdminFees(uint256 withdrawAmount) externalreturns (uint256);
function_withdrawIonicFees(uint256 withdrawAmount) externalreturns (uint256);
functionselfTransferOut(address to, uint256 amount) external;
functionselfTransferIn(addressfrom, uint256 amount) externalreturns (uint256);
}
interfaceCDelegatorInterface{
functionimplementation() externalviewreturns (address);
/**
* @notice Called by the admin to update the implementation of the delegator
* @param implementation_ The address of the new implementation for delegation
* @param becomeImplementationData The encoded bytes data to be passed to _becomeImplementation
*/function_setImplementationSafe(address implementation_, bytescalldata becomeImplementationData) external;
/**
* @dev upgrades the implementation if necessary
*/function_upgrade() external;
}
interfaceCDelegateInterface{
/**
* @notice Called by the delegator on a delegate to initialize it for duty
* @dev Should revert if any issues arise which make it unfit for delegation
* @param data The encoded bytes data for any initialization
*/function_becomeImplementation(bytescalldata data) external;
functiondelegateType() externalpurereturns (uint8);
functioncontractType() externalpurereturns (stringmemory);
}
abstractcontractCErc20AdminBaseisCErc20Storage{
/**
* @notice Returns a boolean indicating if the sender has admin rights
*/functionhasAdminRights() internalviewreturns (bool) {
ComptrollerV3Storage comptrollerStorage = ComptrollerV3Storage(address(comptroller));
return
(msg.sender== comptrollerStorage.admin() && comptrollerStorage.adminHasRights()) ||
(msg.sender==address(ionicAdmin) && comptrollerStorage.ionicAdminHasRights());
}
}
abstractcontractCErc20FirstExtensionBaseisCErc20AdminBase,
CTokenFirstExtensionEvents,
CTokenFirstExtensionInterface{}
abstractcontractCTokenSecondExtensionBaseisCErc20AdminBase,
CTokenSecondExtensionEvents,
CTokenSecondExtensionInterface,
CDelegateInterface{}
abstractcontractCErc20DelegatorBaseisCErc20AdminBase, CTokenSecondExtensionEvents, CDelegatorInterface{}
interfaceCErc20StorageInterface{
functionadmin() externalviewreturns (address);
functionadminHasRights() externalviewreturns (bool);
functionionicAdmin() externalviewreturns (address);
functionionicAdminHasRights() externalviewreturns (bool);
functioncomptroller() externalviewreturns (IonicComptroller);
functionname() externalviewreturns (stringmemory);
functionsymbol() externalviewreturns (stringmemory);
functiondecimals() externalviewreturns (uint8);
functiontotalSupply() externalviewreturns (uint256);
functionadminFeeMantissa() externalviewreturns (uint256);
functionionicFeeMantissa() externalviewreturns (uint256);
functionreserveFactorMantissa() externalviewreturns (uint256);
functionprotocolSeizeShareMantissa() externalviewreturns (uint256);
functionfeeSeizeShareMantissa() externalviewreturns (uint256);
functiontotalReserves() externalviewreturns (uint256);
functiontotalAdminFees() externalviewreturns (uint256);
functiontotalIonicFees() externalviewreturns (uint256);
functiontotalBorrows() externalviewreturns (uint256);
functionaccrualBlockNumber() externalviewreturns (uint256);
functionunderlying() externalviewreturns (address);
functionborrowIndex() externalviewreturns (uint256);
functioninterestRateModel() externalviewreturns (address);
}
interfaceCErc20PluginStorageInterfaceisCErc20StorageInterface{
functionplugin() externalviewreturns (address);
}
interfaceCErc20PluginRewardsInterfaceisCErc20PluginStorageInterface{
functionapprove(address, address) external;
}
interfaceICErc20isCErc20StorageInterface,
CTokenSecondExtensionInterface,
CTokenFirstExtensionInterface,
CDelegatorInterface,
CDelegateInterface{}
interfaceICErc20PluginisCErc20PluginStorageInterface, ICErc20{
function_updatePlugin(address _plugin) external;
}
interfaceICErc20PluginRewardsisCErc20PluginRewardsInterface, ICErc20{}
Código Fuente del Contrato
Archivo 8 de 45: CarefulMath.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity >=0.8.0;/**
* @title Careful Math
* @author Compound
* @notice Derived from OpenZeppelin's SafeMath library
* https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol
*/contractCarefulMath{
/**
* @dev Possible error codes that we can return
*/enumMathError {
NO_ERROR,
DIVISION_BY_ZERO,
INTEGER_OVERFLOW,
INTEGER_UNDERFLOW
}
/**
* @dev Multiplies two numbers, returns an error on overflow.
*/functionmulUInt(uint256 a, uint256 b) internalpurereturns (MathError, uint256) {
if (a ==0) {
return (MathError.NO_ERROR, 0);
}
uint256 c;
unchecked {
c = a * b;
}
if (c / a != b) {
return (MathError.INTEGER_OVERFLOW, 0);
} else {
return (MathError.NO_ERROR, c);
}
}
/**
* @dev Integer division of two numbers, truncating the quotient.
*/functiondivUInt(uint256 a, uint256 b) internalpurereturns (MathError, uint256) {
if (b ==0) {
return (MathError.DIVISION_BY_ZERO, 0);
}
return (MathError.NO_ERROR, a / b);
}
/**
* @dev Subtracts two numbers, returns an error on overflow (i.e. if subtrahend is greater than minuend).
*/functionsubUInt(uint256 a, uint256 b) internalpurereturns (MathError, uint256) {
if (b <= a) {
return (MathError.NO_ERROR, a - b);
} else {
return (MathError.INTEGER_UNDERFLOW, 0);
}
}
/**
* @dev Adds two numbers, returns an error on overflow.
*/functionaddUInt(uint256 a, uint256 b) internalpurereturns (MathError, uint256) {
uint256 c;
unchecked {
c = a + b;
}
if (c >= a) {
return (MathError.NO_ERROR, c);
} else {
return (MathError.INTEGER_OVERFLOW, 0);
}
}
/**
* @dev add a and b and then subtract c
*/functionaddThenSubUInt(uint256 a,
uint256 b,
uint256 c
) internalpurereturns (MathError, uint256) {
(MathError err0, uint256 sum) = addUInt(a, b);
if (err0 != MathError.NO_ERROR) {
return (err0, 0);
}
return subUInt(sum, c);
}
}
Código Fuente del Contrato
Archivo 9 de 45: Comptroller.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity >=0.8.0;import { ICErc20 } from"./CTokenInterfaces.sol";
import { ComptrollerErrorReporter } from"./ErrorReporter.sol";
import { Exponential } from"./Exponential.sol";
import { BasePriceOracle } from"../oracles/BasePriceOracle.sol";
import { Unitroller } from"./Unitroller.sol";
import { IFeeDistributor } from"./IFeeDistributor.sol";
import { IIonicFlywheel } from"../ionic/strategies/flywheel/IIonicFlywheel.sol";
import { DiamondExtension, DiamondBase, LibDiamond } from"../ionic/DiamondExtension.sol";
import { ComptrollerExtensionInterface, ComptrollerBase, ComptrollerInterface } from"./ComptrollerInterface.sol";
import"@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
/**
* @title Compound's Comptroller Contract
* @author Compound
* @dev This contract should not to be deployed alone; instead, deploy `Unitroller` (proxy contract) on top of this `Comptroller` (logic/implementation contract).
*/contractComptrollerisComptrollerBase, ComptrollerInterface, ComptrollerErrorReporter, Exponential, DiamondExtension{
usingEnumerableSetforEnumerableSet.AddressSet;
/// @notice Emitted when an admin supports a marketeventMarketListed(ICErc20 cToken);
/// @notice Emitted when an account enters a marketeventMarketEntered(ICErc20 cToken, address account);
/// @notice Emitted when an account exits a marketeventMarketExited(ICErc20 cToken, address account);
/// @notice Emitted when close factor is changed by admineventNewCloseFactor(uint256 oldCloseFactorMantissa, uint256 newCloseFactorMantissa);
/// @notice Emitted when a collateral factor is changed by admineventNewCollateralFactor(ICErc20 cToken, uint256 oldCollateralFactorMantissa, uint256 newCollateralFactorMantissa);
/// @notice Emitted when liquidation incentive is changed by admineventNewLiquidationIncentive(uint256 oldLiquidationIncentiveMantissa, uint256 newLiquidationIncentiveMantissa);
/// @notice Emitted when price oracle is changedeventNewPriceOracle(BasePriceOracle oldPriceOracle, BasePriceOracle newPriceOracle);
/// @notice Emitted when the whitelist enforcement is changedeventWhitelistEnforcementChanged(bool enforce);
/// @notice Emitted when a new RewardsDistributor contract is added to hookseventAddedRewardsDistributor(address rewardsDistributor);
// closeFactorMantissa must be strictly greater than this valueuint256internalconstant closeFactorMinMantissa =0.05e18; // 0.05// closeFactorMantissa must not exceed this valueuint256internalconstant closeFactorMaxMantissa =0.9e18; // 0.9// No collateralFactorMantissa may exceed this valueuint256internalconstant collateralFactorMaxMantissa =0.9e18; // 0.9// liquidationIncentiveMantissa must be no less than this valueuint256internalconstant liquidationIncentiveMinMantissa =1.0e18; // 1.0// liquidationIncentiveMantissa must be no greater than this valueuint256internalconstant liquidationIncentiveMaxMantissa =1.5e18; // 1.5modifierisAuthorized() {
require(IFeeDistributor(ionicAdmin).canCall(address(this), msg.sender, address(this), msg.sig), "not authorized");
_;
}
/**
* @notice Gets the supply cap of a cToken in the units of the underlying asset.
* @param cToken The address of the cToken.
*/functioneffectiveSupplyCaps(address cToken
) publicviewoverride(ComptrollerBase, ComptrollerInterface) returns (uint256 supplyCap) {
return ComptrollerBase.effectiveSupplyCaps(cToken);
}
/**
* @notice Gets the borrow cap of a cToken in the units of the underlying asset.
* @param cToken The address of the cToken.
*/functioneffectiveBorrowCaps(address cToken
) publicviewoverride(ComptrollerBase, ComptrollerInterface) returns (uint256 borrowCap) {
return ComptrollerBase.effectiveBorrowCaps(cToken);
}
/*** Assets You Are In ***//**
* @notice Returns the assets an account has entered
* @param account The address of the account to pull assets for
* @return A dynamic list with the assets the account has entered
*/functiongetAssetsIn(address account) externalviewreturns (ICErc20[] memory) {
ICErc20[] memory assetsIn = accountAssets[account];
return assetsIn;
}
/**
* @notice Returns whether the given account is entered in the given asset
* @param account The address of the account to check
* @param cToken The cToken to check
* @return True if the account is in the asset, otherwise false.
*/functioncheckMembership(address account, ICErc20 cToken) externalviewreturns (bool) {
return markets[address(cToken)].accountMembership[account];
}
/**
* @notice Add assets to be included in account liquidity calculation
* @param cTokens The list of addresses of the cToken markets to be enabled
* @return Success indicator for whether each corresponding market was entered
*/functionenterMarkets(address[] memory cTokens) publicoverrideisAuthorizedreturns (uint256[] memory) {
uint256 len = cTokens.length;
uint256[] memory results =newuint256[](len);
for (uint256 i =0; i < len; i++) {
ICErc20 cToken = ICErc20(cTokens[i]);
results[i] =uint256(addToMarketInternal(cToken, msg.sender));
}
return results;
}
/**
* @notice Add the market to the borrower's "assets in" for liquidity calculations
* @param cToken The market to enter
* @param borrower The address of the account to modify
* @return Success indicator for whether the market was entered
*/functionaddToMarketInternal(ICErc20 cToken, address borrower) internalreturns (Error) {
Market storage marketToJoin = markets[address(cToken)];
if (!marketToJoin.isListed) {
// market is not listed, cannot joinreturnError.MARKET_NOT_LISTED;
}
if (marketToJoin.accountMembership[borrower] ==true) {
// already joinedreturnError.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(cToken);
// Add to allBorrowersif (!borrowers[borrower]) {
allBorrowers.push(borrower);
borrowers[borrower] =true;
borrowerIndexes[borrower] = allBorrowers.length-1;
}
emit MarketEntered(cToken, borrower);
returnError.NO_ERROR;
}
/**
* @notice Removes asset from sender's account liquidity calculation
* @dev Sender must not have an outstanding borrow balance in the asset,
* or be providing necessary collateral for an outstanding borrow.
* @param cTokenAddress The address of the asset to be removed
* @return Whether or not the account successfully exited the market
*/functionexitMarket(address cTokenAddress) externaloverrideisAuthorizedreturns (uint256) {
// TODOrequire(markets[cTokenAddress].isListed, "!Comptroller:exitMarket");
ICErc20 cToken = ICErc20(cTokenAddress);
/* Get sender tokensHeld and amountOwed underlying from the cToken */
(uint256 oErr, uint256 tokensHeld, uint256 amountOwed, ) = cToken.getAccountSnapshot(msg.sender);
require(oErr ==0, "!exitMarket"); // semi-opaque error code/* Fail if the sender has a borrow balance */if (amountOwed !=0) {
return fail(Error.NONZERO_BORROW_BALANCE, FailureInfo.EXIT_MARKET_BALANCE_OWED);
}
/* Fail if the sender is not permitted to redeem all of their tokens */uint256 allowed = redeemAllowedInternal(cTokenAddress, msg.sender, tokensHeld);
if (allowed !=0) {
return failOpaque(Error.REJECTION, FailureInfo.EXIT_MARKET_REJECTION, allowed);
}
Market storage marketToExit = markets[cTokenAddress];
/* Return true if the sender is not already ‘in’ the market */if (!marketToExit.accountMembership[msg.sender]) {
returnuint256(Error.NO_ERROR);
}
/* Set cToken account membership to false */delete marketToExit.accountMembership[msg.sender];
/* Delete cToken from the account’s list of assets */// load into memory for faster iteration
ICErc20[] memory userAssetList = accountAssets[msg.sender];
uint256 len = userAssetList.length;
uint256 assetIndex = len;
for (uint256 i =0; i < len; i++) {
if (userAssetList[i] == ICErc20(cTokenAddress)) {
assetIndex = i;
break;
}
}
// We *must* have found the asset in the list or our redundant data structure is brokenassert(assetIndex < len);
// copy last item in list to location of item to be removed, reduce length by 1
ICErc20[] storage storedList = accountAssets[msg.sender];
storedList[assetIndex] = storedList[storedList.length-1];
storedList.pop();
// If the user has exited all markets, remove them from the `allBorrowers` arrayif (storedList.length==0) {
allBorrowers[borrowerIndexes[msg.sender]] = allBorrowers[allBorrowers.length-1]; // Copy last item in list to location of item to be removed
allBorrowers.pop(); // Reduce length by 1
borrowerIndexes[allBorrowers[borrowerIndexes[msg.sender]]] = borrowerIndexes[msg.sender]; // Set borrower index of moved item to correct index
borrowerIndexes[msg.sender] =0; // Reset sender borrower index to 0 for a gas refund
borrowers[msg.sender] =false; // Tell the contract that the sender is no longer a borrower (so it knows to add the borrower back if they enter a market in the future)
}
emit MarketExited(ICErc20(cTokenAddress), msg.sender);
returnuint256(Error.NO_ERROR);
}
/*** Policy Hooks ***//**
* @notice Checks if the account should be allowed to mint tokens in the given market
* @param cTokenAddress The market to verify the mint against
* @param minter The account which would get the minted tokens
* @param mintAmount The amount of underlying being supplied to the market in exchange for tokens
* @return 0 if the mint is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol)
*/functionmintAllowed(address cTokenAddress, address minter, uint256 mintAmount) externaloverridereturns (uint256) {
// Pausing is a very serious situation - we revert to sound the alarmsrequire(!mintGuardianPaused[cTokenAddress], "!mint:paused");
// Make sure market is listedif (!markets[cTokenAddress].isListed) {
returnuint256(Error.MARKET_NOT_LISTED);
}
// Make sure minter is whitelistedif (enforceWhitelist &&!whitelist[minter]) {
returnuint256(Error.SUPPLIER_NOT_WHITELISTED);
}
uint256 supplyCap = effectiveSupplyCaps(cTokenAddress);
// Supply cap of 0 corresponds to unlimited supplyingif (supplyCap !=0&&!supplyCapWhitelist[cTokenAddress].contains(minter)) {
uint256 totalUnderlyingSupply = ICErc20(cTokenAddress).getTotalUnderlyingSupplied();
uint256 whitelistedSuppliersSupply = asComptrollerExtension().getWhitelistedSuppliersSupply(cTokenAddress);
uint256 nonWhitelistedTotalSupply;
if (whitelistedSuppliersSupply >= totalUnderlyingSupply) nonWhitelistedTotalSupply =0;
else nonWhitelistedTotalSupply = totalUnderlyingSupply - whitelistedSuppliersSupply;
require(nonWhitelistedTotalSupply + mintAmount < supplyCap, "!supply cap");
}
// Keep the flywheel moving
flywheelPreSupplierAction(cTokenAddress, minter);
returnuint256(Error.NO_ERROR);
}
/**
* @notice Checks if the account should be allowed to redeem tokens in the given market
* @param cToken The market to verify the redeem against
* @param redeemer The account which would redeem the tokens
* @param redeemTokens The number of cTokens to exchange for the underlying asset in the market
* @return 0 if the redeem is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol)
*/functionredeemAllowed(address cToken, address redeemer, uint256 redeemTokens) externaloverridereturns (uint256) {
uint256 allowed = redeemAllowedInternal(cToken, redeemer, redeemTokens);
if (allowed !=uint256(Error.NO_ERROR)) {
return allowed;
}
// Keep the flywheel moving
flywheelPreSupplierAction(cToken, redeemer);
returnuint256(Error.NO_ERROR);
}
functionredeemAllowedInternal(address cToken,
address redeemer,
uint256 redeemTokens
) internalviewreturns (uint256) {
if (!markets[cToken].isListed) {
returnuint256(Error.MARKET_NOT_LISTED);
}
/* If the redeemer is not 'in' the market, then we can bypass the liquidity check */if (!markets[cToken].accountMembership[redeemer]) {
returnuint256(Error.NO_ERROR);
}
/* Otherwise, perform a hypothetical liquidity check to guard against shortfall */
(Error err, , , uint256 shortfall) = getHypotheticalAccountLiquidityInternal(
redeemer,
ICErc20(cToken),
redeemTokens,
0,
0
);
if (err !=Error.NO_ERROR) {
returnuint256(err);
}
if (shortfall >0) {
returnuint256(Error.INSUFFICIENT_LIQUIDITY);
}
returnuint256(Error.NO_ERROR);
}
/**
* @notice Validates mint and reverts on rejection. May emit logs.
* @param cToken Asset being minted
* @param minter The address minting the tokens
* @param actualMintAmount The amount of the underlying asset being minted
* @param mintTokens The number of tokens being minted
*/functionmintVerify(address cToken, address minter, uint256 actualMintAmount, uint256 mintTokens) external{
// Add minter to suppliers mapping
suppliers[minter] =true;
}
/**
* @notice Validates redeem and reverts on rejection. May emit logs.
* @param cToken Asset being redeemed
* @param redeemer The address redeeming the tokens
* @param redeemAmount The amount of the underlying asset being redeemed
* @param redeemTokens The number of tokens being redeemed
*/functionredeemVerify(address cToken,
address redeemer,
uint256 redeemAmount,
uint256 redeemTokens
) externaloverride{
require(markets[msg.sender].isListed, "!market");
// Require tokens is zero or amount is also zeroif (redeemTokens ==0&& redeemAmount >0) {
revert("!zero");
}
}
functiongetMaxRedeemOrBorrow(address account,
ICErc20 cTokenModify,
bool isBorrow
) externalviewoverridereturns (uint256) {
address cToken =address(cTokenModify);
// Accrue interestuint256 balanceOfUnderlying = cTokenModify.balanceOfUnderlying(account);
// Get account liquidity
(Error err, , uint256 liquidity, uint256 shortfall) = getHypotheticalAccountLiquidityInternal(
account,
isBorrow ? cTokenModify : ICErc20(address(0)),
0,
0,
0
);
require(err ==Error.NO_ERROR, "!liquidity");
if (shortfall >0) return0; // Shortfall, so no more borrow/redeem// Get max borrow/redeemuint256 maxBorrowOrRedeemAmount;
if (!isBorrow &&!markets[cToken].accountMembership[account]) {
// Max redeem = balance of underlying if not used as collateral
maxBorrowOrRedeemAmount = balanceOfUnderlying;
} else {
// Avoid "stack too deep" error by separating this logic
maxBorrowOrRedeemAmount = _getMaxRedeemOrBorrow(liquidity, cTokenModify, isBorrow);
// Redeem only: max out at underlying balanceif (!isBorrow && balanceOfUnderlying < maxBorrowOrRedeemAmount) maxBorrowOrRedeemAmount = balanceOfUnderlying;
}
// Get max borrow or redeem considering cToken liquidityuint256 cTokenLiquidity = cTokenModify.getCash();
// Return the minimum of the two maximumsreturn maxBorrowOrRedeemAmount <= cTokenLiquidity ? maxBorrowOrRedeemAmount : cTokenLiquidity;
}
/**
* @dev Portion of the logic in `getMaxRedeemOrBorrow` above separated to avoid "stack too deep" errors.
*/function_getMaxRedeemOrBorrow(uint256 liquidity,
ICErc20 cTokenModify,
bool isBorrow
) internalviewreturns (uint256) {
if (liquidity ==0) return0; // No available account liquidity, so no more borrow/redeem// Get the normalized price of the assetuint256 conversionFactor = oracle.getUnderlyingPrice(cTokenModify);
require(conversionFactor >0, "!oracle");
// Pre-compute a conversion factor from tokens -> ether (normalized price value)if (!isBorrow) {
uint256 collateralFactorMantissa = markets[address(cTokenModify)].collateralFactorMantissa;
conversionFactor = (collateralFactorMantissa * conversionFactor) /1e18;
}
// Get max borrow or redeem considering excess account liquidityreturn (liquidity *1e18) / conversionFactor;
}
/**
* @notice Checks if the account should be allowed to borrow the underlying asset of the given market
* @param cToken The market to verify the borrow against
* @param borrower The account which would borrow the asset
* @param borrowAmount The amount of underlying the account would borrow
* @return 0 if the borrow is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol)
*/functionborrowAllowed(address cToken, address borrower, uint256 borrowAmount) externaloverridereturns (uint256) {
// Pausing is a very serious situation - we revert to sound the alarmsrequire(!borrowGuardianPaused[cToken], "!borrow:paused");
// Make sure market is listedif (!markets[cToken].isListed) {
returnuint256(Error.MARKET_NOT_LISTED);
}
if (!markets[cToken].accountMembership[borrower]) {
// only cTokens may call borrowAllowed if borrower not in marketrequire(msg.sender== cToken, "!ctoken");
// attempt to add borrower to the marketError err = addToMarketInternal(ICErc20(msg.sender), borrower);
if (err !=Error.NO_ERROR) {
returnuint256(err);
}
// it should be impossible to break the important invariantassert(markets[cToken].accountMembership[borrower]);
}
// Make sure oracle price is availableif (oracle.getUnderlyingPrice(ICErc20(cToken)) ==0) {
returnuint256(Error.PRICE_ERROR);
}
// Make sure borrower is whitelistedif (enforceWhitelist &&!whitelist[borrower]) {
returnuint256(Error.SUPPLIER_NOT_WHITELISTED);
}
uint256 borrowCap = effectiveBorrowCaps(cToken);
// Borrow cap of 0 corresponds to unlimited borrowingif (borrowCap !=0&&!borrowCapWhitelist[cToken].contains(borrower)) {
uint256 totalBorrows = ICErc20(cToken).totalBorrowsCurrent();
uint256 whitelistedBorrowersBorrows = asComptrollerExtension().getWhitelistedBorrowersBorrows(cToken);
uint256 nonWhitelistedTotalBorrows;
if (whitelistedBorrowersBorrows >= totalBorrows) nonWhitelistedTotalBorrows =0;
else nonWhitelistedTotalBorrows = totalBorrows - whitelistedBorrowersBorrows;
require(nonWhitelistedTotalBorrows + borrowAmount < borrowCap, "!borrow:cap");
}
// Keep the flywheel moving
flywheelPreBorrowerAction(cToken, borrower);
// Perform a hypothetical liquidity check to guard against shortfall
(uint256 err, , , uint256 shortfall) =this.getHypotheticalAccountLiquidity(borrower, cToken, 0, borrowAmount, 0);
if (err !=uint256(Error.NO_ERROR)) {
return err;
}
if (shortfall >0) {
returnuint256(Error.INSUFFICIENT_LIQUIDITY);
}
returnuint256(Error.NO_ERROR);
}
/**
* @notice Checks if the account should be allowed to borrow the underlying asset of the given market
* @param cToken Asset whose underlying is being borrowed
* @param accountBorrowsNew The user's new borrow balance of the underlying asset
*/functionborrowWithinLimits(address cToken, uint256 accountBorrowsNew) externalviewoverridereturns (uint256) {
// Check if min borrow existsuint256 minBorrowEth = IFeeDistributor(ionicAdmin).minBorrowEth();
if (minBorrowEth >0) {
// Get new underlying borrow balance of account for this cTokenuint256 oraclePriceMantissa = oracle.getUnderlyingPrice(ICErc20(cToken));
if (oraclePriceMantissa ==0) returnuint256(Error.PRICE_ERROR);
(MathError mathErr, uint256 borrowBalanceEth) = mulScalarTruncate(
Exp({ mantissa: oraclePriceMantissa }),
accountBorrowsNew
);
if (mathErr != MathError.NO_ERROR) returnuint256(Error.MATH_ERROR);
// Check against min borrowif (borrowBalanceEth < minBorrowEth) returnuint256(Error.BORROW_BELOW_MIN);
}
// Return no errorreturnuint256(Error.NO_ERROR);
}
/**
* @notice Checks if the account should be allowed to repay a borrow in the given market
* @param cToken The market to verify the repay against
* @param payer The account which would repay the asset
* @param borrower The account which would borrowed the asset
* @param repayAmount The amount of the underlying asset the account would repay
* @return 0 if the repay is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol)
*/functionrepayBorrowAllowed(address cToken,
address payer,
address borrower,
uint256 repayAmount
) externaloverridereturns (uint256) {
// Make sure market is listedif (!markets[cToken].isListed) {
returnuint256(Error.MARKET_NOT_LISTED);
}
// Keep the flywheel moving
flywheelPreBorrowerAction(cToken, borrower);
returnuint256(Error.NO_ERROR);
}
/**
* @notice Checks if the liquidation should be allowed to occur
* @param cTokenBorrowed Asset which was borrowed by the borrower
* @param cTokenCollateral Asset which was used as collateral and will be seized
* @param liquidator The address repaying the borrow and seizing the collateral
* @param borrower The address of the borrower
* @param repayAmount The amount of underlying being repaid
*/functionliquidateBorrowAllowed(address cTokenBorrowed,
address cTokenCollateral,
address liquidator,
address borrower,
uint256 repayAmount
) externaloverridereturns (uint256) {
// Make sure markets are listedif (!markets[cTokenBorrowed].isListed ||!markets[cTokenCollateral].isListed) {
returnuint256(Error.MARKET_NOT_LISTED);
}
// Get borrowers' underlying borrow balanceuint256 borrowBalance = ICErc20(cTokenBorrowed).borrowBalanceCurrent(borrower);
/* allow accounts to be liquidated if the market is deprecated */if (isDeprecated(ICErc20(cTokenBorrowed))) {
require(borrowBalance >= repayAmount, "!borrow>repay");
} else {
/* The borrower must have shortfall in order to be liquidateable */
(Error err, , , uint256 shortfall) = getHypotheticalAccountLiquidityInternal(
borrower,
ICErc20(address(0)),
0,
0,
0
);
if (err !=Error.NO_ERROR) {
returnuint256(err);
}
if (shortfall ==0) {
returnuint256(Error.INSUFFICIENT_SHORTFALL);
}
/* The liquidator may not repay more than what is allowed by the closeFactor */uint256 maxClose = mul_ScalarTruncate(Exp({ mantissa: closeFactorMantissa }), borrowBalance);
if (repayAmount > maxClose) {
returnuint256(Error.TOO_MUCH_REPAY);
}
}
returnuint256(Error.NO_ERROR);
}
/**
* @notice Checks if the seizing of assets should be allowed to occur
* @param cTokenCollateral Asset which was used as collateral and will be seized
* @param cTokenBorrowed Asset which was borrowed by the borrower
* @param liquidator The address repaying the borrow and seizing the collateral
* @param borrower The address of the borrower
* @param seizeTokens The number of collateral tokens to seize
*/functionseizeAllowed(address cTokenCollateral,
address cTokenBorrowed,
address liquidator,
address borrower,
uint256 seizeTokens
) externaloverridereturns (uint256) {
// Pausing is a very serious situation - we revert to sound the alarmsrequire(!seizeGuardianPaused, "!seize:paused");
// Make sure markets are listedif (!markets[cTokenCollateral].isListed ||!markets[cTokenBorrowed].isListed) {
returnuint256(Error.MARKET_NOT_LISTED);
}
// Make sure cToken Comptrollers are identicalif (ICErc20(cTokenCollateral).comptroller() != ICErc20(cTokenBorrowed).comptroller()) {
returnuint256(Error.COMPTROLLER_MISMATCH);
}
// Keep the flywheel moving
flywheelPreTransferAction(cTokenCollateral, borrower, liquidator);
returnuint256(Error.NO_ERROR);
}
/**
* @notice Checks if the account should be allowed to transfer tokens in the given market
* @param cToken The market to verify the transfer against
* @param src The account which sources the tokens
* @param dst The account which receives the tokens
* @param transferTokens The number of cTokens to transfer
* @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol)
*/functiontransferAllowed(address cToken,
address src,
address dst,
uint256 transferTokens
) externaloverridereturns (uint256) {
// Pausing is a very serious situation - we revert to sound the alarmsrequire(!transferGuardianPaused, "!transfer:paused");
// Currently the only consideration is whether or not// the src is allowed to redeem this many tokensuint256 allowed = redeemAllowedInternal(cToken, src, transferTokens);
if (allowed !=uint256(Error.NO_ERROR)) {
return allowed;
}
// Keep the flywheel moving
flywheelPreTransferAction(cToken, src, dst);
returnuint256(Error.NO_ERROR);
}
/*** Flywheel Hooks ***//**
* @notice Keeps the flywheel moving pre-mint and pre-redeem
* @param cToken The relevant market
* @param supplier The minter/redeemer
*/functionflywheelPreSupplierAction(address cToken, address supplier) internal{
for (uint256 i =0; i < rewardsDistributors.length; i++)
IIonicFlywheel(rewardsDistributors[i]).flywheelPreSupplierAction(cToken, supplier);
}
/**
* @notice Keeps the flywheel moving pre-borrow and pre-repay
* @param cToken The relevant market
* @param borrower The borrower
*/functionflywheelPreBorrowerAction(address cToken, address borrower) internal{
for (uint256 i =0; i < rewardsDistributors.length; i++)
IIonicFlywheel(rewardsDistributors[i]).flywheelPreBorrowerAction(cToken, borrower);
}
/**
* @notice Keeps the flywheel moving pre-transfer and pre-seize
* @param cToken The relevant market
* @param src The account which sources the tokens
* @param dst The account which receives the tokens
*/functionflywheelPreTransferAction(address cToken, address src, address dst) internal{
for (uint256 i =0; i < rewardsDistributors.length; i++)
IIonicFlywheel(rewardsDistributors[i]).flywheelPreTransferAction(cToken, src, dst);
}
/*** Liquidity/Liquidation Calculations ***//**
* @dev Local vars for avoiding stack-depth limits in calculating account liquidity.
* Note that `cTokenBalance` is the number of cTokens the account owns in the market,
* whereas `borrowBalance` is the amount of underlying that the account has borrowed.
*/structAccountLiquidityLocalVars {
ICErc20 asset;
uint256 sumCollateral;
uint256 sumBorrowPlusEffects;
uint256 cTokenBalance;
uint256 borrowBalance;
uint256 exchangeRateMantissa;
uint256 oraclePriceMantissa;
Exp collateralFactor;
Exp exchangeRate;
Exp oraclePrice;
Exp tokensToDenom;
uint256 borrowCapForCollateral;
uint256 borrowedAssetPrice;
uint256 assetAsCollateralValueCap;
}
functiongetAccountLiquidity(address account) publicviewoverridereturns (uint256, uint256, uint256, uint256) {
(
Error err,
uint256 collateralValue,
uint256 liquidity,
uint256 shortfall
) = getHypotheticalAccountLiquidityInternal(account, ICErc20(address(0)), 0, 0, 0);
return (uint256(err), collateralValue, liquidity, shortfall);
}
/**
* @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed
* @param cTokenModify 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
* @return (possible error code (semi-opaque),
hypothetical account liquidity in excess of collateral requirements,
* hypothetical account shortfall below collateral requirements)
*/functiongetHypotheticalAccountLiquidity(address account,
address cTokenModify,
uint256 redeemTokens,
uint256 borrowAmount,
uint256 repayAmount
) publicviewreturns (uint256, uint256, uint256, uint256) {
(
Error err,
uint256 collateralValue,
uint256 liquidity,
uint256 shortfall
) = getHypotheticalAccountLiquidityInternal(
account,
ICErc20(cTokenModify),
redeemTokens,
borrowAmount,
repayAmount
);
return (uint256(err), collateralValue, liquidity, shortfall);
}
/**
* @notice Determine what the account liquidity would be if the given amounts were redeemed/borrowed
* @param cTokenModify 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
* @return (possible error code,
hypothetical account collateral value,
hypothetical account liquidity in excess of collateral requirements,
* hypothetical account shortfall below collateral requirements)
*/functiongetHypotheticalAccountLiquidityInternal(address account,
ICErc20 cTokenModify,
uint256 redeemTokens,
uint256 borrowAmount,
uint256 repayAmount
) internalviewreturns (Error, uint256, uint256, uint256) {
AccountLiquidityLocalVars memory vars; // Holds all our calculation resultsif (address(cTokenModify) !=address(0)) {
vars.borrowedAssetPrice = oracle.getUnderlyingPrice(cTokenModify);
}
// For each asset the account is infor (uint256 i =0; i < accountAssets[account].length; i++) {
vars.asset = accountAssets[account][i];
{
// Read the balances and exchange rate from the cTokenuint256 oErr;
(oErr, vars.cTokenBalance, vars.borrowBalance, vars.exchangeRateMantissa) = vars.asset.getAccountSnapshot(
account
);
if (oErr !=0) {
// semi-opaque error code, we assume NO_ERROR == 0 is invariant between upgradesreturn (Error.SNAPSHOT_ERROR, 0, 0, 0);
}
}
{
vars.collateralFactor = Exp({ mantissa: markets[address(vars.asset)].collateralFactorMantissa });
vars.exchangeRate = Exp({ mantissa: vars.exchangeRateMantissa });
// Get the normalized price of the asset
vars.oraclePriceMantissa = oracle.getUnderlyingPrice(vars.asset);
if (vars.oraclePriceMantissa ==0) {
return (Error.PRICE_ERROR, 0, 0, 0);
}
vars.oraclePrice = Exp({ mantissa: vars.oraclePriceMantissa });
// Pre-compute a conversion factor from tokens -> ether (normalized price value)
vars.tokensToDenom = mul_(mul_(vars.collateralFactor, vars.exchangeRate), vars.oraclePrice);
}
{
// Exclude the asset-to-be-borrowed from the liquidity, except for when redeeming
vars.assetAsCollateralValueCap = asComptrollerExtension().getAssetAsCollateralValueCap(
vars.asset,
cTokenModify,
redeemTokens >0,
account
);
// accumulate the collateral value to sumCollateraluint256 assetCollateralValue = mul_ScalarTruncate(vars.tokensToDenom, vars.cTokenBalance);
if (assetCollateralValue > vars.assetAsCollateralValueCap)
assetCollateralValue = vars.assetAsCollateralValueCap;
vars.sumCollateral += assetCollateralValue;
}
// sumBorrowPlusEffects += oraclePrice * borrowBalance
vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt(
vars.oraclePrice,
vars.borrowBalance,
vars.sumBorrowPlusEffects
);
// Calculate effects of interacting with cTokenModifyif (vars.asset == cTokenModify) {
// redeem effect// sumBorrowPlusEffects += tokensToDenom * redeemTokens
vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt(
vars.tokensToDenom,
redeemTokens,
vars.sumBorrowPlusEffects
);
// borrow effect// sumBorrowPlusEffects += oraclePrice * borrowAmount
vars.sumBorrowPlusEffects = mul_ScalarTruncateAddUInt(
vars.oraclePrice,
borrowAmount,
vars.sumBorrowPlusEffects
);
uint256 repayEffect = mul_ScalarTruncate(vars.oraclePrice, repayAmount);
if (repayEffect >= vars.sumBorrowPlusEffects) {
vars.sumBorrowPlusEffects =0;
} else {
vars.sumBorrowPlusEffects -= repayEffect;
}
}
}
// These are safe, as the underflow condition is checked firstif (vars.sumCollateral > vars.sumBorrowPlusEffects) {
return (Error.NO_ERROR, vars.sumCollateral, vars.sumCollateral - vars.sumBorrowPlusEffects, 0);
} else {
return (Error.NO_ERROR, vars.sumCollateral, 0, vars.sumBorrowPlusEffects - vars.sumCollateral);
}
}
/**
* @notice Calculate number of tokens of collateral asset to seize given an underlying amount
* @dev Used in liquidation (called in cToken.liquidateBorrowFresh)
* @param cTokenBorrowed The address of the borrowed cToken
* @param cTokenCollateral The address of the collateral cToken
* @param actualRepayAmount The amount of cTokenBorrowed underlying to convert into cTokenCollateral tokens
* @return (errorCode, number of cTokenCollateral tokens to be seized in a liquidation)
*/functionliquidateCalculateSeizeTokens(address cTokenBorrowed,
address cTokenCollateral,
uint256 actualRepayAmount
) externalviewoverridereturns (uint256, uint256) {
/* Read oracle prices for borrowed and collateral markets */uint256 priceBorrowedMantissa = oracle.getUnderlyingPrice(ICErc20(cTokenBorrowed));
uint256 priceCollateralMantissa = oracle.getUnderlyingPrice(ICErc20(cTokenCollateral));
if (priceBorrowedMantissa ==0|| priceCollateralMantissa ==0) {
return (uint256(Error.PRICE_ERROR), 0);
}
/*
* Get the exchange rate and calculate the number of collateral tokens to seize:
* seizeAmount = actualRepayAmount * liquidationIncentive * priceBorrowed / priceCollateral
* seizeTokens = seizeAmount / exchangeRate
* = actualRepayAmount * (liquidationIncentive * priceBorrowed) / (priceCollateral * exchangeRate)
*/
ICErc20 collateralCToken = ICErc20(cTokenCollateral);
uint256 exchangeRateMantissa = collateralCToken.exchangeRateCurrent();
uint256 seizeTokens;
Exp memory numerator;
Exp memory denominator;
Exp memory ratio;
uint256 protocolSeizeShareMantissa = collateralCToken.protocolSeizeShareMantissa();
uint256 feeSeizeShareMantissa = collateralCToken.feeSeizeShareMantissa();
/*
* The liquidation penalty includes
* - the liquidator incentive
* - the protocol fees (Ionic admin fees)
* - the market fee
*/
Exp memory totalPenaltyMantissa = add_(
add_(Exp({ mantissa: liquidationIncentiveMantissa }), Exp({ mantissa: protocolSeizeShareMantissa })),
Exp({ mantissa: feeSeizeShareMantissa })
);
numerator = mul_(totalPenaltyMantissa, Exp({ mantissa: priceBorrowedMantissa }));
denominator = mul_(Exp({ mantissa: priceCollateralMantissa }), Exp({ mantissa: exchangeRateMantissa }));
ratio = div_(numerator, denominator);
seizeTokens = mul_ScalarTruncate(ratio, actualRepayAmount);
return (uint256(Error.NO_ERROR), seizeTokens);
}
/*** Admin Functions ***//**
* @notice Add a RewardsDistributor contracts.
* @dev Admin function to add a RewardsDistributor contract
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/function_addRewardsDistributor(address distributor) externalreturns (uint256) {
require(hasAdminRights(), "!admin");
// Check marker methodrequire(IIonicFlywheel(distributor).isRewardsDistributor(), "!isRewardsDistributor");
// Check for existing RewardsDistributorfor (uint256 i =0; i < rewardsDistributors.length; i++) require(distributor != rewardsDistributors[i], "!added");
// Add RewardsDistributor to array
rewardsDistributors.push(distributor);
emit AddedRewardsDistributor(distributor);
returnuint256(Error.NO_ERROR);
}
/**
* @notice Sets the whitelist enforcement for the comptroller
* @dev Admin function to set a new whitelist enforcement boolean
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/function_setWhitelistEnforcement(bool enforce) externalreturns (uint256) {
// Check caller is adminif (!hasAdminRights()) {
return fail(Error.UNAUTHORIZED, FailureInfo.SET_WHITELIST_ENFORCEMENT_OWNER_CHECK);
}
// Check if `enforceWhitelist` already equals `enforce`if (enforceWhitelist == enforce) {
returnuint256(Error.NO_ERROR);
}
// Set comptroller's `enforceWhitelist` to `enforce`
enforceWhitelist = enforce;
// Emit WhitelistEnforcementChanged(bool enforce);emit WhitelistEnforcementChanged(enforce);
returnuint256(Error.NO_ERROR);
}
/**
* @notice Sets the whitelist `statuses` for `suppliers`
* @dev Admin function to set the whitelist `statuses` for `suppliers`
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/function_setWhitelistStatuses(address[] calldata suppliers, bool[] calldata statuses) externalreturns (uint256) {
// Check caller is adminif (!hasAdminRights()) {
return fail(Error.UNAUTHORIZED, FailureInfo.SET_WHITELIST_STATUS_OWNER_CHECK);
}
// Set whitelist statuses for suppliersfor (uint256 i =0; i < suppliers.length; i++) {
address supplier = suppliers[i];
if (statuses[i]) {
// If not already whitelisted, add to whitelistif (!whitelist[supplier]) {
whitelist[supplier] =true;
whitelistArray.push(supplier);
whitelistIndexes[supplier] = whitelistArray.length-1;
}
} else {
// If whitelisted, remove from whitelistif (whitelist[supplier]) {
whitelistArray[whitelistIndexes[supplier]] = whitelistArray[whitelistArray.length-1]; // Copy last item in list to location of item to be removed
whitelistArray.pop(); // Reduce length by 1
whitelistIndexes[whitelistArray[whitelistIndexes[supplier]]] = whitelistIndexes[supplier]; // Set whitelist index of moved item to correct index
whitelistIndexes[supplier] =0; // Reset supplier whitelist index to 0 for a gas refund
whitelist[supplier] =false; // Tell the contract that the supplier is no longer whitelisted
}
}
}
returnuint256(Error.NO_ERROR);
}
/**
* @notice Sets a new price oracle for the comptroller
* @dev Admin function to set a new price oracle
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/function_setPriceOracle(BasePriceOracle newOracle) publicreturns (uint256) {
// Check caller is adminif (!hasAdminRights()) {
return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK);
}
// Track the old oracle for the comptroller
BasePriceOracle oldOracle = oracle;
// Set comptroller's oracle to newOracle
oracle = newOracle;
// Emit NewPriceOracle(oldOracle, newOracle)emit NewPriceOracle(oldOracle, newOracle);
returnuint256(Error.NO_ERROR);
}
/**
* @notice Sets the closeFactor used when liquidating borrows
* @dev Admin function to set closeFactor
* @param newCloseFactorMantissa New close factor, scaled by 1e18
* @return uint 0=success, otherwise a failure. (See ErrorReporter for details)
*/function_setCloseFactor(uint256 newCloseFactorMantissa) externalreturns (uint256) {
// Check caller is adminif (!hasAdminRights()) {
return fail(Error.UNAUTHORIZED, FailureInfo.SET_CLOSE_FACTOR_OWNER_CHECK);
}
// Check limits
Exp memory newCloseFactorExp = Exp({ mantissa: newCloseFactorMantissa });
Exp memory lowLimit = Exp({ mantissa: closeFactorMinMantissa });
if (lessThanOrEqualExp(newCloseFactorExp, lowLimit)) {
return fail(Error.INVALID_CLOSE_FACTOR, FailureInfo.SET_CLOSE_FACTOR_VALIDATION);
}
Exp memory highLimit = Exp({ mantissa: closeFactorMaxMantissa });
if (lessThanExp(highLimit, newCloseFactorExp)) {
return fail(Error.INVALID_CLOSE_FACTOR, FailureInfo.SET_CLOSE_FACTOR_VALIDATION);
}
// Set pool close factor to new close factor, remember old valueuint256 oldCloseFactorMantissa = closeFactorMantissa;
closeFactorMantissa = newCloseFactorMantissa;
// Emit eventemit NewCloseFactor(oldCloseFactorMantissa, closeFactorMantissa);
returnuint256(Error.NO_ERROR);
}
/**
* @notice Sets the collateralFactor for a market
* @dev Admin function to set per-market collateralFactor
* @param cToken The market to set the factor on
* @param newCollateralFactorMantissa The new collateral factor, scaled by 1e18
* @return uint 0=success, otherwise a failure. (See ErrorReporter for details)
*/function_setCollateralFactor(ICErc20 cToken, uint256 newCollateralFactorMantissa) publicreturns (uint256) {
// Check caller is adminif (!hasAdminRights()) {
return fail(Error.UNAUTHORIZED, FailureInfo.SET_COLLATERAL_FACTOR_OWNER_CHECK);
}
// Verify market is listed
Market storage market = markets[address(cToken)];
if (!market.isListed) {
return fail(Error.MARKET_NOT_LISTED, FailureInfo.SET_COLLATERAL_FACTOR_NO_EXISTS);
}
Exp memory newCollateralFactorExp = Exp({ mantissa: newCollateralFactorMantissa });
// Check collateral factor <= 0.9
Exp memory highLimit = Exp({ mantissa: collateralFactorMaxMantissa });
if (lessThanExp(highLimit, newCollateralFactorExp)) {
return fail(Error.INVALID_COLLATERAL_FACTOR, FailureInfo.SET_COLLATERAL_FACTOR_VALIDATION);
}
// If collateral factor != 0, fail if price == 0if (newCollateralFactorMantissa !=0&& oracle.getUnderlyingPrice(cToken) ==0) {
return fail(Error.PRICE_ERROR, FailureInfo.SET_COLLATERAL_FACTOR_WITHOUT_PRICE);
}
// Set market's collateral factor to new collateral factor, remember old valueuint256 oldCollateralFactorMantissa = market.collateralFactorMantissa;
market.collateralFactorMantissa = newCollateralFactorMantissa;
// Emit event with asset, old collateral factor, and new collateral factoremit NewCollateralFactor(cToken, oldCollateralFactorMantissa, newCollateralFactorMantissa);
returnuint256(Error.NO_ERROR);
}
/**
* @notice Sets liquidationIncentive
* @dev Admin function to set liquidationIncentive
* @param newLiquidationIncentiveMantissa New liquidationIncentive scaled by 1e18
* @return uint 0=success, otherwise a failure. (See ErrorReporter for details)
*/function_setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) externalreturns (uint256) {
// Check caller is adminif (!hasAdminRights()) {
return fail(Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK);
}
// Check de-scaled min <= newLiquidationIncentive <= max
Exp memory newLiquidationIncentive = Exp({ mantissa: newLiquidationIncentiveMantissa });
Exp memory minLiquidationIncentive = Exp({ mantissa: liquidationIncentiveMinMantissa });
if (lessThanExp(newLiquidationIncentive, minLiquidationIncentive)) {
return fail(Error.INVALID_LIQUIDATION_INCENTIVE, FailureInfo.SET_LIQUIDATION_INCENTIVE_VALIDATION);
}
Exp memory maxLiquidationIncentive = Exp({ mantissa: liquidationIncentiveMaxMantissa });
if (lessThanExp(maxLiquidationIncentive, newLiquidationIncentive)) {
return fail(Error.INVALID_LIQUIDATION_INCENTIVE, FailureInfo.SET_LIQUIDATION_INCENTIVE_VALIDATION);
}
// Save current value for use in loguint256 oldLiquidationIncentiveMantissa = liquidationIncentiveMantissa;
// Set liquidation incentive to new incentive
liquidationIncentiveMantissa = newLiquidationIncentiveMantissa;
// Emit event with old incentive, new incentiveemit NewLiquidationIncentive(oldLiquidationIncentiveMantissa, newLiquidationIncentiveMantissa);
returnuint256(Error.NO_ERROR);
}
/**
* @notice Add the market to the markets mapping and set it as listed
* @dev Admin function to set isListed and add support for the market
* @param cToken The address of the market (token) to list
* @return uint 0=success, otherwise a failure. (See enum Error for details)
*/function_supportMarket(ICErc20 cToken) internalreturns (uint256) {
// Check caller is adminif (!hasAdminRights()) {
return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK);
}
// Is market already listed?if (markets[address(cToken)].isListed) {
return fail(Error.MARKET_ALREADY_LISTED, FailureInfo.SUPPORT_MARKET_EXISTS);
}
// Check cToken.comptroller == thisrequire(address(cToken.comptroller()) ==address(this), "!comptroller");
// Make sure market is not already listedaddress underlying = ICErc20(address(cToken)).underlying();
if (address(cTokensByUnderlying[underlying]) !=address(0)) {
return fail(Error.MARKET_ALREADY_LISTED, FailureInfo.SUPPORT_MARKET_EXISTS);
}
// List market and emit event
Market storage market = markets[address(cToken)];
market.isListed =true;
market.collateralFactorMantissa =0;
allMarkets.push(cToken);
cTokensByUnderlying[underlying] = cToken;
emit MarketListed(cToken);
returnuint256(Error.NO_ERROR);
}
/**
* @notice Deploy cToken, add the market to the markets mapping, and set it as listed and set the collateral factor
* @dev Admin function to deploy cToken, set isListed, and add support for the market and set the collateral factor
* @return uint 0=success, otherwise a failure. (See enum Error for details)
*/function_deployMarket(uint8 delegateType,
bytescalldata constructorData,
bytescalldata becomeImplData,
uint256 collateralFactorMantissa
) externalreturns (uint256) {
// Check caller is adminif (!hasAdminRights()) {
return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK);
}
// Temporarily enable Ionic admin rights for asset deployment (storing the original value)bool oldIonicAdminHasRights = ionicAdminHasRights;
ionicAdminHasRights =true;
// Deploy via Ionic admin
ICErc20 cToken = ICErc20(IFeeDistributor(ionicAdmin).deployCErc20(delegateType, constructorData, becomeImplData));
// Reset Ionic admin rights to the original value
ionicAdminHasRights = oldIonicAdminHasRights;
// Support market here in the Comptrolleruint256 err = _supportMarket(cToken);
IFeeDistributor(ionicAdmin).authoritiesRegistry().reconfigureAuthority(address(this));
// Set collateral factorreturn err ==uint256(Error.NO_ERROR) ? _setCollateralFactor(cToken, collateralFactorMantissa) : err;
}
function_becomeImplementation() external{
require(msg.sender==address(this), "!self call");
if (!_notEnteredInitialized) {
_notEntered =true;
_notEnteredInitialized =true;
}
}
/*** Helper Functions ***//**
* @notice Returns true if the given cToken market has been deprecated
* @dev All borrows in a deprecated cToken market can be immediately liquidated
* @param cToken The market to check if deprecated
*/functionisDeprecated(ICErc20 cToken) publicviewreturns (bool) {
return
markets[address(cToken)].collateralFactorMantissa ==0&&
borrowGuardianPaused[address(cToken)] ==true&&
add_(add_(cToken.reserveFactorMantissa(), cToken.adminFeeMantissa()), cToken.ionicFeeMantissa()) ==1e18;
}
functionasComptrollerExtension() internalviewreturns (ComptrollerExtensionInterface) {
return ComptrollerExtensionInterface(address(this));
}
function_getExtensionFunctions() externalpurevirtualoverridereturns (bytes4[] memory functionSelectors) {
uint8 fnsCount =32;
functionSelectors =newbytes4[](fnsCount);
functionSelectors[--fnsCount] =this.isDeprecated.selector;
functionSelectors[--fnsCount] =this._deployMarket.selector;
functionSelectors[--fnsCount] =this.getAssetsIn.selector;
functionSelectors[--fnsCount] =this.checkMembership.selector;
functionSelectors[--fnsCount] =this._setPriceOracle.selector;
functionSelectors[--fnsCount] =this._setCloseFactor.selector;
functionSelectors[--fnsCount] =this._setCollateralFactor.selector;
functionSelectors[--fnsCount] =this._setLiquidationIncentive.selector;
functionSelectors[--fnsCount] =this._setWhitelistEnforcement.selector;
functionSelectors[--fnsCount] =this._setWhitelistStatuses.selector;
functionSelectors[--fnsCount] =this._addRewardsDistributor.selector;
functionSelectors[--fnsCount] =this.getHypotheticalAccountLiquidity.selector;
functionSelectors[--fnsCount] =this.getMaxRedeemOrBorrow.selector;
functionSelectors[--fnsCount] =this.enterMarkets.selector;
functionSelectors[--fnsCount] =this.exitMarket.selector;
functionSelectors[--fnsCount] =this.mintAllowed.selector;
functionSelectors[--fnsCount] =this.redeemAllowed.selector;
functionSelectors[--fnsCount] =this.redeemVerify.selector;
functionSelectors[--fnsCount] =this.borrowAllowed.selector;
functionSelectors[--fnsCount] =this.borrowWithinLimits.selector;
functionSelectors[--fnsCount] =this.repayBorrowAllowed.selector;
functionSelectors[--fnsCount] =this.liquidateBorrowAllowed.selector;
functionSelectors[--fnsCount] =this.seizeAllowed.selector;
functionSelectors[--fnsCount] =this.transferAllowed.selector;
functionSelectors[--fnsCount] =this.mintVerify.selector;
functionSelectors[--fnsCount] =this.getAccountLiquidity.selector;
functionSelectors[--fnsCount] =this.liquidateCalculateSeizeTokens.selector;
functionSelectors[--fnsCount] =this._beforeNonReentrant.selector;
functionSelectors[--fnsCount] =this._afterNonReentrant.selector;
functionSelectors[--fnsCount] =this._becomeImplementation.selector;
functionSelectors[--fnsCount] =this.effectiveSupplyCaps.selector;
functionSelectors[--fnsCount] =this.effectiveBorrowCaps.selector;
require(fnsCount ==0, "use the correct array length");
}
/*** Pool-Wide/Cross-Asset Reentrancy Prevention ***//**
* @dev Called by cTokens before a non-reentrant function for pool-wide reentrancy prevention.
* Prevents pool-wide/cross-asset reentrancy exploits like AMP on Cream.
*/function_beforeNonReentrant() externaloverride{
require(markets[msg.sender].isListed, "!Comptroller:_beforeNonReentrant");
require(_notEntered, "!reentered");
_notEntered =false;
}
/**
* @dev Called by cTokens after a non-reentrant function for pool-wide reentrancy prevention.
* Prevents pool-wide/cross-asset reentrancy exploits like AMP on Cream.
*/function_afterNonReentrant() externaloverride{
require(markets[msg.sender].isListed, "!Comptroller:_afterNonReentrant");
_notEntered =true; // get a gas-refund post-Istanbul
}
}
Código Fuente del Contrato
Archivo 10 de 45: ComptrollerInterface.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity >=0.8.0;import { BasePriceOracle } from"../oracles/BasePriceOracle.sol";
import { ICErc20 } from"./CTokenInterfaces.sol";
import { DiamondExtension } from"../ionic/DiamondExtension.sol";
import { ComptrollerV4Storage } from"../compound/ComptrollerStorage.sol";
import { PrudentiaLib } from"../adrastia/PrudentiaLib.sol";
import { IHistoricalRates } from"adrastia-periphery/rates/IHistoricalRates.sol";
interfaceComptrollerInterface{
functionisDeprecated(ICErc20 cToken) externalviewreturns (bool);
function_becomeImplementation() external;
function_deployMarket(uint8 delegateType,
bytesmemory constructorData,
bytescalldata becomeImplData,
uint256 collateralFactorMantissa
) externalreturns (uint256);
functiongetAssetsIn(address account) externalviewreturns (ICErc20[] memory);
functioncheckMembership(address account, ICErc20 cToken) externalviewreturns (bool);
function_setPriceOracle(BasePriceOracle newOracle) externalreturns (uint256);
function_setCloseFactor(uint256 newCloseFactorMantissa) externalreturns (uint256);
function_setCollateralFactor(ICErc20 market, uint256 newCollateralFactorMantissa) externalreturns (uint256);
function_setLiquidationIncentive(uint256 newLiquidationIncentiveMantissa) externalreturns (uint256);
function_setWhitelistEnforcement(bool enforce) externalreturns (uint256);
function_setWhitelistStatuses(address[] calldata _suppliers, bool[] calldata statuses) externalreturns (uint256);
function_addRewardsDistributor(address distributor) externalreturns (uint256);
functiongetHypotheticalAccountLiquidity(address account,
address cTokenModify,
uint256 redeemTokens,
uint256 borrowAmount,
uint256 repayAmount
) externalviewreturns (uint256, uint256, uint256, uint256);
functiongetMaxRedeemOrBorrow(address account, ICErc20 cToken, bool isBorrow) externalviewreturns (uint256);
/*** Assets You Are In ***/functionenterMarkets(address[] calldata cTokens) externalreturns (uint256[] memory);
functionexitMarket(address cToken) externalreturns (uint256);
/*** Policy Hooks ***/functionmintAllowed(address cToken, address minter, uint256 mintAmount) externalreturns (uint256);
functionredeemAllowed(address cToken, address redeemer, uint256 redeemTokens) externalreturns (uint256);
functionredeemVerify(address cToken, address redeemer, uint256 redeemAmount, uint256 redeemTokens) external;
functionborrowAllowed(address cToken, address borrower, uint256 borrowAmount) externalreturns (uint256);
functionborrowWithinLimits(address cToken, uint256 accountBorrowsNew) externalviewreturns (uint256);
functionrepayBorrowAllowed(address cToken,
address payer,
address borrower,
uint256 repayAmount
) externalreturns (uint256);
functionliquidateBorrowAllowed(address cTokenBorrowed,
address cTokenCollateral,
address liquidator,
address borrower,
uint256 repayAmount
) externalreturns (uint256);
functionseizeAllowed(address cTokenCollateral,
address cTokenBorrowed,
address liquidator,
address borrower,
uint256 seizeTokens
) externalreturns (uint256);
functiontransferAllowed(address cToken, address src, address dst, uint256 transferTokens) externalreturns (uint256);
functionmintVerify(address cToken, address minter, uint256 actualMintAmount, uint256 mintTokens) external;
/*** Liquidity/Liquidation Calculations ***/functiongetAccountLiquidity(address account
) externalviewreturns (uint256error, uint256 collateralValue, uint256 liquidity, uint256 shortfall);
functionliquidateCalculateSeizeTokens(address cTokenBorrowed,
address cTokenCollateral,
uint256 repayAmount
) externalviewreturns (uint256, uint256);
/*** Pool-Wide/Cross-Asset Reentrancy Prevention ***/function_beforeNonReentrant() external;
function_afterNonReentrant() external;
/*** New supply and borrow cap view functions ***//**
* @notice Gets the supply cap of a cToken in the units of the underlying asset.
* @param cToken The address of the cToken.
*/functioneffectiveSupplyCaps(address cToken) externalviewreturns (uint256 supplyCap);
/**
* @notice Gets the borrow cap of a cToken in the units of the underlying asset.
* @param cToken The address of the cToken.
*/functioneffectiveBorrowCaps(address cToken) externalviewreturns (uint256 borrowCap);
}
interfaceComptrollerStorageInterface{
functionadmin() externalviewreturns (address);
functionadminHasRights() externalviewreturns (bool);
functionionicAdmin() externalviewreturns (address);
functionionicAdminHasRights() externalviewreturns (bool);
functionpendingAdmin() externalviewreturns (address);
functionoracle() externalviewreturns (BasePriceOracle);
functionpauseGuardian() externalviewreturns (address);
functioncloseFactorMantissa() externalviewreturns (uint256);
functionliquidationIncentiveMantissa() externalviewreturns (uint256);
functionisUserOfPool(address user) externalviewreturns (bool);
functionwhitelist(address account) externalviewreturns (bool);
functionenforceWhitelist() externalviewreturns (bool);
functionborrowCapForCollateral(address borrowed, address collateral) externalviewreturns (uint256);
functionborrowingAgainstCollateralBlacklist(address borrowed, address collateral) externalviewreturns (bool);
functionsuppliers(address account) externalviewreturns (bool);
functioncTokensByUnderlying(address) externalviewreturns (address);
/**
* Gets the supply cap of a cToken in the units of the underlying asset.
* @dev WARNING: This function is misleading if Adrastia Prudentia is being used for the supply cap. Instead, use
* `effectiveSupplyCaps` to get the correct supply cap.
* @param cToken The address of the cToken.
* @return The supply cap in the units of the underlying asset.
*/functionsupplyCaps(address cToken) externalviewreturns (uint256);
/**
* Gets the borrow cap of a cToken in the units of the underlying asset.
* @dev WARNING: This function is misleading if Adrastia Prudentia is being used for the borrow cap. Instead, use
* `effectiveBorrowCaps` to get the correct borrow cap.
* @param cToken The address of the cToken.
* @return The borrow cap in the units of the underlying asset.
*/functionborrowCaps(address cToken) externalviewreturns (uint256);
functionmarkets(address cToken) externalviewreturns (bool, uint256);
functionaccountAssets(address, uint256) externalviewreturns (address);
functionborrowGuardianPaused(address cToken) externalviewreturns (bool);
functionmintGuardianPaused(address cToken) externalviewreturns (bool);
functionrewardsDistributors(uint256) externalviewreturns (address);
}
interfaceSFSRegister{
functionregister(address _recipient) externalreturns (uint256 tokenId);
}
interfaceComptrollerExtensionInterface{
functiongetWhitelistedSuppliersSupply(address cToken) externalviewreturns (uint256 supplied);
functiongetWhitelistedBorrowersBorrows(address cToken) externalviewreturns (uint256 borrowed);
functiongetAllMarkets() externalviewreturns (ICErc20[] memory);
functiongetAllBorrowers() externalviewreturns (address[] memory);
functiongetAllBorrowersCount() externalviewreturns (uint256);
functiongetPaginatedBorrowers(uint256 page,
uint256 pageSize
) externalviewreturns (uint256 _totalPages, address[] memory _pageOfBorrowers);
functiongetRewardsDistributors() externalviewreturns (address[] memory);
functiongetAccruingFlywheels() externalviewreturns (address[] memory);
function_supplyCapWhitelist(address cToken, address account, bool whitelisted) external;
function_setBorrowCapForCollateral(address cTokenBorrow, address cTokenCollateral, uint256 borrowCap) external;
function_setBorrowCapForCollateralWhitelist(address cTokenBorrow,
address cTokenCollateral,
address account,
bool whitelisted
) external;
functionisBorrowCapForCollateralWhitelisted(address cTokenBorrow,
address cTokenCollateral,
address account
) externalviewreturns (bool);
function_blacklistBorrowingAgainstCollateral(address cTokenBorrow,
address cTokenCollateral,
bool blacklisted
) external;
function_blacklistBorrowingAgainstCollateralWhitelist(address cTokenBorrow,
address cTokenCollateral,
address account,
bool whitelisted
) external;
functionisBlacklistBorrowingAgainstCollateralWhitelisted(address cTokenBorrow,
address cTokenCollateral,
address account
) externalviewreturns (bool);
functionisSupplyCapWhitelisted(address cToken, address account) externalviewreturns (bool);
function_borrowCapWhitelist(address cToken, address account, bool whitelisted) external;
functionisBorrowCapWhitelisted(address cToken, address account) externalviewreturns (bool);
function_removeFlywheel(address flywheelAddress) externalreturns (bool);
functiongetWhitelist() externalviewreturns (address[] memory);
functionaddNonAccruingFlywheel(address flywheelAddress) externalreturns (bool);
function_setMarketSupplyCaps(ICErc20[] calldata cTokens, uint256[] calldata newSupplyCaps) external;
function_setMarketBorrowCaps(ICErc20[] calldata cTokens, uint256[] calldata newBorrowCaps) external;
function_setBorrowCapGuardian(address newBorrowCapGuardian) external;
function_setPauseGuardian(address newPauseGuardian) externalreturns (uint256);
function_setMintPaused(ICErc20 cToken, bool state) externalreturns (bool);
function_setBorrowPaused(ICErc20 cToken, bool state) externalreturns (bool);
function_setTransferPaused(bool state) externalreturns (bool);
function_setSeizePaused(bool state) externalreturns (bool);
function_unsupportMarket(ICErc20 cToken) externalreturns (uint256);
functiongetAssetAsCollateralValueCap(
ICErc20 collateral,
ICErc20 cTokenModify,
bool redeeming,
address account
) externalviewreturns (uint256);
functionregisterInSFS() externalreturns (uint256);
}
interfaceComptrollerPrudentiaCapsExtInterface{
/**
* @notice Retrieves Adrastia Prudentia borrow cap config from storage.
* @return The config.
*/functiongetBorrowCapConfig() externalviewreturns (PrudentiaLib.PrudentiaConfig memory);
/**
* @notice Retrieves Adrastia Prudentia supply cap config from storage.
* @return The config.
*/functiongetSupplyCapConfig() externalviewreturns (PrudentiaLib.PrudentiaConfig memory);
/**
* @notice Sets the Adrastia Prudentia supply cap config.
* @dev Specifying a zero address for the `controller` parameter will make the Comptroller use the native supply caps.
* @param newConfig The new config.
*/function_setSupplyCapConfig(PrudentiaLib.PrudentiaConfig calldata newConfig) external;
/**
* @notice Sets the Adrastia Prudentia supply cap config.
* @dev Specifying a zero address for the `controller` parameter will make the Comptroller use the native borrow caps.
* @param newConfig The new config.
*/function_setBorrowCapConfig(PrudentiaLib.PrudentiaConfig calldata newConfig) external;
}
interfaceUnitrollerInterface{
functioncomptrollerImplementation() externalviewreturns (address);
function_upgrade() external;
function_acceptAdmin() externalreturns (uint256);
function_setPendingAdmin(address newPendingAdmin) externalreturns (uint256);
function_toggleAdminRights(bool hasRights) externalreturns (uint256);
}
interfaceIComptrollerExtensionisComptrollerExtensionInterface, ComptrollerStorageInterface{}
//interface IComptrollerBase is ComptrollerInterface, ComptrollerStorageInterface {}interfaceIonicComptrollerisComptrollerInterface,
ComptrollerExtensionInterface,
UnitrollerInterface,
ComptrollerStorageInterface{
}
abstractcontractComptrollerBaseisComptrollerV4Storage{
/// @notice Indicator that this is a Comptroller contract (for inspection)boolpublicconstant isComptroller =true;
/**
* @notice Gets the supply cap of a cToken in the units of the underlying asset.
* @param cToken The address of the cToken.
*/functioneffectiveSupplyCaps(address cToken) publicviewvirtualreturns (uint256 supplyCap) {
PrudentiaLib.PrudentiaConfig memory capConfig = supplyCapConfig;
// Check if we're using Adrastia Prudentia for the supply capif (capConfig.controller !=address(0)) {
// We have a controller, so we're using Adrastia Prudentiaaddress underlyingToken = ICErc20(cToken).underlying();
// Get the supply cap from Adrastia Prudentia
supplyCap = IHistoricalRates(capConfig.controller).getRateAt(underlyingToken, capConfig.offset).current;
// Prudentia trims decimal points from amounts while our code requires the mantissa amount, so we// must scale the supply cap to get the correct amountint256 scaleByDecimals =18;
// Not all ERC20s implement decimals(), so we use a staticcall and check the return data
(bool success, bytesmemory data) = underlyingToken.staticcall(abi.encodeWithSignature("decimals()"));
if (success && data.length==32) {
scaleByDecimals =int256(uint256(abi.decode(data, (uint8))));
}
scaleByDecimals += capConfig.decimalShift;
if (scaleByDecimals >=0) {
// We're scaling up, so we need to multiply
supplyCap *=10**uint256(scaleByDecimals);
} else {
// We're scaling down, so we need to divide
supplyCap /=10**uint256(-scaleByDecimals);
}
} else {
// We don't have a controller, so we're using the local supply cap// Get the supply cap from the local supply cap
supplyCap = supplyCaps[cToken];
}
}
/**
* @notice Gets the borrow cap of a cToken in the units of the underlying asset.
* @param cToken The address of the cToken.
*/functioneffectiveBorrowCaps(address cToken) publicviewvirtualreturns (uint256 borrowCap) {
PrudentiaLib.PrudentiaConfig memory capConfig = borrowCapConfig;
// Check if we're using Adrastia Prudentia for the borrow capif (capConfig.controller !=address(0)) {
// We have a controller, so we're using Adrastia Prudentiaaddress underlyingToken = ICErc20(cToken).underlying();
// Get the borrow cap from Adrastia Prudentia
borrowCap = IHistoricalRates(capConfig.controller).getRateAt(underlyingToken, capConfig.offset).current;
// Prudentia trims decimal points from amounts while our code requires the mantissa amount, so we// must scale the supply cap to get the correct amountint256 scaleByDecimals =18;
// Not all ERC20s implement decimals(), so we use a staticcall and check the return data
(bool success, bytesmemory data) = underlyingToken.staticcall(abi.encodeWithSignature("decimals()"));
if (success && data.length==32) {
scaleByDecimals =int256(uint256(abi.decode(data, (uint8))));
}
scaleByDecimals += capConfig.decimalShift;
if (scaleByDecimals >=0) {
// We're scaling up, so we need to multiply
borrowCap *=10**uint256(scaleByDecimals);
} else {
// We're scaling down, so we need to divide
borrowCap /=10**uint256(-scaleByDecimals);
}
} else {
// We don't have a controller, so we're using the local borrow cap
borrowCap = borrowCaps[cToken];
}
}
}
Código Fuente del Contrato
Archivo 11 de 45: ComptrollerStorage.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity >=0.8.0;import"./IFeeDistributor.sol";
import"../oracles/BasePriceOracle.sol";
import { ICErc20 } from"./CTokenInterfaces.sol";
import { PrudentiaLib } from"../adrastia/PrudentiaLib.sol";
import"@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
contractUnitrollerAdminStorage{
/*
* Administrator for Ionic
*/addresspayablepublic ionicAdmin;
/**
* @notice Administrator for this contract
*/addresspublic admin;
/**
* @notice Pending administrator for this contract
*/addresspublic pendingAdmin;
/**
* @notice Whether or not the Ionic admin has admin rights
*/boolpublic ionicAdminHasRights =true;
/**
* @notice Whether or not the admin has admin rights
*/boolpublic adminHasRights =true;
/**
* @notice Returns a boolean indicating if the sender has admin rights
*/functionhasAdminRights() internalviewreturns (bool) {
return (msg.sender== admin && adminHasRights) || (msg.sender==address(ionicAdmin) && ionicAdminHasRights);
}
}
contractComptrollerV1StorageisUnitrollerAdminStorage{
/**
* @notice Oracle which gives the price of any given asset
*/
BasePriceOracle public oracle;
/**
* @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow
*/uint256public closeFactorMantissa;
/**
* @notice Multiplier representing the discount on collateral that a liquidator receives
*/uint256public liquidationIncentiveMantissa;
/*
* UNUSED AFTER UPGRADE: Max number of assets a single account can participate in (borrow or use as collateral)
*/uint256internal maxAssets;
/**
* @notice Per-account mapping of "assets you are in", capped by maxAssets
*/mapping(address=> ICErc20[]) public accountAssets;
}
contractComptrollerV2StorageisComptrollerV1Storage{
structMarket {
// Whether or not this market is listedbool isListed;
// Multiplier representing the most one can borrow against their collateral in this market.// For instance, 0.9 to allow borrowing 90% of collateral value.// Must be between 0 and 1, and stored as a mantissa.uint256 collateralFactorMantissa;
// Per-market mapping of "accounts in this asset"mapping(address=>bool) accountMembership;
}
/**
* @notice Official mapping of cTokens -> Market metadata
* @dev Used e.g. to determine if a market is supported
*/mapping(address=> Market) public markets;
/// @notice A list of all markets
ICErc20[] public allMarkets;
/**
* @dev Maps borrowers to booleans indicating if they have entered any markets
*/mapping(address=>bool) internal borrowers;
/// @notice A list of all borrowers who have entered marketsaddress[] public allBorrowers;
// Indexes of borrower account addresses in the `allBorrowers` arraymapping(address=>uint256) internal borrowerIndexes;
/**
* @dev Maps suppliers to booleans indicating if they have ever supplied to any markets
*/mapping(address=>bool) public suppliers;
/// @notice All cTokens addresses mapped by their underlying token addressesmapping(address=> ICErc20) public cTokensByUnderlying;
/// @notice Whether or not the supplier whitelist is enforcedboolpublic enforceWhitelist;
/// @notice Maps addresses to booleans indicating if they are allowed to supply assets (i.e., mint cTokens)mapping(address=>bool) public whitelist;
/// @notice An array of all whitelisted accountsaddress[] public whitelistArray;
// Indexes of account addresses in the `whitelistArray` arraymapping(address=>uint256) internal whitelistIndexes;
/**
* @notice The Pause Guardian can pause certain actions as a safety mechanism.
* Actions which allow users to remove their own assets cannot be paused.
* Liquidation / seizing / transfer can only be paused globally, not by market.
*/addresspublic pauseGuardian;
boolpublic _mintGuardianPaused;
boolpublic _borrowGuardianPaused;
boolpublic transferGuardianPaused;
boolpublic seizeGuardianPaused;
mapping(address=>bool) public mintGuardianPaused;
mapping(address=>bool) public borrowGuardianPaused;
}
contractComptrollerV3StorageisComptrollerV2Storage{
/// @notice The borrowCapGuardian can set borrowCaps to any number for any market. Lowering the borrow cap could disable borrowing on the given market./// @dev If Adrastia Prudentia is enabled, the values the borrow cap guardian sets are ignored.addresspublic borrowCapGuardian;
/// @notice Borrow caps enforced by borrowAllowed for each cToken address. Defaults to zero which corresponds to unlimited borrowing./// @dev If Adrastia Prudentia is enabled, this value is ignored. Use `effectiveBorrowCaps` instead.mapping(address=>uint256) public borrowCaps;
/// @notice Supply caps enforced by mintAllowed for each cToken address. Defaults to zero which corresponds to unlimited supplying./// @dev If Adrastia Prudentia is enabled, this value is ignored. Use `effectiveSupplyCaps` instead.mapping(address=>uint256) public supplyCaps;
/// @notice RewardsDistributor contracts to notify of flywheel changes.address[] public rewardsDistributors;
/// @dev Guard variable for pool-wide/cross-asset re-entrancy checksboolinternal _notEntered;
/// @dev Whether or not _notEntered has been initializedboolinternal _notEnteredInitialized;
/// @notice RewardsDistributor to list for claiming, but not to notify of flywheel changes.address[] public nonAccruingRewardsDistributors;
/// @dev cap for each user's borrows against specific assets - denominated in the borrowed assetmapping(address=>mapping(address=>uint256)) public borrowCapForCollateral;
/// @dev blacklist to disallow the borrowing of an asset against specific collateralmapping(address=>mapping(address=>bool)) public borrowingAgainstCollateralBlacklist;
/// @dev set of whitelisted accounts that are allowed to bypass the borrowing against specific collateral capmapping(address=>mapping(address=> EnumerableSet.AddressSet)) internal borrowCapForCollateralWhitelist;
/// @dev set of whitelisted accounts that are allowed to bypass the borrow capmapping(address=>mapping(address=> EnumerableSet.AddressSet))
internal borrowingAgainstCollateralBlacklistWhitelist;
/// @dev set of whitelisted accounts that are allowed to bypass the supply capmapping(address=> EnumerableSet.AddressSet) internal supplyCapWhitelist;
/// @dev set of whitelisted accounts that are allowed to bypass the borrow capmapping(address=> EnumerableSet.AddressSet) internal borrowCapWhitelist;
}
contractComptrollerV4StorageisComptrollerV3Storage{
/// @dev Adrastia Prudentia config for controlling borrow caps.
PrudentiaLib.PrudentiaConfig internal borrowCapConfig;
/// @dev Adrastia Prudentia config for controlling supply caps.
PrudentiaLib.PrudentiaConfig internal supplyCapConfig;
}
Código Fuente del Contrato
Archivo 12 de 45: ContextUpgradeable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;import"../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/abstractcontractContextUpgradeableisInitializable{
function__Context_init() internalonlyInitializing{
}
function__Context_init_unchained() internalonlyInitializing{
}
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
/**
* @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[50] private __gap;
}
Código Fuente del Contrato
Archivo 13 de 45: Create2Upgradeable.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (utils/Create2.sol)pragmasolidity ^0.8.0;/**
* @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
* `CREATE2` can be used to compute in advance the address where a smart
* contract will be deployed, which allows for interesting new mechanisms known
* as 'counterfactual interactions'.
*
* See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
* information.
*/libraryCreate2Upgradeable{
/**
* @dev Deploys a contract using `CREATE2`. The address where the contract
* will be deployed can be known in advance via {computeAddress}.
*
* The bytecode for a contract can be obtained from Solidity with
* `type(contractName).creationCode`.
*
* Requirements:
*
* - `bytecode` must not be empty.
* - `salt` must have not been used for `bytecode` already.
* - the factory must have a balance of at least `amount`.
* - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
*/functiondeploy(uint256 amount,
bytes32 salt,
bytesmemory bytecode
) internalreturns (address addr) {
require(address(this).balance>= amount, "Create2: insufficient balance");
require(bytecode.length!=0, "Create2: bytecode length is zero");
/// @solidity memory-safe-assemblyassembly {
addr :=create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
}
require(addr !=address(0), "Create2: Failed on deploy");
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
* `bytecodeHash` or `salt` will result in a new destination address.
*/functioncomputeAddress(bytes32 salt, bytes32 bytecodeHash) internalviewreturns (address) {
return computeAddress(salt, bytecodeHash, address(this));
}
/**
* @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
* `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
*/functioncomputeAddress(bytes32 salt,
bytes32 bytecodeHash,
address deployer
) internalpurereturns (address addr) {
/// @solidity memory-safe-assemblyassembly {
let ptr :=mload(0x40) // Get free memory pointer// | | ↓ ptr ... ↓ ptr + 0x0B (start) ... ↓ ptr + 0x20 ... ↓ ptr + 0x40 ... |// |-------------------|---------------------------------------------------------------------------|// | bytecodeHash | CCCCCCCCCCCCC...CC |// | salt | BBBBBBBBBBBBB...BB |// | deployer | 000000...0000AAAAAAAAAAAAAAAAAAA...AA |// | 0xFF | FF |// |-------------------|---------------------------------------------------------------------------|// | memory | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |// | keccak(start, 85) | ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |mstore(add(ptr, 0x40), bytecodeHash)
mstore(add(ptr, 0x20), salt)
mstore(ptr, deployer) // Right-aligned with 12 preceding garbage byteslet start :=add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xffmstore8(start, 0xff)
addr :=keccak256(start, 85)
}
}
}
Código Fuente del Contrato
Archivo 14 de 45: DiamondExtension.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity >=0.8.0;/**
* @notice a base contract for logic extensions that use the diamond pattern storage
* to map the functions when looking up the extension contract to delegate to.
*/abstractcontractDiamondExtension{
/**
* @return a list of all the function selectors that this logic extension exposes
*/function_getExtensionFunctions() externalpurevirtualreturns (bytes4[] memory);
}
// When no function exists for function callederrorFunctionNotFound(bytes4 _functionSelector);
// When no extension exists for function callederrorExtensionNotFound(bytes4 _functionSelector);
// When the function is already addederrorFunctionAlreadyAdded(bytes4 _functionSelector, address _currentImpl);
abstractcontractDiamondBase{
/**
* @dev register a logic extension
* @param extensionToAdd the extension whose functions are to be added
* @param extensionToReplace the extension whose functions are to be removed/replaced
*/function_registerExtension(DiamondExtension extensionToAdd, DiamondExtension extensionToReplace) externalvirtual;
function_listExtensions() publicviewreturns (address[] memory) {
return LibDiamond.listExtensions();
}
fallback() external{
address extension = LibDiamond.getExtensionForFunction(msg.sig);
if (extension ==address(0)) revert FunctionNotFound(msg.sig);
// Execute external function from extension using delegatecall and return any value.assembly {
// copy function selector and any argumentscalldatacopy(0, 0, calldatasize())
// execute function call using the extensionlet result :=delegatecall(gas(), extension, 0, calldatasize(), 0, 0)
// get any return valuereturndatacopy(0, 0, returndatasize())
// return any return value or error back to the callerswitch result
case0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
}
/**
* @notice a library to use in a contract, whose logic is extended with diamond extension
*/libraryLibDiamond{
bytes32constant DIAMOND_STORAGE_POSITION =keccak256("diamond.extensions.diamond.storage");
structFunction {
address extension;
bytes4 selector;
}
structLogicStorage {
Function[] functions;
address[] extensions;
}
functiongetExtensionForFunction(bytes4 msgSig) internalviewreturns (address) {
return getExtensionForSelector(msgSig, diamondStorage());
}
functiondiamondStorage() internalpurereturns (LogicStorage storage ds) {
bytes32 position = DIAMOND_STORAGE_POSITION;
assembly {
ds.slot:= position
}
}
functionlistExtensions() internalviewreturns (address[] memory) {
return diamondStorage().extensions;
}
functionregisterExtension(DiamondExtension extensionToAdd, DiamondExtension extensionToReplace) internal{
if (address(extensionToReplace) !=address(0)) {
removeExtension(extensionToReplace);
}
addExtension(extensionToAdd);
}
functionremoveExtension(DiamondExtension extension) internal{
LogicStorage storage ds = diamondStorage();
// remove all functions of the extension to replace
removeExtensionFunctions(extension);
for (uint8 i =0; i < ds.extensions.length; i++) {
if (ds.extensions[i] ==address(extension)) {
ds.extensions[i] = ds.extensions[ds.extensions.length-1];
ds.extensions.pop();
}
}
}
functionaddExtension(DiamondExtension extension) internal{
LogicStorage storage ds = diamondStorage();
for (uint8 i =0; i < ds.extensions.length; i++) {
require(ds.extensions[i] !=address(extension), "extension already added");
}
addExtensionFunctions(extension);
ds.extensions.push(address(extension));
}
functionremoveExtensionFunctions(DiamondExtension extension) internal{
bytes4[] memory fnsToRemove = extension._getExtensionFunctions();
LogicStorage storage ds = diamondStorage();
for (uint16 i =0; i < fnsToRemove.length; i++) {
bytes4 selectorToRemove = fnsToRemove[i];
// must never failassert(address(extension) == getExtensionForSelector(selectorToRemove, ds));
// swap with the last element in the selectorAtIndex array and remove the last elementuint16 indexToKeep = getIndexForSelector(selectorToRemove, ds);
ds.functions[indexToKeep] = ds.functions[ds.functions.length-1];
ds.functions.pop();
}
}
functionaddExtensionFunctions(DiamondExtension extension) internal{
bytes4[] memory fnsToAdd = extension._getExtensionFunctions();
LogicStorage storage ds = diamondStorage();
uint16 functionsCount =uint16(ds.functions.length);
for (uint256 functionsIndex =0; functionsIndex < fnsToAdd.length; functionsIndex++) {
bytes4 selector = fnsToAdd[functionsIndex];
address oldImplementation = getExtensionForSelector(selector, ds);
if (oldImplementation !=address(0)) revert FunctionAlreadyAdded(selector, oldImplementation);
ds.functions.push(Function(address(extension), selector));
functionsCount++;
}
}
functiongetExtensionForSelector(bytes4 selector, LogicStorage storage ds) internalviewreturns (address) {
uint256 fnsLen = ds.functions.length;
for (uint256 i =0; i < fnsLen; i++) {
if (ds.functions[i].selector== selector) return ds.functions[i].extension;
}
returnaddress(0);
}
functiongetIndexForSelector(bytes4 selector, LogicStorage storage ds) internalviewreturns (uint16) {
uint16 fnsLen =uint16(ds.functions.length);
for (uint16 i =0; i < fnsLen; i++) {
if (ds.functions[i].selector== selector) return i;
}
returntype(uint16).max;
}
}
Código Fuente del Contrato
Archivo 15 de 45: ERC1967Proxy.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol)pragmasolidity ^0.8.0;import"../Proxy.sol";
import"./ERC1967Upgrade.sol";
/**
* @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an
* implementation address that can be changed. This address is stored in storage in the location specified by
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the
* implementation behind the proxy.
*/contractERC1967ProxyisProxy, ERC1967Upgrade{
/**
* @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`.
*
* If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded
* function call, and allows initializing the storage of the proxy like a Solidity constructor.
*/constructor(address _logic, bytesmemory _data) payable{
_upgradeToAndCall(_logic, _data, false);
}
/**
* @dev Returns the current implementation address.
*/function_implementation() internalviewvirtualoverridereturns (address impl) {
return ERC1967Upgrade._getImplementation();
}
}
Código Fuente del Contrato
Archivo 16 de 45: ERC1967Upgrade.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.5.0) (proxy/ERC1967/ERC1967Upgrade.sol)pragmasolidity ^0.8.2;import"../beacon/IBeacon.sol";
import"../../interfaces/draft-IERC1822.sol";
import"../../utils/Address.sol";
import"../../utils/StorageSlot.sol";
/**
* @dev This abstract contract provides getters and event emitting update functions for
* https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots.
*
* _Available since v4.1._
*
* @custom:oz-upgrades-unsafe-allow delegatecall
*/abstractcontractERC1967Upgrade{
// This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1bytes32privateconstant _ROLLBACK_SLOT =0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143;
/**
* @dev Storage slot with the address of the current implementation.
* This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is
* validated in the constructor.
*/bytes32internalconstant _IMPLEMENTATION_SLOT =0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
/**
* @dev Emitted when the implementation is upgraded.
*/eventUpgraded(addressindexed implementation);
/**
* @dev Returns the current implementation address.
*/function_getImplementation() internalviewreturns (address) {
return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 implementation slot.
*/function_setImplementation(address newImplementation) private{
require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value= newImplementation;
}
/**
* @dev Perform implementation upgrade
*
* Emits an {Upgraded} event.
*/function_upgradeTo(address newImplementation) internal{
_setImplementation(newImplementation);
emit Upgraded(newImplementation);
}
/**
* @dev Perform implementation upgrade with additional setup call.
*
* Emits an {Upgraded} event.
*/function_upgradeToAndCall(address newImplementation,
bytesmemory data,
bool forceCall
) internal{
_upgradeTo(newImplementation);
if (data.length>0|| forceCall) {
Address.functionDelegateCall(newImplementation, data);
}
}
/**
* @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call.
*
* Emits an {Upgraded} event.
*/function_upgradeToAndCallUUPS(address newImplementation,
bytesmemory data,
bool forceCall
) internal{
// Upgrades from old implementations will perform a rollback test. This test requires the new// implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing// this special case will break upgrade paths from old UUPS implementation to new ones.if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) {
_setImplementation(newImplementation);
} else {
try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID");
} catch {
revert("ERC1967Upgrade: new implementation is not UUPS");
}
_upgradeToAndCall(newImplementation, data, forceCall);
}
}
/**
* @dev Storage slot with the admin of the contract.
* This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is
* validated in the constructor.
*/bytes32internalconstant _ADMIN_SLOT =0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
/**
* @dev Emitted when the admin account has changed.
*/eventAdminChanged(address previousAdmin, address newAdmin);
/**
* @dev Returns the current admin.
*/function_getAdmin() internalviewreturns (address) {
return StorageSlot.getAddressSlot(_ADMIN_SLOT).value;
}
/**
* @dev Stores a new address in the EIP1967 admin slot.
*/function_setAdmin(address newAdmin) private{
require(newAdmin !=address(0), "ERC1967: new admin is the zero address");
StorageSlot.getAddressSlot(_ADMIN_SLOT).value= newAdmin;
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {AdminChanged} event.
*/function_changeAdmin(address newAdmin) internal{
emit AdminChanged(_getAdmin(), newAdmin);
_setAdmin(newAdmin);
}
/**
* @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy.
* This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor.
*/bytes32internalconstant _BEACON_SLOT =0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50;
/**
* @dev Emitted when the beacon is upgraded.
*/eventBeaconUpgraded(addressindexed beacon);
/**
* @dev Returns the current beacon.
*/function_getBeacon() internalviewreturns (address) {
return StorageSlot.getAddressSlot(_BEACON_SLOT).value;
}
/**
* @dev Stores a new beacon in the EIP1967 beacon slot.
*/function_setBeacon(address newBeacon) private{
require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract");
require(
Address.isContract(IBeacon(newBeacon).implementation()),
"ERC1967: beacon implementation is not a contract"
);
StorageSlot.getAddressSlot(_BEACON_SLOT).value= newBeacon;
}
/**
* @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does
* not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that).
*
* Emits a {BeaconUpgraded} event.
*/function_upgradeBeaconToAndCall(address newBeacon,
bytesmemory data,
bool forceCall
) internal{
_setBeacon(newBeacon);
emit BeaconUpgraded(newBeacon);
if (data.length>0|| forceCall) {
Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data);
}
}
}
Código Fuente del Contrato
Archivo 17 de 45: ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.abstractcontractERC20{
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventTransfer(addressindexedfrom, addressindexed to, uint256 amount);
eventApproval(addressindexed owner, addressindexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/stringpublic name;
stringpublic symbol;
uint8publicimmutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/uint256public totalSupply;
mapping(address=>uint256) public balanceOf;
mapping(address=>mapping(address=>uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/uint256internalimmutable INITIAL_CHAIN_ID;
bytes32internalimmutable INITIAL_DOMAIN_SEPARATOR;
mapping(address=>uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/constructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID =block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/functionapprove(address spender, uint256 amount) publicvirtualreturns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
returntrue;
}
functiontransfer(address to, uint256 amount) publicvirtualreturns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
returntrue;
}
functiontransferFrom(addressfrom,
address to,
uint256 amount
) publicvirtualreturns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
returntrue;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/functionpermit(address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) publicvirtual{
require(deadline >=block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing// the owner's nonce which cannot realistically overflow.unchecked {
address recoveredAddress =ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress !=address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
functionDOMAIN_SEPARATOR() publicviewvirtualreturns (bytes32) {
returnblock.chainid== INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
functioncomputeDomainSeparator() internalviewvirtualreturns (bytes32) {
returnkeccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/function_mint(address to, uint256 amount) internalvirtual{
totalSupply += amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function_burn(addressfrom, uint256 amount) internalvirtual{
balanceOf[from] -= amount;
// Cannot underflow because a user's balance// will never be larger than the total supply.unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
Código Fuente del Contrato
Archivo 18 de 45: EnumerableSet.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (utils/structs/EnumerableSet.sol)// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.pragmasolidity ^0.8.0;/**
* @dev Library for managing
* https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
* types.
*
* Sets have the following properties:
*
* - Elements are added, removed, and checked for existence in constant time
* (O(1)).
* - Elements are enumerated in O(n). No guarantees are made on the ordering.
*
* ```
* contract Example {
* // Add the library methods
* using EnumerableSet for EnumerableSet.AddressSet;
*
* // Declare a set state variable
* EnumerableSet.AddressSet private mySet;
* }
* ```
*
* As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
* and `uint256` (`UintSet`) are supported.
*
* [WARNING]
* ====
* Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
* unusable.
* See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
*
* In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
* array of EnumerableSet.
* ====
*/libraryEnumerableSet{
// To implement this library for multiple types with as little code// repetition as possible, we write it in terms of a generic Set type with// bytes32 values.// The Set implementation uses private functions, and user-facing// implementations (such as AddressSet) are just wrappers around the// underlying Set.// This means that we can only create new EnumerableSets for types that fit// in bytes32.structSet {
// Storage of set valuesbytes32[] _values;
// Position of the value in the `values` array, plus 1 because index 0// means a value is not in the set.mapping(bytes32=>uint256) _indexes;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/function_add(Set storage set, bytes32 value) privatereturns (bool) {
if (!_contains(set, value)) {
set._values.push(value);
// The value is stored at length-1, but we add 1 to all indexes// and use 0 as a sentinel value
set._indexes[value] = set._values.length;
returntrue;
} else {
returnfalse;
}
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/function_remove(Set storage set, bytes32 value) privatereturns (bool) {
// We read and store the value's index to prevent multiple reads from the same storage slotuint256 valueIndex = set._indexes[value];
if (valueIndex !=0) {
// Equivalent to contains(set, value)// To delete an element from the _values array in O(1), we swap the element to delete with the last one in// the array, and then remove the last element (sometimes called as 'swap and pop').// This modifies the order of the array, as noted in {at}.uint256 toDeleteIndex = valueIndex -1;
uint256 lastIndex = set._values.length-1;
if (lastIndex != toDeleteIndex) {
bytes32 lastValue = set._values[lastIndex];
// Move the last value to the index where the value to delete is
set._values[toDeleteIndex] = lastValue;
// Update the index for the moved value
set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex
}
// Delete the slot where the moved value was stored
set._values.pop();
// Delete the index for the deleted slotdelete set._indexes[value];
returntrue;
} else {
returnfalse;
}
}
/**
* @dev Returns true if the value is in the set. O(1).
*/function_contains(Set storage set, bytes32 value) privateviewreturns (bool) {
return set._indexes[value] !=0;
}
/**
* @dev Returns the number of values on the set. O(1).
*/function_length(Set storage set) privateviewreturns (uint256) {
return set._values.length;
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/function_at(Set storage set, uint256 index) privateviewreturns (bytes32) {
return set._values[index];
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/function_values(Set storage set) privateviewreturns (bytes32[] memory) {
return set._values;
}
// Bytes32SetstructBytes32Set {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/functionadd(Bytes32Set storage set, bytes32 value) internalreturns (bool) {
return _add(set._inner, value);
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/functionremove(Bytes32Set storage set, bytes32 value) internalreturns (bool) {
return _remove(set._inner, value);
}
/**
* @dev Returns true if the value is in the set. O(1).
*/functioncontains(Bytes32Set storage set, bytes32 value) internalviewreturns (bool) {
return _contains(set._inner, value);
}
/**
* @dev Returns the number of values in the set. O(1).
*/functionlength(Bytes32Set storage set) internalviewreturns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/functionat(Bytes32Set storage set, uint256 index) internalviewreturns (bytes32) {
return _at(set._inner, index);
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/functionvalues(Bytes32Set storage set) internalviewreturns (bytes32[] memory) {
bytes32[] memory store = _values(set._inner);
bytes32[] memory result;
/// @solidity memory-safe-assemblyassembly {
result := store
}
return result;
}
// AddressSetstructAddressSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/functionadd(AddressSet storage set, address value) internalreturns (bool) {
return _add(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/functionremove(AddressSet storage set, address value) internalreturns (bool) {
return _remove(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/functioncontains(AddressSet storage set, address value) internalviewreturns (bool) {
return _contains(set._inner, bytes32(uint256(uint160(value))));
}
/**
* @dev Returns the number of values in the set. O(1).
*/functionlength(AddressSet storage set) internalviewreturns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/functionat(AddressSet storage set, uint256 index) internalviewreturns (address) {
returnaddress(uint160(uint256(_at(set._inner, index))));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/functionvalues(AddressSet storage set) internalviewreturns (address[] memory) {
bytes32[] memory store = _values(set._inner);
address[] memory result;
/// @solidity memory-safe-assemblyassembly {
result := store
}
return result;
}
// UintSetstructUintSet {
Set _inner;
}
/**
* @dev Add a value to a set. O(1).
*
* Returns true if the value was added to the set, that is if it was not
* already present.
*/functionadd(UintSet storage set, uint256 value) internalreturns (bool) {
return _add(set._inner, bytes32(value));
}
/**
* @dev Removes a value from a set. O(1).
*
* Returns true if the value was removed from the set, that is if it was
* present.
*/functionremove(UintSet storage set, uint256 value) internalreturns (bool) {
return _remove(set._inner, bytes32(value));
}
/**
* @dev Returns true if the value is in the set. O(1).
*/functioncontains(UintSet storage set, uint256 value) internalviewreturns (bool) {
return _contains(set._inner, bytes32(value));
}
/**
* @dev Returns the number of values in the set. O(1).
*/functionlength(UintSet storage set) internalviewreturns (uint256) {
return _length(set._inner);
}
/**
* @dev Returns the value stored at position `index` in the set. O(1).
*
* Note that there are no guarantees on the ordering of values inside the
* array, and it may change when more values are added or removed.
*
* Requirements:
*
* - `index` must be strictly less than {length}.
*/functionat(UintSet storage set, uint256 index) internalviewreturns (uint256) {
returnuint256(_at(set._inner, index));
}
/**
* @dev Return the entire set in an array
*
* WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
* to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
* this function has an unbounded cost, and using it as part of a state-changing function may render the function
* uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
*/functionvalues(UintSet storage set) internalviewreturns (uint256[] memory) {
bytes32[] memory store = _values(set._inner);
uint256[] memory result;
/// @solidity memory-safe-assemblyassembly {
result := store
}
return result;
}
}
Código Fuente del Contrato
Archivo 19 de 45: ErrorReporter.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity >=0.8.0;contractComptrollerErrorReporter{
enumError {
NO_ERROR,
UNAUTHORIZED,
COMPTROLLER_MISMATCH,
INSUFFICIENT_SHORTFALL,
INSUFFICIENT_LIQUIDITY,
INVALID_CLOSE_FACTOR,
INVALID_COLLATERAL_FACTOR,
INVALID_LIQUIDATION_INCENTIVE,
MARKET_NOT_LISTED,
MARKET_ALREADY_LISTED,
MATH_ERROR,
NONZERO_BORROW_BALANCE,
PRICE_ERROR,
REJECTION,
SNAPSHOT_ERROR,
TOO_MANY_ASSETS,
TOO_MUCH_REPAY,
SUPPLIER_NOT_WHITELISTED,
BORROW_BELOW_MIN,
SUPPLY_ABOVE_MAX,
NONZERO_TOTAL_SUPPLY
}
enumFailureInfo {
ACCEPT_ADMIN_PENDING_ADMIN_CHECK,
ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK,
ADD_REWARDS_DISTRIBUTOR_OWNER_CHECK,
EXIT_MARKET_BALANCE_OWED,
EXIT_MARKET_REJECTION,
TOGGLE_ADMIN_RIGHTS_OWNER_CHECK,
TOGGLE_AUTO_IMPLEMENTATIONS_ENABLED_OWNER_CHECK,
SET_CLOSE_FACTOR_OWNER_CHECK,
SET_CLOSE_FACTOR_VALIDATION,
SET_COLLATERAL_FACTOR_OWNER_CHECK,
SET_COLLATERAL_FACTOR_NO_EXISTS,
SET_COLLATERAL_FACTOR_VALIDATION,
SET_COLLATERAL_FACTOR_WITHOUT_PRICE,
SET_LIQUIDATION_INCENTIVE_OWNER_CHECK,
SET_LIQUIDATION_INCENTIVE_VALIDATION,
SET_PENDING_ADMIN_OWNER_CHECK,
SET_PENDING_IMPLEMENTATION_CONTRACT_CHECK,
SET_PENDING_IMPLEMENTATION_OWNER_CHECK,
SET_PRICE_ORACLE_OWNER_CHECK,
SET_WHITELIST_ENFORCEMENT_OWNER_CHECK,
SET_WHITELIST_STATUS_OWNER_CHECK,
SUPPORT_MARKET_EXISTS,
SUPPORT_MARKET_OWNER_CHECK,
SET_PAUSE_GUARDIAN_OWNER_CHECK,
UNSUPPORT_MARKET_OWNER_CHECK,
UNSUPPORT_MARKET_DOES_NOT_EXIST,
UNSUPPORT_MARKET_IN_USE
}
/**
* @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary
* contract-specific code that enables us to report opaque error codes from upgradeable contracts.
**/eventFailure(uint256error, uint256 info, uint256 detail);
/**
* @dev use this when reporting a known error from the money market or a non-upgradeable collaborator
*/functionfail(Error err, FailureInfo info) internalreturns (uint256) {
emit Failure(uint256(err), uint256(info), 0);
returnuint256(err);
}
/**
* @dev use this when reporting an opaque error from an upgradeable collaborator contract
*/functionfailOpaque(Error err,
FailureInfo info,
uint256 opaqueError
) internalreturns (uint256) {
emit Failure(uint256(err), uint256(info), opaqueError);
returnuint256(err);
}
}
contractTokenErrorReporter{
enumError {
NO_ERROR,
UNAUTHORIZED,
BAD_INPUT,
COMPTROLLER_REJECTION,
COMPTROLLER_CALCULATION_ERROR,
INTEREST_RATE_MODEL_ERROR,
INVALID_ACCOUNT_PAIR,
INVALID_CLOSE_AMOUNT_REQUESTED,
INVALID_COLLATERAL_FACTOR,
MATH_ERROR,
MARKET_NOT_FRESH,
MARKET_NOT_LISTED,
TOKEN_INSUFFICIENT_ALLOWANCE,
TOKEN_INSUFFICIENT_BALANCE,
TOKEN_INSUFFICIENT_CASH,
TOKEN_TRANSFER_IN_FAILED,
TOKEN_TRANSFER_OUT_FAILED,
UTILIZATION_ABOVE_MAX
}
/*
* Note: FailureInfo (but not Error) is kept in alphabetical order
* This is because FailureInfo grows significantly faster, and
* the order of Error has some meaning, while the order of FailureInfo
* is entirely arbitrary.
*/enumFailureInfo {
ACCEPT_ADMIN_PENDING_ADMIN_CHECK,
ACCRUE_INTEREST_ACCUMULATED_INTEREST_CALCULATION_FAILED,
ACCRUE_INTEREST_BORROW_RATE_CALCULATION_FAILED,
ACCRUE_INTEREST_NEW_BORROW_INDEX_CALCULATION_FAILED,
ACCRUE_INTEREST_NEW_TOTAL_BORROWS_CALCULATION_FAILED,
ACCRUE_INTEREST_NEW_TOTAL_RESERVES_CALCULATION_FAILED,
ACCRUE_INTEREST_NEW_TOTAL_IONIC_FEES_CALCULATION_FAILED,
ACCRUE_INTEREST_NEW_TOTAL_ADMIN_FEES_CALCULATION_FAILED,
ACCRUE_INTEREST_SIMPLE_INTEREST_FACTOR_CALCULATION_FAILED,
BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED,
BORROW_ACCRUE_INTEREST_FAILED,
BORROW_CASH_NOT_AVAILABLE,
BORROW_FRESHNESS_CHECK,
BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED,
BORROW_MARKET_NOT_LISTED,
BORROW_COMPTROLLER_REJECTION,
LIQUIDATE_ACCRUE_BORROW_INTEREST_FAILED,
LIQUIDATE_ACCRUE_COLLATERAL_INTEREST_FAILED,
LIQUIDATE_COLLATERAL_FRESHNESS_CHECK,
LIQUIDATE_COMPTROLLER_REJECTION,
LIQUIDATE_COMPTROLLER_CALCULATE_AMOUNT_SEIZE_FAILED,
LIQUIDATE_CLOSE_AMOUNT_IS_UINT_MAX,
LIQUIDATE_CLOSE_AMOUNT_IS_ZERO,
LIQUIDATE_FRESHNESS_CHECK,
LIQUIDATE_LIQUIDATOR_IS_BORROWER,
LIQUIDATE_REPAY_BORROW_FRESH_FAILED,
LIQUIDATE_SEIZE_BALANCE_INCREMENT_FAILED,
LIQUIDATE_SEIZE_BALANCE_DECREMENT_FAILED,
LIQUIDATE_SEIZE_COMPTROLLER_REJECTION,
LIQUIDATE_SEIZE_LIQUIDATOR_IS_BORROWER,
LIQUIDATE_SEIZE_TOO_MUCH,
MINT_ACCRUE_INTEREST_FAILED,
MINT_COMPTROLLER_REJECTION,
MINT_EXCHANGE_CALCULATION_FAILED,
MINT_EXCHANGE_RATE_READ_FAILED,
MINT_FRESHNESS_CHECK,
MINT_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED,
MINT_NEW_TOTAL_SUPPLY_CALCULATION_FAILED,
MINT_TRANSFER_IN_FAILED,
MINT_TRANSFER_IN_NOT_POSSIBLE,
NEW_UTILIZATION_RATE_ABOVE_MAX,
REDEEM_ACCRUE_INTEREST_FAILED,
REDEEM_COMPTROLLER_REJECTION,
REDEEM_EXCHANGE_TOKENS_CALCULATION_FAILED,
REDEEM_EXCHANGE_AMOUNT_CALCULATION_FAILED,
REDEEM_EXCHANGE_RATE_READ_FAILED,
REDEEM_FRESHNESS_CHECK,
REDEEM_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED,
REDEEM_NEW_TOTAL_SUPPLY_CALCULATION_FAILED,
REDEEM_TRANSFER_OUT_NOT_POSSIBLE,
WITHDRAW_IONIC_FEES_ACCRUE_INTEREST_FAILED,
WITHDRAW_IONIC_FEES_CASH_NOT_AVAILABLE,
WITHDRAW_IONIC_FEES_FRESH_CHECK,
WITHDRAW_IONIC_FEES_VALIDATION,
WITHDRAW_ADMIN_FEES_ACCRUE_INTEREST_FAILED,
WITHDRAW_ADMIN_FEES_CASH_NOT_AVAILABLE,
WITHDRAW_ADMIN_FEES_FRESH_CHECK,
WITHDRAW_ADMIN_FEES_VALIDATION,
REDUCE_RESERVES_ACCRUE_INTEREST_FAILED,
REDUCE_RESERVES_ADMIN_CHECK,
REDUCE_RESERVES_CASH_NOT_AVAILABLE,
REDUCE_RESERVES_FRESH_CHECK,
REDUCE_RESERVES_VALIDATION,
REPAY_BEHALF_ACCRUE_INTEREST_FAILED,
REPAY_BORROW_ACCRUE_INTEREST_FAILED,
REPAY_BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED,
REPAY_BORROW_COMPTROLLER_REJECTION,
REPAY_BORROW_FRESHNESS_CHECK,
REPAY_BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED,
REPAY_BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED,
REPAY_BORROW_TRANSFER_IN_NOT_POSSIBLE,
SET_COLLATERAL_FACTOR_OWNER_CHECK,
SET_COLLATERAL_FACTOR_VALIDATION,
SET_COMPTROLLER_OWNER_CHECK,
SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED,
SET_INTEREST_RATE_MODEL_FRESH_CHECK,
SET_INTEREST_RATE_MODEL_OWNER_CHECK,
TOGGLE_ADMIN_RIGHTS_OWNER_CHECK,
SET_PENDING_ADMIN_OWNER_CHECK,
SET_ADMIN_FEE_ACCRUE_INTEREST_FAILED,
SET_ADMIN_FEE_ADMIN_CHECK,
SET_ADMIN_FEE_FRESH_CHECK,
SET_ADMIN_FEE_BOUNDS_CHECK,
SET_IONIC_FEE_ACCRUE_INTEREST_FAILED,
SET_IONIC_FEE_FRESH_CHECK,
SET_IONIC_FEE_BOUNDS_CHECK,
SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED,
SET_RESERVE_FACTOR_ADMIN_CHECK,
SET_RESERVE_FACTOR_FRESH_CHECK,
SET_RESERVE_FACTOR_BOUNDS_CHECK,
TRANSFER_COMPTROLLER_REJECTION,
TRANSFER_NOT_ALLOWED,
TRANSFER_NOT_ENOUGH,
TRANSFER_TOO_MUCH,
ADD_RESERVES_ACCRUE_INTEREST_FAILED,
ADD_RESERVES_FRESH_CHECK,
ADD_RESERVES_TRANSFER_IN_NOT_POSSIBLE
}
/**
* @dev `error` corresponds to enum Error; `info` corresponds to enum FailureInfo, and `detail` is an arbitrary
* contract-specific code that enables us to report opaque error codes from upgradeable contracts.
**/eventFailure(uint256error, uint256 info, uint256 detail);
/**
* @dev use this when reporting a known error from the money market or a non-upgradeable collaborator
*/functionfail(Error err, FailureInfo info) internalreturns (uint256) {
emit Failure(uint256(err), uint256(info), 0);
returnuint256(err);
}
/**
* @dev use this when reporting an opaque error from an upgradeable collaborator contract
*/functionfailOpaque(Error err,
FailureInfo info,
uint256 opaqueError
) internalreturns (uint256) {
emit Failure(uint256(err), uint256(info), opaqueError);
return err ==Error.COMPTROLLER_REJECTION ? 1000+ opaqueError : uint256(err);
}
}
Código Fuente del Contrato
Archivo 20 de 45: Exponential.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity >=0.8.0;import"./CarefulMath.sol";
import"./ExponentialNoError.sol";
/**
* @title Exponential module for storing fixed-precision decimals
* @author Compound
* @dev Legacy contract for compatibility reasons with existing contracts that still use MathError
* @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.
* Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:
* `Exp({mantissa: 5100000000000000000})`.
*/contractExponentialisCarefulMath, ExponentialNoError{
/**
* @dev Creates an exponential from numerator and denominator values.
* Note: Returns an error if (`num` * 10e18) > MAX_INT,
* or if `denom` is zero.
*/functiongetExp(uint256 num, uint256 denom) internalpurereturns (MathError, Exp memory) {
(MathError err0, uint256 scaledNumerator) = mulUInt(num, expScale);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({ mantissa: 0 }));
}
(MathError err1, uint256 rational) = divUInt(scaledNumerator, denom);
if (err1 != MathError.NO_ERROR) {
return (err1, Exp({ mantissa: 0 }));
}
return (MathError.NO_ERROR, Exp({ mantissa: rational }));
}
/**
* @dev Adds two exponentials, returning a new exponential.
*/functionaddExp(Exp memory a, Exp memory b) internalpurereturns (MathError, Exp memory) {
(MathError error, uint256result) = addUInt(a.mantissa, b.mantissa);
return (error, Exp({ mantissa: result }));
}
/**
* @dev Subtracts two exponentials, returning a new exponential.
*/functionsubExp(Exp memory a, Exp memory b) internalpurereturns (MathError, Exp memory) {
(MathError error, uint256result) = subUInt(a.mantissa, b.mantissa);
return (error, Exp({ mantissa: result }));
}
/**
* @dev Multiply an Exp by a scalar, returning a new Exp.
*/functionmulScalar(Exp memory a, uint256 scalar) internalpurereturns (MathError, Exp memory) {
(MathError err0, uint256 scaledMantissa) = mulUInt(a.mantissa, scalar);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({ mantissa: 0 }));
}
return (MathError.NO_ERROR, Exp({ mantissa: scaledMantissa }));
}
/**
* @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
*/functionmulScalarTruncate(Exp memory a, uint256 scalar) internalpurereturns (MathError, uint256) {
(MathError err, Exp memory product) = mulScalar(a, scalar);
if (err != MathError.NO_ERROR) {
return (err, 0);
}
return (MathError.NO_ERROR, truncate(product));
}
/**
* @dev Divide an Exp by a scalar, returning a new Exp.
*/functiondivScalar(Exp memory a, uint256 scalar) internalpurereturns (MathError, Exp memory) {
(MathError err0, uint256 descaledMantissa) = divUInt(a.mantissa, scalar);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({ mantissa: 0 }));
}
return (MathError.NO_ERROR, Exp({ mantissa: descaledMantissa }));
}
/**
* @dev Divide a scalar by an Exp, returning a new Exp.
*/functiondivScalarByExp(uint256 scalar, Exp memory divisor) internalpurereturns (MathError, Exp memory) {
/*
We are doing this as:
getExp(mulUInt(expScale, scalar), divisor.mantissa)
How it works:
Exp = a / b;
Scalar = s;
`s / (a / b)` = `b * s / a` and since for an Exp `a = mantissa, b = expScale`
*/
(MathError err0, uint256 numerator) = mulUInt(expScale, scalar);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({ mantissa: 0 }));
}
return getExp(numerator, divisor.mantissa);
}
/**
* @dev Divide a scalar by an Exp, then truncate to return an unsigned integer.
*/functiondivScalarByExpTruncate(uint256 scalar, Exp memory divisor) internalpurereturns (MathError, uint256) {
(MathError err, Exp memory fraction) = divScalarByExp(scalar, divisor);
if (err != MathError.NO_ERROR) {
return (err, 0);
}
return (MathError.NO_ERROR, truncate(fraction));
}
/**
* @dev Multiplies two exponentials, returning a new exponential.
*/functionmulExp(Exp memory a, Exp memory b) internalpurereturns (MathError, Exp memory) {
(MathError err0, uint256 doubleScaledProduct) = mulUInt(a.mantissa, b.mantissa);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({ mantissa: 0 }));
}
// We add half the scale before dividing so that we get rounding instead of truncation.// See "Listing 6" and text above it at https://accu.org/index.php/journals/1717// Without this change, a result like 6.6...e-19 will be truncated to 0 instead of being rounded to 1e-18.
(MathError err1, uint256 doubleScaledProductWithHalfScale) = addUInt(halfExpScale, doubleScaledProduct);
if (err1 != MathError.NO_ERROR) {
return (err1, Exp({ mantissa: 0 }));
}
(MathError err2, uint256 product) = divUInt(doubleScaledProductWithHalfScale, expScale);
// The only error `div` can return is MathError.DIVISION_BY_ZERO but we control `expScale` and it is not zero.assert(err2 == MathError.NO_ERROR);
return (MathError.NO_ERROR, Exp({ mantissa: product }));
}
/**
* @dev Multiplies two exponentials given their mantissas, returning a new exponential.
*/functionmulExp(uint256 a, uint256 b) internalpurereturns (MathError, Exp memory) {
return mulExp(Exp({ mantissa: a }), Exp({ mantissa: b }));
}
/**
* @dev Multiplies three exponentials, returning a new exponential.
*/functionmulExp3(
Exp memory a,
Exp memory b,
Exp memory c
) internalpurereturns (MathError, Exp memory) {
(MathError err, Exp memory ab) = mulExp(a, b);
if (err != MathError.NO_ERROR) {
return (err, ab);
}
return mulExp(ab, c);
}
/**
* @dev Divides two exponentials, returning a new exponential.
* (a/scale) / (b/scale) = (a/scale) * (scale/b) = a/b,
* which we can scale as an Exp by calling getExp(a.mantissa, b.mantissa)
*/functiondivExp(Exp memory a, Exp memory b) internalpurereturns (MathError, Exp memory) {
return getExp(a.mantissa, b.mantissa);
}
}
Código Fuente del Contrato
Archivo 21 de 45: ExponentialNoError.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity >=0.8.0;/**
* @title Exponential module for storing fixed-precision decimals
* @author Compound
* @notice Exp is a struct which stores decimals with a fixed precision of 18 decimal places.
* Thus, if we wanted to store the 5.1, mantissa would store 5.1e18. That is:
* `Exp({mantissa: 5100000000000000000})`.
*/contractExponentialNoError{
uint256constant expScale =1e18;
uint256constant doubleScale =1e36;
uint256constant halfExpScale = expScale /2;
uint256constant mantissaOne = expScale;
structExp {
uint256 mantissa;
}
structDouble {
uint256 mantissa;
}
/**
* @dev Truncates the given exp to a whole number value.
* For example, truncate(Exp{mantissa: 15 * expScale}) = 15
*/functiontruncate(Exp memory exp) internalpurereturns (uint256) {
// Note: We are not using careful math here as we're performing a division that cannot failreturn exp.mantissa / expScale;
}
/**
* @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
*/functionmul_ScalarTruncate(Exp memory a, uint256 scalar) internalpurereturns (uint256) {
Exp memory product = mul_(a, scalar);
return truncate(product);
}
/**
* @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.
*/functionmul_ScalarTruncateAddUInt(
Exp memory a,
uint256 scalar,
uint256 addend
) internalpurereturns (uint256) {
Exp memory product = mul_(a, scalar);
return add_(truncate(product), addend);
}
/**
* @dev Checks if first Exp is less than second Exp.
*/functionlessThanExp(Exp memory left, Exp memory right) internalpurereturns (bool) {
return left.mantissa < right.mantissa;
}
/**
* @dev Checks if left Exp <= right Exp.
*/functionlessThanOrEqualExp(Exp memory left, Exp memory right) internalpurereturns (bool) {
return left.mantissa <= right.mantissa;
}
/**
* @dev Checks if left Exp > right Exp.
*/functiongreaterThanExp(Exp memory left, Exp memory right) internalpurereturns (bool) {
return left.mantissa > right.mantissa;
}
/**
* @dev returns true if Exp is exactly zero
*/functionisZeroExp(Exp memory value) internalpurereturns (bool) {
return value.mantissa ==0;
}
functionsafe224(uint256 n, stringmemory errorMessage) internalpurereturns (uint224) {
require(n <2**224, errorMessage);
returnuint224(n);
}
functionsafe32(uint256 n, stringmemory errorMessage) internalpurereturns (uint32) {
require(n <2**32, errorMessage);
returnuint32(n);
}
functionadd_(Exp memory a, Exp memory b) internalpurereturns (Exp memory) {
return Exp({ mantissa: add_(a.mantissa, b.mantissa) });
}
functionadd_(Double memory a, Double memory b) internalpurereturns (Double memory) {
return Double({ mantissa: add_(a.mantissa, b.mantissa) });
}
functionadd_(uint256 a, uint256 b) internalpurereturns (uint256) {
return add_(a, b, "addition overflow");
}
functionadd_(uint256 a,
uint256 b,
stringmemory errorMessage
) internalpurereturns (uint256) {
uint256 c = a + b;
require(c >= a, errorMessage);
return c;
}
functionsub_(Exp memory a, Exp memory b) internalpurereturns (Exp memory) {
return Exp({ mantissa: sub_(a.mantissa, b.mantissa) });
}
functionsub_(Double memory a, Double memory b) internalpurereturns (Double memory) {
return Double({ mantissa: sub_(a.mantissa, b.mantissa) });
}
functionsub_(uint256 a, uint256 b) internalpurereturns (uint256) {
return sub_(a, b, "subtraction underflow");
}
functionsub_(uint256 a,
uint256 b,
stringmemory errorMessage
) internalpurereturns (uint256) {
require(b <= a, errorMessage);
return a - b;
}
functionmul_(Exp memory a, Exp memory b) internalpurereturns (Exp memory) {
return Exp({ mantissa: mul_(a.mantissa, b.mantissa) / expScale });
}
functionmul_(Exp memory a, uint256 b) internalpurereturns (Exp memory) {
return Exp({ mantissa: mul_(a.mantissa, b) });
}
functionmul_(uint256 a, Exp memory b) internalpurereturns (uint256) {
return mul_(a, b.mantissa) / expScale;
}
functionmul_(Double memory a, Double memory b) internalpurereturns (Double memory) {
return Double({ mantissa: mul_(a.mantissa, b.mantissa) / doubleScale });
}
functionmul_(Double memory a, uint256 b) internalpurereturns (Double memory) {
return Double({ mantissa: mul_(a.mantissa, b) });
}
functionmul_(uint256 a, Double memory b) internalpurereturns (uint256) {
return mul_(a, b.mantissa) / doubleScale;
}
functionmul_(uint256 a, uint256 b) internalpurereturns (uint256) {
return mul_(a, b, "multiplication overflow");
}
functionmul_(uint256 a,
uint256 b,
stringmemory errorMessage
) internalpurereturns (uint256) {
if (a ==0|| b ==0) {
return0;
}
uint256 c = a * b;
require(c / a == b, errorMessage);
return c;
}
functiondiv_(Exp memory a, Exp memory b) internalpurereturns (Exp memory) {
return Exp({ mantissa: div_(mul_(a.mantissa, expScale), b.mantissa) });
}
functiondiv_(Exp memory a, uint256 b) internalpurereturns (Exp memory) {
return Exp({ mantissa: div_(a.mantissa, b) });
}
functiondiv_(uint256 a, Exp memory b) internalpurereturns (uint256) {
return div_(mul_(a, expScale), b.mantissa);
}
functiondiv_(Double memory a, Double memory b) internalpurereturns (Double memory) {
return Double({ mantissa: div_(mul_(a.mantissa, doubleScale), b.mantissa) });
}
functiondiv_(Double memory a, uint256 b) internalpurereturns (Double memory) {
return Double({ mantissa: div_(a.mantissa, b) });
}
functiondiv_(uint256 a, Double memory b) internalpurereturns (uint256) {
return div_(mul_(a, doubleScale), b.mantissa);
}
functiondiv_(uint256 a, uint256 b) internalpurereturns (uint256) {
return div_(a, b, "divide by zero");
}
functiondiv_(uint256 a,
uint256 b,
stringmemory errorMessage
) internalpurereturns (uint256) {
require(b >0, errorMessage);
return a / b;
}
functionfraction(uint256 a, uint256 b) internalpurereturns (Double memory) {
return Double({ mantissa: div_(mul_(a, doubleScale), b) });
}
}
Código Fuente del Contrato
Archivo 22 de 45: IBeacon.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol)pragmasolidity ^0.8.0;/**
* @dev This is the interface that {BeaconProxy} expects of its beacon.
*/interfaceIBeacon{
/**
* @dev Must return an address that can be used as a delegate call target.
*
* {BeaconProxy} will check that this address is a contract.
*/functionimplementation() externalviewreturns (address);
}
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity ^0.8.10;import {ERC20} from"solmate/tokens/ERC20.sol";
/**
@title Balance Booster Module for Flywheel
@notice Flywheel is a general framework for managing token incentives.
It takes reward streams to various *strategies* such as staking LP tokens and divides them among *users* of those strategies.
The Booster module is an optional module for virtually boosting or otherwise transforming user balances.
If a booster is not configured, the strategies ERC-20 balanceOf/totalSupply will be used instead.
Boosting logic can be associated with referrals, vote-escrow, or other strategies.
SECURITY NOTE: similar to how Core needs to be notified any time the strategy user composition changes, the booster would need to be notified of any conditions which change the boosted balances atomically.
This prevents gaming of the reward calculation function by using manipulated balances when accruing.
*/interfaceIFlywheelBooster{
/**
@notice calculate the boosted supply of a strategy.
@param strategy the strategy to calculate boosted supply of
@return the boosted supply
*/functionboostedTotalSupply(ERC20 strategy) externalviewreturns (uint256);
/**
@notice calculate the boosted balance of a user in a given strategy.
@param strategy the strategy to calculate boosted balance of
@param user the user to calculate boosted balance of
@return the boosted balance
*/functionboostedBalanceOf(ERC20 strategy, address user) externalviewreturns (uint256);
}
Código Fuente del Contrato
Archivo 25 de 45: IFlywheelRewards.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity ^0.8.10;import {ERC20} from"solmate/tokens/ERC20.sol";
import {IonicFlywheelCore} from"../IonicFlywheelCore.sol";
/**
@title Rewards Module for Flywheel
@notice Flywheel is a general framework for managing token incentives.
It takes reward streams to various *strategies* such as staking LP tokens and divides them among *users* of those strategies.
The Rewards module is responsible for:
* determining the ongoing reward amounts to entire strategies (core handles the logic for dividing among users)
* actually holding rewards that are yet to be claimed
The reward stream can follow arbitrary logic as long as the amount of rewards passed to flywheel core has been sent to this contract.
Different module strategies include:
* a static reward rate per second
* a decaying reward rate
* a dynamic just-in-time reward stream
* liquid governance reward delegation (Curve Gauge style)
SECURITY NOTE: The rewards strategy should be smooth and continuous, to prevent gaming the reward distribution by frontrunning.
*/interfaceIFlywheelRewards{
/**
@notice calculate the rewards amount accrued to a strategy since the last update.
@param strategy the strategy to accrue rewards for.
@param lastUpdatedTimestamp the last time rewards were accrued for the strategy.
@return rewards the amount of rewards accrued to the market
*/functiongetAccruedRewards(ERC20 strategy, uint32 lastUpdatedTimestamp) externalreturns (uint256 rewards);
/// @notice return the flywheel core addressfunctionflywheel() externalviewreturns (IonicFlywheelCore);
/// @notice return the reward token associated with flywheel core.functionrewardToken() externalviewreturns (ERC20);
}
Código Fuente del Contrato
Archivo 26 de 45: IHistoricalRates.sol
//SPDX-License-Identifier: MITpragmasolidity >=0.5.0 <0.9.0;import"./RateLibrary.sol";
/**
* @title IHistoricalRates
* @notice An interface that defines a contract that stores historical rates.
*/interfaceIHistoricalRates{
/// @notice Gets an rate for a token at a specific index./// @param token The address of the token to get the rates for./// @param index The index of the rate to get, where index 0 contains the latest rate, and the last/// index contains the oldest rate (uses reverse chronological ordering)./// @return rate The rate for the token at the specified index.functiongetRateAt(address token, uint256 index) externalviewreturns (RateLibrary.Rate memory);
/// @notice Gets the latest rates for a token./// @param token The address of the token to get the rates for./// @param amount The number of rates to get./// @return rates The latest rates for the token, in reverse chronological order, from newest to oldest.functiongetRates(address token, uint256 amount) externalviewreturns (RateLibrary.Rate[] memory);
/// @notice Gets the latest rates for a token./// @param token The address of the token to get the rates for./// @param amount The number of rates to get./// @param offset The index of the first rate to get (default: 0)./// @param increment The increment between rates to get (default: 1)./// @return rates The latest rates for the token, in reverse chronological order, from newest to oldest.functiongetRates(address token,
uint256 amount,
uint256 offset,
uint256 increment
) externalviewreturns (RateLibrary.Rate[] memory);
/// @notice Gets the number of rates for a token./// @param token The address of the token to get the number of rates for./// @return count The number of rates for the token.functiongetRatesCount(address token) externalviewreturns (uint256);
/// @notice Gets the capacity of rates for a token./// @param token The address of the token to get the capacity of rates for./// @return capacity The capacity of rates for the token.functiongetRatesCapacity(address token) externalviewreturns (uint256);
/// @notice Sets the capacity of rates for a token./// @param token The address of the token to set the capacity of rates for./// @param amount The new capacity of rates for the token.functionsetRatesCapacity(address token, uint256 amount) external;
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (proxy/utils/Initializable.sol)pragmasolidity ^0.8.2;import"../../utils/AddressUpgradeable.sol";
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/abstractcontractInitializable{
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/uint8private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/boolprivate _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/eventInitialized(uint8 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
* constructor.
*
* Emits an {Initialized} event.
*/modifierinitializer() {
bool isTopLevelCall =!_initializing;
require(
(isTopLevelCall && _initialized <1) || (!AddressUpgradeable.isContract(address(this)) && _initialized ==1),
"Initializable: contract is already initialized"
);
_initialized =1;
if (isTopLevelCall) {
_initializing =true;
}
_;
if (isTopLevelCall) {
_initializing =false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: setting the version to 255 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/modifierreinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing =true;
_;
_initializing =false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/modifieronlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/function_disableInitializers() internalvirtual{
require(!_initializing, "Initializable: contract is initializing");
if (_initialized <type(uint8).max) {
_initialized =type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Internal function that returns the initialized version. Returns `_initialized`
*/function_getInitializedVersion() internalviewreturns (uint8) {
return _initialized;
}
/**
* @dev Internal function that returns the initialized version. Returns `_initializing`
*/function_isInitializing() internalviewreturns (bool) {
return _initializing;
}
}
Código Fuente del Contrato
Archivo 29 de 45: InterestRateModel.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity >=0.8.0;/**
* @title Compound's InterestRateModel Interface
* @author Compound
*/abstractcontractInterestRateModel{
/// @notice Indicator that this is an InterestRateModel contract (for inspection)boolpublicconstant isInterestRateModel =true;
/**
* @notice Calculates the current borrow interest rate per block
* @param cash The total amount of cash the market has
* @param borrows The total amount of borrows the market has outstanding
* @param reserves The total amount of reserves the market has
* @return The borrow rate per block (as a percentage, and scaled by 1e18)
*/functiongetBorrowRate(uint256 cash,
uint256 borrows,
uint256 reserves
) publicviewvirtualreturns (uint256);
/**
* @notice Calculates the current supply interest rate per block
* @param cash The total amount of cash the market has
* @param borrows The total amount of borrows the market has outstanding
* @param reserves The total amount of reserves the market has
* @param reserveFactorMantissa The current reserve factor the market has
* @return The supply rate per block (as a percentage, and scaled by 1e18)
*/functiongetSupplyRate(uint256 cash,
uint256 borrows,
uint256 reserves,
uint256 reserveFactorMantissa
) publicviewvirtualreturns (uint256);
}
Código Fuente del Contrato
Archivo 30 de 45: IonicFlywheelCore.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity ^0.8.10;import { ERC20 } from"solmate/tokens/ERC20.sol";
import { SafeTransferLib } from"solmate/utils/SafeTransferLib.sol";
import { SafeCastLib } from"solmate/utils/SafeCastLib.sol";
import { IFlywheelRewards } from"./rewards/IFlywheelRewards.sol";
import { IFlywheelBooster } from"./IFlywheelBooster.sol";
import { SafeOwnableUpgradeable } from"../../../ionic/SafeOwnableUpgradeable.sol";
contractIonicFlywheelCoreisSafeOwnableUpgradeable{
usingSafeTransferLibforERC20;
usingSafeCastLibforuint256;
/// @notice How much rewardsToken will be send to treasuryuint256public performanceFee;
/// @notice Address that gets rewardsToken accrued by performanceFeeaddresspublic feeRecipient;
/// @notice The token to reward
ERC20 public rewardToken;
/// @notice append-only list of strategies added
ERC20[] public allStrategies;
/// @notice the rewards contract for managing streams
IFlywheelRewards public flywheelRewards;
/// @notice optional booster module for calculating virtual balances on strategies
IFlywheelBooster public flywheelBooster;
/// @notice The accrued but not yet transferred rewards for each usermapping(address=>uint256) internal _rewardsAccrued;
/// @notice The strategy index and last updated per strategymapping(ERC20 => RewardsState) internal _strategyState;
/// @notice user index per strategymapping(ERC20 =>mapping(address=>uint224)) internal _userIndex;
constructor() {
// prevents the misusage of the implementation contract
_disableInitializers();
}
functioninitialize(
ERC20 _rewardToken,
IFlywheelRewards _flywheelRewards,
IFlywheelBooster _flywheelBooster,
address _owner
) publicinitializer{
__SafeOwnable_init(msg.sender);
rewardToken = _rewardToken;
flywheelRewards = _flywheelRewards;
flywheelBooster = _flywheelBooster;
_transferOwnership(_owner);
performanceFee =10e16; // 10%
feeRecipient = _owner;
}
/*----------------------------------------------------------------
ACCRUE/CLAIM LOGIC
----------------------------------------------------------------*//**
@notice Emitted when a user's rewards accrue to a given strategy.
@param strategy the updated rewards strategy
@param user the user of the rewards
@param rewardsDelta how many new rewards accrued to the user
@param rewardsIndex the market index for rewards per token accrued
*/eventAccrueRewards(ERC20 indexed strategy, addressindexed user, uint256 rewardsDelta, uint256 rewardsIndex);
/**
@notice Emitted when a user claims accrued rewards.
@param user the user of the rewards
@param amount the amount of rewards claimed
*/eventClaimRewards(addressindexed user, uint256 amount);
/**
@notice accrue rewards for a single user on a strategy
@param strategy the strategy to accrue a user's rewards on
@param user the user to be accrued
@return the cumulative amount of rewards accrued to user (including prior)
*/functionaccrue(ERC20 strategy, address user) publicreturns (uint256) {
(uint224 index, uint32 ts) = strategyState(strategy);
RewardsState memory state = RewardsState(index, ts);
if (state.index ==0) return0;
state = accrueStrategy(strategy, state);
return accrueUser(strategy, user, state);
}
/**
@notice accrue rewards for a two users on a strategy
@param strategy the strategy to accrue a user's rewards on
@param user the first user to be accrued
@param user the second user to be accrued
@return the cumulative amount of rewards accrued to the first user (including prior)
@return the cumulative amount of rewards accrued to the second user (including prior)
*/functionaccrue(
ERC20 strategy,
address user,
address secondUser
) publicreturns (uint256, uint256) {
(uint224 index, uint32 ts) = strategyState(strategy);
RewardsState memory state = RewardsState(index, ts);
if (state.index ==0) return (0, 0);
state = accrueStrategy(strategy, state);
return (accrueUser(strategy, user, state), accrueUser(strategy, secondUser, state));
}
/**
@notice claim rewards for a given user
@param user the user claiming rewards
@dev this function is public, and all rewards transfer to the user
*/functionclaimRewards(address user) external{
uint256 accrued = rewardsAccrued(user);
if (accrued !=0) {
_rewardsAccrued[user] =0;
rewardToken.safeTransferFrom(address(flywheelRewards), user, accrued);
emit ClaimRewards(user, accrued);
}
}
/*----------------------------------------------------------------
ADMIN LOGIC
----------------------------------------------------------------*//**
@notice Emitted when a new strategy is added to flywheel by the admin
@param newStrategy the new added strategy
*/eventAddStrategy(addressindexed newStrategy);
/// @notice initialize a new strategyfunctionaddStrategyForRewards(ERC20 strategy) externalonlyOwner{
_addStrategyForRewards(strategy);
}
function_addStrategyForRewards(ERC20 strategy) internal{
(uint224 index, ) = strategyState(strategy);
require(index ==0, "strategy");
_strategyState[strategy] = RewardsState({
index: (10**rewardToken.decimals()).safeCastTo224(),
lastUpdatedTimestamp: block.timestamp.safeCastTo32()
});
allStrategies.push(strategy);
emit AddStrategy(address(strategy));
}
functiongetAllStrategies() externalviewreturns (ERC20[] memory) {
return allStrategies;
}
/**
@notice Emitted when the rewards module changes
@param newFlywheelRewards the new rewards module
*/eventFlywheelRewardsUpdate(addressindexed newFlywheelRewards);
/// @notice swap out the flywheel rewards contractfunctionsetFlywheelRewards(IFlywheelRewards newFlywheelRewards) externalonlyOwner{
if (address(flywheelRewards) !=address(0)) {
uint256 oldRewardBalance = rewardToken.balanceOf(address(flywheelRewards));
if (oldRewardBalance >0) {
rewardToken.safeTransferFrom(address(flywheelRewards), address(newFlywheelRewards), oldRewardBalance);
}
}
flywheelRewards = newFlywheelRewards;
emit FlywheelRewardsUpdate(address(newFlywheelRewards));
}
/**
@notice Emitted when the booster module changes
@param newBooster the new booster module
*/eventFlywheelBoosterUpdate(addressindexed newBooster);
/// @notice swap out the flywheel booster contractfunctionsetBooster(IFlywheelBooster newBooster) externalonlyOwner{
flywheelBooster = newBooster;
emit FlywheelBoosterUpdate(address(newBooster));
}
eventUpdatedFeeSettings(uint256 oldPerformanceFee,
uint256 newPerformanceFee,
address oldFeeRecipient,
address newFeeRecipient
);
/**
* @notice Update performanceFee and/or feeRecipient
* @dev Claim rewards first from the previous feeRecipient before changing it
*/functionupdateFeeSettings(uint256 _performanceFee, address _feeRecipient) externalonlyOwner{
_updateFeeSettings(_performanceFee, _feeRecipient);
}
function_updateFeeSettings(uint256 _performanceFee, address _feeRecipient) internal{
emit UpdatedFeeSettings(performanceFee, _performanceFee, feeRecipient, _feeRecipient);
if (feeRecipient != _feeRecipient) {
_rewardsAccrued[_feeRecipient] += rewardsAccrued(feeRecipient);
_rewardsAccrued[feeRecipient] =0;
}
performanceFee = _performanceFee;
feeRecipient = _feeRecipient;
}
/*----------------------------------------------------------------
INTERNAL ACCOUNTING LOGIC
----------------------------------------------------------------*/structRewardsState {
/// @notice The strategy's last updated indexuint224 index;
/// @notice The timestamp the index was last updated atuint32 lastUpdatedTimestamp;
}
/// @notice accumulate global rewards on a strategyfunctionaccrueStrategy(ERC20 strategy, RewardsState memory state)
privatereturns (RewardsState memory rewardsState)
{
// calculate accrued rewards through moduleuint256 strategyRewardsAccrued = flywheelRewards.getAccruedRewards(strategy, state.lastUpdatedTimestamp);
rewardsState = state;
if (strategyRewardsAccrued >0) {
// use the booster or token supply to calculate reward index denominatoruint256 supplyTokens =address(flywheelBooster) !=address(0)
? flywheelBooster.boostedTotalSupply(strategy)
: strategy.totalSupply();
// 100% = 100e16uint256 accruedFees = (strategyRewardsAccrued * performanceFee) /uint224(100e16);
_rewardsAccrued[feeRecipient] += accruedFees;
strategyRewardsAccrued -= accruedFees;
uint224 deltaIndex;
if (supplyTokens !=0)
deltaIndex = ((strategyRewardsAccrued * (10**strategy.decimals())) / supplyTokens).safeCastTo224();
// accumulate rewards per token onto the index, multiplied by fixed-point factor
rewardsState = RewardsState({
index: state.index + deltaIndex,
lastUpdatedTimestamp: block.timestamp.safeCastTo32()
});
_strategyState[strategy] = rewardsState;
}
}
/// @notice accumulate rewards on a strategy for a specific userfunctionaccrueUser(
ERC20 strategy,
address user,
RewardsState memory state
) privatereturns (uint256) {
// load indicesuint224 strategyIndex = state.index;
uint224 supplierIndex = userIndex(strategy, user);
// sync user index to global
_userIndex[strategy][user] = strategyIndex;
// if user hasn't yet accrued rewards, grant them interest from the strategy beginning if they have a balance// zero balances will have no effect other than syncing to global indexif (supplierIndex ==0) {
supplierIndex = (10**rewardToken.decimals()).safeCastTo224();
}
uint224 deltaIndex = strategyIndex - supplierIndex;
// use the booster or token balance to calculate reward balance multiplieruint256 supplierTokens =address(flywheelBooster) !=address(0)
? flywheelBooster.boostedBalanceOf(strategy, user)
: strategy.balanceOf(user);
// accumulate rewards by multiplying user tokens by rewardsPerToken index and adding on unclaimeduint256 supplierDelta = (deltaIndex * supplierTokens) / (10**strategy.decimals());
uint256 supplierAccrued = rewardsAccrued(user) + supplierDelta;
_rewardsAccrued[user] = supplierAccrued;
emit AccrueRewards(strategy, user, supplierDelta, strategyIndex);
return supplierAccrued;
}
functionrewardsAccrued(address user) publicvirtualreturns (uint256) {
return _rewardsAccrued[user];
}
functionuserIndex(ERC20 strategy, address user) publicvirtualreturns (uint224) {
return _userIndex[strategy][user];
}
functionstrategyState(ERC20 strategy) publicvirtualreturns (uint224 index, uint32 lastUpdatedTimestamp) {
return (_strategyState[strategy].index, _strategyState[strategy].lastUpdatedTimestamp);
}
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)pragmasolidity ^0.8.0;import"../utils/ContextUpgradeable.sol";
import"../proxy/utils/Initializable.sol";
/**
* @dev Contract module which provides a basic access control mechanism, where
* there is an account (an owner) that can be granted exclusive access to
* specific functions.
*
* By default, the owner account will be the one that deploys the contract. This
* can later be changed with {transferOwnership}.
*
* This module is used through inheritance. It will make available the modifier
* `onlyOwner`, which can be applied to your functions to restrict their use to
* the owner.
*/abstractcontractOwnableUpgradeableisInitializable, ContextUpgradeable{
addressprivate _owner;
eventOwnershipTransferred(addressindexed previousOwner, addressindexed newOwner);
/**
* @dev Initializes the contract setting the deployer as the initial owner.
*/function__Ownable_init() internalonlyInitializing{
__Ownable_init_unchained();
}
function__Ownable_init_unchained() internalonlyInitializing{
_transferOwnership(_msgSender());
}
/**
* @dev Throws if called by any account other than the owner.
*/modifieronlyOwner() {
_checkOwner();
_;
}
/**
* @dev Returns the address of the current owner.
*/functionowner() publicviewvirtualreturns (address) {
return _owner;
}
/**
* @dev Throws if the sender is not the owner.
*/function_checkOwner() internalviewvirtual{
require(owner() == _msgSender(), "Ownable: caller is not the owner");
}
/**
* @dev Leaves the contract without owner. It will not be possible to call
* `onlyOwner` functions anymore. Can only be called by the current owner.
*
* NOTE: Renouncing ownership will leave the contract without an owner,
* thereby removing any functionality that is only available to the owner.
*/functionrenounceOwnership() publicvirtualonlyOwner{
_transferOwnership(address(0));
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Can only be called by the current owner.
*/functiontransferOwnership(address newOwner) publicvirtualonlyOwner{
require(newOwner !=address(0), "Ownable: new owner is the zero address");
_transferOwnership(newOwner);
}
/**
* @dev Transfers ownership of the contract to a new account (`newOwner`).
* Internal function without access restriction.
*/function_transferOwnership(address newOwner) internalvirtual{
address oldOwner = _owner;
_owner = newOwner;
emit OwnershipTransferred(oldOwner, newOwner);
}
/**
* @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[49] private __gap;
}
Código Fuente del Contrato
Archivo 33 de 45: PoolDirectory.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity >=0.8.0;import"openzeppelin-contracts-upgradeable/contracts/utils/Create2Upgradeable.sol";
import { IonicComptroller } from"./compound/ComptrollerInterface.sol";
import { BasePriceOracle } from"./oracles/BasePriceOracle.sol";
import { Unitroller } from"./compound/Unitroller.sol";
import"./ionic/SafeOwnableUpgradeable.sol";
import"./ionic/DiamondExtension.sol";
/**
* @title PoolDirectory
* @author David Lucid <david@rari.capital> (https://github.com/davidlucid)
* @notice PoolDirectory is a directory for Ionic interest rate pools.
*/contractPoolDirectoryisSafeOwnableUpgradeable{
/**
* @dev Initializes a deployer whitelist if desired.
* @param _enforceDeployerWhitelist Boolean indicating if the deployer whitelist is to be enforced.
* @param _deployerWhitelist Array of Ethereum accounts to be whitelisted.
*/functioninitialize(bool _enforceDeployerWhitelist, address[] memory _deployerWhitelist) publicinitializer{
__SafeOwnable_init(msg.sender);
enforceDeployerWhitelist = _enforceDeployerWhitelist;
for (uint256 i =0; i < _deployerWhitelist.length; i++) deployerWhitelist[_deployerWhitelist[i]] =true;
}
/**
* @dev Struct for a Ionic interest rate pool.
*/structPool {
string name;
address creator;
address comptroller;
uint256 blockPosted;
uint256 timestampPosted;
}
/**
* @dev Array of Ionic interest rate pools.
*/
Pool[] public pools;
/**
* @dev Maps Ethereum accounts to arrays of Ionic pool indexes.
*/mapping(address=>uint256[]) private _poolsByAccount;
/**
* @dev Maps Ionic pool Comptroller addresses to bools indicating if they have been registered via the directory.
*/mapping(address=>bool) public poolExists;
/**
* @dev Emitted when a new Ionic pool is added to the directory.
*/eventPoolRegistered(uint256 index, Pool pool);
/**
* @dev Booleans indicating if the deployer whitelist is enforced.
*/boolpublic enforceDeployerWhitelist;
/**
* @dev Maps Ethereum accounts to booleans indicating if they are allowed to deploy pools.
*/mapping(address=>bool) public deployerWhitelist;
/**
* @dev Controls if the deployer whitelist is to be enforced.
* @param enforce Boolean indicating if the deployer whitelist is to be enforced.
*/function_setDeployerWhitelistEnforcement(bool enforce) externalonlyOwner{
enforceDeployerWhitelist = enforce;
}
/**
* @dev Adds/removes Ethereum accounts to the deployer whitelist.
* @param deployers Array of Ethereum accounts to be whitelisted.
* @param status Whether to add or remove the accounts.
*/function_editDeployerWhitelist(address[] calldata deployers, bool status) externalonlyOwner{
require(deployers.length>0, "No deployers supplied.");
for (uint256 i =0; i < deployers.length; i++) deployerWhitelist[deployers[i]] = status;
}
/**
* @dev Adds a new Ionic pool to the directory (without checking msg.sender).
* @param name The name of the pool.
* @param comptroller The pool's Comptroller proxy contract address.
* @return The index of the registered Ionic pool.
*/function_registerPool(stringmemory name, address comptroller) internalreturns (uint256) {
require(!poolExists[comptroller], "Pool already exists in the directory.");
require(!enforceDeployerWhitelist || deployerWhitelist[msg.sender], "Sender is not on deployer whitelist.");
require(bytes(name).length<=100, "No pool name supplied.");
Pool memory pool = Pool(name, msg.sender, comptroller, block.number, block.timestamp);
pools.push(pool);
_poolsByAccount[msg.sender].push(pools.length-1);
poolExists[comptroller] =true;
emit PoolRegistered(pools.length-1, pool);
return pools.length-1;
}
function_deprecatePool(address comptroller) externalonlyOwner{
for (uint256 i =0; i < pools.length; i++) {
if (pools[i].comptroller == comptroller) {
_deprecatePool(i);
break;
}
}
}
function_deprecatePool(uint256 index) publiconlyOwner{
Pool storage ionicPool = pools[index];
require(ionicPool.comptroller !=address(0), "pool already deprecated");
// swap with the last pool of the creator and deleteuint256[] storage creatorPools = _poolsByAccount[ionicPool.creator];
for (uint256 i =0; i < creatorPools.length; i++) {
if (creatorPools[i] == index) {
creatorPools[i] = creatorPools[creatorPools.length-1];
creatorPools.pop();
break;
}
}
// leave it to true to deny the re-registering of the same pool
poolExists[ionicPool.comptroller] =true;
// nullify the storage
ionicPool.comptroller =address(0);
ionicPool.creator =address(0);
ionicPool.name="";
ionicPool.blockPosted =0;
ionicPool.timestampPosted =0;
}
/**
* @dev Deploys a new Ionic pool and adds to the directory.
* @param name The name of the pool.
* @param implementation The Comptroller implementation contract address.
* @param constructorData Encoded construction data for `Unitroller constructor()`
* @param enforceWhitelist Boolean indicating if the pool's supplier/borrower whitelist is to be enforced.
* @param closeFactor The pool's close factor (scaled by 1e18).
* @param liquidationIncentive The pool's liquidation incentive (scaled by 1e18).
* @param priceOracle The pool's PriceOracle contract address.
* @return Index of the registered Ionic pool and the Unitroller proxy address.
*/functiondeployPool(stringmemory name,
address implementation,
bytescalldata constructorData,
bool enforceWhitelist,
uint256 closeFactor,
uint256 liquidationIncentive,
address priceOracle
) externalreturns (uint256, address) {
// Input validationrequire(implementation !=address(0), "No Comptroller implementation contract address specified.");
require(priceOracle !=address(0), "No PriceOracle contract address specified.");
// Deploy Unitroller using msg.sender, name, and block.number as a saltbytesmemory unitrollerCreationCode =abi.encodePacked(type(Unitroller).creationCode, constructorData);
address proxy = Create2Upgradeable.deploy(
0,
keccak256(abi.encodePacked(msg.sender, name, ++poolsCounter)),
unitrollerCreationCode
);
// Setup the pool
IonicComptroller comptrollerProxy = IonicComptroller(proxy);
// Set up the extensions
comptrollerProxy._upgrade();
// Set pool parametersrequire(comptrollerProxy._setCloseFactor(closeFactor) ==0, "Failed to set pool close factor.");
require(
comptrollerProxy._setLiquidationIncentive(liquidationIncentive) ==0,
"Failed to set pool liquidation incentive."
);
require(comptrollerProxy._setPriceOracle(BasePriceOracle(priceOracle)) ==0, "Failed to set pool price oracle.");
// Whitelistif (enforceWhitelist)
require(comptrollerProxy._setWhitelistEnforcement(true) ==0, "Failed to enforce supplier/borrower whitelist.");
// Make msg.sender the adminrequire(comptrollerProxy._setPendingAdmin(msg.sender) ==0, "Failed to set pending admin on Unitroller.");
// Register the pool with this PoolDirectoryreturn (_registerPool(name, proxy), proxy);
}
/**
* @notice Returns `ids` and directory information of all non-deprecated Ionic pools.
* @dev This function is not designed to be called in a transaction: it is too gas-intensive.
*/functiongetActivePools() publicviewreturns (uint256[] memory, Pool[] memory) {
uint256 count =0;
for (uint256 i =0; i < pools.length; i++) {
if (pools[i].comptroller !=address(0)) count++;
}
Pool[] memory activePools =new Pool[](count);
uint256[] memory poolIds =newuint256[](count);
uint256 index =0;
for (uint256 i =0; i < pools.length; i++) {
if (pools[i].comptroller !=address(0)) {
poolIds[index] = i;
activePools[index] = pools[i];
index++;
}
}
return (poolIds, activePools);
}
/**
* @notice Returns arrays of all Ionic pools' data.
* @dev This function is not designed to be called in a transaction: it is too gas-intensive.
*/functiongetAllPools() publicviewreturns (Pool[] memory) {
uint256 count =0;
for (uint256 i =0; i < pools.length; i++) {
if (pools[i].comptroller !=address(0)) count++;
}
Pool[] memory result =new Pool[](count);
uint256 index =0;
for (uint256 i =0; i < pools.length; i++) {
if (pools[i].comptroller !=address(0)) {
result[index++] = pools[i];
}
}
return result;
}
/**
* @notice Returns arrays of all public Ionic pool indexes and data.
* @dev This function is not designed to be called in a transaction: it is too gas-intensive.
*/functiongetPublicPools() externalviewreturns (uint256[] memory, Pool[] memory) {
uint256 arrayLength =0;
(, Pool[] memory activePools) = getActivePools();
for (uint256 i =0; i < activePools.length; i++) {
try IonicComptroller(activePools[i].comptroller).enforceWhitelist() returns (bool enforceWhitelist) {
if (enforceWhitelist) continue;
} catch {}
arrayLength++;
}
uint256[] memory indexes =newuint256[](arrayLength);
Pool[] memory publicPools =new Pool[](arrayLength);
uint256 index =0;
for (uint256 i =0; i < activePools.length; i++) {
try IonicComptroller(activePools[i].comptroller).enforceWhitelist() returns (bool enforceWhitelist) {
if (enforceWhitelist) continue;
} catch {}
indexes[index] = i;
publicPools[index] = activePools[i];
index++;
}
return (indexes, publicPools);
}
/**
* @notice Returns arrays of all public Ionic pool indexes and data.
* @dev This function is not designed to be called in a transaction: it is too gas-intensive.
*/functiongetPoolsOfUser(address user) externalviewreturns (uint256[] memory, Pool[] memory) {
uint256 arrayLength =0;
(, Pool[] memory activePools) = getActivePools();
for (uint256 i =0; i < activePools.length; i++) {
try IonicComptroller(activePools[i].comptroller).isUserOfPool(user) returns (bool isUsing) {
if (!isUsing) continue;
} catch {}
arrayLength++;
}
uint256[] memory indexes =newuint256[](arrayLength);
Pool[] memory poolsOfUser =new Pool[](arrayLength);
uint256 index =0;
for (uint256 i =0; i < activePools.length; i++) {
try IonicComptroller(activePools[i].comptroller).isUserOfPool(user) returns (bool isUsing) {
if (!isUsing) continue;
} catch {}
indexes[index] = i;
poolsOfUser[index] = activePools[i];
index++;
}
return (indexes, poolsOfUser);
}
/**
* @notice Returns arrays of Ionic pool indexes and data created by `account`.
*/functiongetPoolsByAccount(address account) externalviewreturns (uint256[] memory, Pool[] memory) {
uint256[] memory indexes =newuint256[](_poolsByAccount[account].length);
Pool[] memory accountPools =new Pool[](_poolsByAccount[account].length);
(, Pool[] memory activePools) = getActivePools();
for (uint256 i =0; i < _poolsByAccount[account].length; i++) {
indexes[i] = _poolsByAccount[account][i];
accountPools[i] = activePools[_poolsByAccount[account][i]];
}
return (indexes, accountPools);
}
/**
* @notice Modify existing Ionic pool name.
*/functionsetPoolName(uint256 index, stringcalldata name) external{
IonicComptroller _comptroller = IonicComptroller(pools[index].comptroller);
require(
(msg.sender== _comptroller.admin() && _comptroller.adminHasRights()) ||msg.sender== owner(),
"!permission"
);
pools[index].name= name;
}
/**
* @dev Maps Ethereum accounts to booleans indicating if they are a whitelisted admin.
*/mapping(address=>bool) public adminWhitelist;
/**
* @dev used as salt for the creation of new pools
*/uint256public poolsCounter;
/**
* @dev Event emitted when the admin whitelist is updated.
*/eventAdminWhitelistUpdated(address[] admins, bool status);
/**
* @dev Adds/removes Ethereum accounts to the admin whitelist.
* @param admins Array of Ethereum accounts to be whitelisted.
* @param status Whether to add or remove the accounts.
*/function_editAdminWhitelist(address[] calldata admins, bool status) externalonlyOwner{
require(admins.length>0, "No admins supplied.");
for (uint256 i =0; i < admins.length; i++) adminWhitelist[admins[i]] = status;
emit AdminWhitelistUpdated(admins, status);
}
/**
* @notice Returns arrays of all Ionic pool indexes and data with whitelisted admins.
* @dev This function is not designed to be called in a transaction: it is too gas-intensive.
*/functiongetPublicPoolsByVerification(bool whitelistedAdmin) externalviewreturns (uint256[] memory, Pool[] memory) {
uint256 arrayLength =0;
(, Pool[] memory activePools) = getActivePools();
for (uint256 i =0; i < activePools.length; i++) {
IonicComptroller comptroller = IonicComptroller(activePools[i].comptroller);
try comptroller.admin() returns (address admin) {
if (whitelistedAdmin != adminWhitelist[admin]) continue;
} catch {}
arrayLength++;
}
uint256[] memory indexes =newuint256[](arrayLength);
Pool[] memory publicPools =new Pool[](arrayLength);
uint256 index =0;
for (uint256 i =0; i < activePools.length; i++) {
IonicComptroller comptroller = IonicComptroller(activePools[i].comptroller);
try comptroller.admin() returns (address admin) {
if (whitelistedAdmin != adminWhitelist[admin]) continue;
} catch {}
indexes[index] = i;
publicPools[index] = activePools[i];
index++;
}
return (indexes, publicPools);
}
/**
* @notice Returns arrays of all verified Ionic pool indexes and data for which the account is whitelisted
* @param account who is whitelisted in the returned verified whitelist-enabled pools.
* @dev This function is not designed to be called in a transaction: it is too gas-intensive.
*/functiongetVerifiedPoolsOfWhitelistedAccount(address account)
externalviewreturns (uint256[] memory, Pool[] memory)
{
uint256 arrayLength =0;
(, Pool[] memory activePools) = getActivePools();
for (uint256 i =0; i < activePools.length; i++) {
IonicComptroller comptroller = IonicComptroller(activePools[i].comptroller);
try comptroller.enforceWhitelist() returns (bool enforceWhitelist) {
if (!enforceWhitelist ||!comptroller.whitelist(account)) continue;
} catch {}
arrayLength++;
}
uint256[] memory indexes =newuint256[](arrayLength);
Pool[] memory accountWhitelistedPools =new Pool[](arrayLength);
uint256 index =0;
for (uint256 i =0; i < activePools.length; i++) {
IonicComptroller comptroller = IonicComptroller(activePools[i].comptroller);
try comptroller.enforceWhitelist() returns (bool enforceWhitelist) {
if (!enforceWhitelist ||!comptroller.whitelist(account)) continue;
} catch {}
indexes[index] = i;
accountWhitelistedPools[index] = activePools[i];
index++;
}
return (indexes, accountWhitelistedPools);
}
}
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol)pragmasolidity ^0.8.0;/**
* @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM
* instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to
* be specified by overriding the virtual {_implementation} function.
*
* Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a
* different contract through the {_delegate} function.
*
* The success and return data of the delegated call will be returned back to the caller of the proxy.
*/abstractcontractProxy{
/**
* @dev Delegates the current call to `implementation`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/function_delegate(address implementation) internalvirtual{
assembly {
// Copy msg.data. We take full control of memory in this inline assembly// block because it will not return to Solidity code. We overwrite the// Solidity scratch pad at memory position 0.calldatacopy(0, 0, calldatasize())
// Call the implementation.// out and outsize are 0 because we don't know the size yet.let result :=delegatecall(gas(), implementation, 0, calldatasize(), 0, 0)
// Copy the returned data.returndatacopy(0, 0, returndatasize())
switch result
// delegatecall returns 0 on error.case0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
/**
* @dev This is a virtual function that should be overridden so it returns the address to which the fallback function
* and {_fallback} should delegate.
*/function_implementation() internalviewvirtualreturns (address);
/**
* @dev Delegates the current call to the address returned by `_implementation()`.
*
* This function does not return to its internal call site, it will return directly to the external caller.
*/function_fallback() internalvirtual{
_beforeFallback();
_delegate(_implementation());
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other
* function in the contract matches the call data.
*/fallback() externalpayablevirtual{
_fallback();
}
/**
* @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data
* is empty.
*/receive() externalpayablevirtual{
_fallback();
}
/**
* @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback`
* call, or as part of the Solidity `fallback` or `receive` functions.
*
* If overridden should call `super._beforeFallback()`.
*/function_beforeFallback() internalvirtual{}
}
Código Fuente del Contrato
Archivo 36 de 45: PrudentiaLib.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity >=0.8.0;libraryPrudentiaLib{
structPrudentiaConfig {
address controller; // Adrastia Prudentia controller addressuint8 offset; // Offset for delayed rate activationint8 decimalShift; // Positive values scale the rate up (in powers of 10), negative values scale the rate down
}
}
// SPDX-License-Identifier: UNLICENSEDpragmasolidity >=0.8.0;import"openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol";
/**
* @dev Ownable extension that requires a two-step process of setting the pending owner and the owner accepting it.
* @notice Existing OwnableUpgradeable contracts cannot be upgraded due to the extra storage variable
* that will shift the other.
*/abstractcontractSafeOwnableUpgradeableisOwnableUpgradeable{
/**
* @notice Pending owner of this contract
*/addresspublic pendingOwner;
function__SafeOwnable_init(address owner_) internalonlyInitializing{
__Ownable_init();
_transferOwnership(owner_);
}
structAddressSlot {
address value;
}
modifieronlyOwnerOrAdmin() {
bool isOwner = owner() == _msgSender();
if (!isOwner) {
address admin = _getProxyAdmin();
bool isAdmin = admin == _msgSender();
require(isAdmin, "Ownable: caller is neither the owner nor the admin");
}
_;
}
/**
* @notice Emitted when pendingOwner is changed
*/eventNewPendingOwner(address oldPendingOwner, address newPendingOwner);
/**
* @notice Emitted when pendingOwner is accepted, which means owner is updated
*/eventNewOwner(address oldOwner, address newOwner);
/**
* @notice Begins transfer of owner rights. The newPendingOwner must call `_acceptOwner` to finalize the transfer.
* @dev Owner function to begin change of owner. The newPendingOwner must call `_acceptOwner` to finalize the transfer.
* @param newPendingOwner New pending owner.
*/function_setPendingOwner(address newPendingOwner) publiconlyOwner{
// Save current value, if any, for inclusion in logaddress oldPendingOwner = pendingOwner;
// Store pendingOwner with value newPendingOwner
pendingOwner = newPendingOwner;
// Emit NewPendingOwner(oldPendingOwner, newPendingOwner)emit NewPendingOwner(oldPendingOwner, newPendingOwner);
}
/**
* @notice Accepts transfer of owner rights. msg.sender must be pendingOwner
* @dev Owner function for pending owner to accept role and update owner
*/function_acceptOwner() public{
// Check caller is pendingOwner and pendingOwner ≠ address(0)require(msg.sender== pendingOwner, "not the pending owner");
// Save current values for inclusion in logaddress oldOwner = owner();
address oldPendingOwner = pendingOwner;
// Store owner with value pendingOwner
_transferOwnership(pendingOwner);
// Clear the pending value
pendingOwner =address(0);
emit NewOwner(oldOwner, pendingOwner);
emit NewPendingOwner(oldPendingOwner, pendingOwner);
}
functionrenounceOwnership() publicoverrideonlyOwner{
// do not remove this overriding fnrevert("not used anymore");
}
functiontransferOwnership(address newOwner) publicoverrideonlyOwner{
emit NewPendingOwner(pendingOwner, newOwner);
pendingOwner = newOwner;
}
function_getProxyAdmin() internalviewreturns (address admin) {
bytes32 _ADMIN_SLOT =0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103;
AddressSlot storage adminSlot;
assembly {
adminSlot.slot:= _ADMIN_SLOT
}
admin = adminSlot.value;
}
}
Código Fuente del Contrato
Archivo 41 de 45: SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;import {ERC20} from"../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer./// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.librarySafeTransferLib{
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferETH(address to, uint256 amount) internal{
bool success;
assembly {
// Transfer the ETH and store if it succeeded or not.
success :=call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferFrom(
ERC20 token,
addressfrom,
address to,
uint256 amount
) internal{
bool success;
assembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
functionsafeTransfer(
ERC20 token,
address to,
uint256 amount
) internal{
bool success;
assembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
functionsafeApprove(
ERC20 token,
address to,
uint256 amount
) internal{
bool success;
assembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
Código Fuente del Contrato
Archivo 42 de 45: StorageSlot.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (utils/StorageSlot.sol)pragmasolidity ^0.8.0;/**
* @dev Library for reading and writing primitive types to specific storage slots.
*
* Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
* This library helps with reading and writing to such slots without the need for inline assembly.
*
* The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
*
* Example usage to set ERC1967 implementation slot:
* ```
* contract ERC1967 {
* bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
*
* function _getImplementation() internal view returns (address) {
* return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
* }
*
* function _setImplementation(address newImplementation) internal {
* require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract");
* StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
* }
* }
* ```
*
* _Available since v4.1 for `address`, `bool`, `bytes32`, and `uint256`._
*/libraryStorageSlot{
structAddressSlot {
address value;
}
structBooleanSlot {
bool value;
}
structBytes32Slot {
bytes32 value;
}
structUint256Slot {
uint256 value;
}
/**
* @dev Returns an `AddressSlot` with member `value` located at `slot`.
*/functiongetAddressSlot(bytes32 slot) internalpurereturns (AddressSlot storage r) {
/// @solidity memory-safe-assemblyassembly {
r.slot:= slot
}
}
/**
* @dev Returns an `BooleanSlot` with member `value` located at `slot`.
*/functiongetBooleanSlot(bytes32 slot) internalpurereturns (BooleanSlot storage r) {
/// @solidity memory-safe-assemblyassembly {
r.slot:= slot
}
}
/**
* @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
*/functiongetBytes32Slot(bytes32 slot) internalpurereturns (Bytes32Slot storage r) {
/// @solidity memory-safe-assemblyassembly {
r.slot:= slot
}
}
/**
* @dev Returns an `Uint256Slot` with member `value` located at `slot`.
*/functiongetUint256Slot(bytes32 slot) internalpurereturns (Uint256Slot storage r) {
/// @solidity memory-safe-assemblyassembly {
r.slot:= slot
}
}
}
Código Fuente del Contrato
Archivo 43 de 45: TransparentUpgradeableProxy.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.7.0) (proxy/transparent/TransparentUpgradeableProxy.sol)pragmasolidity ^0.8.0;import"../ERC1967/ERC1967Proxy.sol";
/**
* @dev This contract implements a proxy that is upgradeable by an admin.
*
* To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector
* clashing], which can potentially be used in an attack, this contract uses the
* https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two
* things that go hand in hand:
*
* 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if
* that call matches one of the admin functions exposed by the proxy itself.
* 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the
* implementation. If the admin tries to call a function on the implementation it will fail with an error that says
* "admin cannot fallback to proxy target".
*
* These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing
* the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due
* to sudden errors when trying to call a function from the proxy implementation.
*
* Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way,
* you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy.
*/contractTransparentUpgradeableProxyisERC1967Proxy{
/**
* @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and
* optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}.
*/constructor(address _logic,
address admin_,
bytesmemory _data
) payableERC1967Proxy(_logic, _data) {
_changeAdmin(admin_);
}
/**
* @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin.
*/modifierifAdmin() {
if (msg.sender== _getAdmin()) {
_;
} else {
_fallback();
}
}
/**
* @dev Returns the current admin.
*
* NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyAdmin}.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
* https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103`
*/functionadmin() externalifAdminreturns (address admin_) {
admin_ = _getAdmin();
}
/**
* @dev Returns the current implementation.
*
* NOTE: Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}.
*
* TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the
* https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call.
* `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`
*/functionimplementation() externalifAdminreturns (address implementation_) {
implementation_ = _implementation();
}
/**
* @dev Changes the admin of the proxy.
*
* Emits an {AdminChanged} event.
*
* NOTE: Only the admin can call this function. See {ProxyAdmin-changeProxyAdmin}.
*/functionchangeAdmin(address newAdmin) externalvirtualifAdmin{
_changeAdmin(newAdmin);
}
/**
* @dev Upgrade the implementation of the proxy.
*
* NOTE: Only the admin can call this function. See {ProxyAdmin-upgrade}.
*/functionupgradeTo(address newImplementation) externalifAdmin{
_upgradeToAndCall(newImplementation, bytes(""), false);
}
/**
* @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified
* by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the
* proxied contract.
*
* NOTE: Only the admin can call this function. See {ProxyAdmin-upgradeAndCall}.
*/functionupgradeToAndCall(address newImplementation, bytescalldata data) externalpayableifAdmin{
_upgradeToAndCall(newImplementation, data, true);
}
/**
* @dev Returns the current admin.
*/function_admin() internalviewvirtualreturns (address) {
return _getAdmin();
}
/**
* @dev Makes sure the admin cannot access the fallback function. See {Proxy-_beforeFallback}.
*/function_beforeFallback() internalvirtualoverride{
require(msg.sender!= _getAdmin(), "TransparentUpgradeableProxy: admin cannot fallback to proxy target");
super._beforeFallback();
}
}
Código Fuente del Contrato
Archivo 44 de 45: Unitroller.sol
// SPDX-License-Identifier: UNLICENSEDpragmasolidity >=0.8.0;import"./ErrorReporter.sol";
import"./ComptrollerStorage.sol";
import"./Comptroller.sol";
import { DiamondExtension, DiamondBase, LibDiamond } from"../ionic/DiamondExtension.sol";
/**
* @title Unitroller
* @dev Storage for the comptroller is at this address, while execution is delegated via the Diamond Extensions
* CTokens should reference this contract as their comptroller.
*/contractUnitrollerisComptrollerV3Storage, ComptrollerErrorReporter, DiamondBase{
/**
* @notice Event emitted when the admin rights are changed
*/eventAdminRightsToggled(bool hasRights);
/**
* @notice Emitted when pendingAdmin is changed
*/eventNewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);
/**
* @notice Emitted when pendingAdmin is accepted, which means admin is updated
*/eventNewAdmin(address oldAdmin, address newAdmin);
constructor(addresspayable _ionicAdmin) {
admin =msg.sender;
ionicAdmin = _ionicAdmin;
}
/*** Admin Functions ***//**
* @notice Toggles admin rights.
* @param hasRights Boolean indicating if the admin is to have rights.
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/function_toggleAdminRights(bool hasRights) externalreturns (uint256) {
if (!hasAdminRights()) {
return fail(Error.UNAUTHORIZED, FailureInfo.TOGGLE_ADMIN_RIGHTS_OWNER_CHECK);
}
// Check that rights have not already been set to the desired valueif (adminHasRights == hasRights) returnuint256(Error.NO_ERROR);
adminHasRights = hasRights;
emit AdminRightsToggled(hasRights);
returnuint256(Error.NO_ERROR);
}
/**
* @notice Begins transfer of admin rights. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
* @dev Admin function to begin change of admin. The newPendingAdmin must call `_acceptAdmin` to finalize the transfer.
* @param newPendingAdmin New pending admin.
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/function_setPendingAdmin(address newPendingAdmin) publicreturns (uint256) {
if (!hasAdminRights()) {
return fail(Error.UNAUTHORIZED, FailureInfo.SET_PENDING_ADMIN_OWNER_CHECK);
}
address oldPendingAdmin = pendingAdmin;
pendingAdmin = newPendingAdmin;
emit NewPendingAdmin(oldPendingAdmin, newPendingAdmin);
returnuint256(Error.NO_ERROR);
}
/**
* @notice Accepts transfer of admin rights. msg.sender must be pendingAdmin
* @dev Admin function for pending admin to accept role and update admin
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details)
*/function_acceptAdmin() publicreturns (uint256) {
// Check caller is pendingAdmin and pendingAdmin ≠ address(0)if (msg.sender!= pendingAdmin ||msg.sender==address(0)) {
return fail(Error.UNAUTHORIZED, FailureInfo.ACCEPT_ADMIN_PENDING_ADMIN_CHECK);
}
// Save current values for inclusion in logaddress oldAdmin = admin;
address oldPendingAdmin = pendingAdmin;
admin = pendingAdmin;
pendingAdmin =address(0);
emit NewAdmin(oldAdmin, admin);
emit NewPendingAdmin(oldPendingAdmin, pendingAdmin);
returnuint256(Error.NO_ERROR);
}
functioncomptrollerImplementation() publicviewreturns (address) {
return LibDiamond.getExtensionForFunction(bytes4(keccak256(bytes("_deployMarket(uint8,bytes,bytes,uint256)"))));
}
/**
* @dev upgrades the implementation if necessary
*/function_upgrade() external{
require(msg.sender==address(this) || hasAdminRights(), "!self || !admin");
address currentImplementation = comptrollerImplementation();
address latestComptrollerImplementation = IFeeDistributor(ionicAdmin).latestComptrollerImplementation(
currentImplementation
);
_updateExtensions(latestComptrollerImplementation);
if (currentImplementation != latestComptrollerImplementation) {
// reinitialize
_functionCall(address(this), abi.encodeWithSignature("_becomeImplementation()"), "!become impl");
}
}
function_functionCall(address target,
bytesmemory data,
stringmemory errorMessage
) internalreturns (bytesmemory) {
(bool success, bytesmemory returndata) = target.call(data);
if (!success) {
// Look for revert reason and bubble it up if presentif (returndata.length>0) {
// The easiest way to bubble the revert reason is using memory via assembly// solhint-disable-next-line no-inline-assemblyassembly {
let returndata_size :=mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
return returndata;
}
function_updateExtensions(address currentComptroller) internal{
address[] memory latestExtensions = IFeeDistributor(ionicAdmin).getComptrollerExtensions(currentComptroller);
address[] memory currentExtensions = LibDiamond.listExtensions();
// removed the current (old) extensionsfor (uint256 i =0; i < currentExtensions.length; i++) {
LibDiamond.removeExtension(DiamondExtension(currentExtensions[i]));
}
// add the new extensionsfor (uint256 i =0; i < latestExtensions.length; i++) {
LibDiamond.addExtension(DiamondExtension(latestExtensions[i]));
}
}
/**
* @dev register a logic extension
* @param extensionToAdd the extension whose functions are to be added
* @param extensionToReplace the extension whose functions are to be removed/replaced
*/function_registerExtension(DiamondExtension extensionToAdd, DiamondExtension extensionToReplace) externaloverride{
require(hasAdminRights(), "!unauthorized");
LibDiamond.registerExtension(extensionToAdd, extensionToReplace);
}
}
Código Fuente del Contrato
Archivo 45 de 45: draft-IERC1822.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.5.0) (interfaces/draft-IERC1822.sol)pragmasolidity ^0.8.0;/**
* @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified
* proxy whose upgrades are fully controlled by the current implementation.
*/interfaceIERC1822Proxiable{
/**
* @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation
* address.
*
* IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
* bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
* function revert if invoked through a proxy.
*/functionproxiableUUID() externalviewreturns (bytes32);
}