Overview
ETH Balance
0 ETH
Eth Value
$0.00Latest 1 from a total of 1 transactions
| Transaction Hash |
Method
|
Block
|
From
|
|
To
|
||||
|---|---|---|---|---|---|---|---|---|---|
| Initialize | 18991365 | 771 days ago | IN | 0 ETH | 0.00323315 |
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
JumpRateModelV3
Compiler Version
v0.8.19+commit.7dd6d404
Optimization Enabled:
Yes with 200 runs
Other Settings:
default evmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "./InterestRateModel.sol";
import "../bToken/BTokenInterfaces.sol";
/**
* @title JumpRateModel Contract V3.
* @notice V3 interest rate Model.
*/
contract JumpRateModelV3 is Initializable, InterestRateModel, AccessControlUpgradeable {
using SafeMath for uint;
bytes32 public constant MODERATOR_ROLE = keccak256("MODERATOR_ROLE");
/**
* @dev Emitted when the owner of the interest rate model is updated.
* @param gainPerBlock The new gainPerBlock.
* @param jumGainPerBlock The new jumGainPerBlock.
* @param targetUtil The new targetUtil.
*/
event NewInterestParams(uint256 gainPerBlock, uint256 jumGainPerBlock, uint256 targetUtil);
/**
* @dev Emitted when the owner of the contract is updated.
* @param newOwner The address of the new owner.
*/
event NewOwner(address newOwner);
/**
* @dev Emitted when a new interest rate is set.
* @param appliedBlock The block number at which the interest rate was applied.
* @param interestRate The new interest rate.
*/
event NewInterest(uint256 appliedBlock, uint256 interestRate);
struct BlendingTokenInfo {
uint256 gainPerBlock;
uint256 jumGainPerBlock;
uint256 targetUtil;
}
struct RateInfo {
uint256 lastInterestRate;
uint256 lastAccrualBlockNumber;
uint256 maxBorrowRate;
}
/**
* @dev The approximate number of blocks per year that is assumed by the interest rate model.
*/
uint256 public blocksPerYear;
mapping(address => BlendingTokenInfo) public blendingTokenInfo;
mapping(address => RateInfo) public rateInfo;
mapping(address => bool) public isBlendingTokenSupport;
/**
* @dev Modifier to restrict access to only the blending token contract.
*/
modifier onlyBlendingToken() {
require(isBlendingTokenSupport[msg.sender], "Caller is not Blending token");
_;
}
/**
* @dev Modifier to check if the caller is the default admin role.
*/
modifier onlyAdmin() {
require(hasRole(DEFAULT_ADMIN_ROLE, msg.sender), "Caller is not the Admin");
_;
}
/**
* @dev Modifier to check if the caller has the moderator role.
*/
modifier onlyModerator() {
require(hasRole(MODERATOR_ROLE, msg.sender), "Caller is not the Moderator");
_;
}
/**
* @dev Constructs an interest rate model.
* @param blocksPerYear_ Number of blocks in a year for compounding.
*/
function initialize(uint256 blocksPerYear_) public initializer {
__AccessControl_init();
_setupRole(DEFAULT_ADMIN_ROLE, msg.sender);
_setupRole(MODERATOR_ROLE, msg.sender);
blocksPerYear = blocksPerYear_;
}
//************* ADMIN FUNCTIONS ********************************
/**
* @dev Grants the `MODERATOR_ROLE` to a new address.
* The caller must have the `ADMIN_ROLE`.
* @param newModerator The address to grant the role to.
*/
function grantModerator(address newModerator) public onlyAdmin {
grantRole(MODERATOR_ROLE, newModerator);
}
/**
* @dev Revokes the moderator role from the specified address.
* The caller must have the admin role.
* @param moderator The address of the moderator to revoke the role from.
*/
function revokeModerator(address moderator) public onlyAdmin {
revokeRole(MODERATOR_ROLE, moderator);
}
//************* MODERATOR FUNCTIONS ********************************
/**
* @dev Updates the parameters of the interest rate model (only callable by owner, i.e. Timelock).
* Only the contract moderator can call this function.
* @param gainPerYear The rate of increase in interest rate wrt utilization (scaled by 1e18).
* @param jumGainPerYear The jumGainPerBlock after hitting a specified utilization point.
* @param targetUtil_ The utilization point at which the jump multiplier is applied.
*/
function updateJumpRateModel(uint256 gainPerYear, uint256 jumGainPerYear, uint256 targetUtil_, address blendingToken) external onlyModerator {
updateJumpRateModelInternal(gainPerYear, jumGainPerYear, targetUtil_, blendingToken);
}
/**
* @dev Sets the number of blocks per year for the JumpRateModelV3 contract.
* Only the contract moderator can call this function.
* @param blocksPerYear_ The new number of blocks per year.
*/
function setBlockPerYear(uint256 blocksPerYear_) external onlyModerator {
blocksPerYear = blocksPerYear_;
}
/**
* @dev Adds support for a new blending token to the JumpRateModelV3 contract.
*
* Requirements:
* - `blendingToken` cannot be the zero address.
* - Only the contract moderator can call this function.
* @param blendingToken The address of the blending token to add support for.
* @param gainPerYear The gain per year for the blending token.
* @param jumGainPerYear The jump gain per year for the blending token.
* @param targetUtil_ The target utilization rate for the blending token.
* @param newMaxBorrow The new maximum borrow rate for the blending token.
*/
function addBLendingTokenSupport(
address blendingToken,
uint256 gainPerYear,
uint256 jumGainPerYear,
uint256 targetUtil_,
uint256 newMaxBorrow
) external onlyModerator {
require(blendingToken != address(0), "JumpRateModelV3: Invalid address");
isBlendingTokenSupport[blendingToken] = true;
updateJumpRateModelInternal(gainPerYear, jumGainPerYear, targetUtil_, blendingToken);
rateInfo[blendingToken].maxBorrowRate = newMaxBorrow;
updateBlockNumber(blendingToken);
}
/**
* @dev Removes blending token support for the specified blending token address.
*
* Requirements:
* - `_blending` cannot be the zero address.
* - `_blending` must be a supported blending token.
* @param _blending The address of the blending token to remove support for.
*/
function removeBLendingTokenSupport(address _blending) external onlyModerator {
require(_blending != address(0), "JumpRateModelV3: Invalid address");
require(isBlendingTokenSupport[_blending], "JumpRateModelV3: Not found");
isBlendingTokenSupport[_blending] = false;
}
/**
* @dev Sets the maximum borrow rate for a blending token.
*
* Requirements:
* - The caller must have the `onlyModerator` modifier.
* - The blending token must be supported by the contract.
* @param blendingToken The address of the blending token.
* @param newMaxBorrow The new maximum borrow rate to be set.
*/
function setMaxBorrowRate(address blendingToken, uint256 newMaxBorrow) external onlyModerator {
require(isBlendingTokenSupport[blendingToken], "JumpRateModelV3: Not found");
rateInfo[blendingToken].maxBorrowRate = newMaxBorrow;
}
/**
* @dev Updates the block number for a given blending token.
*
* Requirements:
* - The caller must have the `onlyModerator` modifier.
* - The blending token must be supported.
* @param blendingToken The address of the blending token to update.
*/
function updateBlockNumber(address blendingToken) public onlyModerator {
require(isBlendingTokenSupport[blendingToken], "JumpRateModelV3: Not found");
uint256 blockNumber = BTokenInterface(blendingToken).accrualBlockNumber();
rateInfo[blendingToken].lastAccrualBlockNumber = blockNumber;
}
/**
* @dev Calculates the utilization rate of the market: `borrows / (cash + borrows - reserves)`.
* @param cash The amount of cash in the market.
* @param borrows The amount of borrows in the market.
* @param reserves The amount of reserves in the market (currently unused).
* @return The utilization rate as a mantissa between [0, 1e18].
*/
function utilizationRate(uint256 cash, uint256 borrows, uint256 reserves) public pure returns (uint) {
// Utilization rate is 0 when there are no borrows
if (borrows == 0) {
return 0;
}
return borrows.mul(1e18).div(cash.add(borrows).sub(reserves));
}
/**
* @dev Calculates the change in the interest rate per block per block.
* @param cash The amount of cash in the market.
* @param borrows The amount of borrows in the market.
* @param reserves The amount of reserves in the market.
* @return The change in the interest rate per block per block as a mantissa (scaled by 1e18).
*/
function getInterestRateChange(uint256 cash, uint256 borrows, uint256 reserves, address blendingToken) public view returns (int) {
BlendingTokenInfo memory info = blendingTokenInfo[blendingToken];
/*
* E = Cu - Tu
*/
uint256 currentUtil = utilizationRate(cash, borrows, reserves);
int utilErr = int(currentUtil) - int(info.targetUtil);
/* Calculate delta interest rate
* Cu > Tu => Dir = E * G * Jg
* Cu < Tu => Dir = E * G
* Cu = Tu => Dir = 0
*/
int interestRateChange;
if (currentUtil > info.targetUtil) {
interestRateChange = (utilErr * int(info.gainPerBlock.mul(info.jumGainPerBlock))) / 1e36; // utilErr, Dir : positive
} else if (currentUtil < info.targetUtil) {
interestRateChange = (utilErr * int(info.gainPerBlock)) / 1e18; // utilErr, Dir : negative
}
return interestRateChange;
}
/**
* @dev Calculates the number of blocks elapsed since the last accrual.
* @param blendingToken The address of the blending token.
* @return elapsedBlocks The number of elapsed blocks.
*/
function getElapsedBlocks(address blendingToken) internal view returns (uint256 elapsedBlocks) {
/* Calculate the number of blocks elapsed since the last accrual */
elapsedBlocks = getBlockNumber().sub(rateInfo[blendingToken].lastAccrualBlockNumber);
}
/**
* @dev Calculates the current borrow rate, with the error code expected by the market.
* @param cash The amount of cash in the market.
* @param borrows The amount of borrows in the market.
* @param reserves The amount of reserves in the market.
* @return The borrow rate percentage as a mantissa (scaled by 1e18).
*/
function getBorrowRateInternal(uint256 cash, uint256 borrows, uint256 reserves, address blendingToken) internal view returns (uint) {
RateInfo memory borrowRateInfo = rateInfo[blendingToken];
int interestRateChange = getInterestRateChange(cash, borrows, reserves, blendingToken);
/* Calculate the number of blocks elapsed since the last accrual */
uint256 elapsedBlocks = getElapsedBlocks(blendingToken);
/* Calculate interest rate
* IR = Lir + (Dt * Dir)
* Cu > Tu => IR increase
* Cu < Tu => IR decrease
* Cu = Tu => IR not change
* IR < 0 => IR = 0
* IR > maxIR => IR = maxIR
*/
int normalRate = int(borrowRateInfo.lastInterestRate) + (int(elapsedBlocks) * interestRateChange);
uint256 interestRate = normalRate > 0 ? uint256(normalRate) : 0;
if (interestRate > borrowRateInfo.maxBorrowRate) {
interestRate = borrowRateInfo.maxBorrowRate;
}
return interestRate;
}
/**
* @dev Function to simply retrieve block number.
* This exists mainly for inheriting test contracts to stub this result.
*/
function getBlockNumber() public view returns (uint) {
return block.number;
}
/**
* @dev Calculates and stores the current borrow interest rate per block for the specified blending token.
* @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 calculated borrow rate per block, represented as a percentage and scaled by 1e18.
*/
function storeBorrowRate(uint256 cash, uint256 borrows, uint256 reserves) public override onlyBlendingToken returns (uint) {
RateInfo storage borrowRateInfo = rateInfo[msg.sender];
uint256 interestRate = getBorrowRateInternal(cash, borrows, reserves, msg.sender);
borrowRateInfo.lastInterestRate = interestRate;
borrowRateInfo.lastAccrualBlockNumber = block.number;
return interestRate;
}
/**
* @dev Calculates the current supply rate per block.
* @param cash The amount of cash in the market.
* @param borrows The amount of borrows in the market.
* @param reserves The amount of reserves in the market.
* @param reserveFactorMantissa The current reserve factor for the market.
* @return The supply rate percentage per block as a mantissa (scaled by 1e18).
*/
function getSupplyRate(
uint256 cash,
uint256 borrows,
uint256 reserves,
uint256 reserveFactorMantissa,
address blendingToken
) public view override returns (uint) {
/* Calculate supply rate
* oneMinusReserveFactor: percentage remaining after subtracting ReserveFactor
* rateToPool = IR * oneMinusReserveFactor
* supplyrate = Cu * rateToPool
*/
uint256 oneMinusReserveFactor = uint256(1e18).sub(reserveFactorMantissa);
uint256 borrowRate = getBorrowRateInternal(cash, borrows, reserves, blendingToken);
uint256 rateToPool = borrowRate.mul(oneMinusReserveFactor).div(1e18);
return utilizationRate(cash, borrows, reserves).mul(rateToPool).div(1e18);
}
/**
* @dev Calculates the current borrow rate per block.
* @param cash The amount of cash in the market.
* @param borrows The amount of borrows in the market.
* @param reserves The amount of reserves in the market.
* @return The borrow rate percentage per block as a mantissa (scaled by 1e18).
*/
function getBorrowRate(uint256 cash, uint256 borrows, uint256 reserves, address blendingToken) external view override returns (uint) {
return getBorrowRateInternal(cash, borrows, reserves, blendingToken);
}
/**
* @dev Internal function to update the parameters of the interest rate model.
* gainPerYear The gain factor.
* jumGainPerYear The jump gain that only applies if Cu > Tu.
* targetUtil_ The target utilization rate.
*/
function updateJumpRateModelInternal(uint256 gainPerYear, uint256 jumGainPerYear, uint256 targetUtil_, address blendingToken) internal {
BlendingTokenInfo storage info = blendingTokenInfo[blendingToken];
info.gainPerBlock = (gainPerYear).div(blocksPerYear);
info.jumGainPerBlock = jumGainPerYear.div(blocksPerYear);
info.targetUtil = targetUtil_;
emit NewInterestParams(info.gainPerBlock, info.jumGainPerBlock, info.targetUtil);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)
pragma solidity ^0.8.0;
import "./IAccessControlUpgradeable.sol";
import "../utils/ContextUpgradeable.sol";
import "../utils/StringsUpgradeable.sol";
import "../utils/introspection/ERC165Upgradeable.sol";
import "../proxy/utils/Initializable.sol";
/**
* @dev Contract module that allows children to implement role-based access
* control mechanisms. This is a lightweight version that doesn't allow enumerating role
* members except through off-chain means by accessing the contract event logs. Some
* applications may benefit from on-chain enumerability, for those cases see
* {AccessControlEnumerable}.
*
* Roles are referred to by their `bytes32` identifier. These should be exposed
* in the external API and be unique. The best way to achieve this is by
* using `public constant` hash digests:
*
* ```solidity
* bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
* ```
*
* Roles can be used to represent a set of permissions. To restrict access to a
* function call, use {hasRole}:
*
* ```solidity
* function foo() public {
* require(hasRole(MY_ROLE, msg.sender));
* ...
* }
* ```
*
* Roles can be granted and revoked dynamically via the {grantRole} and
* {revokeRole} functions. Each role has an associated admin role, and only
* accounts that have a role's admin role can call {grantRole} and {revokeRole}.
*
* By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
* that only accounts with this role will be able to grant or revoke other
* roles. More complex role relationships can be created by using
* {_setRoleAdmin}.
*
* WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
* grant and revoke this role. Extra precautions should be taken to secure
* accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
* to enforce additional security measures for this role.
*/
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable {
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
struct RoleData {
mapping(address => bool) members;
bytes32 adminRole;
}
mapping(bytes32 => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with a standardized message including the required role.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*
* _Available since v4.1._
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual override returns (bool) {
return _roles[role].members[account];
}
/**
* @dev Revert with a standard message if `_msgSender()` is missing `role`.
* Overriding this function changes the behavior of the {onlyRole} modifier.
*
* Format of the revert message is described in {_checkRole}.
*
* _Available since v4.6._
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Revert with a standard message if `account` is missing `role`.
*
* The format of the revert reason is given by the following regular expression:
*
* /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
StringsUpgradeable.toHexString(account),
" is missing role ",
StringsUpgradeable.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) {
return _roles[role].adminRole;
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleGranted} event.
*/
function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_grantRole(role, account);
}
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*
* May emit a {RoleRevoked} event.
*/
function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) {
_revokeRole(role, account);
}
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been revoked `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address account) public virtual override {
require(account == _msgSender(), "AccessControl: can only renounce roles for self");
_revokeRole(role, account);
}
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event. Note that unlike {grantRole}, this function doesn't perform any
* checks on the calling account.
*
* May emit a {RoleGranted} event.
*
* [WARNING]
* ====
* This function should only be called from the constructor when setting
* up the initial roles for the system.
*
* Using this function in any other way is effectively circumventing the admin
* system imposed by {AccessControl}.
* ====
*
* NOTE: This function is deprecated in favor of {_grantRole}.
*/
function _setupRole(bytes32 role, address account) internal virtual {
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
bytes32 previousAdminRole = getRoleAdmin(role);
_roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Grants `role` to `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual {
if (!hasRole(role, account)) {
_roles[role].members[account] = true;
emit RoleGranted(role, account, _msgSender());
}
}
/**
* @dev Revokes `role` from `account`.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual {
if (hasRole(role, account)) {
_roles[role].members[account] = false;
emit RoleRevoked(role, account, _msgSender());
}
}
/**
* @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;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)
pragma solidity ^0.8.0;
/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/
interface IAccessControlUpgradeable {
/**
* @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole`
*
* `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite
* {RoleAdminChanged} not being emitted signaling this.
*
* _Available since v3.1._
*/
event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole);
/**
* @dev Emitted when `account` is granted `role`.
*
* `sender` is the account that originated the contract call, an admin role
* bearer except when using {AccessControl-_setupRole}.
*/
event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Emitted when `account` is revoked `role`.
*
* `sender` is the account that originated the contract call:
* - if using `revokeRole`, it is the admin role bearer
* - if using `renounceRole`, it is the role bearer (i.e. `account`)
*/
event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) external view returns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/
function getRoleAdmin(bytes32 role) external view returns (bytes32);
/**
* @dev Grants `role` to `account`.
*
* If `account` had not been already granted `role`, emits a {RoleGranted}
* event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function grantRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from `account`.
*
* If `account` had been granted `role`, emits a {RoleRevoked} event.
*
* Requirements:
*
* - the caller must have ``role``'s admin role.
*/
function revokeRole(bytes32 role, address account) external;
/**
* @dev Revokes `role` from the calling account.
*
* Roles are often managed via {grantRole} and {revokeRole}: this function's
* purpose is to provide a mechanism for accounts to lose their privileges
* if they are compromised (such as when a trusted device is misplaced).
*
* If the calling account had been granted `role`, emits a {RoleRevoked}
* event.
*
* Requirements:
*
* - the caller must be `account`.
*/
function renounceRole(bytes32 role, address account) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol)
pragma solidity ^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]
* ```solidity
* 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();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Indicates that the contract has been initialized.
* @custom:oz-retyped-from bool
*/
uint8 private _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool private _initializing;
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(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.
*/
modifier initializer() {
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.
*/
modifier reinitializer(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.
*/
modifier onlyInitializing() {
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() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized != type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library AddressUpgradeable {
/**
* @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
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [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.
* ====
*/
function isContract(address account) internal view returns (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.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable 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._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
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._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
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._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
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._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory 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._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
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._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory 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._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
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._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory 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._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
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 contract
require(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._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
pragma solidity ^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.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.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;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)
pragma solidity ^0.8.0;
import "./IERC165Upgradeable.sol";
import "../../proxy/utils/Initializable.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
* for the additional interface id that will be supported. For example:
*
* ```solidity
* function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
* return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
* }
* ```
*
* Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
*/
abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable {
function __ERC165_init() internal onlyInitializing {
}
function __ERC165_init_unchained() internal onlyInitializing {
}
/**
* @dev See {IERC165-supportsInterface}.
*/
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IERC165Upgradeable).interfaceId;
}
/**
* @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;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[EIP].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165Upgradeable {
/**
* @dev Returns true if this contract implements the interface defined by
* `interfaceId`. See the corresponding
* https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
* to learn more about how these ids are created.
*
* This function call must use less than 30 000 gas.
*/
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library MathUpgradeable {
enum Rounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds up instead
* of rounding down.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
* with further edits by Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod0 := mul(x, y)
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
require(denominator > prod1, "Math: mulDiv overflow");
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
// See https://cs.stackexchange.com/q/138556/92363.
// Does not overflow because the denominator cannot be zero at this stage in the function.
uint256 twos = denominator & (~denominator + 1);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
// in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10, rounded down, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256, rounded down, of a positive value.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)
pragma solidity ^0.8.0;
/**
* @dev Standard signed math utilities missing in the Solidity language.
*/
library SignedMathUpgradeable {
/**
* @dev Returns the largest of two signed numbers.
*/
function max(int256 a, int256 b) internal pure returns (int256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two signed numbers.
*/
function min(int256 a, int256 b) internal pure returns (int256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two signed numbers without overflow.
* The result is rounded towards zero.
*/
function average(int256 a, int256 b) internal pure returns (int256) {
// Formula from the book "Hacker's Delight"
int256 x = (a & b) + ((a ^ b) >> 1);
return x + (int256(uint256(x) >> 255) & (a ^ b));
}
/**
* @dev Returns the absolute unsigned value of a signed value.
*/
function abs(int256 n) internal pure returns (uint256) {
unchecked {
// must be unchecked in order to support `n = type(int256).min`
return uint256(n >= 0 ? n : -n);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)
pragma solidity ^0.8.0;
import "./math/MathUpgradeable.sol";
import "./math/SignedMathUpgradeable.sol";
/**
* @dev String operations.
*/
library StringsUpgradeable {
bytes16 private constant _SYMBOLS = "0123456789abcdef";
uint8 private constant _ADDRESS_LENGTH = 20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/
function toString(uint256 value) internal pure returns (string memory) {
unchecked {
uint256 length = MathUpgradeable.log10(value) + 1;
string memory buffer = new string(length);
uint256 ptr;
/// @solidity memory-safe-assembly
assembly {
ptr := add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assembly
assembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /= 10;
if (value == 0) break;
}
return buffer;
}
}
/**
* @dev Converts a `int256` to its ASCII `string` decimal representation.
*/
function toString(int256 value) internal pure returns (string memory) {
return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMathUpgradeable.abs(value))));
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/
function toHexString(uint256 value) internal pure returns (string memory) {
unchecked {
return toHexString(value, MathUpgradeable.log256(value) + 1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/
function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
bytes memory buffer = new bytes(2 * length + 2);
buffer[0] = "0";
buffer[1] = "x";
for (uint256 i = 2 * length + 1; i > 1; --i) {
buffer[i] = _SYMBOLS[value & 0xf];
value >>= 4;
}
require(value == 0, "Strings: hex length insufficient");
return string(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/
function toHexString(address addr) internal pure returns (string memory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
/**
* @dev Returns true if the two strings are equal.
*/
function equal(string memory a, string memory b) internal pure returns (bool) {
return keccak256(bytes(a)) == keccak256(bytes(b));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/SafeMath.sol)
pragma solidity ^0.8.0;
// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.
/**
* @dev Wrappers over Solidity's arithmetic operations.
*
* NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
* now has built in overflow checking.
*/
library SafeMath {
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*
* _Available since v3.4._
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*
* _Available since v3.4._
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the addition of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `+` operator.
*
* Requirements:
*
* - Addition cannot overflow.
*/
function add(uint256 a, uint256 b) internal pure returns (uint256) {
return a + b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting on
* overflow (when the result is negative).
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
return a - b;
}
/**
* @dev Returns the multiplication of two unsigned integers, reverting on
* overflow.
*
* Counterpart to Solidity's `*` operator.
*
* Requirements:
*
* - Multiplication cannot overflow.
*/
function mul(uint256 a, uint256 b) internal pure returns (uint256) {
return a * b;
}
/**
* @dev Returns the integer division of two unsigned integers, reverting on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator.
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b) internal pure returns (uint256) {
return a / b;
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting when dividing by zero.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b) internal pure returns (uint256) {
return a % b;
}
/**
* @dev Returns the subtraction of two unsigned integers, reverting with custom message on
* overflow (when the result is negative).
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {trySub}.
*
* Counterpart to Solidity's `-` operator.
*
* Requirements:
*
* - Subtraction cannot overflow.
*/
function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
unchecked {
require(b <= a, errorMessage);
return a - b;
}
}
/**
* @dev Returns the integer division of two unsigned integers, reverting with custom message on
* division by zero. The result is rounded towards zero.
*
* Counterpart to Solidity's `/` operator. Note: this function uses a
* `revert` opcode (which leaves remaining gas untouched) while Solidity
* uses an invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a / b;
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
* reverting with custom message when dividing by zero.
*
* CAUTION: This function is deprecated because it requires allocating memory for the error
* message unnecessarily. For custom revert reasons use {tryMod}.
*
* Counterpart to Solidity's `%` operator. This function uses a `revert`
* opcode (which leaves remaining gas untouched) while Solidity uses an
* invalid opcode to revert (consuming all remaining gas).
*
* Requirements:
*
* - The divisor cannot be zero.
*/
function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
unchecked {
require(b > 0, errorMessage);
return a % b;
}
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "../bToken/BToken.sol";
import "../util/ErrorReporter.sol";
import "../util/ExponentialNoError.sol";
import "./BondtrollerStorage.sol";
/**
* @title Remastered from Compound's Bondtroller Contract
* @author Bonded
* @dev Contract for managing the Bond market and its associated BToken contracts.
*/
contract Bondtroller is BondtrollerV5Storage, BondtrollerErrorReporter, ExponentialNoError, Initializable {
/// @notice Emitted when an admin supports a market
event MarketListed(BToken bToken);
/// @notice Emitted when an account enters a market
event MarketEntered(BToken bToken, address account);
/// @notice Emitted when an account exits a market
event MarketExited(BToken bToken, address account);
/// @notice Emitted when price oracle is changed
event NewPriceOracle(address oldPriceOracle, address newPriceOracle);
/// @notice Emitted when pause guardian is changed
event NewPauseGuardian(address oldPauseGuardian, address newPauseGuardian);
/// @notice Emitted when an action is paused globally
event GlobalActionPaused(string action, bool pauseState);
/// @notice Emitted when an action is paused on a market
event ActionPaused(BToken bToken, string action, bool pauseState);
/// @notice Emitted when borrow cap for a bToken is changed
event NewBorrowCap(BToken indexed bToken, uint256 newBorrowCap);
/// @notice Emitted when borrow cap guardian is changed
event NewBorrowCapGuardian(address oldBorrowCapGuardian, address newBorrowCapGuardian);
/// @notice Emitted when COMP is granted by admin
event CompGranted(address recipient, uint256 amount);
event NewPrimaryLendingPlatform(address oldPrimaryLendingPlatform, address newPrimaryLendingPlatform);
/// @notice Emitted when admin address is changed by previous admin
event NewAdmin(address newAdmin);
/// @notice the address of primary index token
address public primaryLendingPlatform;
/**
* @dev Initializes the Bondtroller contract by setting the admin to the sender's address and setting the pause guardian to the admin.
*/
function init() public initializer {
admin = msg.sender;
setPauseGuardian(admin);
}
/**
* @dev Throws if called by any account other than the primary index token.
*/
modifier onlyPrimaryLendingPlatform() {
require(msg.sender == primaryLendingPlatform);
_;
}
/**
* @dev Returns the address of the primary lending platform.
* @return The address of the primary lending platform.
*/
function getPrimaryLendingPlatformAddress() external view returns (address) {
return primaryLendingPlatform;
}
/*** Assets You Are In ***/
/**
* @dev 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.
*/
function getAssetsIn(address account) external view returns (BToken[] memory) {
BToken[] memory assetsIn = accountAssets[account];
return assetsIn;
}
/**
* @dev Returns whether the given account is entered in the given asset.
* @param account The address of the account to check.
* @param bToken The bToken to check.
* @return True if the account is in the asset, otherwise false.
*/
function checkMembership(address account, BToken bToken) external view returns (bool) {
return accountMembership[account][address(bToken)];
}
/**
* @dev Changes the admin address of the Bondtroller contract.
* @param newAdmin The new admin address to be set.
*/
function changeAdmin(address newAdmin) external {
require(msg.sender == admin && newAdmin != address(0), "Bondtroller: Invalid address");
admin = newAdmin;
emit NewAdmin(newAdmin);
}
/**
* @dev Add assets to be included in account liquidity calculation.
* @param bTokens The list of addresses of the bToken markets to be enabled.
* @return Success indicator for whether each corresponding market was entered.
*/
function enterMarkets(address[] memory bTokens) public onlyPrimaryLendingPlatform returns (uint256[] memory) {
uint256 len = bTokens.length;
uint256[] memory results = new uint256[](len);
for (uint256 i = 0; i < len; i++) {
BToken bToken = BToken(bTokens[i]);
results[i] = uint256(addToMarketInternal(bToken, msg.sender));
}
return results;
}
/**
* @dev Allows a borrower to enter a market by adding the corresponding BToken to the market and updating the borrower's status.
* @param bToken The address of the BToken to add to the market.
* @param borrower The address of the borrower to update status for.
* @return An Error code indicating if the operation was successful or not.
*/
function enterMarket(address bToken, address borrower) public onlyPrimaryLendingPlatform returns (Error) {
return addToMarketInternal(BToken(bToken), borrower);
}
/**
* @dev Adds the market to the borrower's "assets in" for liquidity calculations.
* @param bToken The market to enter.
* @param borrower The address of the account to modify.
* @return Success indicator for whether the market was entered.
*/
function addToMarketInternal(BToken bToken, address borrower) internal returns (Error) {
Market storage marketToJoin = markets[address(bToken)];
if (!marketToJoin.isListed) {
// market is not listed, cannot join
return Error.MARKET_NOT_LISTED;
}
if (accountMembership[borrower][address(bToken)] == true) {
// already joined
return Error.NO_ERROR;
}
// survived the gauntlet, add to list
// NOTE: we store these somewhat redundantly as a significant optimization
// this avoids having to iterate through the list for the most common use cases
// that is, only when we need to perform liquidity checks
// and not whenever we want to check if an account is in a particular market
accountMembership[borrower][address(bToken)] = true;
accountAssets[borrower].push(bToken);
emit MarketEntered(bToken, borrower);
return Error.NO_ERROR;
}
/**
* @dev Removes asset from sender's account liquidity calculation.
* 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.
*/
function exitMarket(address cTokenAddress) external onlyPrimaryLendingPlatform returns (uint) {
BToken bToken = BToken(cTokenAddress);
/* Get sender tokensHeld and amountOwed underlying from the bToken */
(uint256 oErr, uint256 tokensHeld, uint256 amountOwed, ) = bToken.getAccountSnapshot(msg.sender);
require(oErr == 0, "Bondtroller: GetAccountSnapshot failed"); // 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[address(bToken)];
/* Return true if the sender is not already ‘in’ the market */
if (!accountMembership[msg.sender][address(bToken)]) {
return uint256(Error.NO_ERROR);
}
/* Set bToken account membership to false */
delete accountMembership[msg.sender][address(bToken)];
/* Delete bToken from the account’s list of assets */
// load into memory for faster iteration
BToken[] memory userAssetList = accountAssets[msg.sender];
uint256 len = userAssetList.length;
uint256 assetIndex = len;
for (uint256 i = 0; i < len; i++) {
if (userAssetList[i] == bToken) {
assetIndex = i;
break;
}
}
// We *must* have found the asset in the list or our redundant data structure is broken
assert(assetIndex < len);
// copy last item in list to location of item to be removed, reduce length by 1
BToken[] storage storedList = accountAssets[msg.sender];
storedList[assetIndex] = storedList[storedList.length - 1];
storedList.pop();
emit MarketExited(bToken, msg.sender);
return uint256(Error.NO_ERROR);
}
/*** Policy Hooks ***/
/**
* @dev Checks if the account should be allowed to mint tokens in the given market.
* @param bToken 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).
*/
function mintAllowed(address bToken, address minter, uint256 mintAmount) external view returns (uint) {
// Shh - currently unused
bToken;
minter;
mintAmount;
// Pausing is a very serious situation - we revert to sound the alarms
require(!mintGuardianPaused[bToken], "Bondtroller: Mint is paused");
// Shh - currently unused
minter;
mintAmount;
if (!markets[bToken].isListed) {
return uint256(Error.MARKET_NOT_LISTED);
}
// // Keep the flywheel moving
// updateCompSupplyIndex(bToken);
// distributeSupplierComp(bToken, minter);
return uint256(Error.NO_ERROR);
}
/**
* @dev Validates mint and reverts on rejection. May emit logs.
* @param bToken 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.
*/
function mintVerify(address bToken, address minter, uint256 actualMintAmount, uint256 mintTokens) external {
// Shh - currently unused
bToken;
minter;
actualMintAmount;
mintTokens;
// Shh - we don't ever want this hook to be marked pure
if (false) {
maxAssets = maxAssets;
}
}
/**
* @dev Checks if the account should be allowed to redeem tokens in the given market.
* @param bToken The market to verify the redeem against.
* @param redeemer The account which would redeem the tokens.
* @param redeemTokens The number of bTokens 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).
*/
function redeemAllowed(address bToken, address redeemer, uint256 redeemTokens) external view returns (uint) {
// Shh - - currently unused
bToken;
redeemer;
redeemTokens;
uint256 allowed = redeemAllowedInternal(bToken, redeemer, redeemTokens);
if (allowed != uint256(Error.NO_ERROR)) {
return allowed;
}
return uint256(Error.NO_ERROR);
}
/**
* @dev Checks if redeeming tokens is allowed for a given bToken and redeemer.
* @param bToken The address of the bToken to check.
* @param redeemer The address of the redeemer to check.
* @param redeemTokens The amount of tokens to redeem.
* @return uint256 0 if redeeming is allowed, otherwise an error code.
*/
function redeemAllowedInternal(address bToken, address redeemer, uint256 redeemTokens) internal view returns (uint) {
// Shh - currently unused
redeemTokens;
if (!markets[bToken].isListed) {
return uint256(Error.MARKET_NOT_LISTED);
}
/* If the redeemer is not 'in' the market, then we can bypass the liquidity check */
if (!accountMembership[redeemer][address(bToken)]) {
return uint256(Error.NO_ERROR);
}
return uint256(Error.NO_ERROR);
}
/**
* @dev Validates redeem and reverts on rejection. May emit logs.
* @param bToken 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.
*/
function redeemVerify(address bToken, address redeemer, uint256 redeemAmount, uint256 redeemTokens) external pure {
// Shh - currently unused
bToken;
redeemer;
// Require tokens is zero or amount is also zero
if (redeemTokens == 0 && redeemAmount > 0) {
revert("Bondtroller: RedeemTokens zero");
}
}
/**
* @dev Checks if the account should be allowed to borrow the underlying asset of the given market.
* @param bToken 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).
*/
function borrowAllowed(address bToken, address borrower, uint256 borrowAmount) external returns (uint) {
// Pausing is a very serious situation - we revert to sound the alarms
require(!borrowGuardianPaused[bToken], "Bondtroller: Borrow is paused");
if (!markets[bToken].isListed) {
return uint256(Error.MARKET_NOT_LISTED);
}
if (!accountMembership[borrower][address(bToken)]) {
// only bTokens may call borrowAllowed if borrower not in market
//require(msg.sender == bToken, "sender must be bToken");
// attempt to add borrower to the market
Error errAddMarketInternal = addToMarketInternal(BToken(msg.sender), borrower);
if (errAddMarketInternal != Error.NO_ERROR) {
return uint256(errAddMarketInternal);
}
// it should be impossible to break the important invariant
assert(accountMembership[borrower][address(bToken)]);
}
// if (oracle.getUnderlyingPrice(BToken(bToken)) == 0) {
// return uint256(Error.PRICE_ERROR);
// }
uint256 borrowCap = borrowCaps[bToken];
// Borrow cap of 0 corresponds to unlimited borrowing
if (borrowCap != 0) {
uint256 totalBorrows = BToken(bToken).totalBorrows();
uint256 nextTotalBorrows = add_(totalBorrows, borrowAmount);
require(nextTotalBorrows < borrowCap, "Bondtroller: Market borrow cap reached");
}
return uint256(Error.NO_ERROR);
}
/**
* @dev Validates borrow and reverts on rejection. May emit logs.
* @param bToken Asset whose underlying is being borrowed.
* @param borrower The address borrowing the underlying.
* @param borrowAmount The amount of the underlying asset requested to borrow.
*/
function borrowVerify(address bToken, address borrower, uint256 borrowAmount) external {
// Shh - currently unused
bToken;
borrower;
borrowAmount;
// Shh - we don't ever want this hook to be marked pure
if (false) {
maxAssets = maxAssets;
}
}
/**
* @dev Checks if the account should be allowed to repay a borrow in the given market.
* @param bToken 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).
*/
function repayBorrowAllowed(address bToken, address payer, address borrower, uint256 repayAmount) external view returns (uint) {
// Shh - currently unused
payer;
borrower;
repayAmount;
if (!markets[bToken].isListed) {
return uint256(Error.MARKET_NOT_LISTED);
}
// // Keep the flywheel moving
// Exp memory borrowIndex = Exp({mantissa: BToken(bToken).borrowIndex()});
// updateCompBorrowIndex(bToken, borrowIndex);
// distributeBorrowerComp(bToken, borrower, borrowIndex);
return uint256(Error.NO_ERROR);
}
/**
* @dev Validates repayBorrow and reverts on rejection. May emit logs.
* @param bToken Asset being repaid.
* @param payer The address repaying the borrow.
* @param borrower The address of the borrower.
* @param actualRepayAmount The amount of underlying being repaid.
*/
function repayBorrowVerify(address bToken, address payer, address borrower, uint256 actualRepayAmount, uint256 borrowerIndex) external {
// Shh - currently unused
bToken;
payer;
borrower;
actualRepayAmount;
borrowerIndex;
// Shh - we don't ever want this hook to be marked pure
if (false) {
maxAssets = maxAssets;
}
}
/**
* @dev Checks if the account should be allowed to transfer tokens in the given market.
* @param bToken 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 bTokens to transfer.
* @return 0 if the transfer is allowed, otherwise a semi-opaque error code (See ErrorReporter.sol).
*/
function transferAllowed(address bToken, address src, address dst, uint256 transferTokens) external returns (uint) {
// Shh - currently unused
bToken;
src;
dst;
transferTokens;
// Pausing is a very serious situation - we revert to sound the alarms
require(!transferGuardianPaused, "Bondtroller: Transfer is paused");
// Currently the only consideration is whether or not
// the src is allowed to redeem this many tokens
// uint256 allowed = redeemAllowedInternal(bToken, src, transferTokens);
// if (allowed != uint256(Error.NO_ERROR)) {
// return allowed;
// }
// Shh - we don't ever want this hook to be marked pure
if (false) {
maxAssets = maxAssets;
}
return uint256(Error.NO_ERROR);
}
/**
* @dev Validates transfer and reverts on rejection. May emit logs.
* @param bToken Asset being transferred.
* @param src The account which sources the tokens.
* @param dst The account which receives the tokens.
* @param transferTokens The number of bTokens to transfer.
*/
function transferVerify(address bToken, address src, address dst, uint256 transferTokens) external onlyPrimaryLendingPlatform {
// Shh - currently unused
bToken;
src;
dst;
transferTokens;
// Shh - we don't ever want this hook to be marked pure
if (false) {
maxAssets = maxAssets;
}
}
/*** Admin Functions ***/
/**
* @dev Sets a new price oracle for the bondtroller.
* Admin function to set a new price oracle.
* @return uint256 0=success, otherwise a failure (see ErrorReporter.sol for details).
*/
function setPriceOracle(address newOracle) public returns (uint) {
// Check caller is admin
if (msg.sender != admin) {
return fail(Error.UNAUTHORIZED, FailureInfo.SET_PRICE_ORACLE_OWNER_CHECK);
}
// Track the old oracle for the bondtroller
address oldOracle = oracle;
// Set bondtroller's oracle to newOracle
oracle = newOracle;
// Emit NewPriceOracle(oldOracle, newOracle)
emit NewPriceOracle(oldOracle, newOracle);
return uint256(Error.NO_ERROR);
}
/**
* @dev Sets the address of the primary lending platform.
* @param _newPrimaryLendingPlatform The new address of the primary lending platform.
* @return uint 0=success, otherwise a failure (see ErrorReporter.sol for details).
*/
function setPrimaryLendingPlatformAddress(address _newPrimaryLendingPlatform) external returns (uint) {
// Check caller is admin
if (msg.sender != admin) {
return fail(Error.UNAUTHORIZED, FailureInfo.SET_LIQUIDATION_INCENTIVE_OWNER_CHECK);
}
address oldPrimaryLendingPlatform = primaryLendingPlatform;
primaryLendingPlatform = _newPrimaryLendingPlatform;
emit NewPrimaryLendingPlatform(oldPrimaryLendingPlatform, _newPrimaryLendingPlatform);
return uint256(Error.NO_ERROR);
}
/**
* @dev Add the market to the markets mapping and set it as listed.
* Admin function to set isListed and add support for the market.
* @param bToken The address of the market (token) to list.
* @return uint256 0=success, otherwise a failure. (See enum Error for details).
*/
function supportMarket(BToken bToken) external returns (uint) {
if (msg.sender != admin) {
return fail(Error.UNAUTHORIZED, FailureInfo.SUPPORT_MARKET_OWNER_CHECK);
}
if (markets[address(bToken)].isListed) {
return fail(Error.MARKET_ALREADY_LISTED, FailureInfo.SUPPORT_MARKET_EXISTS);
}
bToken.isCToken(); // Sanity check to make sure its really a BToken
// Note that isComped is not in active use anymore
markets[address(bToken)] = Market({isListed: true, isComped: false, collateralFactorMantissa: 0});
_addMarketInternal(address(bToken));
emit MarketListed(bToken);
return uint256(Error.NO_ERROR);
}
/**
* @dev Adds a new market to the list of all markets.
* @param bToken The address of the BToken contract to be added.
*/
function _addMarketInternal(address bToken) internal {
for (uint256 i = 0; i < allMarkets.length; i++) {
require(allMarkets[i] != BToken(bToken), "Bondtroller: Market already added");
}
allMarkets.push(BToken(bToken));
}
/**
* @dev Sets the given borrow caps for the given bToken markets. Borrowing that brings total borrows to or above borrow cap will revert.
* Admin or borrowCapGuardian function to set the borrow caps. A borrow cap of 0 corresponds to unlimited borrowing.
* @param bTokens The addresses of the markets (tokens) to change the borrow caps for.
* @param newBorrowCaps The new borrow cap values in underlying to be set. A value of 0 corresponds to unlimited borrowing.
*/
function setMarketBorrowCaps(BToken[] calldata bTokens, uint256[] calldata newBorrowCaps) external {
require(msg.sender == admin || msg.sender == borrowCapGuardian, "Bondtroller: Only admin or borrow cap guardian can set borrow caps");
uint256 numMarkets = bTokens.length;
uint256 numBorrowCaps = newBorrowCaps.length;
require(numMarkets != 0 && numMarkets == numBorrowCaps, "Bondtroller: Invalid input");
for (uint256 i = 0; i < numMarkets; i++) {
borrowCaps[address(bTokens[i])] = newBorrowCaps[i];
emit NewBorrowCap(bTokens[i], newBorrowCaps[i]);
}
}
/**
* @dev Admin function to change the Borrow Cap Guardian.
* @param newBorrowCapGuardian The address of the new Borrow Cap Guardian.
*/
function setBorrowCapGuardian(address newBorrowCapGuardian) external {
require(msg.sender == admin, "Bondtroller: Only admin can set borrow cap guardian");
// Save current value for inclusion in log
address oldBorrowCapGuardian = borrowCapGuardian;
// Store borrowCapGuardian with value newBorrowCapGuardian
borrowCapGuardian = newBorrowCapGuardian;
// Emit NewBorrowCapGuardian(OldBorrowCapGuardian, NewBorrowCapGuardian)
emit NewBorrowCapGuardian(oldBorrowCapGuardian, newBorrowCapGuardian);
}
/**
* @dev Admin function to change the Pause Guardian.
* @param newPauseGuardian The address of the new Pause Guardian.
* @return uint256 0=success, otherwise a failure. (See enum Error for details).
*/
function setPauseGuardian(address newPauseGuardian) public returns (uint) {
if (msg.sender != admin) {
return fail(Error.UNAUTHORIZED, FailureInfo.SET_PAUSE_GUARDIAN_OWNER_CHECK);
}
// Save current value for inclusion in log
address oldPauseGuardian = pauseGuardian;
// Store pauseGuardian with value newPauseGuardian
pauseGuardian = newPauseGuardian;
// Emit NewPauseGuardian(OldPauseGuardian, NewPauseGuardian)
emit NewPauseGuardian(oldPauseGuardian, pauseGuardian);
return uint256(Error.NO_ERROR);
}
/**
* @dev Pauses or unpauses minting of a specific BToken.
* @param bToken The address of the BToken to pause or unpause minting for.
* @param state The boolean state to set the minting pause status to.
* @return A boolean indicating whether the minting pause status was successfully set.
*/
function setMintPaused(BToken bToken, bool state) public returns (bool) {
require(markets[address(bToken)].isListed, "Bondtroller: Cannot pause a market that is not listed");
require(msg.sender == pauseGuardian || msg.sender == admin, "Bondtroller: Only pause guardian and admin can pause");
require(msg.sender == admin || state == true, "Bondtroller: Only admin can unpause");
mintGuardianPaused[address(bToken)] = state;
emit ActionPaused(bToken, "Mint", state);
return state;
}
/**
* @dev Pauses or unpauses borrowing for a given market.
* @param bToken The address of the BToken to pause or unpause borrowing.
* @param state The boolean state to set the borrowing pause to.
* @return A boolean indicating whether the operation was successful.
*/
function setBorrowPaused(BToken bToken, bool state) public returns (bool) {
require(markets[address(bToken)].isListed, "Bondtroller: Cannot pause a market that is not listed");
require(msg.sender == pauseGuardian || msg.sender == admin, "Bondtroller: Only pause guardian and admin can pause");
require(msg.sender == admin || state == true, "Bondtroller: Only admin can unpause");
borrowGuardianPaused[address(bToken)] = state;
emit ActionPaused(bToken, "Borrow", state);
return state;
}
/**
* @dev Sets the transfer pause state.
* @param state The new transfer pause state.
* @return bool Returns the new transfer pause state.
*/
function setTransferPaused(bool state) public returns (bool) {
require(msg.sender == pauseGuardian || msg.sender == admin, "Bondtroller: Only pause guardian and admin can pause");
require(msg.sender == admin || state == true, "Bondtroller: Only admin can unpause");
transferGuardianPaused = state;
emit GlobalActionPaused("Transfer", state);
return state;
}
/**
* @dev Sets the state of the seizeGuardianPaused variable to the given state.
* @param state The new state of the seizeGuardianPaused variable.
* @return The new state of the seizeGuardianPaused variable.
*/
function setSeizePaused(bool state) public returns (bool) {
require(msg.sender == pauseGuardian || msg.sender == admin, "Bondtroller: Only pause guardian and admin can pause");
require(msg.sender == admin || state == true, "Bondtroller: Only admin can unpause");
seizeGuardianPaused = state;
emit GlobalActionPaused("Seize", state);
return state;
}
/**
* @dev Checks caller is admin, or this contract is becoming the new implementation.
*/
function adminOrInitializing() internal view returns (bool) {
return msg.sender == admin;
}
/**
* @dev Returns all of the markets.
* The automatic getter may be used to access an individual market.
* @return The list of market addresses.
*/
function getAllMarkets() public view returns (BToken[] memory) {
return allMarkets;
}
/**
* @dev Returns true if the given bToken market has been deprecated.
* All borrows in a deprecated bToken market can be immediately liquidated.
* @param bToken The market to check if deprecated.
*/
function isDeprecated(BToken bToken) public view returns (bool) {
return
markets[address(bToken)].collateralFactorMantissa == 0 &&
borrowGuardianPaused[address(bToken)] == true &&
bToken.reserveFactorMantissa() == 1e18;
}
/**
* @dev Returns the current block number.
* @return uint representing the current block number.
*/
function getBlockNumber() public view returns (uint) {
return block.number;
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "../bToken/BToken.sol";
contract BondtrollerV1Storage {
/**
* @notice watermark that says that this is Bondtroller
*/
bool public constant isBondtroller = true;
/**
* @notice Administrator for this contract
*/
address public admin;
/**
* @notice Oracle which gives the price of any given asset
*/
address public oracle;
/**
* @notice Multiplier used to calculate the maximum repayAmount when liquidating a borrow
*/
uint256 public closeFactorMantissa;
/**
* @notice Multiplier representing the discount on collateral that a liquidator receives
*/
uint256 public liquidationIncentiveMantissa;
/**
* @notice Max number of assets a single account can participate in (borrow or use as collateral)
*/
uint256 public maxAssets;
/**
* @notice Per-account mapping of "assets you are in", capped by maxAssets
*/
mapping(address => BToken[]) public accountAssets;
}
contract BondtrollerV2Storage is BondtrollerV1Storage {
struct Market {
/// @notice Whether or not this market is listed
bool isListed;
/**
* @notice 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;
/// @notice Whether or not this market receives COMP
bool isComped;
}
/// @notice Per-market mapping of "accounts in this asset"
mapping(address => mapping(address => bool)) public accountMembership; //user address => BToken address => isListed
/**
* @notice Official mapping of BTokens -> Market metadata
* @dev Used e.g. to determine if a market is supported
*/
mapping(address => Market) public markets;
/**
* @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.
*/
address public pauseGuardian;
bool public _mintGuardianPaused;
bool public _borrowGuardianPaused;
bool public transferGuardianPaused;
bool public seizeGuardianPaused;
mapping(address => bool) public mintGuardianPaused;
mapping(address => bool) public borrowGuardianPaused;
}
contract BondtrollerV3Storage is BondtrollerV2Storage {
struct CompMarketState {
/// @notice The market's last updated compBorrowIndex or compSupplyIndex
uint224 index;
/// @notice The block number the index was last updated at
uint32 block;
}
/// @notice A list of all markets
BToken[] public allMarkets;
/// @notice The rate at which the flywheel distributes COMP, per block
uint256 public compRate;
/// @notice The portion of compRate that each market currently receives
mapping(address => uint) public compSpeeds;
/// @notice The COMP market supply state for each market
mapping(address => CompMarketState) public compSupplyState;
/// @notice The COMP market borrow state for each market
mapping(address => CompMarketState) public compBorrowState;
/// @notice The COMP borrow index for each market for each supplier as of the last time they accrued COMP
mapping(address => mapping(address => uint)) public compSupplierIndex;
/// @notice The COMP borrow index for each market for each borrower as of the last time they accrued COMP
mapping(address => mapping(address => uint)) public compBorrowerIndex;
/// @notice The COMP accrued but not yet transferred to each user
mapping(address => uint) public compAccrued;
}
contract BondtrollerV4Storage is BondtrollerV3Storage {
// @notice The borrowCapGuardian can set borrowCaps to any number for any market. Lowering the borrow cap could disable borrowing on the given market.
address public borrowCapGuardian;
// @notice Borrow caps enforced by borrowAllowed for each BToken address. Defaults to zero which corresponds to unlimited borrowing.
mapping(address => uint) public borrowCaps;
}
contract BondtrollerV5Storage is BondtrollerV4Storage {
/// @notice The portion of COMP that each contributor receives per block
mapping(address => uint) public compContributorSpeeds;
/// @notice Last block at which a contributor's COMP rewards have been allocated
mapping(address => uint) public lastContributorBlock;
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "../bondtroller/Bondtroller.sol";
import "./BTokenInterfaces.sol";
import "../util/ErrorReporter.sol";
import "../util/Exponential.sol";
import "../interfaces/EIP20Interface.sol";
import "../interestRateModel/InterestRateModel.sol";
/**
* @title Compound's CToken Contract
* @notice Abstract base for CTokens
* @author Compound
*/
abstract contract BToken is BTokenInterface, Exponential, TokenErrorReporter {
/**
* @dev Initializes the money market.
* @param bondtroller_ The address of the Bondtroller.
* @param interestRateModel_ The address of the interest rate model.
* @param initialExchangeRateMantissa_ The initial exchange rate, scaled by 1e18.
* @param name_ EIP-20 name of this token.
* @param symbol_ EIP-20 symbol of this token.
* @param decimals_ EIP-20 decimal precision of this token.
*/
function initialize(
Bondtroller bondtroller_,
InterestRateModel interestRateModel_,
uint256 initialExchangeRateMantissa_,
string memory name_,
string memory symbol_,
uint8 decimals_
) public {
require(msg.sender == admin, "BToken: Only admin may initialize the market");
require(accrualBlockNumber == 0 && borrowIndex == 0, "BToken: Market may only be initialized once");
// Set initial exchange rate
initialExchangeRateMantissa = initialExchangeRateMantissa_;
require(initialExchangeRateMantissa > 0, "BToken: Initial exchange rate must be greater than zero.");
// Set the bondtroller
uint256 err = _setBondtroller(bondtroller_);
require(err == uint256(Error.NO_ERROR), "BToken: Setting bondtroller failed");
// Initialize block number and borrow index (block number mocks depend on bondtroller being set)
accrualBlockNumber = getBlockNumber();
borrowIndex = mantissaOne;
// Set the interest rate model (depends on block number / borrow index)
err = _setInterestRateModelFresh(interestRateModel_);
require(err == uint256(Error.NO_ERROR), "BToken: Setting interest rate model failed");
name = name_;
symbol = symbol_;
decimals = decimals_;
// The counter starts true to prevent changing it from zero to non-zero (i.e. smaller cost/refund)
_notEntered = true;
}
/**
* @dev Transfers `tokens` tokens from `src` to `dst` by `spender`.
* Called by both `transfer` and `transferFrom` internally.
* @param spender The address of the account performing the transfer.
* @param src The address of the source account.
* @param dst The address of the destination account.
* @param tokens The number of tokens to transfer.
* @return Whether or not the transfer succeeded.
*/
function transferTokens(address spender, address src, address dst, uint256 tokens) internal returns (uint) {
/* Fail if transfer not allowed */
uint256 allowed = bondtroller.transferAllowed(address(this), src, dst, tokens);
if (allowed != 0) {
return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.TRANSFER_COMPTROLLER_REJECTION, allowed);
}
/* Do not allow self-transfers */
if (src == dst) {
return fail(Error.BAD_INPUT, FailureInfo.TRANSFER_NOT_ALLOWED);
}
/* Get the allowance, infinite for the account owner */
uint256 startingAllowance = 0;
if (spender == src) {
startingAllowance = ((2 ** 256) - 1);
} else {
startingAllowance = transferAllowances[src][spender];
}
/* Do the calculations, checking for {under,over}flow */
MathError mathErr;
uint256 allowanceNew;
uint256 srcTokensNew;
uint256 dstTokensNew;
(mathErr, allowanceNew) = subUInt(startingAllowance, tokens);
if (mathErr != MathError.NO_ERROR) {
return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_NOT_ALLOWED);
}
(mathErr, srcTokensNew) = subUInt(accountTokens[src], tokens);
if (mathErr != MathError.NO_ERROR) {
return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_NOT_ENOUGH);
}
(mathErr, dstTokensNew) = addUInt(accountTokens[dst], tokens);
if (mathErr != MathError.NO_ERROR) {
return fail(Error.MATH_ERROR, FailureInfo.TRANSFER_TOO_MUCH);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
accountTokens[src] = srcTokensNew;
accountTokens[dst] = dstTokensNew;
/* Eat some of the allowance (if necessary) */
if (startingAllowance != ((2 ** 256) - 1)) {
transferAllowances[src][spender] = allowanceNew;
}
/* We emit a Transfer event */
emit Transfer(src, dst, tokens);
// unused function
// bondtroller.transferVerify(address(this), src, dst, tokens);
return uint256(Error.NO_ERROR);
}
/**
* @dev Transfers `amount` tokens from `msg.sender` to `dst`.
* @param dst The address of the destination account.
* @param amount The number of tokens to transfer.
* @return Whether or not the transfer succeeded.
*/
function transfer(address dst, uint256 amount) external override nonReentrant returns (bool) {
return transferTokens(msg.sender, msg.sender, dst, amount) == uint256(Error.NO_ERROR);
}
/**
* @dev Transfers `amount` tokens from `src` to `dst`.
* @param src The address of the source account.
* @param dst The address of the destination account.
* @param amount The number of tokens to transfer.
* @return Whether or not the transfer succeeded.
*/
function transferFrom(address src, address dst, uint256 amount) external override nonReentrant returns (bool) {
return transferTokens(msg.sender, src, dst, amount) == uint256(Error.NO_ERROR);
}
/**
* @dev Approves `spender` to transfer up to `amount` from `src`.
* This will overwrite the approval amount for `spender`
* and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve).
* @param spender The address of the account which may transfer tokens.
* @param amount The number of tokens that are approved (-1 means infinite).
* @return Whether or not the approval succeeded.
*/
function approve(address spender, uint256 amount) external override returns (bool) {
address src = msg.sender;
transferAllowances[src][spender] = amount;
emit Approval(src, spender, amount);
return true;
}
/**
* @dev Gets the current allowance from `owner` for `spender`.
* @param owner The address of the account which owns the tokens to be spent.
* @param spender The address of the account which may transfer tokens.
* @return The number of tokens allowed to be spent (-1 means infinite).
*/
function allowance(address owner, address spender) external view override returns (uint256) {
return transferAllowances[owner][spender];
}
/**
* @dev Gets the token balance of the `owner`.
* @param owner The address of the account to query.
* @return The number of tokens owned by `owner`.
*/
function balanceOf(address owner) external view override returns (uint256) {
return accountTokens[owner];
}
/**
* @dev Gets the underlying balance of the `owner`.
* This also accrues interest in a transaction.
* @param owner The address of the account to query.
* @return The amount of underlying owned by `owner`.
*/
function balanceOfUnderlying(address owner) external override returns (uint) {
Exp memory exchangeRate = Exp({mantissa: exchangeRateCurrent()});
(MathError mErr, uint256 balance) = mulScalarTruncate(exchangeRate, accountTokens[owner]);
require(mErr == MathError.NO_ERROR, "BToken: Balance could not be calculated");
return balance;
}
/**
* @dev Returns the balance of the underlying asset of this bToken for the given account.
* This is a view function, which means it will not modify the blockchain state.
* @param owner The address of the account to query.
* @return The balance of the underlying asset of this bToken for the given account.
*/
function balanceOfUnderlyingView(address owner) external view returns (uint) {
Exp memory exchangeRate = Exp({mantissa: exchangeRateStored()});
(MathError mErr, uint256 balance) = mulScalarTruncate(exchangeRate, accountTokens[owner]);
require(mErr == MathError.NO_ERROR, "BToken: Balance could not be calculated");
return balance;
}
/**
* @dev Gets a snapshot of the account's balances, and the cached exchange rate.
* This is used by bondtroller to more efficiently perform liquidity checks.
* @param account Address of the account to snapshot
* @return (possible error, token balance, borrow balance, exchange rate mantissa)
*/
function getAccountSnapshot(address account) external view override returns (uint, uint, uint, uint) {
uint256 cTokenBalance = accountTokens[account];
uint256 borrowBalance;
uint256 exchangeRateMantissa;
MathError mErr;
(mErr, borrowBalance) = borrowBalanceStoredInternal(account);
if (mErr != MathError.NO_ERROR) {
return (uint256(Error.MATH_ERROR), 0, 0, 0);
}
(mErr, exchangeRateMantissa) = exchangeRateStoredInternal();
if (mErr != MathError.NO_ERROR) {
return (uint256(Error.MATH_ERROR), 0, 0, 0);
}
return (uint256(Error.NO_ERROR), cTokenBalance, borrowBalance, exchangeRateMantissa);
}
/**
* @dev Function to simply retrieve block number.
* This exists mainly for inheriting test contracts to stub this result.
*/
function getBlockNumber() internal view returns (uint) {
return block.number;
}
/**
* @dev Returns the current per-block borrow interest rate for this cToken.
* @return The borrow interest rate per block, scaled by 1e18.
*/
function borrowRatePerBlock() external view override returns (uint) {
return interestRateModel.getBorrowRate(getCashPrior(), totalBorrows, totalReserves, address(this));
}
/**
* @dev Returns the current per-block supply interest rate for this cToken.
* @return The supply interest rate per block, scaled by 1e18.
*/
function supplyRatePerBlock() external view override returns (uint) {
return interestRateModel.getSupplyRate(getCashPrior(), totalBorrows, totalReserves, reserveFactorMantissa, address(this));
}
/**
* @dev Returns the current total borrows plus accrued interest.
* @return The total borrows with interest.
*/
function totalBorrowsCurrent() external override nonReentrant returns (uint) {
require(accrueInterest() == uint256(Error.NO_ERROR), "BToken: Accrue interest failed");
return totalBorrows;
}
/**
* @dev Accrues interest to updated borrowIndex and then calculate account's borrow balance using the updated borrowIndex.
* @param account The address whose balance should be calculated after updating borrowIndex.
* @return The calculated balance.
*/
function borrowBalanceCurrent(address account) external override nonReentrant returns (uint) {
require(accrueInterest() == uint256(Error.NO_ERROR), "BToken: Accrue interest failed");
return borrowBalanceStored(account);
}
/**
* @dev Returns the borrow balance of account based on stored data.
* @param account The address whose balance should be calculated.
* @return The calculated balance.
*/
function borrowBalanceStored(address account) public view override returns (uint) {
(MathError err, uint256 result) = borrowBalanceStoredInternal(account);
require(err == MathError.NO_ERROR, "BToken: BorrowBalanceStoredInternal failed");
return result;
}
/**
* @dev Returns the borrow balance of account based on stored data.
* @param account The address whose balance should be calculated.
* @return (error code, the calculated balance or 0 if error code is non-zero).
*/
function borrowBalanceStoredInternal(address account) internal view returns (MathError, uint) {
/* Note: we do not assert that the market is up to date */
MathError mathErr;
uint256 principalTimesIndex;
uint256 result;
/* Get borrowBalance and borrowIndex */
BorrowSnapshot storage borrowSnapshot = accountBorrows[account];
/* If borrowBalance = 0 then borrowIndex is likely also 0.
* Rather than failing the calculation with a division by 0, we immediately return 0 in this case.
*/
if (borrowSnapshot.principal == 0) {
return (MathError.NO_ERROR, 0);
}
/* Calculate new borrow balance using the interest index:
* recentBorrowBalance = borrower.borrowBalance * market.borrowIndex / borrower.borrowIndex
*/
(mathErr, principalTimesIndex) = mulUInt(borrowSnapshot.principal, borrowIndex);
if (mathErr != MathError.NO_ERROR) {
return (mathErr, 0);
}
(mathErr, result) = divUInt(principalTimesIndex, borrowSnapshot.interestIndex);
if (mathErr != MathError.NO_ERROR) {
return (mathErr, 0);
}
return (MathError.NO_ERROR, result);
}
/**
* @dev Accrues interest then return the up-to-date exchange rate.
* @return Calculated exchange rate scaled by 1e18.
*/
function exchangeRateCurrent() public override nonReentrant returns (uint) {
require(accrueInterest() == uint256(Error.NO_ERROR), "BToken: Accrue interest failed");
return exchangeRateStored();
}
/**
* @dev Calculates the exchange rate from the underlying to the CToken.
* @dev This function does not accrue interest before calculating the exchange rate.
* @return Calculated exchange rate scaled by 1e18.
*/
function exchangeRateStored() public view override returns (uint) {
(MathError err, uint256 result) = exchangeRateStoredInternal();
require(err == MathError.NO_ERROR, "BToken: ExchangeRateStoredInternal failed");
return result;
}
/**
* @dev Calculates the exchange rate from the underlying to the CToken.
* @dev This function does not accrue interest before calculating the exchange rate.
* @return (error code, calculated exchange rate scaled by 1e18).
*/
function exchangeRateStoredInternal() internal view returns (MathError, uint) {
uint256 _totalSupply = totalSupply;
if (_totalSupply == 0) {
/*
* If there are no tokens minted:
* exchangeRate = initialExchangeRate
*/
return (MathError.NO_ERROR, initialExchangeRateMantissa);
} else {
/*
* Otherwise:
* exchangeRate = (totalCash + totalBorrows - totalReserves) / totalSupply
*/
uint256 totalCash = getCashPrior();
uint256 cashPlusBorrowsMinusReserves;
Exp memory exchangeRate;
MathError mathErr;
(mathErr, cashPlusBorrowsMinusReserves) = addThenSubUInt(totalCash, totalBorrows, totalReserves);
if (mathErr != MathError.NO_ERROR) {
return (mathErr, 0);
}
(mathErr, exchangeRate) = getExp(cashPlusBorrowsMinusReserves, _totalSupply);
if (mathErr != MathError.NO_ERROR) {
return (mathErr, 0);
}
return (MathError.NO_ERROR, exchangeRate.mantissa);
}
}
/**
* @dev Gets cash balance of this cToken in the underlying asset.
* @return The quantity of underlying asset owned by this contract.
*/
function getCash() external view override returns (uint) {
return getCashPrior();
}
/**
* @dev Applies accrued interest to total borrows and reserves.
* This calculates interest accrued from the last checkpointed block
* up to the current block and writes new checkpoint to storage.
*/
function accrueInterest() public override returns (uint) {
/* Remember the initial block number */
uint256 currentBlockNumber = getBlockNumber();
uint256 accrualBlockNumberPrior = accrualBlockNumber;
/* Short-circuit accumulating 0 interest */
if (accrualBlockNumberPrior == currentBlockNumber) {
return uint256(Error.NO_ERROR);
}
/* Read the previous values out of storage */
uint256 cashPrior = getCashPrior();
uint256 borrowsPrior = totalBorrows;
uint256 reservesPrior = totalReserves;
uint256 borrowIndexPrior = borrowIndex;
/* Calculate the current borrow interest rate */
interestRateModel.storeBorrowRate(cashPrior, borrowsPrior, reservesPrior);
uint256 borrowRateMantissa = interestRateModel.getBorrowRate(cashPrior, borrowsPrior, reservesPrior, address(this));
require(borrowRateMantissa <= borrowRateMaxMantissa, "borrow rate is absurdly high");
/* Calculate the number of blocks elapsed since the last accrual */
(MathError mathErr, uint256 blockDelta) = subUInt(currentBlockNumber, accrualBlockNumberPrior);
require(mathErr == MathError.NO_ERROR, "BToken: Could not calculate block delta");
/*
* Calculate the interest accumulated into borrows and reserves and the new index:
* simpleInterestFactor = borrowRate * blockDelta
* interestAccumulated = simpleInterestFactor * totalBorrows
* totalBorrowsNew = interestAccumulated + totalBorrows
* totalReservesNew = interestAccumulated * reserveFactor + totalReserves
* borrowIndexNew = simpleInterestFactor * borrowIndex + borrowIndex
*/
Exp memory simpleInterestFactor;
uint256 interestAccumulated;
uint256 totalBorrowsNew;
uint256 totalReservesNew;
uint256 borrowIndexNew;
(mathErr, simpleInterestFactor) = mulScalar(Exp({mantissa: borrowRateMantissa}), blockDelta);
if (mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_SIMPLE_INTEREST_FACTOR_CALCULATION_FAILED, uint256(mathErr));
}
(mathErr, interestAccumulated) = mulScalarTruncate(simpleInterestFactor, borrowsPrior);
if (mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_ACCUMULATED_INTEREST_CALCULATION_FAILED, uint256(mathErr));
}
(mathErr, totalBorrowsNew) = addUInt(interestAccumulated, borrowsPrior);
if (mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_TOTAL_BORROWS_CALCULATION_FAILED, uint256(mathErr));
}
(mathErr, totalReservesNew) = mulScalarTruncateAddUInt(Exp({mantissa: reserveFactorMantissa}), interestAccumulated, reservesPrior);
if (mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_TOTAL_RESERVES_CALCULATION_FAILED, uint256(mathErr));
}
(mathErr, borrowIndexNew) = mulScalarTruncateAddUInt(simpleInterestFactor, borrowIndexPrior, borrowIndexPrior);
if (mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.ACCRUE_INTEREST_NEW_BORROW_INDEX_CALCULATION_FAILED, uint256(mathErr));
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We write the previously calculated values into storage */
accrualBlockNumber = currentBlockNumber;
borrowIndex = borrowIndexNew;
totalBorrows = totalBorrowsNew;
totalReserves = totalReservesNew;
/* We emit an AccrueInterest event */
emit AccrueInterest(cashPrior, interestAccumulated, borrowIndexNew, totalBorrowsNew);
return uint256(Error.NO_ERROR);
}
/**
* @dev Sender supplies assets into the market and receives cTokens in exchange.
* Accrues interest whether or not the operation succeeds, unless reverted.
* @param mintAmount The amount of the underlying asset to supply.
* @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount.
*/
function mintInternal(uint256 mintAmount) internal nonReentrant returns (uint, uint) {
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
return (fail(Error(error), FailureInfo.MINT_ACCRUE_INTEREST_FAILED), 0);
}
// mintFresh emits the actual Mint event if successful and logs on errors, so we don't need to
return mintFresh(msg.sender, mintAmount);
}
struct MintLocalVars {
Error err;
MathError mathErr;
uint256 exchangeRateMantissa;
uint256 mintTokens;
uint256 totalSupplyNew;
uint256 accountTokensNew;
uint256 actualMintAmount;
}
/**
* @dev User supplies assets into the market and receives cTokens in exchange.
* Assumes interest has already been accrued up to the current block.
* @param minter The address of the account which is supplying the assets.
* @param mintAmount The amount of the underlying asset to supply.
* @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual mint amount.
*/
function mintFresh(address minter, uint256 mintAmount) internal returns (uint, uint) {
/* Fail if mint not allowed */
uint256 allowed = bondtroller.mintAllowed(address(this), minter, mintAmount);
if (allowed != 0) {
return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.MINT_COMPTROLLER_REJECTION, allowed), 0);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
return (fail(Error.MARKET_NOT_FRESH, FailureInfo.MINT_FRESHNESS_CHECK), 0);
}
MintLocalVars memory vars;
(vars.mathErr, vars.exchangeRateMantissa) = exchangeRateStoredInternal();
if (vars.mathErr != MathError.NO_ERROR) {
return (failOpaque(Error.MATH_ERROR, FailureInfo.MINT_EXCHANGE_RATE_READ_FAILED, uint256(vars.mathErr)), 0);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We call `doTransferIn` for the minter and the mintAmount.
* Note: The cToken must handle variations between ERC-20 and ETH underlying.
* `doTransferIn` reverts if anything goes wrong, since we can't be sure if
* side-effects occurred. The function returns the amount actually transferred,
* in case of a fee. On success, the cToken holds an additional `actualMintAmount`
* of cash.
*/
vars.actualMintAmount = doTransferIn(minter, mintAmount);
/*
* We get the current exchange rate and calculate the number of cTokens to be minted:
* mintTokens = actualMintAmount / exchangeRate
*/
(vars.mathErr, vars.mintTokens) = divScalarByExpTruncate(vars.actualMintAmount, Exp({mantissa: vars.exchangeRateMantissa}));
require(vars.mathErr == MathError.NO_ERROR, "MINT_EXCHANGE_CALCULATION_FAILED");
/*
* We calculate the new total supply of cTokens and minter token balance, checking for overflow:
* totalSupplyNew = totalSupply + mintTokens
* accountTokensNew = accountTokens[minter] + mintTokens
*/
(vars.mathErr, vars.totalSupplyNew) = addUInt(totalSupply, vars.mintTokens);
require(vars.mathErr == MathError.NO_ERROR, "MINT_NEW_TOTAL_SUPPLY_CALCULATION_FAILED");
(vars.mathErr, vars.accountTokensNew) = addUInt(accountTokens[minter], vars.mintTokens);
require(vars.mathErr == MathError.NO_ERROR, "MINT_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED");
/* We write previously calculated values into storage */
totalSupply = vars.totalSupplyNew;
accountTokens[minter] = vars.accountTokensNew;
/* We emit a Mint event, and a Transfer event */
emit Mint(minter, vars.actualMintAmount, vars.mintTokens);
emit Transfer(address(this), minter, vars.mintTokens);
/* We call the defense hook */
// unused function
// bondtroller.mintVerify(address(this), minter, vars.actualMintAmount, vars.mintTokens);
return (uint256(Error.NO_ERROR), vars.actualMintAmount);
}
/**
* @dev Sender redeems cTokens in exchange for the underlying asset.
* Accrues interest whether or not the operation succeeds, unless reverted.
* @param redeemTokens The number of cTokens to redeem into underlying.
* @return uint256 0=success, otherwise a failure (see ErrorReporter.sol for details).
*/
function redeemInternal(uint256 redeemTokens) internal nonReentrant returns (uint) {
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed
return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED);
}
// redeemFresh emits redeem-specific logs on errors, so we don't need to
return redeemFresh(payable(msg.sender), redeemTokens, 0);
}
/**
* @dev Sender redeems cTokens in exchange for a specified amount of underlying asset.
* Accrues interest whether or not the operation succeeds, unless reverted.
* @param redeemAmount The amount of underlying to receive from redeeming cTokens.
* @return uint256 0=success, otherwise a failure (see ErrorReporter.sol for details).
*/
function redeemUnderlyingInternal(uint256 redeemAmount) internal nonReentrant returns (uint) {
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but we still want to log the fact that an attempted redeem failed
return fail(Error(error), FailureInfo.REDEEM_ACCRUE_INTEREST_FAILED);
}
// redeemFresh emits redeem-specific logs on errors, so we don't need to
return redeemFresh(payable(msg.sender), 0, redeemAmount);
}
struct RedeemLocalVars {
Error err;
MathError mathErr;
uint256 exchangeRateMantissa;
uint256 redeemTokens;
uint256 redeemAmount;
uint256 totalSupplyNew;
uint256 accountTokensNew;
}
/**
* @dev User redeems cTokens in exchange for the underlying asset.
* Assumes interest has already been accrued up to the current block.
* @param redeemer The address of the account which is redeeming the tokens.
* @param redeemTokensIn The number of cTokens to redeem into underlying (only one of redeemTokensIn or redeemAmountIn may be non-zero).
* @param redeemAmountIn The number of underlying tokens to receive from redeeming cTokens (only one of redeemTokensIn or redeemAmountIn may be non-zero).
* @return uint256 0=success, otherwise a failure (see ErrorReporter.sol for details).
*/
function redeemFresh(address payable redeemer, uint256 redeemTokensIn, uint256 redeemAmountIn) internal returns (uint) {
require(redeemTokensIn == 0 || redeemAmountIn == 0, "BToken: One of redeemTokensIn or redeemAmountIn must be zero");
RedeemLocalVars memory vars;
/* exchangeRate = invoke Exchange Rate Stored() */
(vars.mathErr, vars.exchangeRateMantissa) = exchangeRateStoredInternal();
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_EXCHANGE_RATE_READ_FAILED, uint256(vars.mathErr));
}
/* If redeemTokensIn > 0: */
if (redeemTokensIn > 0) {
/*
* We calculate the exchange rate and the amount of underlying to be redeemed:
* redeemTokens = redeemTokensIn
* redeemAmount = redeemTokensIn x exchangeRateCurrent
*/
vars.redeemTokens = redeemTokensIn;
(vars.mathErr, vars.redeemAmount) = mulScalarTruncate(Exp({mantissa: vars.exchangeRateMantissa}), redeemTokensIn);
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_EXCHANGE_TOKENS_CALCULATION_FAILED, uint256(vars.mathErr));
}
} else {
/*
* We get the current exchange rate and calculate the amount to be redeemed:
* redeemTokens = redeemAmountIn / exchangeRate
* redeemAmount = redeemAmountIn
*/
(vars.mathErr, vars.redeemTokens) = divScalarByExpTruncate(redeemAmountIn, Exp({mantissa: vars.exchangeRateMantissa}));
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_EXCHANGE_AMOUNT_CALCULATION_FAILED, uint256(vars.mathErr));
}
vars.redeemAmount = redeemAmountIn;
}
/* Fail if redeem not allowed */
uint256 allowed = bondtroller.redeemAllowed(address(this), redeemer, vars.redeemTokens);
if (allowed != 0) {
return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REDEEM_COMPTROLLER_REJECTION, allowed);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDEEM_FRESHNESS_CHECK);
}
/*
* We calculate the new total supply and redeemer balance, checking for underflow:
* totalSupplyNew = totalSupply - redeemTokens
* accountTokensNew = accountTokens[redeemer] - redeemTokens
*/
(vars.mathErr, vars.totalSupplyNew) = subUInt(totalSupply, vars.redeemTokens);
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_NEW_TOTAL_SUPPLY_CALCULATION_FAILED, uint256(vars.mathErr));
}
(vars.mathErr, vars.accountTokensNew) = subUInt(accountTokens[redeemer], vars.redeemTokens);
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.REDEEM_NEW_ACCOUNT_BALANCE_CALCULATION_FAILED, uint256(vars.mathErr));
}
/* Fail gracefully if protocol has insufficient cash */
if (getCashPrior() < vars.redeemAmount) {
return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDEEM_TRANSFER_OUT_NOT_POSSIBLE);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We write previously calculated values into storage */
totalSupply = vars.totalSupplyNew;
accountTokens[redeemer] = vars.accountTokensNew;
/*
* We invoke doTransferOut for the redeemer and the redeemAmount.
* Note: The cToken must handle variations between ERC-20 and ETH underlying.
* On success, the cToken has redeemAmount less of cash.
* doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
*/
doTransferOut(redeemer, vars.redeemAmount);
/* We emit a Transfer event, and a Redeem event */
emit Transfer(redeemer, address(this), vars.redeemTokens);
emit Redeem(redeemer, vars.redeemAmount, vars.redeemTokens);
/* We call the defense hook */
bondtroller.redeemVerify(address(this), redeemer, vars.redeemAmount, vars.redeemTokens);
return uint256(Error.NO_ERROR);
}
/**
* @dev Sender borrows assets from the protocol to their own address.
* @param borrowAmount The amount of the underlying asset to borrow.
* @return uint256 0=success, otherwise a failure (see ErrorReporter.sol for details).
*/
function borrowInternal(uint256 borrowAmount) internal nonReentrant returns (uint) {
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
return fail(Error(error), FailureInfo.BORROW_ACCRUE_INTEREST_FAILED);
}
// borrowFresh emits borrow-specific logs on errors, so we don't need to
return borrowFresh(payable(msg.sender), borrowAmount);
}
struct BorrowLocalVars {
MathError mathErr;
uint256 accountBorrows;
uint256 accountBorrowsNew;
uint256 totalBorrowsNew;
}
/**
* @dev Users borrow assets from the protocol to their own address.
* @param borrowAmount The amount of the underlying asset to borrow.
* @return uint256 0=success, otherwise a failure (see ErrorReporter.sol for details).
*/
function borrowFresh(address payable borrower, uint256 borrowAmount) internal returns (uint) {
/* Fail if borrow not allowed */
uint256 allowed = bondtroller.borrowAllowed(address(this), borrower, borrowAmount);
if (allowed != 0) {
return failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.BORROW_COMPTROLLER_REJECTION, allowed);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
return fail(Error.MARKET_NOT_FRESH, FailureInfo.BORROW_FRESHNESS_CHECK);
}
/* Fail gracefully if protocol has insufficient underlying cash */
if (getCashPrior() < borrowAmount) {
revert("BToken: Insufficient cash");
//return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.BORROW_CASH_NOT_AVAILABLE);
}
BorrowLocalVars memory vars;
/*
* We calculate the new borrower and total borrow balances, failing on overflow:
* accountBorrowsNew = accountBorrows + borrowAmount
* totalBorrowsNew = totalBorrows + borrowAmount
*/
(vars.mathErr, vars.accountBorrows) = borrowBalanceStoredInternal(borrower);
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED, uint256(vars.mathErr));
}
(vars.mathErr, vars.accountBorrowsNew) = addUInt(vars.accountBorrows, borrowAmount);
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED, uint256(vars.mathErr));
}
(vars.mathErr, vars.totalBorrowsNew) = addUInt(totalBorrows, borrowAmount);
if (vars.mathErr != MathError.NO_ERROR) {
return failOpaque(Error.MATH_ERROR, FailureInfo.BORROW_NEW_TOTAL_BALANCE_CALCULATION_FAILED, uint256(vars.mathErr));
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/* We write the previously calculated values into storage */
accountBorrows[borrower].principal = vars.accountBorrowsNew;
accountBorrows[borrower].interestIndex = borrowIndex;
totalBorrows = vars.totalBorrowsNew;
/*
* We invoke doTransferOut for the borrower and the borrowAmount.
* Note: The cToken must handle variations between ERC-20 and ETH underlying.
* On success, the cToken borrowAmount less of cash.
* doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
*/
doTransferOut(borrower, borrowAmount);
/* We emit a Borrow event */
emit Borrow(borrower, borrowAmount, vars.accountBorrowsNew, vars.totalBorrowsNew);
/* We call the defense hook */
// unused function
// bondtroller.borrowVerify(address(this), borrower, borrowAmount);
return uint256(Error.NO_ERROR);
}
/**
* @dev Sender repays their own borrow.
* @param repayAmount The amount to repay.
* @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
*/
function repayBorrowInternal(uint256 repayAmount) internal nonReentrant returns (uint, uint) {
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
return (fail(Error(error), FailureInfo.REPAY_BORROW_ACCRUE_INTEREST_FAILED), 0);
}
// repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to
return repayBorrowFresh(msg.sender, msg.sender, repayAmount);
}
/**
* @dev Sender repays a borrow belonging to borrower.
* @param borrower the account with the debt being payed off.
* @param repayAmount The amount to repay.
* @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
*/
function repayBorrowBehalfInternal(address borrower, uint256 repayAmount) internal nonReentrant returns (uint, uint) {
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but we still want to log the fact that an attempted borrow failed
return (fail(Error(error), FailureInfo.REPAY_BEHALF_ACCRUE_INTEREST_FAILED), 0);
}
// repayBorrowFresh emits repay-borrow-specific logs on errors, so we don't need to
return repayBorrowFresh(msg.sender, borrower, repayAmount);
}
struct RepayBorrowLocalVars {
Error err;
MathError mathErr;
uint256 repayAmount;
uint256 borrowerIndex;
uint256 accountBorrows;
uint256 accountBorrowsNew;
uint256 totalBorrowsNew;
uint256 actualRepayAmount;
}
/**
* @dev Borrows are repaid by another user (possibly the borrower).
* @param payer the account paying off the borrow.
* @param borrower the account with the debt being payed off.
* @param repayAmount the amount of undelrying tokens being returned.
* @return (uint, uint) An error code (0=success, otherwise a failure, see ErrorReporter.sol), and the actual repayment amount.
*/
function repayBorrowFresh(address payer, address borrower, uint256 repayAmount) internal returns (uint, uint) {
/* Fail if repayBorrow not allowed */
uint256 allowed = bondtroller.repayBorrowAllowed(address(this), payer, borrower, repayAmount);
if (allowed != 0) {
return (failOpaque(Error.COMPTROLLER_REJECTION, FailureInfo.REPAY_BORROW_COMPTROLLER_REJECTION, allowed), 0);
}
/* Verify market's block number equals current block number */
if (accrualBlockNumber != getBlockNumber()) {
return (fail(Error.MARKET_NOT_FRESH, FailureInfo.REPAY_BORROW_FRESHNESS_CHECK), 0);
}
RepayBorrowLocalVars memory vars;
/* We remember the original borrowerIndex for verification purposes */
vars.borrowerIndex = accountBorrows[borrower].interestIndex;
/* We fetch the amount the borrower owes, with accumulated interest */
(vars.mathErr, vars.accountBorrows) = borrowBalanceStoredInternal(borrower);
if (vars.mathErr != MathError.NO_ERROR) {
return (failOpaque(Error.MATH_ERROR, FailureInfo.REPAY_BORROW_ACCUMULATED_BALANCE_CALCULATION_FAILED, uint256(vars.mathErr)), 0);
}
/* If repayAmount == -1, repayAmount = accountBorrows */
if (repayAmount == ((2 ** 256) - 1)) {
vars.repayAmount = vars.accountBorrows;
} else {
vars.repayAmount = repayAmount;
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We call doTransferIn for the payer and the repayAmount
* Note: The cToken must handle variations between ERC-20 and ETH underlying.
* On success, the cToken holds an additional repayAmount of cash.
* doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.
* it returns the amount actually transferred, in case of a fee.
*/
vars.actualRepayAmount = doTransferIn(payer, vars.repayAmount);
/*
* We calculate the new borrower and total borrow balances, failing on underflow:
* accountBorrowsNew = accountBorrows - actualRepayAmount
* totalBorrowsNew = totalBorrows - actualRepayAmount
*/
(vars.mathErr, vars.accountBorrowsNew) = subUInt(vars.accountBorrows, vars.actualRepayAmount);
require(vars.mathErr == MathError.NO_ERROR, "REPAY_BORROW_NEW_ACCOUNT_BORROW_BALANCE_CALCULATION_FAILED");
(vars.mathErr, vars.totalBorrowsNew) = subUInt(totalBorrows, vars.actualRepayAmount);
if (vars.mathErr == MathError.INTEGER_UNDERFLOW) {
vars.totalBorrowsNew = 0; // Repaid all borrows to platform
}
/* We write the previously calculated values into storage */
accountBorrows[borrower].principal = vars.accountBorrowsNew;
accountBorrows[borrower].interestIndex = borrowIndex;
totalBorrows = vars.totalBorrowsNew;
/* We emit a RepayBorrow event */
emit RepayBorrow(payer, borrower, vars.actualRepayAmount, vars.accountBorrowsNew, vars.totalBorrowsNew);
/* We call the defense hook */
// unused function
// bondtroller.repayBorrowVerify(address(this), payer, borrower, vars.actualRepayAmount, vars.borrowerIndex);
return(uint256(Error.NO_ERROR), vars.actualRepayAmount);
}
/*** Admin Functions ***/
/**
* @dev Sets a new bondtroller for the market.
* Admin function to set a new bondtroller.
* @return uint256 0=success, otherwise a failure (see ErrorReporter.sol for details).
*/
function _setBondtroller(Bondtroller newBondtroller) public override returns (uint) {
// Check caller has moderator role
if (!hasRoleModerator(msg.sender)) {
return fail(Error.UNAUTHORIZED, FailureInfo.SET_COMPTROLLER_OWNER_CHECK);
}
Bondtroller oldBondtroller = bondtroller;
// Ensure invoke bondtroller.isBondtroller() returns true
require(newBondtroller.isBondtroller(), "BToken: Marker method returned false");
// Set market's bondtroller to newBondtroller
bondtroller = newBondtroller;
// Emit NewBondtroller(oldBondtroller, newBondtroller)
emit NewBondtroller(oldBondtroller, newBondtroller);
return uint256(Error.NO_ERROR);
}
/**
* @dev Accrues interest and sets a new reserve factor for the protocol using _setReserveFactorFresh.
* Admin function to accrue interest and set a new reserve factor.
* @return uint256 0=success, otherwise a failure (see ErrorReporter.sol for details).
*/
function _setReserveFactor(uint256 newReserveFactorMantissa) external override nonReentrant returns (uint) {
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reserve factor change failed.
return fail(Error(error), FailureInfo.SET_RESERVE_FACTOR_ACCRUE_INTEREST_FAILED);
}
// _setReserveFactorFresh emits reserve-factor-specific logs on errors, so we don't need to.
return _setReserveFactorFresh(newReserveFactorMantissa);
}
/**
* @dev Sets a new reserve factor for the protocol (*requires fresh interest accrual).
* Admin function to set a new reserve factor.
* @return uint256 0=success, otherwise a failure (see ErrorReporter.sol for details).
*/
function _setReserveFactorFresh(uint256 newReserveFactorMantissa) internal returns (uint) {
// Check caller has moderator role
if (!hasRoleModerator(msg.sender)) {
return fail(Error.UNAUTHORIZED, FailureInfo.SET_RESERVE_FACTOR_ADMIN_CHECK);
}
// Verify market's block number equals current block number
if (accrualBlockNumber != getBlockNumber()) {
return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_RESERVE_FACTOR_FRESH_CHECK);
}
// Check newReserveFactor ≤ maxReserveFactor
if (newReserveFactorMantissa > reserveFactorMaxMantissa) {
return fail(Error.BAD_INPUT, FailureInfo.SET_RESERVE_FACTOR_BOUNDS_CHECK);
}
uint256 oldReserveFactorMantissa = reserveFactorMantissa;
reserveFactorMantissa = newReserveFactorMantissa;
emit NewReserveFactor(oldReserveFactorMantissa, newReserveFactorMantissa);
return uint256(Error.NO_ERROR);
}
/**
* @dev Accrues interest and reduces reserves by transferring from msg.sender.
* @param addAmount Amount of addition to reserves.
* @return uint256 0=success, otherwise a failure (see ErrorReporter.sol for details).
*/
function _addReservesInternal(uint256 addAmount) internal nonReentrant returns (uint) {
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reduce reserves failed.
return fail(Error(error), FailureInfo.ADD_RESERVES_ACCRUE_INTEREST_FAILED);
}
// _addReservesFresh emits reserve-addition-specific logs on errors, so we don't need to.
(error, ) = _addReservesFresh(addAmount);
return error;
}
/**
* @dev Adds reserves by transferring from caller.
* Requires fresh interest accrual.
* @param addAmount Amount of addition to reserves.
* @return (uint, uint) An error code (0=success, otherwise a failure (see ErrorReporter.sol for details)) and the actual amount added, net token fees.
*/
function _addReservesFresh(uint256 addAmount) internal returns (uint, uint) {
// totalReserves + actualAddAmount
uint256 totalReservesNew;
uint256 actualAddAmount;
// We fail gracefully unless market's block number equals current block number
if (accrualBlockNumber != getBlockNumber()) {
return (fail(Error.MARKET_NOT_FRESH, FailureInfo.ADD_RESERVES_FRESH_CHECK), actualAddAmount);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
/*
* We call doTransferIn for the caller and the addAmount
* Note: The cToken must handle variations between ERC-20 and ETH underlying.
* On success, the cToken holds an additional addAmount of cash.
* doTransferIn reverts if anything goes wrong, since we can't be sure if side effects occurred.
* it returns the amount actually transferred, in case of a fee.
*/
actualAddAmount = doTransferIn(msg.sender, addAmount);
totalReservesNew = totalReserves + actualAddAmount;
/* Revert on overflow */
require(totalReservesNew >= totalReserves, "BToken: Add reserves unexpected overflow");
// Store reserves[n+1] = reserves[n] + actualAddAmount
totalReserves = totalReservesNew;
/* Emit NewReserves(moderator, actualAddAmount, reserves[n+1]) */
emit ReservesAdded(msg.sender, actualAddAmount, totalReservesNew);
/* Return (NO_ERROR, actualAddAmount) */
return(uint256(Error.NO_ERROR), actualAddAmount);
}
/**
* @dev Accrues interest and reduces reserves by transferring to moderator.
* @param reduceAmount Amount of reduction to reserves.
* @return uint256 0=success, otherwise a failure (see ErrorReporter.sol for details).
*/
function _reduceReserves(uint256 reduceAmount) external override nonReentrant returns (uint) {
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted reduce reserves failed.
return fail(Error(error), FailureInfo.REDUCE_RESERVES_ACCRUE_INTEREST_FAILED);
}
// _reduceReservesFresh emits reserve-reduction-specific logs on errors, so we don't need to.
return _reduceReservesFresh(reduceAmount);
}
/**
* @dev Reduces reserves by transferring to moderator.
* Requires fresh interest accrual.
* @param reduceAmount Amount of reduction to reserves.
* @return uint256 0=success, otherwise a failure (see ErrorReporter.sol for details).
*/
function _reduceReservesFresh(uint256 reduceAmount) internal returns (uint) {
// totalReserves - reduceAmount
uint256 totalReservesNew;
// Check caller has moderator role
if (!hasRoleModerator(msg.sender)) {
return fail(Error.UNAUTHORIZED, FailureInfo.REDUCE_RESERVES_ADMIN_CHECK);
}
// We fail gracefully unless market's block number equals current block number
if (accrualBlockNumber != getBlockNumber()) {
return fail(Error.MARKET_NOT_FRESH, FailureInfo.REDUCE_RESERVES_FRESH_CHECK);
}
// Fail gracefully if protocol has insufficient underlying cash
if (getCashPrior() < reduceAmount) {
return fail(Error.TOKEN_INSUFFICIENT_CASH, FailureInfo.REDUCE_RESERVES_CASH_NOT_AVAILABLE);
}
// Check reduceAmount ≤ reserves[n] (totalReserves)
if (reduceAmount > totalReserves) {
return fail(Error.BAD_INPUT, FailureInfo.REDUCE_RESERVES_VALIDATION);
}
/////////////////////////
// EFFECTS & INTERACTIONS
// (No safe failures beyond this point)
totalReservesNew = totalReserves - reduceAmount;
// We checked reduceAmount <= totalReserves above, so this should never revert.
require(totalReservesNew <= totalReserves, "BToken: Reduce reserves unexpected underflow");
// Store reserves[n+1] = reserves[n] - reduceAmount
totalReserves = totalReservesNew;
// doTransferOut reverts if anything goes wrong, since we can't be sure if side effects occurred.
doTransferOut(payable(msg.sender), reduceAmount);
emit ReservesReduced(msg.sender, reduceAmount, totalReservesNew);
return uint256(Error.NO_ERROR);
}
/**
* @dev accrues interest and updates the interest rate model using _setInterestRateModelFresh.
* Admin function to accrue interest and update the interest rate model.
* @param newInterestRateModel the new interest rate model to use.
* @return uint256 0=success, otherwise a failure (see ErrorReporter.sol for details).
*/
function _setInterestRateModel(InterestRateModel newInterestRateModel) public override returns (uint) {
uint256 error = accrueInterest();
if (error != uint256(Error.NO_ERROR)) {
// accrueInterest emits logs on errors, but on top of that we want to log the fact that an attempted change of interest rate model failed
return fail(Error(error), FailureInfo.SET_INTEREST_RATE_MODEL_ACCRUE_INTEREST_FAILED);
}
// _setInterestRateModelFresh emits interest-rate-model-update-specific logs on errors, so we don't need to.
return _setInterestRateModelFresh(newInterestRateModel);
}
/**
* @dev updates the interest rate model (*requires fresh interest accrual).
* Admin function to update the interest rate model.
* @param newInterestRateModel the new interest rate model to use.
* @return uint256 0=success, otherwise a failure (see ErrorReporter.sol for details).
*/
function _setInterestRateModelFresh(InterestRateModel newInterestRateModel) internal returns (uint) {
// Used to store old model for use in the event that is emitted on success
InterestRateModel oldInterestRateModel;
// Check caller has moderator role
if (!hasRoleModerator(msg.sender)) {
return fail(Error.UNAUTHORIZED, FailureInfo.SET_INTEREST_RATE_MODEL_OWNER_CHECK);
}
// We fail gracefully unless market's block number equals current block number
if (accrualBlockNumber != getBlockNumber()) {
return fail(Error.MARKET_NOT_FRESH, FailureInfo.SET_INTEREST_RATE_MODEL_FRESH_CHECK);
}
// Track the market's current interest rate model
oldInterestRateModel = interestRateModel;
// Ensure invoke newInterestRateModel.isInterestRateModel() returns true
require(newInterestRateModel.isInterestRateModel(), "BToken: Marker method returned false");
// Set the interest rate model to newInterestRateModel
interestRateModel = newInterestRateModel;
// Emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel)
emit NewMarketInterestRateModel(oldInterestRateModel, newInterestRateModel);
return uint256(Error.NO_ERROR);
}
/*** Safe Token ***/
/**
* @dev Gets balance of this contract in terms of the underlying.
* This excludes the value of the current message, if any.
* @return The quantity of underlying owned by this contract.
*/
function getCashPrior() internal view virtual returns (uint);
/**
* @dev Performs a transfer in, reverting upon failure. Returns the amount actually transferred to the protocol, in case of a fee.
* This may revert due to insufficient balance or insufficient allowance.
*/
function doTransferIn(address from, uint256 amount) internal virtual returns (uint) {}
/**
* @dev Performs a transfer out, ideally returning an explanatory error code upon failure tather than reverting.
* If caller has not called checked protocol's balance, may revert due to insufficient cash held in the contract.
* If caller has checked protocol's balance, and verified it is >= amount, this should not revert in normal conditions.
*/
function doTransferOut(address payable to, uint256 amount) internal virtual {}
/**
* @dev Returns whether the specified account has the moderator role.
* @param account The address to check for moderator role.
* @return A boolean indicating whether the account has the moderator role.
*/
function hasRoleModerator(address account) public view virtual returns (bool) {}
/*** Reentrancy Guard ***/
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
*/
modifier nonReentrant() {
require(_notEntered, "BToken: re-entered");
_notEntered = false;
_;
_notEntered = true; // get a gas-refund post-Istanbul
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
import "../bondtroller/Bondtroller.sol";
import "../interestRateModel/InterestRateModel.sol";
import "../interfaces/EIP20NonStandardInterface.sol";
contract BTokenStorage {
/**
* @dev Guard variable for re-entrancy checks
*/
bool internal _notEntered;
/**
* @notice EIP-20 token name for this token
*/
string public name;
/**
* @notice EIP-20 token symbol for this token
*/
string public symbol;
/**
* @notice EIP-20 token decimals for this token
*/
uint8 public decimals;
/**
* @notice Maximum borrow rate that can ever be applied (.0005% / block)
*/
uint256 internal constant borrowRateMaxMantissa = 0.0005e16;
/**
* @notice Maximum fraction of interest that can be set aside for reserves
*/
uint256 internal constant reserveFactorMaxMantissa = 1e18;
/**
* @notice Administrator for this contract
*/
address payable public admin;
/**
* @notice Pending administrator for this contract
*/
address payable public pendingAdmin;
/**
* @notice Contract which oversees inter-cToken operations
*/
Bondtroller public bondtroller;
/**
* @notice Model which tells what the current interest rate should be
*/
InterestRateModel public interestRateModel;
/**
* @notice Initial exchange rate used when minting the first CTokens (used when totalSupply = 0)
*/
uint256 internal initialExchangeRateMantissa;
/**
* @notice Fraction of interest currently set aside for reserves
*/
uint256 public reserveFactorMantissa;
/**
* @notice Block number that interest was last accrued at
*/
uint256 public accrualBlockNumber;
/**
* @notice Accumulator of the total earned interest rate since the opening of the market
*/
uint256 public borrowIndex;
/**
* @notice Total amount of outstanding borrows of the underlying in this market
*/
uint256 public totalBorrows;
/**
* @notice Total amount of reserves of the underlying held in this market
*/
uint256 public totalReserves;
/**
* @notice Total number of tokens in circulation
*/
uint256 public totalSupply;
/**
* @notice Official record of token balances for each account
*/
mapping(address => uint) public accountTokens;
/**
* @notice Approved token transfer amounts on behalf of others
*/
mapping(address => mapping(address => uint)) 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
*/
struct BorrowSnapshot {
uint256 principal;
uint256 interestIndex;
}
/**
* @notice Mapping of account addresses to outstanding borrow balances
*/
mapping(address => BorrowSnapshot) internal accountBorrows;
/**
* @notice Share of seized collateral that is added to reserves
*/
uint256 public constant protocolSeizeShareMantissa = 2.8e16; //2.8%
}
abstract contract BTokenInterface is BTokenStorage {
/**
* @notice Indicator that this is a CToken contract (for inspection)
*/
bool public constant isCToken = true;
/*** Market Events ***/
/**
* @notice Event emitted when interest is accrued
*/
event AccrueInterest(uint256 cashPrior, uint256 interestAccumulated, uint256 borrowIndex, uint256 totalBorrows);
/**
* @notice Event emitted when tokens are minted
*/
event Mint(address minter, uint256 mintAmount, uint256 mintTokens);
/**
* @notice Event emitted when tokens are redeemed
*/
event Redeem(address redeemer, uint256 redeemAmount, uint256 redeemTokens);
/**
* @notice Event emitted when underlying is borrowed
*/
event Borrow(address borrower, uint256 borrowAmount, uint256 accountBorrows, uint256 totalBorrows);
/**
* @notice Event emitted when a borrow is repaid
*/
event RepayBorrow(address payer, address borrower, uint256 repayAmount, uint256 accountBorrows, uint256 totalBorrows);
/**
* @notice Event emitted when a borrow is liquidated
*/
event LiquidateBorrow(address liquidator, address borrower, uint256 repayAmount, address cTokenCollateral, uint256 seizeTokens);
/*** Admin Events ***/
/**
* @notice Event emitted when pendingAdmin is changed
*/
event NewPendingAdmin(address oldPendingAdmin, address newPendingAdmin);
/**
* @notice Event emitted when pendingAdmin is accepted, which means admin is updated
*/
event NewAdmin(address oldAdmin, address newAdmin);
/**
* @notice Event emitted when bondtroller is changed
*/
event NewBondtroller(Bondtroller oldBondtroller, Bondtroller newBondtroller);
/**
* @notice Event emitted when interestRateModel is changed
*/
event NewMarketInterestRateModel(InterestRateModel oldInterestRateModel, InterestRateModel newInterestRateModel);
/**
* @notice Event emitted when the reserve factor is changed
*/
event NewReserveFactor(uint256 oldReserveFactorMantissa, uint256 newReserveFactorMantissa);
/**
* @notice Event emitted when the reserves are added
*/
event ReservesAdded(address benefactor, uint256 addAmount, uint256 newTotalReserves);
/**
* @notice Event emitted when the reserves are reduced
*/
event ReservesReduced(address admin, uint256 reduceAmount, uint256 newTotalReserves);
/**
* @notice EIP20 Transfer event
*/
event Transfer(address indexed from, address indexed to, uint256 amount);
/**
* @notice EIP20 Approval event
*/
event Approval(address indexed owner, address indexed spender, uint256 amount);
/**
* @notice Failure event
*/
//event Failure(uint256 error, uint256 info, uint256 detail);
/*** User Interface ***/
function transfer(address dst, uint256 amount) external virtual returns (bool);
function transferFrom(address src, address dst, uint256 amount) external virtual returns (bool);
function approve(address spender, uint256 amount) external virtual returns (bool);
function allowance(address owner, address spender) external view virtual returns (uint);
function balanceOf(address owner) external view virtual returns (uint);
function balanceOfUnderlying(address owner) external virtual returns (uint);
function getAccountSnapshot(address account) external view virtual returns (uint, uint, uint, uint);
function borrowRatePerBlock() external view virtual returns (uint);
function supplyRatePerBlock() external view virtual returns (uint);
function totalBorrowsCurrent() external virtual returns (uint);
function borrowBalanceCurrent(address account) external virtual returns (uint);
function borrowBalanceStored(address account) public view virtual returns (uint);
function exchangeRateCurrent() public virtual returns (uint);
function exchangeRateStored() public view virtual returns (uint);
function getCash() external view virtual returns (uint);
function accrueInterest() public virtual returns (uint);
/*** Admin Functions ***/
function _setBondtroller(Bondtroller newBondtroller) public virtual returns (uint);
function _setReserveFactor(uint256 newReserveFactorMantissa) external virtual returns (uint);
function _reduceReserves(uint256 reduceAmount) external virtual returns (uint);
function _setInterestRateModel(InterestRateModel newInterestRateModel) public virtual returns (uint);
}
contract BErc20Storage {
/**
* @notice Underlying asset for this CToken
*/
address public underlying;
}
abstract contract BErc20Interface is BErc20Storage {
/*** User Interface ***/
function sweepToken(EIP20NonStandardInterface token) external virtual;
/*** Admin Functions ***/
function _addReserves(uint256 addAmount) external virtual returns (uint);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
/**
* @title Compound's InterestRateModel Interface
* @author Compound
*/
abstract contract InterestRateModel {
/// @notice Indicator that this is an InterestRateModel contract (for inspection)
bool public constant isInterestRateModel = true;
/**
* @dev 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.
* @param blendingToken The address of the blending token used for interest calculation.
* @return The borrow rate per block (as a percentage, and scaled by 1e18).
*/
function getBorrowRate(uint256 cash, uint256 borrows, uint256 reserves, address blendingToken) external view virtual returns (uint);
/**
* @dev 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.
* @param blendingToken The address of the blending token used for interest calculation.
* @return The supply rate per block (as a percentage, and scaled by 1e18).
*/
function getSupplyRate(
uint256 cash,
uint256 borrows,
uint256 reserves,
uint256 reserveFactorMantissa,
address blendingToken
) external view virtual returns (uint);
/**
* @dev Calculates and stores the current borrow interest rate per block for the specified blending token.
* @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 calculated borrow rate per block, represented as a percentage and scaled by 1e18.
*/
function storeBorrowRate(uint256 cash, uint256 borrows, uint256 reserves) external virtual returns (uint);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
/**
* @title ERC 20 Token Standard Interface
* https://eips.ethereum.org/EIPS/eip-20
*/
interface EIP20Interface {
function name() external view returns (string memory);
function symbol() external view returns (string memory);
function decimals() external view returns (uint8);
/**
* @notice Get the total number of tokens in circulation
* @return The supply of tokens
*/
function totalSupply() external view returns (uint256);
/**
* @notice Gets the balance of the specified address
* @param owner The address from which the balance will be retrieved
* return The `balance`
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @notice Transfer `amount` tokens from `msg.sender` to `dst`
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
* return Whether or not the transfer succeeded
*/
function transfer(address dst, uint256 amount) external returns (bool success);
/**
* @notice Transfer `amount` tokens from `src` to `dst`
* @param src The address of the source account
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
* return Whether or not the transfer succeeded
*/
function transferFrom(address src, address dst, uint256 amount) external returns (bool success);
/**
* @notice Approve `spender` to transfer up to `amount` from `src`
* @dev This will overwrite the approval amount for `spender`
* and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
* @param spender The address of the account which may transfer tokens
* @param amount The number of tokens that are approved (-1 means infinite)
* return Whether or not the approval succeeded
*/
function approve(address spender, uint256 amount) external returns (bool success);
/**
* @notice Get the current allowance from `owner` for `spender`
* @param owner The address of the account which owns the tokens to be spent
* @param spender The address of the account which may transfer tokens
* return The number of tokens allowed to be spent (-1 means infinite)
*/
function allowance(address owner, address spender) external view returns (uint256 remaining);
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
/**
* @title EIP20NonStandardInterface
* @dev Version of ERC20 with no return values for `transfer` and `transferFrom`
* See https://medium.com/coinmonks/missing-return-value-bug-at-least-130-tokens-affected-d67bf08521ca
*/
interface EIP20NonStandardInterface {
/**
* @notice Get the total number of tokens in circulation
* @return The supply of tokens
*/
function totalSupply() external view returns (uint256);
/**
* @notice Gets the balance of the specified address
* @param owner The address from which the balance will be retrieved
*
*/
function balanceOf(address owner) external view returns (uint256 balance);
///
/// !!!!!!!!!!!!!!
/// !!! NOTICE !!! `transfer` does not return a value, in violation of the ERC-20 specification
/// !!!!!!!!!!!!!!
///
/**
* @notice Transfer `amount` tokens from `msg.sender` to `dst`
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
*/
function transfer(address dst, uint256 amount) external;
///
/// !!!!!!!!!!!!!!
/// !!! NOTICE !!! `transferFrom` does not return a value, in violation of the ERC-20 specification
/// !!!!!!!!!!!!!!
///
/**
* @notice Transfer `amount` tokens from `src` to `dst`
* @param src The address of the source account
* @param dst The address of the destination account
* @param amount The number of tokens to transfer
*/
function transferFrom(address src, address dst, uint256 amount) external;
/**
* @notice Approve `spender` to transfer up to `amount` from `src`
* @dev This will overwrite the approval amount for `spender`
* and is subject to issues noted [here](https://eips.ethereum.org/EIPS/eip-20#approve)
* @param spender The address of the account which may transfer tokens
* @param amount The number of tokens that are approved
* return Whether or not the approval succeeded
*/
function approve(address spender, uint256 amount) external returns (bool success);
/**
* @notice Get the current allowance from `owner` for `spender`
* @param owner The address of the account which owns the tokens to be spent
* @param spender The address of the account which may transfer tokens
* return The number of tokens allowed to be spent
*/
function allowance(address owner, address spender) external view returns (uint256 remaining);
event Transfer(address indexed from, address indexed to, uint256 amount);
event Approval(address indexed owner, address indexed spender, uint256 amount);
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
/**
* @title Careful Math
* @author Compound
* @notice Derived from OpenZeppelin's SafeMath library
* https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/math/SafeMath.sol
*/
contract CarefulMath {
/**
* @dev Possible error codes that we can return
*/
enum MathError {
NO_ERROR,
DIVISION_BY_ZERO,
INTEGER_OVERFLOW,
INTEGER_UNDERFLOW
}
/**
* @dev Multiplies two numbers, returns an error on overflow.
*/
function mulUInt(uint a, uint b) internal pure returns (MathError, uint) {
if (a == 0) {
return (MathError.NO_ERROR, 0);
}
uint 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.
*/
function divUInt(uint a, uint b) internal pure returns (MathError, uint) {
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).
*/
function subUInt(uint a, uint b) internal pure returns (MathError, uint) {
if (b <= a) {
return (MathError.NO_ERROR, a - b);
} else {
return (MathError.INTEGER_UNDERFLOW, 0);
}
}
/**
* @dev Adds two numbers, returns an error on overflow.
*/
function addUInt(uint a, uint b) internal pure returns (MathError, uint) {
uint 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
*/
function addThenSubUInt(uint a, uint b, uint c) internal pure returns (MathError, uint) {
(MathError err0, uint sum) = addUInt(a, b);
if (err0 != MathError.NO_ERROR) {
return (err0, 0);
}
return subUInt(sum, c);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
contract BondtrollerErrorReporter {
enum Error {
NO_ERROR,
UNAUTHORIZED,
BONDTROLLER_MISMATCH,
INSUFFICIENT_SHORTFALL,
INSUFFICIENT_LIQUIDITY,
INVALID_CLOSE_FACTOR,
INVALID_COLLATERAL_FACTOR,
INVALID_LIQUIDATION_INCENTIVE,
MARKET_NOT_ENTERED, // no longer possible
MARKET_NOT_LISTED,
MARKET_ALREADY_LISTED,
MATH_ERROR,
NONZERO_BORROW_BALANCE,
PRICE_ERROR,
REJECTION,
SNAPSHOT_ERROR,
TOO_MANY_ASSETS,
TOO_MUCH_REPAY
}
enum FailureInfo {
ACCEPT_ADMIN_PENDING_ADMIN_CHECK,
ACCEPT_PENDING_IMPLEMENTATION_ADDRESS_CHECK,
EXIT_MARKET_BALANCE_OWED,
EXIT_MARKET_REJECTION,
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_IMPLEMENTATION_OWNER_CHECK,
SET_LIQUIDATION_INCENTIVE_OWNER_CHECK,
SET_LIQUIDATION_INCENTIVE_VALIDATION,
SET_MAX_ASSETS_OWNER_CHECK,
SET_PENDING_ADMIN_OWNER_CHECK,
SET_PENDING_IMPLEMENTATION_OWNER_CHECK,
SET_PRICE_ORACLE_OWNER_CHECK,
SUPPORT_MARKET_EXISTS,
SUPPORT_MARKET_OWNER_CHECK,
SET_PAUSE_GUARDIAN_OWNER_CHECK
}
/**
* @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.
**/
event Failure(uint error, uint info, uint detail);
/**
* @dev use this when reporting a known error from the money market or a non-upgradeable collaborator
*/
function fail(Error err, FailureInfo info) internal returns (uint) {
emit Failure(uint256(err), uint256(info), 0);
return uint256(err);
}
/**
* @dev use this when reporting an opaque error from an upgradeable collaborator contract
*/
function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) {
emit Failure(uint256(err), uint256(info), opaqueError);
return uint256(err);
}
}
contract TokenErrorReporter {
enum Error {
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
}
/*
* 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.
*/
enum FailureInfo {
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_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,
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,
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,
SET_MAX_ASSETS_OWNER_CHECK,
SET_ORACLE_MARKET_NOT_LISTED,
SET_PENDING_ADMIN_OWNER_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.
**/
event Failure(uint error, uint info, uint detail);
/**
* @dev use this when reporting a known error from the money market or a non-upgradeable collaborator
*/
function fail(Error err, FailureInfo info) internal returns (uint) {
emit Failure(uint256(err), uint256(info), 0);
return uint256(err);
}
/**
* @dev use this when reporting an opaque error from an upgradeable collaborator contract
*/
function failOpaque(Error err, FailureInfo info, uint opaqueError) internal returns (uint) {
emit Failure(uint256(err), uint256(info), opaqueError);
return uint256(err);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
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})`.
*/
contract Exponential is CarefulMath, ExponentialNoError {
/**
* @dev Creates an exponential from numerator and denominator values.
* Note: Returns an error if (`num` * 10e18) > MAX_INT,
* or if `denom` is zero.
*/
function getExp(uint num, uint denom) pure internal returns (MathError, Exp memory) {
(MathError err0, uint scaledNumerator) = mulUInt(num, expScale);
if (err0 != MathError.NO_ERROR) {
return (err0, Exp({mantissa: 0}));
}
(MathError err1, uint 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.
*/
function addExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
(MathError error, uint result) = addUInt(a.mantissa, b.mantissa);
return (error, Exp({mantissa: result}));
}
/**
* @dev Subtracts two exponentials, returning a new exponential.
*/
function subExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
(MathError error, uint result) = subUInt(a.mantissa, b.mantissa);
return (error, Exp({mantissa: result}));
}
/**
* @dev Multiply an Exp by a scalar, returning a new Exp.
*/
function mulScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) {
(MathError err0, uint 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.
*/
function mulScalarTruncate(Exp memory a, uint scalar) pure internal returns (MathError, uint) {
(MathError err, Exp memory product) = mulScalar(a, scalar);
if (err != MathError.NO_ERROR) {
return (err, 0);
}
return (MathError.NO_ERROR, truncate(product));
}
/**
* @dev Multiply an Exp by a scalar, truncate, then add an to an unsigned integer, returning an unsigned integer.
*/
function mulScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (MathError, uint) {
(MathError err, Exp memory product) = mulScalar(a, scalar);
if (err != MathError.NO_ERROR) {
return (err, 0);
}
return addUInt(truncate(product), addend);
}
/**
* @dev Divide an Exp by a scalar, returning a new Exp.
*/
function divScalar(Exp memory a, uint scalar) pure internal returns (MathError, Exp memory) {
(MathError err0, uint 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.
*/
function divScalarByExp(uint scalar, Exp memory divisor) pure internal returns (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, uint 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.
*/
function divScalarByExpTruncate(uint scalar, Exp memory divisor) pure internal returns (MathError, uint) {
(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.
*/
function mulExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
(MathError err0, uint 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, uint doubleScaledProductWithHalfScale) = addUInt(halfExpScale, doubleScaledProduct);
if (err1 != MathError.NO_ERROR) {
return (err1, Exp({mantissa: 0}));
}
(MathError err2, uint 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.
*/
function mulExp(uint a, uint b) pure internal returns (MathError, Exp memory) {
return mulExp(Exp({mantissa: a}), Exp({mantissa: b}));
}
/**
* @dev Multiplies three exponentials, returning a new exponential.
*/
function mulExp3(Exp memory a, Exp memory b, Exp memory c) pure internal returns (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)
*/
function divExp(Exp memory a, Exp memory b) pure internal returns (MathError, Exp memory) {
return getExp(a.mantissa, b.mantissa);
}
}// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;
/**
* @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})`.
*/
contract ExponentialNoError {
uint constant expScale = 1e18;
uint constant doubleScale = 1e36;
uint constant halfExpScale = expScale/2;
uint constant mantissaOne = expScale;
struct Exp {
uint mantissa;
}
struct Double {
uint mantissa;
}
/**
* @dev Truncates the given exp to a whole number value.
* For example, truncate(Exp{mantissa: 15 * expScale}) = 15
*/
function truncate(Exp memory exp) pure internal returns (uint) {
// Note: We are not using careful math here as we're performing a division that cannot fail
return exp.mantissa / expScale;
}
/**
* @dev Multiply an Exp by a scalar, then truncate to return an unsigned integer.
*/
function mul_ScalarTruncate(Exp memory a, uint scalar) pure internal returns (uint) {
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.
*/
function mul_ScalarTruncateAddUInt(Exp memory a, uint scalar, uint addend) pure internal returns (uint) {
Exp memory product = mul_(a, scalar);
return add_(truncate(product), addend);
}
/**
* @dev Checks if first Exp is less than second Exp.
*/
function lessThanExp(Exp memory left, Exp memory right) pure internal returns (bool) {
return left.mantissa < right.mantissa;
}
/**
* @dev Checks if left Exp <= right Exp.
*/
function lessThanOrEqualExp(Exp memory left, Exp memory right) pure internal returns (bool) {
return left.mantissa <= right.mantissa;
}
/**
* @dev Checks if left Exp > right Exp.
*/
function greaterThanExp(Exp memory left, Exp memory right) pure internal returns (bool) {
return left.mantissa > right.mantissa;
}
/**
* @dev returns true if Exp is exactly zero
*/
function isZeroExp(Exp memory value) pure internal returns (bool) {
return value.mantissa == 0;
}
function safe224(uint n, string memory errorMessage) pure internal returns (uint224) {
require(n < 2**224, errorMessage);
return uint224(n);
}
function safe32(uint n, string memory errorMessage) pure internal returns (uint32) {
require(n < 2**32, errorMessage);
return uint32(n);
}
function add_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: add_(a.mantissa, b.mantissa)});
}
function add_(Double memory a, Double memory b) pure internal returns (Double memory) {
return Double({mantissa: add_(a.mantissa, b.mantissa)});
}
function add_(uint a, uint b) pure internal returns (uint) {
return add_(a, b, "addition overflow");
}
function add_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
uint c = a + b;
require(c >= a, errorMessage);
return c;
}
function sub_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: sub_(a.mantissa, b.mantissa)});
}
function sub_(Double memory a, Double memory b) pure internal returns (Double memory) {
return Double({mantissa: sub_(a.mantissa, b.mantissa)});
}
function sub_(uint a, uint b) pure internal returns (uint) {
return sub_(a, b, "subtraction underflow");
}
function sub_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
require(b <= a, errorMessage);
return a - b;
}
function mul_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: mul_(a.mantissa, b.mantissa) / expScale});
}
function mul_(Exp memory a, uint b) pure internal returns (Exp memory) {
return Exp({mantissa: mul_(a.mantissa, b)});
}
function mul_(uint a, Exp memory b) pure internal returns (uint) {
return mul_(a, b.mantissa) / expScale;
}
function mul_(Double memory a, Double memory b) pure internal returns (Double memory) {
return Double({mantissa: mul_(a.mantissa, b.mantissa) / doubleScale});
}
function mul_(Double memory a, uint b) pure internal returns (Double memory) {
return Double({mantissa: mul_(a.mantissa, b)});
}
function mul_(uint a, Double memory b) pure internal returns (uint) {
return mul_(a, b.mantissa) / doubleScale;
}
function mul_(uint a, uint b) pure internal returns (uint) {
return mul_(a, b, "multiplication overflow");
}
function mul_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
if (a == 0 || b == 0) {
return 0;
}
uint c = a * b;
require(c / a == b, errorMessage);
return c;
}
function div_(Exp memory a, Exp memory b) pure internal returns (Exp memory) {
return Exp({mantissa: div_(mul_(a.mantissa, expScale), b.mantissa)});
}
function div_(Exp memory a, uint b) pure internal returns (Exp memory) {
return Exp({mantissa: div_(a.mantissa, b)});
}
function div_(uint a, Exp memory b) pure internal returns (uint) {
return div_(mul_(a, expScale), b.mantissa);
}
function div_(Double memory a, Double memory b) pure internal returns (Double memory) {
return Double({mantissa: div_(mul_(a.mantissa, doubleScale), b.mantissa)});
}
function div_(Double memory a, uint b) pure internal returns (Double memory) {
return Double({mantissa: div_(a.mantissa, b)});
}
function div_(uint a, Double memory b) pure internal returns (uint) {
return div_(mul_(a, doubleScale), b.mantissa);
}
function div_(uint a, uint b) pure internal returns (uint) {
return div_(a, b, "divide by zero");
}
function div_(uint a, uint b, string memory errorMessage) pure internal returns (uint) {
require(b > 0, errorMessage);
return a / b;
}
function fraction(uint a, uint b) pure internal returns (Double memory) {
return Double({mantissa: div_(mul_(a, doubleScale), b)});
}
}{
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"libraries": {}
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"appliedBlock","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"interestRate","type":"uint256"}],"name":"NewInterest","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"gainPerBlock","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"jumGainPerBlock","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"targetUtil","type":"uint256"}],"name":"NewInterestParams","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"newOwner","type":"address"}],"name":"NewOwner","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MODERATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"blendingToken","type":"address"},{"internalType":"uint256","name":"gainPerYear","type":"uint256"},{"internalType":"uint256","name":"jumGainPerYear","type":"uint256"},{"internalType":"uint256","name":"targetUtil_","type":"uint256"},{"internalType":"uint256","name":"newMaxBorrow","type":"uint256"}],"name":"addBLendingTokenSupport","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"blendingTokenInfo","outputs":[{"internalType":"uint256","name":"gainPerBlock","type":"uint256"},{"internalType":"uint256","name":"jumGainPerBlock","type":"uint256"},{"internalType":"uint256","name":"targetUtil","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"blocksPerYear","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBlockNumber","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"cash","type":"uint256"},{"internalType":"uint256","name":"borrows","type":"uint256"},{"internalType":"uint256","name":"reserves","type":"uint256"},{"internalType":"address","name":"blendingToken","type":"address"}],"name":"getBorrowRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"cash","type":"uint256"},{"internalType":"uint256","name":"borrows","type":"uint256"},{"internalType":"uint256","name":"reserves","type":"uint256"},{"internalType":"address","name":"blendingToken","type":"address"}],"name":"getInterestRateChange","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"cash","type":"uint256"},{"internalType":"uint256","name":"borrows","type":"uint256"},{"internalType":"uint256","name":"reserves","type":"uint256"},{"internalType":"uint256","name":"reserveFactorMantissa","type":"uint256"},{"internalType":"address","name":"blendingToken","type":"address"}],"name":"getSupplyRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newModerator","type":"address"}],"name":"grantModerator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"blocksPerYear_","type":"uint256"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"isBlendingTokenSupport","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isInterestRateModel","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"rateInfo","outputs":[{"internalType":"uint256","name":"lastInterestRate","type":"uint256"},{"internalType":"uint256","name":"lastAccrualBlockNumber","type":"uint256"},{"internalType":"uint256","name":"maxBorrowRate","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_blending","type":"address"}],"name":"removeBLendingTokenSupport","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"moderator","type":"address"}],"name":"revokeModerator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"blocksPerYear_","type":"uint256"}],"name":"setBlockPerYear","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"blendingToken","type":"address"},{"internalType":"uint256","name":"newMaxBorrow","type":"uint256"}],"name":"setMaxBorrowRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"cash","type":"uint256"},{"internalType":"uint256","name":"borrows","type":"uint256"},{"internalType":"uint256","name":"reserves","type":"uint256"}],"name":"storeBorrowRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"blendingToken","type":"address"}],"name":"updateBlockNumber","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"gainPerYear","type":"uint256"},{"internalType":"uint256","name":"jumGainPerYear","type":"uint256"},{"internalType":"uint256","name":"targetUtil_","type":"uint256"},{"internalType":"address","name":"blendingToken","type":"address"}],"name":"updateJumpRateModel","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"cash","type":"uint256"},{"internalType":"uint256","name":"borrows","type":"uint256"},{"internalType":"uint256","name":"reserves","type":"uint256"}],"name":"utilizationRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"}]Contract Creation Code
608060405234801561001057600080fd5b50611777806100206000396000f3fe608060405234801561001057600080fd5b50600436106101c45760003560e01c80636e71e2d8116100f9578063a385fb9611610097578063d547741f11610071578063d547741f146103fe578063f982894414610411578063fe4b84df14610424578063ff60fb681461043757600080fd5b8063a385fb96146103bf578063a8801029146103c8578063b7979487146103db57600080fd5b806389469df9116100d357806389469df91461037e57806391d1485414610391578063938c9cf6146103a4578063a217fddf146103b757600080fd5b80636e71e2d814610343578063797669c91461035657806386959d811461036b57600080fd5b8063364456361161016657806342cbb15c1161014057806342cbb15c146102e8578063582d785a146102ee5780635eeaafea1461031d5780636981c7ae1461033057600080fd5b8063364456361461027857806336568abe1461028b5780633a86fb411461029e57600080fd5b80632191f92a116101a25780632191f92a14610219578063248a9ca3146102215780632f2ff15d1461025257806332dc9b1c1461026557600080fd5b806301ffc9a7146101c957806303700d6b146101f157806310b8627614610206575b600080fd5b6101dc6101d736600461129b565b61044a565b60405190151581526020015b60405180910390f35b6102046101ff3660046112c5565b610481565b005b6102046102143660046112fa565b6104c3565b6101dc600181565b61024461022f3660046112c5565b60009081526065602052604090206001015490565b6040519081526020016101e8565b610204610260366004611339565b610509565b610244610273366004611365565b610533565b6102046102863660046113ae565b6105a8565b610204610299366004611339565b610614565b6102cd6102ac3660046113ae565b60996020526000908152604090208054600182015460029092015490919083565b604080519384526020840192909252908201526060016101e8565b43610244565b6102cd6102fc3660046113ae565b60986020526000908152604090208054600182015460029092015490919083565b61024461032b3660046113c9565b610692565b61020461033e3660046113ae565b610728565b6102446103513660046113c9565b610791565b61024460008051602061172283398151915281565b6102446103793660046112fa565b6107d4565b61024461038c3660046112fa565b6108b7565b6101dc61039f366004611339565b6108ce565b6102046103b23660046113ae565b6108f9565b610244600081565b61024460975481565b6102046103d63660046113f5565b6109ec565b6101dc6103e93660046113ae565b609a6020526000908152604090205460ff1681565b61020461040c366004611339565b610a77565b61020461041f36600461141f565b610a9c565b6102046104323660046112c5565b610b83565b6102046104453660046113ae565b610cbc565b60006001600160e01b03198216637965db0b60e01b148061047b57506301ffc9a760e01b6001600160e01b03198316145b92915050565b610499600080516020611722833981519152336108ce565b6104be5760405162461bcd60e51b81526004016104b590611461565b60405180910390fd5b609755565b6104db600080516020611722833981519152336108ce565b6104f75760405162461bcd60e51b81526004016104b590611461565b61050384848484610d9f565b50505050565b60008281526065602052604090206001015461052481610e2e565b61052e8383610e38565b505050565b600080610548670de0b6b3a764000085610ebe565b9050600061055888888887610eca565b90506000610578670de0b6b3a76400006105728486610f6c565b90610f78565b905061059b670de0b6b3a7640000610572836105958d8d8d610791565b90610f6c565b9998505050505050505050565b6105b36000336108ce565b6105f95760405162461bcd60e51b815260206004820152601760248201527621b0b63632b91034b9903737ba103a34329020b236b4b760491b60448201526064016104b5565b61061160008051602061172283398151915282610a77565b50565b6001600160a01b03811633146106845760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084016104b5565b61068e8282610f84565b5050565b336000908152609a602052604081205460ff166106f15760405162461bcd60e51b815260206004820152601c60248201527f43616c6c6572206973206e6f7420426c656e64696e6720746f6b656e0000000060448201526064016104b5565b3360008181526099602052604081209161071090879087908790610eca565b808355436001909301929092555090505b9392505050565b6107336000336108ce565b6107795760405162461bcd60e51b815260206004820152601760248201527621b0b63632b91034b9903737ba103a34329020b236b4b760491b60448201526064016104b5565b61061160008051602061172283398151915282610509565b6000826000036107a357506000610721565b6107cc6107ba836107b48787610feb565b90610ebe565b61057285670de0b6b3a7640000610f6c565b949350505050565b6001600160a01b038116600090815260986020908152604080832081516060810183528154815260018201549381019390935260020154908201528161081b878787610791565b9050600082604001518261082f91906114ae565b90506000836040015183111561087c57602084015184516ec097ce7bc90715b34b9f1000000000916108619190610f6c565b61086b90846114d5565b610875919061151b565b90506108ab565b83604001518310156108ab578351670de0b6b3a76400009061089e90846114d5565b6108a8919061151b565b90505b98975050505050505050565b60006108c585858585610eca565b95945050505050565b60009182526065602090815260408084206001600160a01b0393909316845291905290205460ff1690565b610911600080516020611722833981519152336108ce565b61092d5760405162461bcd60e51b81526004016104b590611461565b6001600160a01b0381166000908152609a602052604090205460ff166109655760405162461bcd60e51b81526004016104b590611549565b6000816001600160a01b0316636c540baf6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109c99190611580565b6001600160a01b0390921660009081526099602052604090206001019190915550565b610a04600080516020611722833981519152336108ce565b610a205760405162461bcd60e51b81526004016104b590611461565b6001600160a01b0382166000908152609a602052604090205460ff16610a585760405162461bcd60e51b81526004016104b590611549565b6001600160a01b03909116600090815260996020526040902060020155565b600082815260656020526040902060010154610a9281610e2e565b61052e8383610f84565b610ab4600080516020611722833981519152336108ce565b610ad05760405162461bcd60e51b81526004016104b590611461565b6001600160a01b038516610b265760405162461bcd60e51b815260206004820181905260248201527f4a756d70526174654d6f64656c56333a20496e76616c6964206164647265737360448201526064016104b5565b6001600160a01b0385166000908152609a60205260409020805460ff19166001179055610b5584848488610d9f565b6001600160a01b0385166000908152609960205260409020600201819055610b7c856108f9565b5050505050565b600054610100900460ff1615808015610ba35750600054600160ff909116105b80610bbd5750303b158015610bbd575060005460ff166001145b610c205760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016104b5565b6000805460ff191660011790558015610c43576000805461ff0019166101001790555b610c4b610ff7565b610c56600033611064565b610c6e60008051602061172283398151915233611064565b6097829055801561068e576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15050565b610cd4600080516020611722833981519152336108ce565b610cf05760405162461bcd60e51b81526004016104b590611461565b6001600160a01b038116610d465760405162461bcd60e51b815260206004820181905260248201527f4a756d70526174654d6f64656c56333a20496e76616c6964206164647265737360448201526064016104b5565b6001600160a01b0381166000908152609a602052604090205460ff16610d7e5760405162461bcd60e51b81526004016104b590611549565b6001600160a01b03166000908152609a60205260409020805460ff19169055565b6001600160a01b0381166000908152609860205260409020609754610dc5908690610f78565b8155609754610dd5908590610f78565b600182018190556002820184905581546040805191825260208201929092529081018490527f865bfff1eb39dc370f97b2eb5990d963c50228429828ad935a4470166c711fc19060600160405180910390a15050505050565b610611813361106e565b610e4282826108ce565b61068e5760008281526065602090815260408083206001600160a01b03851684529091529020805460ff19166001179055610e7a3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60006107218284611599565b6001600160a01b0381166000908152609960209081526040808320815160608101835281548152600182015493810193909352600201549082015281610f12878787876107d4565b90506000610f1f856110c7565b90506000610f2d83836114d5565b8451610f3991906115ac565b90506000808213610f4b576000610f4d565b815b9050846040015181111561059b57505050506040015195945050505050565b600061072182846115d4565b600061072182846115eb565b610f8e82826108ce565b1561068e5760008281526065602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b600061072182846115ff565b600054610100900460ff166110625760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b60648201526084016104b5565b565b61068e8282610e38565b61107882826108ce565b61068e57611085816110ed565b6110908360206110ff565b6040516020016110a1929190611636565b60408051601f198184030181529082905262461bcd60e51b82526104b5916004016116ab565b6001600160a01b03811660009081526099602052604081206001015461047b90436107b4565b606061047b6001600160a01b03831660145b6060600061110e8360026115d4565b6111199060026115ff565b67ffffffffffffffff811115611131576111316116de565b6040519080825280601f01601f19166020018201604052801561115b576020820181803683370190505b509050600360fc1b81600081518110611176576111766116f4565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106111a5576111a56116f4565b60200101906001600160f81b031916908160001a90535060006111c98460026115d4565b6111d49060016115ff565b90505b600181111561124c576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110611208576112086116f4565b1a60f81b82828151811061121e5761121e6116f4565b60200101906001600160f81b031916908160001a90535060049490941c936112458161170a565b90506111d7565b5083156107215760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016104b5565b6000602082840312156112ad57600080fd5b81356001600160e01b03198116811461072157600080fd5b6000602082840312156112d757600080fd5b5035919050565b80356001600160a01b03811681146112f557600080fd5b919050565b6000806000806080858703121561131057600080fd5b84359350602085013592506040850135915061132e606086016112de565b905092959194509250565b6000806040838503121561134c57600080fd5b8235915061135c602084016112de565b90509250929050565b600080600080600060a0868803121561137d57600080fd5b853594506020860135935060408601359250606086013591506113a2608087016112de565b90509295509295909350565b6000602082840312156113c057600080fd5b610721826112de565b6000806000606084860312156113de57600080fd5b505081359360208301359350604090920135919050565b6000806040838503121561140857600080fd5b611411836112de565b946020939093013593505050565b600080600080600060a0868803121561143757600080fd5b611440866112de565b97602087013597506040870135966060810135965060800135945092505050565b6020808252601b908201527f43616c6c6572206973206e6f7420746865204d6f64657261746f720000000000604082015260600190565b634e487b7160e01b600052601160045260246000fd5b81810360008312801583831316838312821617156114ce576114ce611498565b5092915050565b80820260008212600160ff1b841416156114f1576114f1611498565b818105831482151761047b5761047b611498565b634e487b7160e01b600052601260045260246000fd5b60008261152a5761152a611505565b600160ff1b82146000198414161561154457611544611498565b500590565b6020808252601a908201527f4a756d70526174654d6f64656c56333a204e6f7420666f756e64000000000000604082015260600190565b60006020828403121561159257600080fd5b5051919050565b8181038181111561047b5761047b611498565b80820182811260008312801582168215821617156115cc576115cc611498565b505092915050565b808202811582820484141761047b5761047b611498565b6000826115fa576115fa611505565b500490565b8082018082111561047b5761047b611498565b60005b8381101561162d578181015183820152602001611615565b50506000910152565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161166e816017850160208801611612565b7001034b99036b4b9b9b4b733903937b6329607d1b601791840191820152835161169f816028840160208801611612565b01602801949350505050565b60208152600082518060208401526116ca816040850160208701611612565b601f01601f19169190910160400192915050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008161171957611719611498565b50600019019056fe71f3d55856e4058ed06ee057d79ada615f65cdf5f9ee88181b914225088f834fa26469706673582212202da4c73323108e8e673aaa46a6ee1ee447d82fe28ce48904847de06df4e91ee764736f6c63430008130033
Deployed Bytecode
0x608060405234801561001057600080fd5b50600436106101c45760003560e01c80636e71e2d8116100f9578063a385fb9611610097578063d547741f11610071578063d547741f146103fe578063f982894414610411578063fe4b84df14610424578063ff60fb681461043757600080fd5b8063a385fb96146103bf578063a8801029146103c8578063b7979487146103db57600080fd5b806389469df9116100d357806389469df91461037e57806391d1485414610391578063938c9cf6146103a4578063a217fddf146103b757600080fd5b80636e71e2d814610343578063797669c91461035657806386959d811461036b57600080fd5b8063364456361161016657806342cbb15c1161014057806342cbb15c146102e8578063582d785a146102ee5780635eeaafea1461031d5780636981c7ae1461033057600080fd5b8063364456361461027857806336568abe1461028b5780633a86fb411461029e57600080fd5b80632191f92a116101a25780632191f92a14610219578063248a9ca3146102215780632f2ff15d1461025257806332dc9b1c1461026557600080fd5b806301ffc9a7146101c957806303700d6b146101f157806310b8627614610206575b600080fd5b6101dc6101d736600461129b565b61044a565b60405190151581526020015b60405180910390f35b6102046101ff3660046112c5565b610481565b005b6102046102143660046112fa565b6104c3565b6101dc600181565b61024461022f3660046112c5565b60009081526065602052604090206001015490565b6040519081526020016101e8565b610204610260366004611339565b610509565b610244610273366004611365565b610533565b6102046102863660046113ae565b6105a8565b610204610299366004611339565b610614565b6102cd6102ac3660046113ae565b60996020526000908152604090208054600182015460029092015490919083565b604080519384526020840192909252908201526060016101e8565b43610244565b6102cd6102fc3660046113ae565b60986020526000908152604090208054600182015460029092015490919083565b61024461032b3660046113c9565b610692565b61020461033e3660046113ae565b610728565b6102446103513660046113c9565b610791565b61024460008051602061172283398151915281565b6102446103793660046112fa565b6107d4565b61024461038c3660046112fa565b6108b7565b6101dc61039f366004611339565b6108ce565b6102046103b23660046113ae565b6108f9565b610244600081565b61024460975481565b6102046103d63660046113f5565b6109ec565b6101dc6103e93660046113ae565b609a6020526000908152604090205460ff1681565b61020461040c366004611339565b610a77565b61020461041f36600461141f565b610a9c565b6102046104323660046112c5565b610b83565b6102046104453660046113ae565b610cbc565b60006001600160e01b03198216637965db0b60e01b148061047b57506301ffc9a760e01b6001600160e01b03198316145b92915050565b610499600080516020611722833981519152336108ce565b6104be5760405162461bcd60e51b81526004016104b590611461565b60405180910390fd5b609755565b6104db600080516020611722833981519152336108ce565b6104f75760405162461bcd60e51b81526004016104b590611461565b61050384848484610d9f565b50505050565b60008281526065602052604090206001015461052481610e2e565b61052e8383610e38565b505050565b600080610548670de0b6b3a764000085610ebe565b9050600061055888888887610eca565b90506000610578670de0b6b3a76400006105728486610f6c565b90610f78565b905061059b670de0b6b3a7640000610572836105958d8d8d610791565b90610f6c565b9998505050505050505050565b6105b36000336108ce565b6105f95760405162461bcd60e51b815260206004820152601760248201527621b0b63632b91034b9903737ba103a34329020b236b4b760491b60448201526064016104b5565b61061160008051602061172283398151915282610a77565b50565b6001600160a01b03811633146106845760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b60648201526084016104b5565b61068e8282610f84565b5050565b336000908152609a602052604081205460ff166106f15760405162461bcd60e51b815260206004820152601c60248201527f43616c6c6572206973206e6f7420426c656e64696e6720746f6b656e0000000060448201526064016104b5565b3360008181526099602052604081209161071090879087908790610eca565b808355436001909301929092555090505b9392505050565b6107336000336108ce565b6107795760405162461bcd60e51b815260206004820152601760248201527621b0b63632b91034b9903737ba103a34329020b236b4b760491b60448201526064016104b5565b61061160008051602061172283398151915282610509565b6000826000036107a357506000610721565b6107cc6107ba836107b48787610feb565b90610ebe565b61057285670de0b6b3a7640000610f6c565b949350505050565b6001600160a01b038116600090815260986020908152604080832081516060810183528154815260018201549381019390935260020154908201528161081b878787610791565b9050600082604001518261082f91906114ae565b90506000836040015183111561087c57602084015184516ec097ce7bc90715b34b9f1000000000916108619190610f6c565b61086b90846114d5565b610875919061151b565b90506108ab565b83604001518310156108ab578351670de0b6b3a76400009061089e90846114d5565b6108a8919061151b565b90505b98975050505050505050565b60006108c585858585610eca565b95945050505050565b60009182526065602090815260408084206001600160a01b0393909316845291905290205460ff1690565b610911600080516020611722833981519152336108ce565b61092d5760405162461bcd60e51b81526004016104b590611461565b6001600160a01b0381166000908152609a602052604090205460ff166109655760405162461bcd60e51b81526004016104b590611549565b6000816001600160a01b0316636c540baf6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156109a5573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906109c99190611580565b6001600160a01b0390921660009081526099602052604090206001019190915550565b610a04600080516020611722833981519152336108ce565b610a205760405162461bcd60e51b81526004016104b590611461565b6001600160a01b0382166000908152609a602052604090205460ff16610a585760405162461bcd60e51b81526004016104b590611549565b6001600160a01b03909116600090815260996020526040902060020155565b600082815260656020526040902060010154610a9281610e2e565b61052e8383610f84565b610ab4600080516020611722833981519152336108ce565b610ad05760405162461bcd60e51b81526004016104b590611461565b6001600160a01b038516610b265760405162461bcd60e51b815260206004820181905260248201527f4a756d70526174654d6f64656c56333a20496e76616c6964206164647265737360448201526064016104b5565b6001600160a01b0385166000908152609a60205260409020805460ff19166001179055610b5584848488610d9f565b6001600160a01b0385166000908152609960205260409020600201819055610b7c856108f9565b5050505050565b600054610100900460ff1615808015610ba35750600054600160ff909116105b80610bbd5750303b158015610bbd575060005460ff166001145b610c205760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084016104b5565b6000805460ff191660011790558015610c43576000805461ff0019166101001790555b610c4b610ff7565b610c56600033611064565b610c6e60008051602061172283398151915233611064565b6097829055801561068e576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15050565b610cd4600080516020611722833981519152336108ce565b610cf05760405162461bcd60e51b81526004016104b590611461565b6001600160a01b038116610d465760405162461bcd60e51b815260206004820181905260248201527f4a756d70526174654d6f64656c56333a20496e76616c6964206164647265737360448201526064016104b5565b6001600160a01b0381166000908152609a602052604090205460ff16610d7e5760405162461bcd60e51b81526004016104b590611549565b6001600160a01b03166000908152609a60205260409020805460ff19169055565b6001600160a01b0381166000908152609860205260409020609754610dc5908690610f78565b8155609754610dd5908590610f78565b600182018190556002820184905581546040805191825260208201929092529081018490527f865bfff1eb39dc370f97b2eb5990d963c50228429828ad935a4470166c711fc19060600160405180910390a15050505050565b610611813361106e565b610e4282826108ce565b61068e5760008281526065602090815260408083206001600160a01b03851684529091529020805460ff19166001179055610e7a3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b60006107218284611599565b6001600160a01b0381166000908152609960209081526040808320815160608101835281548152600182015493810193909352600201549082015281610f12878787876107d4565b90506000610f1f856110c7565b90506000610f2d83836114d5565b8451610f3991906115ac565b90506000808213610f4b576000610f4d565b815b9050846040015181111561059b57505050506040015195945050505050565b600061072182846115d4565b600061072182846115eb565b610f8e82826108ce565b1561068e5760008281526065602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b600061072182846115ff565b600054610100900460ff166110625760405162461bcd60e51b815260206004820152602b60248201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960448201526a6e697469616c697a696e6760a81b60648201526084016104b5565b565b61068e8282610e38565b61107882826108ce565b61068e57611085816110ed565b6110908360206110ff565b6040516020016110a1929190611636565b60408051601f198184030181529082905262461bcd60e51b82526104b5916004016116ab565b6001600160a01b03811660009081526099602052604081206001015461047b90436107b4565b606061047b6001600160a01b03831660145b6060600061110e8360026115d4565b6111199060026115ff565b67ffffffffffffffff811115611131576111316116de565b6040519080825280601f01601f19166020018201604052801561115b576020820181803683370190505b509050600360fc1b81600081518110611176576111766116f4565b60200101906001600160f81b031916908160001a905350600f60fb1b816001815181106111a5576111a56116f4565b60200101906001600160f81b031916908160001a90535060006111c98460026115d4565b6111d49060016115ff565b90505b600181111561124c576f181899199a1a9b1b9c1cb0b131b232b360811b85600f1660108110611208576112086116f4565b1a60f81b82828151811061121e5761121e6116f4565b60200101906001600160f81b031916908160001a90535060049490941c936112458161170a565b90506111d7565b5083156107215760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e7460448201526064016104b5565b6000602082840312156112ad57600080fd5b81356001600160e01b03198116811461072157600080fd5b6000602082840312156112d757600080fd5b5035919050565b80356001600160a01b03811681146112f557600080fd5b919050565b6000806000806080858703121561131057600080fd5b84359350602085013592506040850135915061132e606086016112de565b905092959194509250565b6000806040838503121561134c57600080fd5b8235915061135c602084016112de565b90509250929050565b600080600080600060a0868803121561137d57600080fd5b853594506020860135935060408601359250606086013591506113a2608087016112de565b90509295509295909350565b6000602082840312156113c057600080fd5b610721826112de565b6000806000606084860312156113de57600080fd5b505081359360208301359350604090920135919050565b6000806040838503121561140857600080fd5b611411836112de565b946020939093013593505050565b600080600080600060a0868803121561143757600080fd5b611440866112de565b97602087013597506040870135966060810135965060800135945092505050565b6020808252601b908201527f43616c6c6572206973206e6f7420746865204d6f64657261746f720000000000604082015260600190565b634e487b7160e01b600052601160045260246000fd5b81810360008312801583831316838312821617156114ce576114ce611498565b5092915050565b80820260008212600160ff1b841416156114f1576114f1611498565b818105831482151761047b5761047b611498565b634e487b7160e01b600052601260045260246000fd5b60008261152a5761152a611505565b600160ff1b82146000198414161561154457611544611498565b500590565b6020808252601a908201527f4a756d70526174654d6f64656c56333a204e6f7420666f756e64000000000000604082015260600190565b60006020828403121561159257600080fd5b5051919050565b8181038181111561047b5761047b611498565b80820182811260008312801582168215821617156115cc576115cc611498565b505092915050565b808202811582820484141761047b5761047b611498565b6000826115fa576115fa611505565b500490565b8082018082111561047b5761047b611498565b60005b8381101561162d578181015183820152602001611615565b50506000910152565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081526000835161166e816017850160208801611612565b7001034b99036b4b9b9b4b733903937b6329607d1b601791840191820152835161169f816028840160208801611612565b01602801949350505050565b60208152600082518060208401526116ca816040850160208701611612565b601f01601f19169190910160400192915050565b634e487b7160e01b600052604160045260246000fd5b634e487b7160e01b600052603260045260246000fd5b60008161171957611719611498565b50600019019056fe71f3d55856e4058ed06ee057d79ada615f65cdf5f9ee88181b914225088f834fa26469706673582212202da4c73323108e8e673aaa46a6ee1ee447d82fe28ce48904847de06df4e91ee764736f6c63430008130033
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 34 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.