Description:
Multi-signature wallet contract requiring multiple confirmations for transaction execution.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"src/hooks/MetaHooks.sol": {
"content": "// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.24;
import {IHooks, IVault} from "lib/yieldnest-vault/src/interface/IHooks.sol";
import {AccessControl} from "lib/openzeppelin-contracts/contracts/access/AccessControl.sol";
import {IVaultForHooks} from "src/interface/IVaultForHooks.sol";
/**
* @title MetaHooks
* @notice MetaHooks is a contract that manages a set of hooks for a vault.
* @dev It is used to manage the hooks for a vault and to call the hooks in the order set by the setHooks call.
* @dev Important: Order in which hoos run is of utmost importance. Eg. if a hook verifies the effect of
* @dev afterProcessAccounting minting fee shares as performed by another hook, it needs to be set *after*
* @dev the other hook in order to make the check useful.
* @dev It supports the IHooks interface, in order to be used as a hook for the vault.
*/
contract MetaHooks is IHooks, IVaultForHooks, AccessControl {
string public constant VERSION = "0.1.0";
error ZeroVaultAddress();
error DuplicateInInput(IHooks hook);
error CallerNotHook(address caller);
error NotSupported();
error EmptyHooksArray();
error TooManyHooks();
error IndexOutOfBounds();
event ConfigBitmapUpdated(ConfigBitmap configBitmap, ConfigBitmap newConfigBitmap);
event SharesMinted(address to, uint256 shares, address caller);
event HookAdded(IHooks hook);
event HookRemoved(IHooks hook);
/**
* @notice The data of a hook
* @dev The index of the hook in the hooks array;
* @dev The active boolean is used to check if the hook is registered, when calling the onlyHook modifier
*/
struct HookData {
uint8 index;
bool active;
}
struct ConfigBitmap {
uint16 beforeDeposit;
uint16 afterDeposit;
uint16 beforeMint;
uint16 afterMint;
uint16 beforeRedeem;
uint16 afterRedeem;
uint16 beforeWithdraw;
uint16 afterWithdraw;
uint16 beforeProcessAccounting;
uint16 afterProcessAccounting;
}
/**
* @notice The vault that the MetaHooks contract is attached to
*/
IVault public immutable override VAULT;
/**
* @notice The array of hooks that are active for this MetaHooks contract
* @dev The array is used to call the hooks in the correct order
*/
IHooks[] public hooks;
/**
* @notice The mapping of the hooks that are active for each operation
* @dev The mapping is used to store the index and active status of the hooks
* @dev The index is used to call the hooks in the correct order
* @dev The active status is used to check if the hook is active for the operation
*/
mapping(IHooks => HookData) public hookData;
/**
* @notice The bitmap of the hooks that are active for each operation
* @dev The bitmap is a uint16 where each bit represents a hook
* @dev The bit is set to 1 if the hook is active for the operation
* @dev The bit is set to 0 if the hook is not active for the operation
* @dev The bitmap is used to optimize the check for what hooks to call (single slot read)
*/
ConfigBitmap private configBitmap;
/// @notice Role identifier for hook managers.
bytes32 public constant HOOK_MANAGER_ROLE = keccak256("HOOK_MANAGER_ROLE");
modifier onlyVault() {
if (msg.sender != address(VAULT)) revert CallerNotVault();
_;
}
modifier onlyHook() {
if (!hookData[IHooks(msg.sender)].active) revert CallerNotHook(msg.sender);
_;
}
/**
* @notice Constructor for the MetaHooks contract
* @param vault_ The address of the vault to be managed by the MetaHooks contract
* @param defaultAdmin The address of the default admin
* @param hookManager The address of the hook manager
*/
constructor(address vault_, address defaultAdmin, address hookManager) {
if (vault_ == address(0)) revert ZeroVaultAddress();
VAULT = IVault(vault_);
_grantRole(DEFAULT_ADMIN_ROLE, defaultAdmin);
_grantRole(HOOK_MANAGER_ROLE, hookManager);
}
/// @inheritdoc IHooks
function name() external pure override returns (string memory) {
return "MetaHooks";
}
/**
* @notice Sets the array of hooks to be executed by this MetaHooks contract
* @dev This function replaces all existing hooks with the provided array. Clears existing hook data.
* @dev Hooks are called IN THE ORDER they are added to the array.
* @param hooks_ Array of hook contracts to be set. Each hook must implement the IHooks interface.
*/
function setHooks(IHooks[] memory hooks_) external onlyRole(HOOK_MANAGER_ROLE) {
// Check if the array is empty
if (hooks_.length == 0) revert EmptyHooksArray();
// uint16 can only hold 16 hooks
if (hooks_.length > 16) revert TooManyHooks();
// Check for duplicates in hooks_
for (uint256 i = 0; i < hooks_.length; i++) {
for (uint256 j = i + 1; j < hooks_.length; j++) {
if (hooks_[i] == hooks_[j]) revert DuplicateInInput(hooks_[i]);
}
}
// Clear existing hook data mappings
for (uint256 i = 0; i < hooks.length; i++) {
delete hookData[hooks[i]];
emit HookRemoved(hooks[i]);
}
// Clear the hooks array before setting new hooks
delete hooks;
// add the hooks to the hookData array
for (uint256 i = 0; i < hooks_.length; i++) {
hooks.push(hooks_[i]);
hookData[hooks_[i]] = HookData({index: uint8(i), active: true});
emit HookAdded(hooks_[i]);
}
_syncConfigBitmap();
}
/**
* @notice Syncs the config bitmap for all the hooks
* @dev This function is used to sync the config bitmap for the hooks, based on the config of each hook
* @dev Must be called if the getConfig() output of any of the hooks changes.
* @dev This function is called when the hooks are set
*/
function syncConfigBitmap() external onlyRole(HOOK_MANAGER_ROLE) {
_syncConfigBitmap();
}
function _syncConfigBitmap() internal {
ConfigBitmap memory newConfigBitmap;
IHooks[] memory _hooks = hooks;
for (uint256 i = 0; i < _hooks.length; i++) {
IHooks.Config memory config = _hooks[i].getConfig();
// encode the config of hook at index i into the bitmap of each Hook Type flag
if (config.beforeDeposit) newConfigBitmap.beforeDeposit = setHook(i, newConfigBitmap.beforeDeposit);
if (config.afterDeposit) newConfigBitmap.afterDeposit = setHook(i, newConfigBitmap.afterDeposit);
if (config.beforeMint) newConfigBitmap.beforeMint = setHook(i, newConfigBitmap.beforeMint);
if (config.afterMint) newConfigBitmap.afterMint = setHook(i, newConfigBitmap.afterMint);
if (config.beforeRedeem) newConfigBitmap.beforeRedeem = setHook(i, newConfigBitmap.beforeRedeem);
if (config.afterRedeem) newConfigBitmap.afterRedeem = setHook(i, newConfigBitmap.afterRedeem);
if (config.beforeWithdraw) newConfigBitmap.beforeWithdraw = setHook(i, newConfigBitmap.beforeWithdraw);
if (config.afterWithdraw) newConfigBitmap.afterWithdraw = setHook(i, newConfigBitmap.afterWithdraw);
if (config.beforeProcessAccounting) {
newConfigBitmap.beforeProcessAccounting = setHook(i, newConfigBitmap.beforeProcessAccounting);
}
if (config.afterProcessAccounting) {
newConfigBitmap.afterProcessAccounting = setHook(i, newConfigBitmap.afterProcessAccounting);
}
}
emit ConfigBitmapUpdated(configBitmap, newConfigBitmap);
configBitmap = newConfigBitmap;
}
/// CONFIG ///
/// @inheritdoc IHooks
function setConfig(Config memory) public pure override {
revert NotSupported();
}
/// @inheritdoc IHooks
function getConfig() public view override returns (Config memory) {
ConfigBitmap memory bitmap = configBitmap;
// Convert bitmap values to boolean flags for each hook type
// Each bitmap field is a uint16 where bits represent which hooks support that operation
// If any bit is set (bitmap != 0), then at least one hook supports that operation
return Config({
beforeDeposit: bitmap.beforeDeposit != 0,
afterDeposit: bitmap.afterDeposit != 0,
beforeMint: bitmap.beforeMint != 0,
afterMint: bitmap.afterMint != 0,
beforeRedeem: bitmap.beforeRedeem != 0,
afterRedeem: bitmap.afterRedeem != 0,
beforeWithdraw: bitmap.beforeWithdraw != 0,
afterWithdraw: bitmap.afterWithdraw != 0,
beforeProcessAccounting: bitmap.beforeProcessAccounting != 0,
afterProcessAccounting: bitmap.afterProcessAccounting != 0
});
}
/**
* @notice Check if a hook is supported in the bitmap
* @param index The index of the hook to check
* @param bitmap The bitmap to check the hook in
* @return True if the hook is supported, false otherwise
*/
function supportsHook(uint256 index, uint16 bitmap) public pure returns (bool) {
if (index >= 16) revert IndexOutOfBounds();
return (bitmap & (1 << index)) != 0;
}
/**
* @notice Toggle a hook in the bitmap
* @param index The index of the hook to set
* @param bitmap The bitmap to set the hook in
* @return The new bitmap
*/
function setHook(uint256 index, uint16 bitmap) public pure returns (uint16) {
if (index >= 16) revert IndexOutOfBounds();
return bitmap | uint16(1 << index);
}
/**
* @notice Returns the hooks array
* @return The hooks array
*/
function getHooks() public view returns (IHooks[] memory) {
return hooks;
}
/**
* @notice Returns the length of the hooks array
* @return The length of the hooks array
*/
function hooksLength() public view returns (uint256) {
return hooks.length;
}
/// HOOKS FUNCTIONS ///
/// @inheritdoc IHooks
function beforeDeposit(DepositParams memory params) external override onlyVault {
uint16 bitmap = configBitmap.beforeDeposit;
if (bitmap == 0) return;
uint256 _hooksLength = hooks.length;
for (uint256 i = 0; i < _hooksLength; i++) {
if (supportsHook(i, bitmap)) {
hooks[i].beforeDeposit(params);
}
}
}
/// @inheritdoc IHooks
function afterDeposit(DepositParams memory params) external override onlyVault {
uint16 bitmap = configBitmap.afterDeposit;
if (bitmap == 0) return;
uint256 _hooksLength = hooks.length;
for (uint256 i = 0; i < _hooksLength; i++) {
if (supportsHook(i, bitmap)) {
hooks[i].afterDeposit(params);
}
}
}
/// @inheritdoc IHooks
function beforeMint(MintParams memory params) external override onlyVault {
uint16 bitmap = configBitmap.beforeMint;
if (bitmap == 0) return;
uint256 _hooksLength = hooks.length;
for (uint256 i = 0; i < _hooksLength; i++) {
if (supportsHook(i, bitmap)) {
hooks[i].beforeMint(params);
}
}
}
/// @inheritdoc IHooks
function afterMint(MintParams memory params) external override onlyVault {
uint16 bitmap = configBitmap.afterMint;
if (bitmap == 0) return;
uint256 _hooksLength = hooks.length;
for (uint256 i = 0; i < _hooksLength; i++) {
if (supportsHook(i, bitmap)) {
hooks[i].afterMint(params);
}
}
}
/// @inheritdoc IHooks
function beforeRedeem(RedeemParams memory params) external override onlyVault {
uint16 bitmap = configBitmap.beforeRedeem;
if (bitmap == 0) return;
uint256 _hooksLength = hooks.length;
for (uint256 i = 0; i < _hooksLength; i++) {
if (supportsHook(i, bitmap)) {
hooks[i].beforeRedeem(params);
}
}
}
/// @inheritdoc IHooks
function afterRedeem(RedeemParams memory params) external override onlyVault {
uint16 bitmap = configBitmap.afterRedeem;
if (bitmap == 0) return;
uint256 _hooksLength = hooks.length;
for (uint256 i = 0; i < _hooksLength; i++) {
if (supportsHook(i, bitmap)) {
hooks[i].afterRedeem(params);
}
}
}
/// @inheritdoc IHooks
function beforeWithdraw(WithdrawParams memory params) external override onlyVault {
uint16 bitmap = configBitmap.beforeWithdraw;
if (bitmap == 0) return;
uint256 _hooksLength = hooks.length;
for (uint256 i = 0; i < _hooksLength; i++) {
if (supportsHook(i, bitmap)) {
hooks[i].beforeWithdraw(params);
}
}
}
/// @inheritdoc IHooks
function afterWithdraw(WithdrawParams memory params) external override onlyVault {
uint16 bitmap = configBitmap.afterWithdraw;
if (bitmap == 0) return;
uint256 _hooksLength = hooks.length;
for (uint256 i = 0; i < _hooksLength; i++) {
if (supportsHook(i, bitmap)) {
hooks[i].afterWithdraw(params);
}
}
}
/// @inheritdoc IHooks
function beforeProcessAccounting(BeforeProcessAccountingParams memory params) external override onlyVault {
uint16 bitmap = configBitmap.beforeProcessAccounting;
if (bitmap == 0) return;
uint256 _hooksLength = hooks.length;
for (uint256 i = 0; i < _hooksLength; i++) {
if (supportsHook(i, bitmap)) {
hooks[i].beforeProcessAccounting(params);
}
}
}
/// @inheritdoc IHooks
function afterProcessAccounting(AfterProcessAccountingParams memory params) external override onlyVault {
uint16 bitmap = configBitmap.afterProcessAccounting;
if (bitmap == 0) return;
uint256 _hooksLength = hooks.length;
for (uint256 i = 0; i < _hooksLength; i++) {
if (supportsHook(i, bitmap)) {
hooks[i].afterProcessAccounting(params);
}
}
}
/// VAULT FUNCTIONS ///
/**
* @notice Allows authorized hooks to mint shares directly to a specified address
* @dev This function acts as a proxy to the vault's mintShares function, restricted to registered hooks
* @param to The address that will receive the newly minted shares
* @param shares The number of shares to mint
*/
function mintShares(address to, uint256 shares) external override onlyHook {
VAULT.mintShares(to, shares);
emit SharesMinted(to, shares, msg.sender);
}
/**
* @notice Converts a given amount of assets to the equivalent amount of shares
* @dev This function acts as a proxy to the vault's convertToShares function
* @param assets The amount of assets to convert
* @return The equivalent amount of shares
*/
function convertToShares(uint256 assets) external view override returns (uint256) {
return VAULT.convertToShares(assets);
}
/**
* @notice Returns the address of the underlying asset
* @dev This function acts as a proxy to the vault's asset function
* @return The address of the underlying asset
*/
function asset() external view override returns (address) {
return VAULT.asset();
}
/**
* @notice Calculates the fee on raw assets
* @dev This function acts as a proxy to the vault's _feeOnRaw function
* @param amount The amount of assets to calculate the fee for
* @param caller The address of the caller
* @return The calculated fee amount
*/
function _feeOnRaw(uint256 amount, address caller) external view override returns (uint256) {
return VAULT._feeOnRaw(amount, caller);
}
/**
* @notice Calculates the fee on total shares
* @dev This function acts as a proxy to the vault's _feeOnTotal function
* @param amount The amount of shares to calculate the fee for
* @param caller The address of the caller
* @return The calculated fee amount
*/
function _feeOnTotal(uint256 amount, address caller) external view override returns (uint256) {
return VAULT._feeOnTotal(amount, caller);
}
/**
* @notice Previews the amount of shares that would be received for depositing a specific asset
* @dev This function acts as a proxy to the vault's previewDepositAsset function
* @param assetAddress The address of the asset to deposit
* @param assets The amount of assets to deposit
* @return The amount of shares that would be received
*/
function previewDepositAsset(address assetAddress, uint256 assets) external view override returns (uint256) {
return VAULT.previewDepositAsset(assetAddress, assets);
}
/**
* @notice Converts a given amount of shares to the equivalent amount of assets
* @dev This function acts as a proxy to the vault's convertToAssets function
* @param shares The amount of shares to convert
* @return The equivalent amount of assets
*/
function convertToAssets(uint256 shares) external view override returns (uint256) {
return VAULT.convertToAssets(shares);
}
/**
* @notice Returns the total supply of shares
* @dev This function acts as a proxy to the vault's totalSupply function
* @return The total supply of shares
*/
function totalSupply() external view override returns (uint256) {
return VAULT.totalSupply();
}
/**
* @notice Returns the total amount of assets held by the vault
* @dev This function acts as a proxy to the vault's totalAssets function
* @return The total amount of assets
*/
function totalAssets() external view override returns (uint256) {
return VAULT.totalAssets();
}
/**
* @notice Returns whether the vault always computes total assets
* @dev This function acts as a proxy to the vault's alwaysComputeTotalAssets function
* @return True if the vault always computes total assets, false otherwise
*/
function alwaysComputeTotalAssets() external view override returns (bool) {
return VAULT.alwaysComputeTotalAssets();
}
}
"
},
"lib/yieldnest-vault/src/interface/IHooks.sol": {
"content": "// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.24;
import {IVault} from "src/interface/IVault.sol";
interface IHooks {
/**
* @notice Parameters for deposit operations
*/
struct DepositParams {
address asset;
uint256 assets;
address caller;
address receiver;
uint256 shares;
uint256 baseAssets;
}
/**
* @notice Parameters for mint operations
*/
struct MintParams {
address asset;
uint256 shares;
address caller;
address receiver;
uint256 assets;
uint256 baseAssets;
}
/**
* @notice Parameters for redeem operations
*/
struct RedeemParams {
address asset;
uint256 shares;
address caller;
address receiver;
address owner;
uint256 assets;
}
/**
* @notice Parameters for withdraw operations
*/
struct WithdrawParams {
address asset;
uint256 assets;
address caller;
address receiver;
address owner;
uint256 shares;
}
/**
* @notice Parameters for before process accounting operations
*/
struct BeforeProcessAccountingParams {
uint256 totalAssetsBeforeAccounting;
uint256 totalSupplyBeforeAccounting;
uint256 totalBaseAssetsBeforeAccounting;
}
/**
* @notice Parameters for after process accounting operations
*/
struct AfterProcessAccountingParams {
uint256 totalAssetsBeforeAccounting;
uint256 totalAssetsAfterAccounting;
uint256 totalSupplyBeforeAccounting;
uint256 totalSupplyAfterAccounting;
uint256 totalBaseAssetsBeforeAccounting;
uint256 totalBaseAssetsAfterAccounting;
}
/**
* @notice Config struct for the hooks
* @dev Each flag is a boolean value that indicates if the corresponding hook function is enabled for the vault
* if the flag is true, the hook function must be called by the vault.
* if the flag is false, the hook function is expected to be a no-op.
*/
struct Config {
bool beforeDeposit;
bool afterDeposit;
bool beforeMint;
bool afterMint;
bool beforeRedeem;
bool afterRedeem;
bool beforeWithdraw;
bool afterWithdraw;
bool beforeProcessAccounting;
bool afterProcessAccounting;
}
error CallerNotVault();
/**
* @notice Returns the name of the hooks module
* @return The name of the hooks module
*/
function name() external view returns (string memory);
/**
* @notice Returns the vault that the hooks are attached to
* @return The vault contract interface
*/
function VAULT() external view returns (IVault);
/**
* @notice Sets the hooks configuration
* @param config_ The configuration struct containing hook permissions
*/
function setConfig(Config memory config_) external;
/**
* @notice Gets the current hooks configuration
* @return The configuration struct containing hook permissions
*/
function getConfig() external view returns (Config memory);
/**
* @notice Hook called before deposit is processed
* @param params The deposit parameters
*/
function beforeDeposit(DepositParams memory params) external;
/**
* @notice Hook called after deposit is processed
* @param params The deposit parameters
*/
function afterDeposit(DepositParams memory params) external;
/**
* @notice Hook called before mint is processed
* @param params The mint parameters
*/
function beforeMint(MintParams memory params) external;
/**
* @notice Hook called after mint is processed
* @param params The mint parameters
*/
function afterMint(MintParams memory params) external;
/**
* @notice Hook called before redeem is processed
* @param params The redeem parameters
*/
function beforeRedeem(RedeemParams memory params) external;
/**
* @notice Hook called after redeem is processed
* @param params The redeem parameters
*/
function afterRedeem(RedeemParams memory params) external;
/**
* @notice Hook called before withdraw is processed
* @param params The withdraw parameters
*/
function beforeWithdraw(WithdrawParams memory params) external;
/**
* @notice Hook called after withdraw is processed
* @param params The withdraw parameters
*/
function afterWithdraw(WithdrawParams memory params) external;
/**
* @notice Hook called before process accounting is executed
* @param params The before process accounting parameters
*/
function beforeProcessAccounting(BeforeProcessAccountingParams memory params) external;
/**
* @notice Hook called after process accounting is executed
* @param params The after process accounting parameters
*/
function afterProcessAccounting(AfterProcessAccountingParams memory params) external;
}
"
},
"lib/openzeppelin-contracts/contracts/access/AccessControl.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "./IAccessControl.sol";
import {Context} from "../utils/Context.sol";
import {IERC165, ERC165} from "../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:
*
* ```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 AccessControl is Context, IAccessControl, ERC165 {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
mapping(bytes32 role => RoleData) private _roles;
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
return _roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @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 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 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 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 `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @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 Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
if (!hasRole(role, account)) {
_roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
if (hasRole(role, account)) {
_roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}
"
},
"src/interface/IVaultForHooks.sol": {
"content": "// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.24;
interface IVaultForHooks {
function mintShares(address to, uint256 shares) external;
function convertToShares(uint256 assets) external view returns (uint256);
function asset() external view returns (address);
function _feeOnRaw(uint256 assets, address caller) external view returns (uint256);
function _feeOnTotal(uint256 shares, address caller) external view returns (uint256);
function previewDepositAsset(address assetAddress, uint256 assets) external view returns (uint256);
function convertToAssets(uint256 shares) external view returns (uint256);
function totalSupply() external view returns (uint256);
function totalAssets() external view returns (uint256);
function alwaysComputeTotalAssets() external view returns (bool);
}
"
},
"lib/yieldnest-vault/src/interface/IVault.sol": {
"content": "// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.24;
import {IERC4626} from "src/Common.sol";
import {IValidator} from "src/interface/IValidator.sol";
import {IHooks} from "src/interface/IHooks.sol";
interface IVault is IERC4626 {
struct VaultStorage {
uint256 totalAssets;
address provider;
address buffer;
bool paused;
uint8 decimals;
bool countNativeAsset;
bool alwaysComputeTotalAssets;
/// @notice The index of the default asset.
/// The default asset is vault.asset(), used for deposit, withdraw, redeem, mint as default.
/// If defaultAssetIndex is 0, the vault will use the base asset as default asset.
uint256 defaultAssetIndex;
}
struct AssetParams {
uint256 index;
bool active;
uint8 decimals;
}
struct AssetUpdateFields {
bool active;
}
struct AssetStorage {
mapping(address => AssetParams) assets;
address[] list;
}
struct OverriddenBaseWithdrawalFeeFields {
/// @notice The base withdrawal fee in basis points (1e8 = 100%) for the user to override
uint64 baseWithdrawalFee;
/// @notice Whether the fee is overridden for the user
bool isOverridden;
}
struct FeeStorage {
/// @notice The base withdrawal fee in basis points (1e8 = 100%)
uint64 baseWithdrawalFee;
mapping(address user => OverriddenBaseWithdrawalFeeFields fields) overriddenBaseWithdrawalFee;
}
struct HooksStorage {
IHooks hooks;
}
enum ParamType {
UINT256,
ADDRESS
}
struct ParamRule {
ParamType paramType;
bool isArray;
address[] allowList;
}
struct FunctionRule {
bool isActive;
ParamRule[] paramRules;
IValidator validator;
}
struct ProcessorStorage {
uint256 lastProcessed;
uint256 lastAccounting;
mapping(address => mapping(bytes4 => FunctionRule)) rules;
}
error Paused();
error Unpaused();
error ZeroAddress();
error ZeroAmount();
error ZeroRate();
error InvalidString();
error InvalidArray();
error ExceededMaxDeposit(address sender, uint256 amount, uint256 maxAssets);
error DefaultAsset();
error AssetNotEmpty(address);
error InvalidAsset(address);
error InvalidTarget(address);
error InvalidDecimals();
error InvalidFunction(address target, bytes4 funcSig);
error DuplicateAsset(address asset);
error ExceededMaxWithdraw(address, uint256, uint256);
error ExceededMaxRedeem(address, uint256, uint256);
error ProcessFailed(bytes, bytes);
error ProcessInvalid(bytes);
error ProviderNotSet();
error BufferNotSet();
error DepositFailed();
error AssetNotActive();
error ExceedsMaxBasisPoints(uint256 value);
error InvalidNativeAssetDecimals(uint256 decimals);
error InvalidAssetDecimals(uint256 decimals);
error InvalidDefaultAssetIndex(uint256 index);
error ExceedsMaxPerformanceFee(uint256 value);
error BaseAsset();
error CallerNotHooks();
error InvalidHooks();
event DepositAsset(
address indexed sender,
address indexed receiver,
address indexed asset,
uint256 assets,
uint256 baseAssets,
uint256 shares
);
event WithdrawAsset(
address indexed sender,
address indexed receiver,
address indexed owner,
address asset,
uint256 assets,
uint256 shares
);
event SetProvider(address indexed provider);
event SetBuffer(address indexed buffer);
event SetAlwaysComputeTotalAssets(bool alwaysComputeTotalAssets);
event NewAsset(address indexed asset, uint256 decimals, uint256 index);
event ProcessSuccess(address[] targets, uint256[] values, bytes[] data);
event Pause(bool paused);
event SetProcessorRule(address indexed target, bytes4, FunctionRule);
event NativeDeposit(uint256 amount);
event ProcessAccounting(uint256 timestamp, uint256 totalAssetsBefore, uint256 totalAssetsAfter);
event UpdateAsset(uint256 indexed index, address indexed asset, AssetUpdateFields fields);
event DeleteAsset(uint256 indexed index, address indexed asset);
event SetBaseWithdrawalFee(uint64 oldFee, uint64 newFee);
event WithdrawalFeeOverridden(address indexed user, uint64 baseWithdrawalFee, bool isOverridden);
event SetHooks(address indexed oldHooks, address indexed newHooks);
// 4626-MAX
function getAssets() external view returns (address[] memory list);
function getAsset(address asset_) external view returns (AssetParams memory);
function hasAsset(address asset_) external view returns (bool);
function getProcessorRule(address contractAddress, bytes4 funcSig) external view returns (FunctionRule memory);
function previewDepositAsset(address assetAddress, uint256 assets) external view returns (uint256);
function depositAsset(address assetAddress, uint256 amount, address receiver) external returns (uint256);
function provider() external view returns (address);
function buffer() external view returns (address);
function totalBaseAssets() external view returns (uint256);
function computeTotalAssets() external view returns (uint256);
function alwaysComputeTotalAssets() external view returns (bool);
// ADMIN
function setProvider(address provider) external;
function setBuffer(address buffer) external;
function setProcessorRule(address target, bytes4 functionSig, FunctionRule memory rule) external;
function setProcessorRules(address[] memory targets, bytes4[] memory functionSigs, FunctionRule[] memory rules)
external;
function addAsset(address asset_, bool active_) external;
function deleteAsset(uint256 index) external;
function pause() external;
function unpause() external;
function hooks() external view returns (IHooks);
function setHooks(address hooks) external;
function mintShares(address recipient, uint256 shares) external;
function withdrawAsset(address asset_, uint256 assets, address receiver, address owner)
external
returns (uint256);
function processAccounting() external;
function processor(address[] calldata targets, uint256[] calldata values, bytes[] calldata data)
external
returns (bytes[] memory);
// FEES
function _feeOnRaw(uint256 amount, address user) external view returns (uint256);
function _feeOnTotal(uint256 amount, address user) external view returns (uint256);
}
"
},
"lib/openzeppelin-contracts/contracts/access/IAccessControl.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/IAccessControl.sol)
pragma solidity >=0.8.4;
/**
* @dev External interface of AccessControl declared to support ERC-165 detection.
*/
interface IAccessControl {
/**
* @dev The `account` is missing a role.
*/
error AccessControlUnauthorizedAccount(address account, bytes32 neededRole);
/**
* @dev The caller of a function is not the expected one.
*
* NOTE: Don't confuse with {AccessControlUnauthorizedAccount}.
*/
error AccessControlBadConfirmation();
/**
* @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 to signal this.
*/
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. This account bears the admin role (for the granted role).
* Expected in cases where the role was granted using the internal {AccessControl-_grantRole}.
*/
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 `callerConfirmation`.
*/
function renounceRole(bytes32 role, address callerConfirmation) external;
}
"
},
"lib/openzeppelin-contracts/contracts/utils/Context.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @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 Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
"
},
"lib/openzeppelin-contracts/contracts/utils/introspection/ERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/ERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "./IERC165.sol";
/**
* @dev Implementation of the {IERC165} interface.
*
* Contracts that want to implement ERC-165 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);
* }
* ```
*/
abstract contract ERC165 is IERC165 {
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
return interfaceId == type(IERC165).interfaceId;
}
}
"
},
"lib/yieldnest-vault/src/Common.sol": {
"content": "/* solhint-disable no-empty-blocks, no-unused-import */
// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.24;
import {AccessControlUpgradeable} from
"lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol";
import {Address} from "lib/openzeppelin-contracts/contracts/utils/Address.sol";
import {ERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol";
import {ERC20PermitUpgradeable} from
"lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/ERC20PermitUpgradeable.sol";
import {ERC20Upgradeable} from "lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol";
import {IAccessControl} from "lib/openzeppelin-contracts/contracts/access/IAccessControl.sol";
import {IERC20} from "lib/openzeppelin-contracts/contracts/interfaces/IERC20.sol";
import {IERC20Metadata} from "lib/openzeppelin-contracts/contracts/interfaces/IERC20Metadata.sol";
import {IERC20Permit} from "lib/openzeppelin-contracts/contracts/token/ERC20/extensions/IERC20Permit.sol";
import {IERC4626} from "lib/openzeppelin-contracts/contracts/interfaces/IERC4626.sol";
import {Math} from "lib/openzeppelin-contracts/contracts/utils/math/Math.sol";
import {ProxyAdmin} from "lib/openzeppelin-contracts/contracts/proxy/transparent/ProxyAdmin.sol";
import {ReentrancyGuardUpgradeable} from
"lib/openzeppelin-contracts-upgradeable/contracts/utils/ReentrancyGuardUpgradeable.sol";
import {SafeERC20} from "lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol";
import {TimelockController} from "lib/openzeppelin-contracts/contracts/governance/TimelockController.sol";
import {
TransparentUpgradeableProxy,
ITransparentUpgradeableProxy
} from "lib/openzeppelin-contracts/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
import {IERC165} from "lib/openzeppelin-contracts/contracts/interfaces/IERC165.sol";
import {Initializable} from "lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol";
import {OwnableUpgradeable} from "lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol";
import {Ownable} from "lib/openzeppelin-contracts/contracts/access/Ownable.sol";
contract Common {}
"
},
"lib/yieldnest-vault/src/interface/IValidator.sol": {
"content": "// SPDX-License-Identifier: BSD-3-Clause
pragma solidity ^0.8.24;
interface IValidator {
/// @notice Validates a transaction before execution
/// @param target The address the transaction will be sent to
/// @param value The amount of ETH (in wei) that will be sent with the transaction
/// @param data The calldata that will be sent with the transaction
/// @dev This function should revert if the transaction is invalid
/// @dev This function is called before executing a transaction
function validate(address target, uint256 value, bytes calldata data) external view;
}
"
},
"lib/openzeppelin-contracts/contracts/utils/introspection/IERC165.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/introspection/IERC165.sol)
pragma solidity >=0.4.16;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* Implementers can declare support of contract interfaces, which can then be
* queried by others ({ERC165Checker}).
*
* For an implementation, see {ERC165}.
*/
interface IERC165 {
/**
* @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[ERC 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);
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/AccessControl.sol)
pragma solidity ^0.8.20;
import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../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, IAccessControl, ERC165Upgradeable {
struct RoleData {
mapping(address account => bool) hasRole;
bytes32 adminRole;
}
bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;
/// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
struct AccessControlStorage {
mapping(bytes32 role => RoleData) _roles;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;
function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
assembly {
$.slot := AccessControlStorageLocation
}
}
/**
* @dev Modifier that checks that an account has a specific role. Reverts
* with an {AccessControlUnauthorizedAccount} error including the required role.
*/
modifier onlyRole(bytes32 role) {
_checkRole(role);
_;
}
function __AccessControl_init() internal onlyInitializing {
}
function __AccessControl_init_unchained() internal onlyInitializing {
}
/// @inheritdoc IERC165
function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
}
/**
* @dev Returns `true` if `account` has been granted `role`.
*/
function hasRole(bytes32 role, address account) public view virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
return $._roles[role].hasRole[account];
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
* is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
*/
function _checkRole(bytes32 role) internal view virtual {
_checkRole(role, _msgSender());
}
/**
* @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
* is missing `role`.
*/
function _checkRole(bytes32 role, address account) internal view virtual {
if (!hasRole(role, account)) {
revert AccessControlUnauthorizedAccount(account, role);
}
}
/**
* @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 returns (bytes32) {
AccessControlStorage storage $ = _getAccessControlStorage();
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 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 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 `callerConfirmation`.
*
* May emit a {RoleRevoked} event.
*/
function renounceRole(bytes32 role, address callerConfirmation) public virtual {
if (callerConfirmation != _msgSender()) {
revert AccessControlBadConfirmation();
}
_revokeRole(role, callerConfirmation);
}
/**
* @dev Sets `adminRole` as ``role``'s admin role.
*
* Emits a {RoleAdminChanged} event.
*/
function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
AccessControlStorage storage $ = _getAccessControlStorage();
bytes32 previousAdminRole = getRoleAdmin(role);
$._roles[role].adminRole = adminRole;
emit RoleAdminChanged(role, previousAdminRole, adminRole);
}
/**
* @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
*
* Internal function without access restriction.
*
* May emit a {RoleGranted} event.
*/
function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (!hasRole(role, account)) {
$._roles[role].hasRole[account] = true;
emit RoleGranted(role, account, _msgSender());
return true;
} else {
return false;
}
}
/**
* @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
*
* Internal function without access restriction.
*
* May emit a {RoleRevoked} event.
*/
function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
AccessControlStorage storage $ = _getAccessControlStorage();
if (hasRole(role, account)) {
$._roles[role].hasRole[account] = false;
emit RoleRevoked(role, account, _msgSender());
return true;
} else {
return false;
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/utils/Address.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @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.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, bytes memory returndata) = recipient.call{value: amount}("");
if (!success) {
_revert(returndata);
}
}
/**
* @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 or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {Errors.FailedCall} error.
*
* 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.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @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`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert Errors.InsufficientBalance(address(this).balance, value);
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {Errors.FailedCall}) in case
* of an unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {Errors.FailedCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {Errors.FailedCall}.
*/
function _revert(bytes memory returndata) 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
assembly ("memory-safe") {
revert(add(returndata, 0x20), mload(returndata))
}
} else {
revert Errors.FailedCall();
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/token/ERC20/ERC20.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts gui
Submitted on: 2025-10-17 10:40:00
Comments
Log in to comment.
No comments yet.