// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0) (access/AccessControl.sol)pragmasolidity ^0.8.0;import"./IAccessControl.sol";
import"../utils/Context.sol";
import"../utils/Strings.sol";
import"../utils/introspection/ERC165.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:
*
* ```
* 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}:
*
* ```
* 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.
*/abstractcontractAccessControlisContext, IAccessControl, ERC165{
structRoleData {
mapping(address=>bool) members;
bytes32 adminRole;
}
mapping(bytes32=> RoleData) private _roles;
bytes32publicconstant 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._
*/modifieronlyRole(bytes32 role) {
_checkRole(role);
_;
}
/**
* @dev See {IERC165-supportsInterface}.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverridereturns (bool) {
return interfaceId ==type(IAccessControl).interfaceId||super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/functionhasRole(bytes32 role, address account) publicviewvirtualoverridereturns (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) internalviewvirtual{
_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) internalviewvirtual{
if (!hasRole(role, account)) {
revert(
string(
abi.encodePacked(
"AccessControl: account ",
Strings.toHexString(account),
" is missing role ",
Strings.toHexString(uint256(role), 32)
)
)
);
}
}
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {_setRoleAdmin}.
*/functiongetRoleAdmin(bytes32 role) publicviewvirtualoverridereturns (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.
*/functiongrantRole(bytes32 role, address account) publicvirtualoverrideonlyRole(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.
*/functionrevokeRole(bytes32 role, address account) publicvirtualoverrideonlyRole(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.
*/functionrenounceRole(bytes32 role, address account) publicvirtualoverride{
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) internalvirtual{
_grantRole(role, account);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/function_setRoleAdmin(bytes32 role, bytes32 adminRole) internalvirtual{
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) internalvirtual{
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) internalvirtual{
if (hasRole(role, account)) {
_roles[role].members[account] =false;
emit RoleRevoked(role, account, _msgSender());
}
}
}
Contract Source Code
File 2 of 16: BonusTracker.sol
// SPDX-License-Identifier: AGPL-3.0pragmasolidity ^0.8.10;import {ERC20} from"solmate/tokens/ERC20.sol";
import {ERC4626} from"solmate/mixins/ERC4626.sol";
import {FixedPointMathLib} from"solmate/utils/FixedPointMathLib.sol";
import {ReentrancyGuard} from"openzeppelin-contracts/security/ReentrancyGuard.sol";
abstractcontractBonusTrackerisERC4626, ReentrancyGuard{
usingFixedPointMathLibforuint256;
eventBonusPaid(addressindexed user, uint256 bonus);
eventBonusBurned(addressindexed user, uint256 bonus);
uint256internalconstant PRECISION =1e30;
/// @notice The last Unix timestamp (in seconds) when bonusPerTokenStored was updateduint64public lastBonusUpdateTime;
/// @notice The last stored bonusPerToken valueuint256public bonusPerTokenStored;
/// @notice The total bonus amount currently held by usersuint256public totalBonus;
/// @notice The bonusPerToken value when an account last compounded/withdrew bonusmapping(address=>uint256) public userBonusPerTokenPaid;
/// @notice The number of multiplier points compounded for an accountmapping(address=>uint256) public multiplierPointsOf;
/// @notice The bonusOf() value when an account last staked/withdrew bonusmapping(address=>uint256) public bonus;
constructor(ERC20 _asset, stringmemory _name, stringmemory _symbol) ERC4626(_asset, _name, _symbol) {
lastBonusUpdateTime =uint64(block.timestamp);
}
/// @notice Claim bonusfunctionboost() externalnonReentrantreturns (uint256 _bonus) {
_updateReward(msg.sender);
_updateBonus(msg.sender);
_bonus = bonus[msg.sender];
if (_bonus >0) {
bonus[msg.sender] =0;
multiplierPointsOf[msg.sender] += _bonus;
totalBonus += _bonus;
emit BonusPaid(msg.sender, _bonus);
}
}
/// @notice The latest time at which stakers are earning bonus.functionlastTimeBonusApplicable() publicviewreturns (uint64) {
returnuint64(block.timestamp);
}
/// @notice The amount of bonus tokens each staked token has earned so farfunctionbonusPerToken() externalviewreturns (uint256) {
return _bonusPerToken(lastTimeBonusApplicable());
}
/// @notice The amount of bonus tokens an account has accrued so far.functionbonusOf(address _account) externalviewreturns (uint256) {
return _earnedBonus(_account, _bonusPerToken(lastTimeBonusApplicable()));
}
function_earnedBonus(address _account, uint256 _bonusPerToken_) internalviewreturns (uint256) {
return balanceOf[_account].mulDivDown(_bonusPerToken_ - userBonusPerTokenPaid[_account], PRECISION)
+ bonus[_account];
}
function_bonusPerToken(uint256 _lastTimeBonusApplicable_) internalviewreturns (uint256) {
return bonusPerTokenStored + (_lastTimeBonusApplicable_ - lastBonusUpdateTime).mulDivDown(PRECISION, 365days);
}
function_updateBonus(address _account) internal{
// storage loadsuint64 lastTimeBonusApplicable_ = lastTimeBonusApplicable();
uint256 bonusPerToken_ = _bonusPerToken(lastTimeBonusApplicable_);
// accrue bonus
bonusPerTokenStored = bonusPerToken_;
lastBonusUpdateTime = lastTimeBonusApplicable_;
bonus[_account] = _earnedBonus(_account, bonusPerToken_);
userBonusPerTokenPaid[_account] = bonusPerToken_;
}
function_updateReward(address) internalvirtual{}
}
Contract Source Code
File 3 of 16: Context.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)pragmasolidity ^0.8.0;/**
* @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.
*/abstractcontractContext{
function_msgSender() internalviewvirtualreturns (address) {
returnmsg.sender;
}
function_msgData() internalviewvirtualreturns (bytescalldata) {
returnmsg.data;
}
}
Contract Source Code
File 4 of 16: DebtTracker.sol
// SPDX-License-Identifier: AGPL-3.0pragmasolidity ^0.8.10;import {FixedPointMathLib} from"solmate/utils/FixedPointMathLib.sol";
abstractcontractDebtTracker{
usingFixedPointMathLibforuint256;
eventDebtAdded(addressindexed user, uint256 debt, uint64 timestamp);
eventDebtPaid(addressindexed user, uint256 debt);
errorUserHasDebt();
/// @notice The initial debt for an accountmapping(address=>uint256) public debtOf;
/// @notice The debt start time for a usermapping(address=>uint64) public debtStartTimeFor;
/// @notice The treasury address that recieves any paid debtaddresspublic treasury;
/// @notice The amount of current debt left for an accountfunctiondebtFor(address _account) externalviewreturns (uint256) {
return _debtFor(_account);
}
function_updateDebt(address _account) internal{
// storage loadsuint64 now_ =uint64(block.timestamp);
uint256 startTime_ = debtStartTimeFor[_account];
// if 30 days has passed: eliminate debtif (now_ - startTime_ >=30days) {
debtOf[_account] =0;
}
}
function_debtFor(address _account) internalviewreturns (uint256) {
// storage loadsuint64 now_ =uint64(block.timestamp);
uint256 startTime = debtStartTimeFor[_account];
uint256 delta = now_ - startTime;
uint256 debt = debtOf[_account];
// if 30 days passed: no debtif (delta >=30days) return0;
// otherwise debt decreases linearly to 0 over 30 daysreturn (debt - debt.mulDivDown(delta, 30days));
}
}
Contract Source Code
File 5 of 16: ERC165.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)pragmasolidity ^0.8.0;import"./IERC165.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.
*/abstractcontractERC165isIERC165{
/**
* @dev See {IERC165-supportsInterface}.
*/functionsupportsInterface(bytes4 interfaceId) publicviewvirtualoverridereturns (bool) {
return interfaceId ==type(IERC165).interfaceId;
}
}
Contract Source Code
File 6 of 16: ERC20.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Modern and gas efficient ERC20 + EIP-2612 implementation./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)/// @author Modified from Uniswap (https://github.com/Uniswap/uniswap-v2-core/blob/master/contracts/UniswapV2ERC20.sol)/// @dev Do not manually set balances without updating totalSupply, as the sum of all user balances must not exceed it.abstractcontractERC20{
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventTransfer(addressindexedfrom, addressindexed to, uint256 amount);
eventApproval(addressindexed owner, addressindexed spender, uint256 amount);
/*//////////////////////////////////////////////////////////////
METADATA STORAGE
//////////////////////////////////////////////////////////////*/stringpublic name;
stringpublic symbol;
uint8publicimmutable decimals;
/*//////////////////////////////////////////////////////////////
ERC20 STORAGE
//////////////////////////////////////////////////////////////*/uint256public totalSupply;
mapping(address=>uint256) public balanceOf;
mapping(address=>mapping(address=>uint256)) public allowance;
/*//////////////////////////////////////////////////////////////
EIP-2612 STORAGE
//////////////////////////////////////////////////////////////*/uint256internalimmutable INITIAL_CHAIN_ID;
bytes32internalimmutable INITIAL_DOMAIN_SEPARATOR;
mapping(address=>uint256) public nonces;
/*//////////////////////////////////////////////////////////////
CONSTRUCTOR
//////////////////////////////////////////////////////////////*/constructor(stringmemory _name,
stringmemory _symbol,
uint8 _decimals
) {
name = _name;
symbol = _symbol;
decimals = _decimals;
INITIAL_CHAIN_ID =block.chainid;
INITIAL_DOMAIN_SEPARATOR = computeDomainSeparator();
}
/*//////////////////////////////////////////////////////////////
ERC20 LOGIC
//////////////////////////////////////////////////////////////*/functionapprove(address spender, uint256 amount) publicvirtualreturns (bool) {
allowance[msg.sender][spender] = amount;
emit Approval(msg.sender, spender, amount);
returntrue;
}
functiontransfer(address to, uint256 amount) publicvirtualreturns (bool) {
balanceOf[msg.sender] -= amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(msg.sender, to, amount);
returntrue;
}
functiontransferFrom(addressfrom,
address to,
uint256 amount
) publicvirtualreturns (bool) {
uint256 allowed = allowance[from][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[from][msg.sender] = allowed - amount;
balanceOf[from] -= amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(from, to, amount);
returntrue;
}
/*//////////////////////////////////////////////////////////////
EIP-2612 LOGIC
//////////////////////////////////////////////////////////////*/functionpermit(address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) publicvirtual{
require(deadline >=block.timestamp, "PERMIT_DEADLINE_EXPIRED");
// Unchecked because the only math done is incrementing// the owner's nonce which cannot realistically overflow.unchecked {
address recoveredAddress =ecrecover(
keccak256(
abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR(),
keccak256(
abi.encode(
keccak256(
"Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"
),
owner,
spender,
value,
nonces[owner]++,
deadline
)
)
)
),
v,
r,
s
);
require(recoveredAddress !=address(0) && recoveredAddress == owner, "INVALID_SIGNER");
allowance[recoveredAddress][spender] = value;
}
emit Approval(owner, spender, value);
}
functionDOMAIN_SEPARATOR() publicviewvirtualreturns (bytes32) {
returnblock.chainid== INITIAL_CHAIN_ID ? INITIAL_DOMAIN_SEPARATOR : computeDomainSeparator();
}
functioncomputeDomainSeparator() internalviewvirtualreturns (bytes32) {
returnkeccak256(
abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes(name)),
keccak256("1"),
block.chainid,
address(this)
)
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL MINT/BURN LOGIC
//////////////////////////////////////////////////////////////*/function_mint(address to, uint256 amount) internalvirtual{
totalSupply += amount;
// Cannot overflow because the sum of all user// balances can't exceed the max uint256 value.unchecked {
balanceOf[to] += amount;
}
emit Transfer(address(0), to, amount);
}
function_burn(addressfrom, uint256 amount) internalvirtual{
balanceOf[from] -= amount;
// Cannot underflow because a user's balance// will never be larger than the total supply.unchecked {
totalSupply -= amount;
}
emit Transfer(from, address(0), amount);
}
}
Contract Source Code
File 7 of 16: ERC4626.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;import {ERC20} from"../tokens/ERC20.sol";
import {SafeTransferLib} from"../utils/SafeTransferLib.sol";
import {FixedPointMathLib} from"../utils/FixedPointMathLib.sol";
/// @notice Minimal ERC4626 tokenized Vault implementation./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/mixins/ERC4626.sol)abstractcontractERC4626isERC20{
usingSafeTransferLibforERC20;
usingFixedPointMathLibforuint256;
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/eventDeposit(addressindexed caller, addressindexed owner, uint256 assets, uint256 shares);
eventWithdraw(addressindexed caller,
addressindexed receiver,
addressindexed owner,
uint256 assets,
uint256 shares
);
/*//////////////////////////////////////////////////////////////
IMMUTABLES
//////////////////////////////////////////////////////////////*/
ERC20 publicimmutable asset;
constructor(
ERC20 _asset,
stringmemory _name,
stringmemory _symbol
) ERC20(_name, _symbol, _asset.decimals()) {
asset = _asset;
}
/*//////////////////////////////////////////////////////////////
DEPOSIT/WITHDRAWAL LOGIC
//////////////////////////////////////////////////////////////*/functiondeposit(uint256 assets, address receiver) publicvirtualreturns (uint256 shares) {
// Check for rounding error since we round down in previewDeposit.require((shares = previewDeposit(assets)) !=0, "ZERO_SHARES");
// Need to transfer before minting or ERC777s could reenter.
asset.safeTransferFrom(msg.sender, address(this), assets);
_mint(receiver, shares);
emit Deposit(msg.sender, receiver, assets, shares);
afterDeposit(assets, shares);
}
functionmint(uint256 shares, address receiver) publicvirtualreturns (uint256 assets) {
assets = previewMint(shares); // No need to check for rounding error, previewMint rounds up.// Need to transfer before minting or ERC777s could reenter.
asset.safeTransferFrom(msg.sender, address(this), assets);
_mint(receiver, shares);
emit Deposit(msg.sender, receiver, assets, shares);
afterDeposit(assets, shares);
}
functionwithdraw(uint256 assets,
address receiver,
address owner
) publicvirtualreturns (uint256 shares) {
shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.if (msg.sender!= owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
}
beforeWithdraw(assets, shares);
_burn(owner, shares);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
asset.safeTransfer(receiver, assets);
}
functionredeem(uint256 shares,
address receiver,
address owner
) publicvirtualreturns (uint256 assets) {
if (msg.sender!= owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
}
// Check for rounding error since we round down in previewRedeem.require((assets = previewRedeem(shares)) !=0, "ZERO_ASSETS");
beforeWithdraw(assets, shares);
_burn(owner, shares);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
asset.safeTransfer(receiver, assets);
}
/*//////////////////////////////////////////////////////////////
ACCOUNTING LOGIC
//////////////////////////////////////////////////////////////*/functiontotalAssets() publicviewvirtualreturns (uint256);
functionconvertToShares(uint256 assets) publicviewvirtualreturns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.return supply ==0 ? assets : assets.mulDivDown(supply, totalAssets());
}
functionconvertToAssets(uint256 shares) publicviewvirtualreturns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.return supply ==0 ? shares : shares.mulDivDown(totalAssets(), supply);
}
functionpreviewDeposit(uint256 assets) publicviewvirtualreturns (uint256) {
return convertToShares(assets);
}
functionpreviewMint(uint256 shares) publicviewvirtualreturns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.return supply ==0 ? shares : shares.mulDivUp(totalAssets(), supply);
}
functionpreviewWithdraw(uint256 assets) publicviewvirtualreturns (uint256) {
uint256 supply = totalSupply; // Saves an extra SLOAD if totalSupply is non-zero.return supply ==0 ? assets : assets.mulDivUp(supply, totalAssets());
}
functionpreviewRedeem(uint256 shares) publicviewvirtualreturns (uint256) {
return convertToAssets(shares);
}
/*//////////////////////////////////////////////////////////////
DEPOSIT/WITHDRAWAL LIMIT LOGIC
//////////////////////////////////////////////////////////////*/functionmaxDeposit(address) publicviewvirtualreturns (uint256) {
returntype(uint256).max;
}
functionmaxMint(address) publicviewvirtualreturns (uint256) {
returntype(uint256).max;
}
functionmaxWithdraw(address owner) publicviewvirtualreturns (uint256) {
return convertToAssets(balanceOf[owner]);
}
functionmaxRedeem(address owner) publicviewvirtualreturns (uint256) {
return balanceOf[owner];
}
/*//////////////////////////////////////////////////////////////
INTERNAL HOOKS LOGIC
//////////////////////////////////////////////////////////////*/functionbeforeWithdraw(uint256 assets, uint256 shares) internalvirtual{}
functionafterDeposit(uint256 assets, uint256 shares) internalvirtual{}
}
Contract Source Code
File 8 of 16: FixedPointMathLib.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;/// @notice Arithmetic library with operations for fixed-point numbers./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)/// @author Inspired by USM (https://github.com/usmfum/USM/blob/master/contracts/WadMath.sol)libraryFixedPointMathLib{
/*//////////////////////////////////////////////////////////////
SIMPLIFIED FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/uint256internalconstant MAX_UINT256 =2**256-1;
uint256internalconstant WAD =1e18; // The scalar of ETH and most ERC20s.functionmulWadDown(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivDown(x, y, WAD); // Equivalent to (x * y) / WAD rounded down.
}
functionmulWadUp(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivUp(x, y, WAD); // Equivalent to (x * y) / WAD rounded up.
}
functiondivWadDown(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivDown(x, WAD, y); // Equivalent to (x * WAD) / y rounded down.
}
functiondivWadUp(uint256 x, uint256 y) internalpurereturns (uint256) {
return mulDivUp(x, WAD, y); // Equivalent to (x * WAD) / y rounded up.
}
/*//////////////////////////////////////////////////////////////
LOW LEVEL FIXED POINT OPERATIONS
//////////////////////////////////////////////////////////////*/functionmulDivDown(uint256 x,
uint256 y,
uint256 denominator
) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))ifiszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// Divide x * y by the denominator.
z :=div(mul(x, y), denominator)
}
}
functionmulDivUp(uint256 x,
uint256 y,
uint256 denominator
) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
// Equivalent to require(denominator != 0 && (y == 0 || x <= type(uint256).max / y))ifiszero(mul(denominator, iszero(mul(y, gt(x, div(MAX_UINT256, y)))))) {
revert(0, 0)
}
// If x * y modulo the denominator is strictly greater than 0,// 1 is added to round up the division of x * y by the denominator.
z :=add(gt(mod(mul(x, y), denominator), 0), div(mul(x, y), denominator))
}
}
functionrpow(uint256 x,
uint256 n,
uint256 scalar
) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
switch x
case0 {
switch n
case0 {
// 0 ** 0 = 1
z := scalar
}
default {
// 0 ** n = 0
z :=0
}
}
default {
switchmod(n, 2)
case0 {
// If n is even, store scalar in z for now.
z := scalar
}
default {
// If n is odd, store x in z for now.
z := x
}
// Shifting right by 1 is like dividing by 2.let half :=shr(1, scalar)
for {
// Shift n right by 1 before looping to halve it.
n :=shr(1, n)
} n {
// Shift n right by 1 each iteration to halve it.
n :=shr(1, n)
} {
// Revert immediately if x ** 2 would overflow.// Equivalent to iszero(eq(div(xx, x), x)) here.ifshr(128, x) {
revert(0, 0)
}
// Store x squared.let xx :=mul(x, x)
// Round to the nearest number.let xxRound :=add(xx, half)
// Revert if xx + half overflowed.iflt(xxRound, xx) {
revert(0, 0)
}
// Set x to scaled xxRound.
x :=div(xxRound, scalar)
// If n is even:ifmod(n, 2) {
// Compute z * x.let zx :=mul(z, x)
// If z * x overflowed:ifiszero(eq(div(zx, x), z)) {
// Revert if x is non-zero.ifiszero(iszero(x)) {
revert(0, 0)
}
}
// Round to the nearest number.let zxRound :=add(zx, half)
// Revert if zx + half overflowed.iflt(zxRound, zx) {
revert(0, 0)
}
// Return properly scaled zxRound.
z :=div(zxRound, scalar)
}
}
}
}
}
/*//////////////////////////////////////////////////////////////
GENERAL NUMBER UTILITIES
//////////////////////////////////////////////////////////////*/functionsqrt(uint256 x) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
let y := x // We start y at x, which will help us make our initial estimate.
z :=181// The "correct" value is 1, but this saves a multiplication later.// This segment is to get a reasonable initial estimate for the Babylonian method. With a bad// start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.// We check y >= 2^(k + 8) but shift right by k bits// each branch to ensure that if x >= 256, then y >= 256.ifiszero(lt(y, 0x10000000000000000000000000000000000)) {
y :=shr(128, y)
z :=shl(64, z)
}
ifiszero(lt(y, 0x1000000000000000000)) {
y :=shr(64, y)
z :=shl(32, z)
}
ifiszero(lt(y, 0x10000000000)) {
y :=shr(32, y)
z :=shl(16, z)
}
ifiszero(lt(y, 0x1000000)) {
y :=shr(16, y)
z :=shl(8, z)
}
// Goal was to get z*z*y within a small factor of x. More iterations could// get y in a tighter range. Currently, we will have y in [256, 256*2^16).// We ensured y >= 256 so that the relative difference between y and y+1 is small.// That's not possible if x < 256 but we can just verify those cases exhaustively.// Now, z*z*y <= x < z*z*(y+1), and y <= 2^(16+8), and either y >= 256, or x < 256.// Correctness can be checked exhaustively for x < 256, so we assume y >= 256.// Then z*sqrt(y) is within sqrt(257)/sqrt(256) of sqrt(x), or about 20bps.// For s in the range [1/256, 256], the estimate f(s) = (181/1024) * (s+1) is in the range// (1/2.84 * sqrt(s), 2.84 * sqrt(s)), with largest error when s = 1 and when s = 256 or 1/256.// Since y is in [256, 256*2^16), let a = y/65536, so that a is in [1/256, 256). Then we can estimate// sqrt(y) using sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2^18.// There is no overflow risk here since y < 2^136 after the first branch above.
z :=shr(18, mul(z, add(y, 65536))) // A mul() is saved from starting z at 181.// Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
z :=shr(1, add(z, div(x, z)))
// If x+1 is a perfect square, the Babylonian method cycles between// floor(sqrt(x)) and ceil(sqrt(x)). This statement ensures we return floor.// See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division// Since the ceil is rare, we save gas on the assignment and repeat division in the rare case.// If you don't care whether the floor or ceil square root is returned, you can remove this statement.
z :=sub(z, lt(div(x, z), z))
}
}
functionunsafeMod(uint256 x, uint256 y) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
// Mod x by y. Note this will return// 0 instead of reverting if y is zero.
z :=mod(x, y)
}
}
functionunsafeDiv(uint256 x, uint256 y) internalpurereturns (uint256 r) {
/// @solidity memory-safe-assemblyassembly {
// Divide x by y. Note this will return// 0 instead of reverting if y is zero.
r :=div(x, y)
}
}
functionunsafeDivUp(uint256 x, uint256 y) internalpurereturns (uint256 z) {
/// @solidity memory-safe-assemblyassembly {
// Add 1 to x * y if x % y > 0. Note this will// return 0 instead of reverting if y is zero.
z :=add(gt(mod(x, y), 0), div(x, y))
}
}
}
Contract Source Code
File 9 of 16: IAccessControl.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol)pragmasolidity ^0.8.0;/**
* @dev External interface of AccessControl declared to support ERC165 detection.
*/interfaceIAccessControl{
/**
* @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._
*/eventRoleAdminChanged(bytes32indexed role, bytes32indexed previousAdminRole, bytes32indexed 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}.
*/eventRoleGranted(bytes32indexed role, addressindexed account, addressindexed 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`)
*/eventRoleRevoked(bytes32indexed role, addressindexed account, addressindexed sender);
/**
* @dev Returns `true` if `account` has been granted `role`.
*/functionhasRole(bytes32 role, address account) externalviewreturns (bool);
/**
* @dev Returns the admin role that controls `role`. See {grantRole} and
* {revokeRole}.
*
* To change a role's admin, use {AccessControl-_setRoleAdmin}.
*/functiongetRoleAdmin(bytes32 role) externalviewreturns (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.
*/functiongrantRole(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.
*/functionrevokeRole(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`.
*/functionrenounceRole(bytes32 role, address account) external;
}
Contract Source Code
File 10 of 16: IERC165.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)pragmasolidity ^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}.
*/interfaceIERC165{
/**
* @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.
*/functionsupportsInterface(bytes4 interfaceId) externalviewreturns (bool);
}
Contract Source Code
File 11 of 16: Math.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)pragmasolidity ^0.8.0;/**
* @dev Standard math utilities missing in the Solidity language.
*/libraryMath{
enumRounding {
Down, // Toward negative infinity
Up, // Toward infinity
Zero // Toward zero
}
/**
* @dev Returns the largest of two numbers.
*/functionmax(uint256 a, uint256 b) internalpurereturns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/functionmin(uint256 a, uint256 b) internalpurereturns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/functionaverage(uint256 a, uint256 b) internalpurereturns (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.
*/functionceilDiv(uint256 a, uint256 b) internalpurereturns (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.
*/functionmulDiv(uint256 x,
uint256 y,
uint256 denominator
) internalpurereturns (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 productuint256 prod1; // Most significant 256 bits of the productassembly {
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) {
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.require(denominator > prod1);
///////////////////////////////////////////////// 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.
*/functionmulDiv(uint256 x,
uint256 y,
uint256 denominator,
Rounding rounding
) internalpurereturns (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).
*/functionsqrt(uint256 a) internalpurereturns (uint256) {
if (a ==0) {
return0;
}
// 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.
*/functionsqrt(uint256 a, Rounding rounding) internalpurereturns (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.
*/functionlog2(uint256 value) internalpurereturns (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.
*/functionlog2(uint256 value, Rounding rounding) internalpurereturns (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.
*/functionlog10(uint256 value) internalpurereturns (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.
*/functionlog10(uint256 value, Rounding rounding) internalpurereturns (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.
*/functionlog256(uint256 value) internalpurereturns (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 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/functionlog256(uint256 value, Rounding rounding) internalpurereturns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (rounding == Rounding.Up &&1<< (result *8) < value ? 1 : 0);
}
}
}
Contract Source Code
File 12 of 16: ReentrancyGuard.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)pragmasolidity ^0.8.0;/**
* @dev Contract module that helps prevent reentrant calls to a function.
*
* Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
* available, which can be applied to functions to make sure there are no nested
* (reentrant) calls to them.
*
* Note that because there is a single `nonReentrant` guard, functions marked as
* `nonReentrant` may not call one another. This can be worked around by making
* those functions `private`, and then adding `external` `nonReentrant` entry
* points to them.
*
* TIP: If you would like to learn more about reentrancy and alternative ways
* to protect against it, check out our blog post
* https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
*/abstractcontractReentrancyGuard{
// Booleans are more expensive than uint256 or any type that takes up a full// word because each write operation emits an extra SLOAD to first read the// slot's contents, replace the bits taken up by the boolean, and then write// back. This is the compiler's defense against contract upgrades and// pointer aliasing, and it cannot be disabled.// The values being non-zero value makes deployment a bit more expensive,// but in exchange the refund on every call to nonReentrant will be lower in// amount. Since refunds are capped to a percentage of the total// transaction's gas, it is best to keep them low in cases like this one, to// increase the likelihood of the full refund coming into effect.uint256privateconstant _NOT_ENTERED =1;
uint256privateconstant _ENTERED =2;
uint256private _status;
constructor() {
_status = _NOT_ENTERED;
}
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Calling a `nonReentrant` function from another `nonReentrant`
* function is not supported. It is possible to prevent this from happening
* by making the `nonReentrant` function external, and making it call a
* `private` function that does the actual work.
*/modifiernonReentrant() {
_nonReentrantBefore();
_;
_nonReentrantAfter();
}
function_nonReentrantBefore() private{
// On the first call to nonReentrant, _status will be _NOT_ENTEREDrequire(_status != _ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
_status = _ENTERED;
}
function_nonReentrantAfter() private{
// By storing the original value once again, a refund is triggered (see// https://eips.ethereum.org/EIPS/eip-2200)
_status = _NOT_ENTERED;
}
}
Contract Source Code
File 13 of 16: RewardTracker.sol
// SPDX-License-Identifier: AGPL-3.0pragmasolidity ^0.8.13;import {ERC20} from"solmate/tokens/ERC20.sol";
import {ERC4626} from"solmate/mixins/ERC4626.sol";
import {SafeTransferLib} from"solmate/utils/SafeTransferLib.sol";
import {FixedPointMathLib} from"solmate/utils/FixedPointMathLib.sol";
import {AccessControl} from"openzeppelin-contracts/access/AccessControl.sol";
import {ReentrancyGuard} from"openzeppelin-contracts/security/ReentrancyGuard.sol";
import {BonusTracker} from"./BonusTracker.sol";
import {DebtTracker} from"./DebtTracker.sol";
import {CallerNotAdmin} from"../errors/scErrors.sol";
contractRewardTrackerisBonusTracker, DebtTracker, AccessControl{
usingSafeTransferLibforERC20;
usingFixedPointMathLibforuint256;
eventRewardAdded(uint256 reward);
eventRewardPaid(addressindexed user, uint256 reward);
eventVaultAdded(address vault);
eventTreasuryUpdated(addressindexed user, address newTreasury);
errorCallerNotDistirbutor();
errorVaultNotWhitelisted();
errorVaultAssetNotSupported();
errorSenderHasToBeReceiver();
errorTreasuryCannotBeZero();
/// @notice The last Unix timestamp (in seconds) when rewardPerTokenStored was updateduint64public lastUpdateTime;
/// @notice The Unix timestamp (in seconds) at which the current reward period endsuint64public periodFinish;
/// @notice The per-second rate at which rewardPerToken increasesuint256public rewardRate;
/// @notice The last stored rewardPerToken valueuint256public rewardPerTokenStored;
/// @notice The last stored balance of reward tokensuint256public rewardBalanceStored;
/// @notice Role allowed to call notifyReward()bytes32publicconstant DISTRIBUTOR =keccak256("DISTRIBUTOR");
/// @notice The rewardPerToken value when an account last staked/withdrew/withdrew rewardsmapping(address=>uint256) public userRewardPerTokenPaid;
/// @notice The earned() value when an account last staked/withdrew/withdrew rewardsmapping(address=>uint256) public rewards;
/// @notice A whitelist of vaults staking contract is collecting fees frommapping(address=>bool) public isVault;
/// @notice The token being rewarded to stakers
ERC20 publicimmutable rewardToken;
/// @notice The length of each reward period, in secondsuint64publicimmutable duration;
constructor(address admin,
address _treasury,
address _stakeToken,
stringmemory _name,
stringmemory _symbol,
address _rewardToken,
uint64 _duration
) BonusTracker(ERC20(_stakeToken), _name, _symbol) {
_grantRole(DEFAULT_ADMIN_ROLE, admin);
if (_treasury ==address(0)) revert TreasuryCannotBeZero();
treasury = _treasury;
rewardToken = ERC20(_rewardToken);
duration = _duration;
}
modifieronlyDistributor() {
if (!hasRole(DISTRIBUTOR, msg.sender)) revert CallerNotDistirbutor();
_;
}
modifieronlyAdmin() {
if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) revert CallerNotAdmin();
_;
}
functiontotalAssets() publicviewoverridereturns (uint256) {
return totalSupply;
}
functiondeposit(uint256 _assets, address _receiver) publicoverridereturns (uint256 shares) {
// sender needs to be the receiverif (_receiver !=msg.sender) revert SenderHasToBeReceiver();
// if user has debt no new deposits are allowed
_updateDebt(_receiver);
if (debtOf[_receiver] >0) revert UserHasDebt();
_updateReward(_receiver);
_updateBonus(_receiver);
shares =super.deposit(_assets, _receiver);
}
functionmint(uint256 _shares, address _receiver) publicoverridereturns (uint256 assets) {
// sender needs to be the receiverif (_receiver !=msg.sender) revert SenderHasToBeReceiver();
// if user has debt no new deposits are allowed
_updateDebt(_receiver);
if (debtOf[_receiver] >0) revert UserHasDebt();
_updateReward(_receiver);
_updateBonus(_receiver);
assets =super.mint(_shares, _receiver);
}
functionafterDeposit(uint256 _assets, uint256) internaloverride{
// debt starts at 10% the deposited amount, decreases linearly over 30 daysuint256 debt = _assets.mulWadDown(0.1e18);
uint64 now_ =uint64(block.timestamp);
debtOf[msg.sender] = debt;
debtStartTimeFor[msg.sender] = now_;
emit DebtAdded(msg.sender, debt, now_);
}
functiontransfer(address _to, uint256 _amount) publicoverridereturns (bool) {
// if user has debt transfers are not allowed
_updateDebt(msg.sender);
if (debtOf[msg.sender] >0) revert UserHasDebt();
_updateReward(msg.sender);
_updateReward(_to);
_updateBonus(msg.sender);
_updateBonus(_to);
_burnMultiplierPoints(_amount, msg.sender);
returnsuper.transfer(_to, _amount);
}
functiontransferFrom(address _from, address _to, uint256 _amount) publicoverridereturns (bool) {
// if user has debt transfers are not allowed
_updateDebt(_from);
if (debtOf[_from] >0) revert UserHasDebt();
_updateReward(_from);
_updateReward(_to);
_updateBonus(_from);
_updateBonus(_to);
_burnMultiplierPoints(_amount, _from);
returnsuper.transferFrom(_from, _to, _amount);
}
/// @notice Withdraws all earned rewardsfunctionclaimRewards(address _receiver) externalnonReentrantreturns (uint256 reward) {
_updateReward(_receiver);
reward = rewards[_receiver];
if (reward >0) {
rewards[_receiver] =0;
rewardBalanceStored -= reward;
rewardToken.safeTransfer(_receiver, reward);
emit RewardPaid(_receiver, reward);
}
}
/// @notice The latest time at which stakers are earning rewards.functionlastTimeRewardApplicable() publicviewreturns (uint64) {
returnblock.timestamp< periodFinish ? uint64(block.timestamp) : periodFinish;
}
/// @notice The amount of reward tokens each staked token has earned so farfunctionrewardPerToken() externalviewreturns (uint256) {
return _calcRewardPerToken(lastTimeRewardApplicable(), rewardRate);
}
/// @notice The amount of reward tokens an account has accrued so far. Does not/// include already withdrawn rewards.functionearned(address _account) externalviewreturns (uint256) {
return _earned(_account, _calcRewardPerToken(lastTimeRewardApplicable(), rewardRate));
}
/// @notice Starts a new reward distribution period. The reward tokens must have already/// been transferred to this contract before calling this function. If it is called/// when a reward period is still active, a new reward period will begin from the time/// of calling this function, using the leftover rewards from the old reward period plus/// the newly sent rewards as the reward.functionstartRewardsDistribution() external{
_startRewardsDistribution();
}
/// @notice Lets a reward distributor fetch performance fees from/// a vault and start a new reward period.functionfetchRewards(ERC4626 _vault) externalonlyDistributor{
if (!isVault[address(_vault)]) revert VaultNotWhitelisted();
_vault.redeem(_vault.balanceOf(address(this)), address(this), address(this));
_startRewardsDistribution();
}
/// @notice Lets an admin add a vault for collecting fees from.functionaddVault(address _vault) externalonlyAdmin{
if (ERC4626(_vault).asset() != rewardToken) revert VaultAssetNotSupported();
isVault[_vault] =true;
emit VaultAdded(_vault);
}
/// @notice set the treasury address/// @param _newTreasury the new treasury addressfunctionsetTreasury(address _newTreasury) externalonlyAdmin{
if (_newTreasury ==address(0)) revert TreasuryCannotBeZero();
treasury = _newTreasury;
emit TreasuryUpdated(msg.sender, _newTreasury);
}
function_startRewardsDistribution() internal{
/// -----------------------------------------------------------------------/// Validation/// -----------------------------------------------------------------------uint256 rewardBalanceCurrent = rewardToken.balanceOf(address(this));
uint256 rewardBalanceStored_ = rewardBalanceStored;
if (rewardBalanceCurrent == rewardBalanceStored_) {
return;
}
/// -----------------------------------------------------------------------/// Storage loads/// -----------------------------------------------------------------------uint256 rewardRate_ = rewardRate;
uint64 periodFinish_ = periodFinish;
uint64 lastTimeRewardApplicable_ = lastTimeRewardApplicable();
uint64 duration_ = duration;
/// -----------------------------------------------------------------------/// State updates/// -----------------------------------------------------------------------// accrue rewards
rewardPerTokenStored = _calcRewardPerToken(lastTimeRewardApplicable_, rewardRate_);
// record new rewarduint256 reward = rewardBalanceCurrent - rewardBalanceStored_;
rewardBalanceStored = rewardBalanceCurrent;
uint256 newRewardRate;
if (block.timestamp>= periodFinish_) {
newRewardRate = reward / duration_;
} else {
uint256 remaining = periodFinish_ -block.timestamp;
uint256 leftover = remaining * rewardRate_;
newRewardRate = (reward + leftover) / duration_;
}
rewardRate = newRewardRate;
lastUpdateTime =uint64(block.timestamp);
periodFinish =uint64(block.timestamp+ duration_);
emit RewardAdded(reward);
}
functionwithdraw(uint256 assets, address receiver, address owner) publicoverridereturns (uint256 shares) {
shares = previewWithdraw(assets); // No need to check for rounding error, previewWithdraw rounds up.if (msg.sender!= owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
}
// if user has debt normal withdrawals are not allowed, instead use payDebt() first
_updateDebt(owner);
if (debtOf[owner] >0) revert UserHasDebt();
_updateReward(owner);
_updateBonus(owner);
_burnMultiplierPoints(assets, owner);
_burn(owner, shares);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
asset.safeTransfer(receiver, assets);
}
functionredeem(uint256 shares, address receiver, address owner) publicoverridereturns (uint256 assets) {
if (msg.sender!= owner) {
uint256 allowed = allowance[owner][msg.sender]; // Saves gas for limited approvals.if (allowed !=type(uint256).max) allowance[owner][msg.sender] = allowed - shares;
}
// Check for rounding error since we round down in previewRedeem.require((assets = previewRedeem(shares)) !=0, "ZERO_ASSETS");
// if user has debt normal withdrawals are not allowed, instead use payDebt() first
_updateDebt(owner);
if (debtOf[owner] >0) revert UserHasDebt();
_updateReward(owner);
_updateBonus(owner);
_burnMultiplierPoints(assets, owner);
_burn(owner, shares);
emit Withdraw(msg.sender, receiver, owner, assets, shares);
asset.safeTransfer(receiver, assets);
}
/// @notice Function for paying all debt for an account at oncefunctionpayDebt() externalnonReentrant{
uint256 debt = _debtFor(msg.sender);
debtOf[msg.sender] =0;
// pay debt by withdrawing sQuartz with treasury as receiver
withdraw(debt, treasury, msg.sender);
emit DebtPaid(msg.sender, debt);
}
function_earned(address _account, uint256 rewardPerToken_) internalviewreturns (uint256) {
uint256 accountBalance = balanceOf[_account] + multiplierPointsOf[_account];
return
accountBalance.mulDivDown(rewardPerToken_ - userRewardPerTokenPaid[_account], PRECISION) + rewards[_account];
}
function_calcRewardPerToken(uint256 _lastTimeRewardApplicable, uint256 _rewardRate)
internalviewreturns (uint256)
{
uint256 totalSupply_ = totalSupply + totalBonus;
if (totalSupply_ ==0) {
return rewardPerTokenStored;
}
return rewardPerTokenStored
+ _rewardRate.mulDivDown((_lastTimeRewardApplicable - lastUpdateTime) * PRECISION, totalSupply_);
}
function_updateReward(address _account) internaloverride{
// storage loadsuint64 lastTimeRewardApplicable_ = lastTimeRewardApplicable();
uint256 rewardPerToken_ = _calcRewardPerToken(lastTimeRewardApplicable_, rewardRate);
// accrue rewards
rewardPerTokenStored = rewardPerToken_;
lastUpdateTime = lastTimeRewardApplicable_;
rewards[_account] = _earned(_account, rewardPerToken_);
userRewardPerTokenPaid[_account] = rewardPerToken_;
}
// burn multiplier pointsfunction_burnMultiplierPoints(uint256 _amount, address _sender) internal{
uint256 bonus_ = multiplierPointsOf[_sender];
// return if no bonus pointsif (bonus_ ==0) return;
// otherwise burn an equivalent percentageuint256 balance = balanceOf[_sender];
bonus_ = bonus_.mulDivDown(_amount, balance);
multiplierPointsOf[_sender] -= bonus_;
totalBonus -= bonus_;
emit BonusBurned(_sender, bonus_);
}
}
Contract Source Code
File 14 of 16: SafeTransferLib.sol
// SPDX-License-Identifier: AGPL-3.0-onlypragmasolidity >=0.8.0;import {ERC20} from"../tokens/ERC20.sol";
/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values./// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)/// @dev Use with caution! Some functions in this library knowingly create dirty bits at the destination of the free memory pointer./// @dev Note that none of the functions in this library check that a token has code at all! That responsibility is delegated to the caller.librarySafeTransferLib{
/*//////////////////////////////////////////////////////////////
ETH OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferETH(address to, uint256 amount) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Transfer the ETH and store if it succeeded or not.
success :=call(gas(), to, amount, 0, 0, 0, 0)
}
require(success, "ETH_TRANSFER_FAILED");
}
/*//////////////////////////////////////////////////////////////
ERC20 OPERATIONS
//////////////////////////////////////////////////////////////*/functionsafeTransferFrom(
ERC20 token,
addressfrom,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0x23b872dd00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), from) // Append the "from" argument.mstore(add(freeMemoryPointer, 36), to) // Append the "to" argument.mstore(add(freeMemoryPointer, 68), amount) // Append the "amount" argument.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 100 because the length of our calldata totals up like so: 4 + 32 * 3.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 100, 0, 32)
)
}
require(success, "TRANSFER_FROM_FAILED");
}
functionsafeTransfer(
ERC20 token,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0xa9059cbb00000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "TRANSFER_FAILED");
}
functionsafeApprove(
ERC20 token,
address to,
uint256 amount
) internal{
bool success;
/// @solidity memory-safe-assemblyassembly {
// Get a pointer to some free memory.let freeMemoryPointer :=mload(0x40)
// Write the abi-encoded calldata into memory, beginning with the function selector.mstore(freeMemoryPointer, 0x095ea7b300000000000000000000000000000000000000000000000000000000)
mstore(add(freeMemoryPointer, 4), to) // Append the "to" argument.mstore(add(freeMemoryPointer, 36), amount) // Append the "amount" argument.
success :=and(
// Set success to whether the call reverted, if not we check it either// returned exactly 1 (can't just be non-zero data), or had no return data.or(and(eq(mload(0), 1), gt(returndatasize(), 31)), iszero(returndatasize())),
// We use 68 because the length of our calldata totals up like so: 4 + 32 * 2.// We use 0 and 32 to copy up to 32 bytes of return data into the scratch space.// Counterintuitively, this call must be positioned second to the or() call in the// surrounding and() call or else returndatasize() will be zero during the computation.call(gas(), token, 0, freeMemoryPointer, 68, 0, 32)
)
}
require(success, "APPROVE_FAILED");
}
}
Contract Source Code
File 15 of 16: Strings.sol
// SPDX-License-Identifier: MIT// OpenZeppelin Contracts (last updated v4.8.0) (utils/Strings.sol)pragmasolidity ^0.8.0;import"./math/Math.sol";
/**
* @dev String operations.
*/libraryStrings{
bytes16privateconstant _SYMBOLS ="0123456789abcdef";
uint8privateconstant _ADDRESS_LENGTH =20;
/**
* @dev Converts a `uint256` to its ASCII `string` decimal representation.
*/functiontoString(uint256 value) internalpurereturns (stringmemory) {
unchecked {
uint256 length = Math.log10(value) +1;
stringmemory buffer =newstring(length);
uint256 ptr;
/// @solidity memory-safe-assemblyassembly {
ptr :=add(buffer, add(32, length))
}
while (true) {
ptr--;
/// @solidity memory-safe-assemblyassembly {
mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
}
value /=10;
if (value ==0) break;
}
return buffer;
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
*/functiontoHexString(uint256 value) internalpurereturns (stringmemory) {
unchecked {
return toHexString(value, Math.log256(value) +1);
}
}
/**
* @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
*/functiontoHexString(uint256 value, uint256 length) internalpurereturns (stringmemory) {
bytesmemory buffer =newbytes(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");
returnstring(buffer);
}
/**
* @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
*/functiontoHexString(address addr) internalpurereturns (stringmemory) {
return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
}
}