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/factories/HubPeripheryFactory.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";
import {BeaconProxy} from "@openzeppelin/contracts/proxy/beacon/BeaconProxy.sol";
import {IOwnable2Step} from "@makina-core/interfaces/IOwnable2Step.sol";
import {IHubPeripheryFactory} from "../interfaces/IHubPeripheryFactory.sol";
import {IHubPeripheryRegistry} from "../interfaces/IHubPeripheryRegistry.sol";
import {ISecurityModuleReference} from "../interfaces/ISecurityModuleReference.sol";
import {IMachinePeriphery} from "../interfaces/IMachinePeriphery.sol";
import {ISecurityModule} from "../interfaces/ISecurityModule.sol";
import {Errors} from "../libraries/Errors.sol";
import {MakinaPeripheryContext} from "../utils/MakinaPeripheryContext.sol";
import {SMCooldownReceipt} from "../security-module/SMCooldownReceipt.sol";
contract HubPeripheryFactory is AccessManagedUpgradeable, MakinaPeripheryContext, IHubPeripheryFactory {
/// @custom:storage-location erc7201:makina.storage.HubPeripheryFactory
struct HubPeripheryFactoryStorage {
mapping(address depositor => bool isDepositor) _isDepositor;
mapping(address redeemer => bool isRedeemer) _isRedeemer;
mapping(address feeManager => bool isFeeManager) _isFeeManager;
mapping(address securityModule => bool isSecurityModule) _isSecurityModule;
mapping(address depositor => uint16 implemId) _depositorImplemId;
mapping(address redeemer => uint16 implemId) _redeemerImplemId;
mapping(address feeManager => uint16 implemId) _feeManagerImplemId;
}
// keccak256(abi.encode(uint256(keccak256("makina.storage.HubPeripheryFactory")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant HubPeripheryFactoryStorageLocation =
0x6b50a937759edff8a6a5b23fe11eb54a74c1c4f4d159fd3622707013a01a1e00;
function _getHubPeripheryFactoryStorage() internal pure returns (HubPeripheryFactoryStorage storage $) {
assembly {
$.slot := HubPeripheryFactoryStorageLocation
}
}
constructor(address _peripheryRegistry) MakinaPeripheryContext(_peripheryRegistry) {
_disableInitializers();
}
function initialize(address _initialAuthority) external initializer {
__AccessManaged_init(_initialAuthority);
}
/// @inheritdoc IHubPeripheryFactory
function isDepositor(address _depositor) external view override returns (bool) {
return _getHubPeripheryFactoryStorage()._isDepositor[_depositor];
}
/// @inheritdoc IHubPeripheryFactory
function isRedeemer(address _redeemer) external view override returns (bool) {
return _getHubPeripheryFactoryStorage()._isRedeemer[_redeemer];
}
/// @inheritdoc IHubPeripheryFactory
function isFeeManager(address _feeManager) external view override returns (bool) {
return _getHubPeripheryFactoryStorage()._isFeeManager[_feeManager];
}
/// @inheritdoc IHubPeripheryFactory
function isSecurityModule(address _securityModule) external view override returns (bool) {
return _getHubPeripheryFactoryStorage()._isSecurityModule[_securityModule];
}
/// @inheritdoc IHubPeripheryFactory
function depositorImplemId(address _depositor) external view override returns (uint16) {
HubPeripheryFactoryStorage storage $ = _getHubPeripheryFactoryStorage();
if (!$._isDepositor[_depositor]) {
revert Errors.NotDepositor();
}
return $._depositorImplemId[_depositor];
}
/// @inheritdoc IHubPeripheryFactory
function redeemerImplemId(address _redeemer) external view override returns (uint16) {
HubPeripheryFactoryStorage storage $ = _getHubPeripheryFactoryStorage();
if (!$._isRedeemer[_redeemer]) {
revert Errors.NotRedeemer();
}
return $._redeemerImplemId[_redeemer];
}
/// @inheritdoc IHubPeripheryFactory
function feeManagerImplemId(address _feeManager) external view override returns (uint16) {
HubPeripheryFactoryStorage storage $ = _getHubPeripheryFactoryStorage();
if (!$._isFeeManager[_feeManager]) {
revert Errors.NotFeeManager();
}
return $._feeManagerImplemId[_feeManager];
}
/// @inheritdoc IHubPeripheryFactory
function setMachine(address machinePeriphery, address machine) external override restricted {
HubPeripheryFactoryStorage storage $ = _getHubPeripheryFactoryStorage();
if (!$._isDepositor[machinePeriphery] && !$._isRedeemer[machinePeriphery] && !$._isFeeManager[machinePeriphery])
{
revert Errors.InvalidMachinePeriphery();
}
IMachinePeriphery(machinePeriphery).setMachine(machine);
}
/// @inheritdoc IHubPeripheryFactory
function setSecurityModule(address feeManager, address securityModule) external override restricted {
HubPeripheryFactoryStorage storage $ = _getHubPeripheryFactoryStorage();
if (!$._isFeeManager[feeManager]) {
revert Errors.NotFeeManager();
}
if (!$._isSecurityModule[securityModule]) {
revert Errors.NotSecurityModule();
}
ISecurityModuleReference(feeManager).setSecurityModule(securityModule);
}
/// @inheritdoc IHubPeripheryFactory
function createDepositor(uint16 _implemId, bytes calldata _initializationData)
external
override
restricted
returns (address)
{
HubPeripheryFactoryStorage storage $ = _getHubPeripheryFactoryStorage();
address beacon = IHubPeripheryRegistry(peripheryRegistry).depositorBeacon(_implemId);
if (beacon == address(0)) {
revert Errors.InvalidDepositorImplemId();
}
address depositor =
address(new BeaconProxy(beacon, abi.encodeCall(IMachinePeriphery.initialize, (_initializationData))));
$._isDepositor[depositor] = true;
$._depositorImplemId[depositor] = _implemId;
emit DepositorCreated(depositor, _implemId);
return depositor;
}
/// @inheritdoc IHubPeripheryFactory
function createRedeemer(uint16 _implemId, bytes calldata _initializationData)
external
override
restricted
returns (address)
{
HubPeripheryFactoryStorage storage $ = _getHubPeripheryFactoryStorage();
address beacon = IHubPeripheryRegistry(peripheryRegistry).redeemerBeacon(_implemId);
if (beacon == address(0)) {
revert Errors.InvalidRedeemerImplemId();
}
address redeemer =
address(new BeaconProxy(beacon, abi.encodeCall(IMachinePeriphery.initialize, (_initializationData))));
$._isRedeemer[redeemer] = true;
$._redeemerImplemId[redeemer] = _implemId;
emit RedeemerCreated(redeemer, _implemId);
return redeemer;
}
/// @inheritdoc IHubPeripheryFactory
function createFeeManager(uint16 _implemId, bytes calldata _initializationData)
external
override
restricted
returns (address)
{
HubPeripheryFactoryStorage storage $ = _getHubPeripheryFactoryStorage();
address beacon = IHubPeripheryRegistry(peripheryRegistry).feeManagerBeacon(_implemId);
if (beacon == address(0)) {
revert Errors.InvalidFeeManagerImplemId();
}
address feeManager =
address(new BeaconProxy(beacon, abi.encodeCall(IMachinePeriphery.initialize, (_initializationData))));
$._isFeeManager[feeManager] = true;
$._feeManagerImplemId[feeManager] = _implemId;
emit FeeManagerCreated(feeManager, _implemId);
return feeManager;
}
/// @inheritdoc IHubPeripheryFactory
function createSecurityModule(ISecurityModule.SecurityModuleInitParams calldata smParams)
external
override
restricted
returns (address)
{
HubPeripheryFactoryStorage storage $ = _getHubPeripheryFactoryStorage();
address cooldownReceiptNFT = address(new SMCooldownReceipt(address(this)));
address securityModule =
address(new BeaconProxy(IHubPeripheryRegistry(peripheryRegistry).securityModuleBeacon(), ""));
IOwnable2Step(cooldownReceiptNFT).transferOwnership(securityModule);
ISecurityModule(securityModule).initialize(abi.encode(smParams, cooldownReceiptNFT));
$._isSecurityModule[securityModule] = true;
emit SecurityModuleCreated(securityModule);
return securityModule;
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/access/manager/AccessManagedUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/manager/AccessManaged.sol)
pragma solidity ^0.8.20;
import {AuthorityUtils} from "@openzeppelin/contracts/access/manager/AuthorityUtils.sol";
import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol";
import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev This contract module makes available a {restricted} modifier. Functions decorated with this modifier will be
* permissioned according to an "authority": a contract like {AccessManager} that follows the {IAuthority} interface,
* implementing a policy that allows certain callers to access certain functions.
*
* IMPORTANT: The `restricted` modifier should never be used on `internal` functions, judiciously used in `public`
* functions, and ideally only used in `external` functions. See {restricted}.
*/
abstract contract AccessManagedUpgradeable is Initializable, ContextUpgradeable, IAccessManaged {
/// @custom:storage-location erc7201:openzeppelin.storage.AccessManaged
struct AccessManagedStorage {
address _authority;
bool _consumingSchedule;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessManaged")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessManagedStorageLocation = 0xf3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00;
function _getAccessManagedStorage() private pure returns (AccessManagedStorage storage $) {
assembly {
$.slot := AccessManagedStorageLocation
}
}
/**
* @dev Initializes the contract connected to an initial authority.
*/
function __AccessManaged_init(address initialAuthority) internal onlyInitializing {
__AccessManaged_init_unchained(initialAuthority);
}
function __AccessManaged_init_unchained(address initialAuthority) internal onlyInitializing {
_setAuthority(initialAuthority);
}
/**
* @dev Restricts access to a function as defined by the connected Authority for this contract and the
* caller and selector of the function that entered the contract.
*
* [IMPORTANT]
* ====
* In general, this modifier should only be used on `external` functions. It is okay to use it on `public`
* functions that are used as external entry points and are not called internally. Unless you know what you're
* doing, it should never be used on `internal` functions. Failure to follow these rules can have critical security
* implications! This is because the permissions are determined by the function that entered the contract, i.e. the
* function at the bottom of the call stack, and not the function where the modifier is visible in the source code.
* ====
*
* [WARNING]
* ====
* Avoid adding this modifier to the https://docs.soliditylang.org/en/v0.8.20/contracts.html#receive-ether-function[`receive()`]
* function or the https://docs.soliditylang.org/en/v0.8.20/contracts.html#fallback-function[`fallback()`]. These
* functions are the only execution paths where a function selector cannot be unambiguously determined from the calldata
* since the selector defaults to `0x00000000` in the `receive()` function and similarly in the `fallback()` function
* if no calldata is provided. (See {_checkCanCall}).
*
* The `receive()` function will always panic whereas the `fallback()` may panic depending on the calldata length.
* ====
*/
modifier restricted() {
_checkCanCall(_msgSender(), _msgData());
_;
}
/// @inheritdoc IAccessManaged
function authority() public view virtual returns (address) {
AccessManagedStorage storage $ = _getAccessManagedStorage();
return $._authority;
}
/// @inheritdoc IAccessManaged
function setAuthority(address newAuthority) public virtual {
address caller = _msgSender();
if (caller != authority()) {
revert AccessManagedUnauthorized(caller);
}
if (newAuthority.code.length == 0) {
revert AccessManagedInvalidAuthority(newAuthority);
}
_setAuthority(newAuthority);
}
/// @inheritdoc IAccessManaged
function isConsumingScheduledOp() public view returns (bytes4) {
AccessManagedStorage storage $ = _getAccessManagedStorage();
return $._consumingSchedule ? this.isConsumingScheduledOp.selector : bytes4(0);
}
/**
* @dev Transfers control to a new authority. Internal function with no access restriction. Allows bypassing the
* permissions set by the current authority.
*/
function _setAuthority(address newAuthority) internal virtual {
AccessManagedStorage storage $ = _getAccessManagedStorage();
$._authority = newAuthority;
emit AuthorityUpdated(newAuthority);
}
/**
* @dev Reverts if the caller is not allowed to call the function identified by a selector. Panics if the calldata
* is less than 4 bytes long.
*/
function _checkCanCall(address caller, bytes calldata data) internal virtual {
AccessManagedStorage storage $ = _getAccessManagedStorage();
(bool immediate, uint32 delay) = AuthorityUtils.canCallWithDelay(
authority(),
caller,
address(this),
bytes4(data[0:4])
);
if (!immediate) {
if (delay > 0) {
$._consumingSchedule = true;
IAccessManager(authority()).consumeScheduledOp(caller, data);
$._consumingSchedule = false;
} else {
revert AccessManagedUnauthorized(caller);
}
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/proxy/beacon/BeaconProxy.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.2.0) (proxy/beacon/BeaconProxy.sol)
pragma solidity ^0.8.22;
import {IBeacon} from "./IBeacon.sol";
import {Proxy} from "../Proxy.sol";
import {ERC1967Utils} from "../ERC1967/ERC1967Utils.sol";
/**
* @dev This contract implements a proxy that gets the implementation address for each call from an {UpgradeableBeacon}.
*
* The beacon address can only be set once during construction, and cannot be changed afterwards. It is stored in an
* immutable variable to avoid unnecessary storage reads, and also in the beacon storage slot specified by
* https://eips.ethereum.org/EIPS/eip-1967[ERC-1967] so that it can be accessed externally.
*
* CAUTION: Since the beacon address can never be changed, you must ensure that you either control the beacon, or trust
* the beacon to not upgrade the implementation maliciously.
*
* IMPORTANT: Do not use the implementation logic to modify the beacon storage slot. Doing so would leave the proxy in
* an inconsistent state where the beacon storage slot does not match the beacon address.
*/
contract BeaconProxy is Proxy {
// An immutable address for the beacon to avoid unnecessary SLOADs before each delegate call.
address private immutable _beacon;
/**
* @dev Initializes the proxy with `beacon`.
*
* If `data` is nonempty, it's used as data in a delegate call to the implementation returned by the beacon. This
* will typically be an encoded function call, and allows initializing the storage of the proxy like a Solidity
* constructor.
*
* Requirements:
*
* - `beacon` must be a contract with the interface {IBeacon}.
* - If `data` is empty, `msg.value` must be zero.
*/
constructor(address beacon, bytes memory data) payable {
ERC1967Utils.upgradeBeaconToAndCall(beacon, data);
_beacon = beacon;
}
/**
* @dev Returns the current implementation address of the associated beacon.
*/
function _implementation() internal view virtual override returns (address) {
return IBeacon(_getBeacon()).implementation();
}
/**
* @dev Returns the beacon.
*/
function _getBeacon() internal view virtual returns (address) {
return _beacon;
}
}
"
},
"lib/makina-core/src/interfaces/IOwnable2Step.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IOwnable2Step {
function owner() external view returns (address);
function pendingOwner() external view returns (address);
function transferOwnership(address newOwner) external;
function acceptOwnership() external;
}
"
},
"src/interfaces/IHubPeripheryFactory.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {ISecurityModule} from "../interfaces/ISecurityModule.sol";
interface IHubPeripheryFactory {
event DepositorCreated(address indexed depositor, uint16 indexed implemId);
event RedeemerCreated(address indexed redeemer, uint16 indexed implemId);
event FeeManagerCreated(address indexed feeManager, uint16 indexed implemId);
event SecurityModuleCreated(address indexed securityModule);
/// @notice Address => Whether this is a depositor deployed by this factory
function isDepositor(address depositor) external view returns (bool);
/// @notice Address => Whether this is a redeemer deployed by this factory
function isRedeemer(address redeemer) external view returns (bool);
/// @notice Address => Whether this is a fee manager deployed by this factory
function isFeeManager(address feeManager) external view returns (bool);
/// @notice Address => Whether this is a security module deployed by this factory
function isSecurityModule(address securityModule) external view returns (bool);
/// @notice Depositor => Implementation ID
function depositorImplemId(address depositor) external view returns (uint16);
/// @notice Redeemer => Implementation ID
function redeemerImplemId(address redeemer) external view returns (uint16);
/// @notice Fee manager => Implementation ID
function feeManagerImplemId(address feeManager) external view returns (uint16);
/// @notice Sets the machine address in the machine periphery contract.
/// @param machinePeriphery The address of the machine periphery contract.
/// @param machine The address of the machine to be set.
function setMachine(address machinePeriphery, address machine) external;
/// @notice Sets the security module address in the fee manager contract.
/// @param feeManager The address of the fee manager contract.
/// @param securityModule The address of the security module to be set.
function setSecurityModule(address feeManager, address securityModule) external;
/// @notice Creates a new machine depositor using the specified implementation ID.
/// @param implemId The ID of the depositor implementation to be used.
/// @param initializationData Additional initialization data.
/// @return depositor The address of the newly created depositor.
function createDepositor(uint16 implemId, bytes calldata initializationData) external returns (address depositor);
/// @notice Creates a new machine redeemer using the specified implementation ID.
/// @param implemId The ID of the redeemer implementation to be used.
/// @param initializationData Additional initialization data.
/// @return redeemer The address of the newly created redeemer.
function createRedeemer(uint16 implemId, bytes calldata initializationData) external returns (address redeemer);
/// @notice Creates a new machine fee manager using the specified implementation ID.
/// @param implemId The ID of the fee manager implementation to be used.
/// @param initializationData Additional initialization data.
/// @return feeManager The address of the newly created fee manager.
function createFeeManager(uint16 implemId, bytes calldata initializationData)
external
returns (address feeManager);
/// @notice Creates a new security module.
/// @param smParams The security module initialization parameters.
/// @return securityModule The address of the newly created security module.
function createSecurityModule(ISecurityModule.SecurityModuleInitParams calldata smParams)
external
returns (address securityModule);
}
"
},
"src/interfaces/IHubPeripheryRegistry.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IHubPeripheryRegistry {
event DepositorBeaconChanged(
uint16 indexed implemId, address indexed oldDepositorBeacon, address indexed newDepositorBeacon
);
event FeeManagerBeaconChanged(
uint16 indexed implemId, address indexed oldFeeManagerBeacon, address indexed newFeeManagerBeacon
);
event PeripheryFactoryChanged(address indexed oldPeripheryFactory, address indexed newPeripheryFactory);
event RedeemerBeaconChanged(
uint16 indexed implemId, address indexed oldRedeemerBeacon, address indexed newRedeemerBeacon
);
event SecurityModuleBeaconChanged(address indexed oldSecurityModuleBeacon, address indexed newSecurityModuleBeacon);
/// @notice Address of the periphery factory.
function peripheryFactory() external view returns (address);
/// @notice Implementation ID => Address of the depositor beacon
function depositorBeacon(uint16 implemId) external view returns (address);
/// @notice Implementation ID => Address of the redeemer beacon
function redeemerBeacon(uint16 implemId) external view returns (address);
/// @notice Implementation ID => Address of the fee manager beacon
function feeManagerBeacon(uint16 implemId) external view returns (address);
/// @notice Address of the security module beacon.
function securityModuleBeacon() external view returns (address);
/// @notice Sets the address of the periphery factory.
/// @param _peripheryFactory The periphery factory address.
function setPeripheryFactory(address _peripheryFactory) external;
/// @notice Sets the beacon address for the depositor implementation.
/// @param implemId The ID of the machine depositor implementation.
/// @param _depositorBeacon The machine depositor beacon address.
function setDepositorBeacon(uint16 implemId, address _depositorBeacon) external;
/// @notice Sets the beacon address for the redeemer implementation.
/// @param implemId The ID of the redeemer implementation.
/// @param _redeemerBeacon The machine redeemer beacon address.
function setRedeemerBeacon(uint16 implemId, address _redeemerBeacon) external;
/// @notice Sets the beacon address for the fee manager implementation.
/// @param implemId The ID of the fee manager implementation.
/// @param _feeManagerBeacon The fee manager beacon address.
function setFeeManagerBeacon(uint16 implemId, address _feeManagerBeacon) external;
/// @notice Sets the security module beacon address.
/// @param _securityModuleBeacon The security module beacon address.
function setSecurityModuleBeacon(address _securityModuleBeacon) external;
}
"
},
"src/interfaces/ISecurityModuleReference.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface ISecurityModuleReference {
/// @notice Security module address.
function securityModule() external view returns (address);
/// @notice Sets the security module address.
/// @param securityModule The address of the security module.
function setSecurityModule(address securityModule) external;
}
"
},
"src/interfaces/IMachinePeriphery.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
interface IMachinePeriphery {
event MachineSet(address indexed machine);
/// @notice Initializer of the contract.
/// @param _data The initialization data, if any.
function initialize(bytes calldata _data) external;
/// @notice Address of the associated machine.
function machine() external view returns (address);
/// @notice Sets the machine address.
function setMachine(address _machine) external;
}
"
},
"src/interfaces/ISecurityModule.sol": {
"content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.28;
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IMachinePeriphery} from "../interfaces/IMachinePeriphery.sol";
interface ISecurityModule is IERC20Metadata, IMachinePeriphery {
event Cooldown(
uint256 indexed cooldownId, address indexed account, address indexed receiver, uint256 shares, uint256 maturity
);
event CooldownCancelled(uint256 indexed cooldownId, address indexed receiver, uint256 shares);
event CooldownDurationChanged(uint256 oldCooldownDuration, uint256 newCooldownDuration);
event MaxSlashableBpsChanged(uint256 oldMaxSlashableBps, uint256 newMaxSlashableBps);
event MinBalanceAfterSlashChanged(uint256 oldMinBalanceAfterSlash, uint256 newMinBalanceAfterSlash);
event Lock(address indexed account, address indexed receiver, uint256 assets, uint256 shares);
event Redeem(uint256 indexed cooldownId, address indexed receiver, uint256 assets, uint256 shares);
event Slash(uint256 amount);
event SlashingSettled();
/// @notice Initialization parameters.
/// @param machineShare Address of the machine share token locked in this contract.
/// @param initialCooldownDuration Cooldown duration in seconds for unlocking.
/// @param initialMaxSlashableBps Maximum slashable proportion of the vault balance in basis points.
/// @param minBalanceAfterSlash Minimum balance that must remain in the vault after a slash.
struct SecurityModuleInitParams {
address machineShare;
uint256 initialCooldownDuration;
uint256 initialMaxSlashableBps;
uint256 initialMinBalanceAfterSlash;
}
/// @notice Pending cooldown parameters.
/// @param shares Amount of security shares to be redeemed.
/// @param maxAssets Maximum amount of machine shares that can be redeemed.
/// @param maturity Timestamp at which the cooldown period will end and the shares can be redeemed.
struct PendingCooldown {
uint256 shares;
uint256 maxAssets;
uint256 maturity;
}
/// @notice Address of the machine share token locked in this contract.
function machineShare() external view returns (address);
/// @notice Address of the cooldown receipt NFT.
function cooldownReceipt() external view returns (address);
/// @notice Cooldown duration in seconds for unlocking.
function cooldownDuration() external view returns (uint256);
/// @notice Maximum slashable proportion of the vault balance in basis points.
function maxSlashableBps() external view returns (uint256);
/// @notice Minimum balance that must remain in the vault after a slash.
function minBalanceAfterSlash() external view returns (uint256);
/// @notice Returns data of a pending cooldown.
/// @param cooldownId ID of the cooldown receipt NFT representing the pending cooldown.
/// @return shares Amount of security shares to be redeemed.
/// @return currentExpectedAssets Current expected amount of machine shares that can be redeemed.
/// @return maturity Timestamp at which the cooldown period will end and the shares can be redeemed.
function pendingCooldown(uint256 cooldownId)
external
view
returns (uint256 shares, uint256 currentExpectedAssets, uint256 maturity);
/// @notice Whether the security module is in slashing mode.
function slashingMode() external view returns (bool);
/// @notice Total amount of machine shares locked in the module.
function totalLockedAmount() external view returns (uint256);
/// @notice Total amount of machine shares currently slashable in the module.
function maxSlashable() external view returns (uint256);
/// @notice Converts machine shares to security shares.
/// @param assets Amount of machine shares to convert.
/// @return shares Amount of security shares corresponding to the input machine shares.
function convertToShares(uint256 assets) external view returns (uint256 shares);
/// @notice Converts security shares to machine shares.
/// @param shares Amount of security shares to convert.
/// @return assets Amount of machine shares corresponding to the input security shares.
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/// @notice Estimates the amount of security shares that would be received for a given amount of machine shares.
/// @param assets Amount of machine shares to convert.
/// @return shares Estimated amount of security shares corresponding to the input machine shares.
function previewLock(uint256 assets) external view returns (uint256 shares);
/// @notice Locks machine shares in the module and mints security shares.
/// @param assets Amount of machine shares to lock.
/// @param receiver Address that will receive the security shares.
/// @param minShares Minimum amount of security shares to receive.
/// @return shares Amount of security shares minted.
function lock(uint256 assets, address receiver, uint256 minShares) external returns (uint256 shares);
/// @notice Requests a cooldown for redeeming security shares.
/// Shares are locked in the contract until the cooldown is cancelled or expires.
/// A cooldown receipt NFT is minted to the specified receiver address.
/// @param shares Amount of security shares to redeem.
/// @param receiver Address that will receive the cooldown receipt.
/// @return cooldownId ID of the minted cooldown receipt NFT representing the pending cooldown.
/// @return maxAssets Maximum amount of machine shares that can be redeemed.
/// @return maturity Timestamp at which the cooldown period will end and the shares can be redeemed.
function startCooldown(uint256 shares, address receiver)
external
returns (uint256 cooldownId, uint256 maxAssets, uint256 maturity);
/// @notice Cancels a pending cooldown.
/// Shares for which the cooldown was cancelled are transferred back to caller.
/// The associated cooldown receipt NFT is burned.
/// @param cooldownId ID of the cooldown receipt NFT representing the pending cooldown.
/// @return shares Amount of security shares for which the cooldown was cancelled.
function cancelCooldown(uint256 cooldownId) external returns (uint256 shares);
/// @notice Redeems security shares and transfers machine shares to caller.
/// @param cooldownId ID of the cooldown receipt NFT representing the pending cooldown.
/// @param minAssets Minimum amount of machine shares to receive.
/// @return assets Amount of machine shares transferred to the receiver.
function redeem(uint256 cooldownId, uint256 minAssets) external returns (uint256 assets);
/// @notice Slashes a specified amount from the total locked amount and triggers the slashing mode.
/// @param amount Amount to slash from the total locked amount.
function slash(uint256 amount) external;
/// @notice Settles the current slashing, allowing the contract to exit slashing mode and resume normal operations.
function settleSlashing() external;
/// @notice Sets the cooldown duration for unlocking.
/// @param cooldownDuration New cooldown duration in seconds.
function setCooldownDuration(uint256 cooldownDuration) external;
/// @notice Sets the maximum slashable proportion of the vault balance in basis points.
/// @param maxSlashableBps New maximum slashable proportion in basis points.
function setMaxSlashableBps(uint256 maxSlashableBps) external;
/// @notice Sets the minimum balance that must remain in the vault after a slash.
/// @param minBalanceAfterSlash New minimum balance after slash.
function setMinBalanceAfterSlash(uint256 minBalanceAfterSlash) external;
}
"
},
"src/libraries/Errors.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {Errors as CoreErrors} from "@makina-core/libraries/Errors.sol";
library Errors {
error AlreadyClaimed();
error AlreadyFinalized();
error CooldownExpired();
error CooldownOngoing();
error FinalizationDelayPending();
error FutureRequest();
error GreaterThanCurrentWatermark();
error InvalidDepositorImplemId();
error InvalidFeeManagerImplemId();
error InvalidFeeSplit();
error InvalidMachinePeriphery();
error InvalidRedeemerImplemId();
error InvalidSecurityModule();
error MachineAlreadySet();
error MachineNotSet();
error MaxBpsValueExceeded();
error MaxFeeRateValueExceeded();
error MaxSlashableExceeded();
error NotDepositor();
error NotEnoughAssets();
error NotFeeManager();
error NotFinalized();
error NotImplemented();
error NotRedeemer();
error NotSecurityModule();
error SecurityModuleAlreadySet();
error SlashingSettlementOngoing();
error ZeroMachineAddress();
error ZeroRequestId();
error ZeroShares();
}
"
},
"src/utils/MakinaPeripheryContext.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {IMakinaPeripheryContext} from "../interfaces/IMakinaPeripheryContext.sol";
abstract contract MakinaPeripheryContext is IMakinaPeripheryContext {
/// @inheritdoc IMakinaPeripheryContext
address public immutable override peripheryRegistry;
constructor(address _peripheryRegistry) {
peripheryRegistry = _peripheryRegistry;
}
}
"
},
"src/security-module/SMCooldownReceipt.sol": {
"content": "// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {ERC721} from "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import {Ownable2Step, Ownable} from "@openzeppelin/contracts/access/Ownable2Step.sol";
import {ISMCooldownReceipt} from "../interfaces/ISMCooldownReceipt.sol";
contract SMCooldownReceipt is ERC721, Ownable2Step, ISMCooldownReceipt {
/// @inheritdoc ISMCooldownReceipt
uint256 public nextTokenId;
constructor(address _initialMinter)
ERC721("Makina Security Module Cooldown NFT", "MakinaSMCooldownNFT")
Ownable(_initialMinter)
{
nextTokenId = 1;
}
/// @inheritdoc ISMCooldownReceipt
function mint(address to) external onlyOwner returns (uint256) {
uint256 tokenId = nextTokenId++;
_safeMint(to, tokenId);
return tokenId;
}
/// @inheritdoc ISMCooldownReceipt
function burn(uint256 tokenId) external onlyOwner {
_burn(tokenId);
}
}
"
},
"lib/openzeppelin-contracts/contracts/access/manager/AuthorityUtils.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (access/manager/AuthorityUtils.sol)
pragma solidity ^0.8.20;
import {IAuthority} from "./IAuthority.sol";
library AuthorityUtils {
/**
* @dev Since `AccessManager` implements an extended IAuthority interface, invoking `canCall` with backwards compatibility
* for the preexisting `IAuthority` interface requires special care to avoid reverting on insufficient return data.
* This helper function takes care of invoking `canCall` in a backwards compatible way without reverting.
*/
function canCallWithDelay(
address authority,
address caller,
address target,
bytes4 selector
) internal view returns (bool immediate, uint32 delay) {
bytes memory data = abi.encodeCall(IAuthority.canCall, (caller, target, selector));
assembly ("memory-safe") {
mstore(0x00, 0x00)
mstore(0x20, 0x00)
if staticcall(gas(), authority, add(data, 0x20), mload(data), 0x00, 0x40) {
immediate := mload(0x00)
delay := mload(0x20)
// If delay does not fit in a uint32, return 0 (no delay)
// equivalent to: if gt(delay, 0xFFFFFFFF) { delay := 0 }
delay := mul(delay, iszero(shr(32, delay)))
}
}
}
}
"
},
"lib/openzeppelin-contracts/contracts/access/manager/IAccessManager.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (access/manager/IAccessManager.sol)
pragma solidity ^0.8.20;
interface IAccessManager {
/**
* @dev A delayed operation was scheduled.
*/
event OperationScheduled(
bytes32 indexed operationId,
uint32 indexed nonce,
uint48 schedule,
address caller,
address target,
bytes data
);
/**
* @dev A scheduled operation was executed.
*/
event OperationExecuted(bytes32 indexed operationId, uint32 indexed nonce);
/**
* @dev A scheduled operation was canceled.
*/
event OperationCanceled(bytes32 indexed operationId, uint32 indexed nonce);
/**
* @dev Informational labelling for a roleId.
*/
event RoleLabel(uint64 indexed roleId, string label);
/**
* @dev Emitted when `account` is granted `roleId`.
*
* NOTE: The meaning of the `since` argument depends on the `newMember` argument.
* If the role is granted to a new member, the `since` argument indicates when the account becomes a member of the role,
* otherwise it indicates the execution delay for this account and roleId is updated.
*/
event RoleGranted(uint64 indexed roleId, address indexed account, uint32 delay, uint48 since, bool newMember);
/**
* @dev Emitted when `account` membership or `roleId` is revoked. Unlike granting, revoking is instantaneous.
*/
event RoleRevoked(uint64 indexed roleId, address indexed account);
/**
* @dev Role acting as admin over a given `roleId` is updated.
*/
event RoleAdminChanged(uint64 indexed roleId, uint64 indexed admin);
/**
* @dev Role acting as guardian over a given `roleId` is updated.
*/
event RoleGuardianChanged(uint64 indexed roleId, uint64 indexed guardian);
/**
* @dev Grant delay for a given `roleId` will be updated to `delay` when `since` is reached.
*/
event RoleGrantDelayChanged(uint64 indexed roleId, uint32 delay, uint48 since);
/**
* @dev Target mode is updated (true = closed, false = open).
*/
event TargetClosed(address indexed target, bool closed);
/**
* @dev Role required to invoke `selector` on `target` is updated to `roleId`.
*/
event TargetFunctionRoleUpdated(address indexed target, bytes4 selector, uint64 indexed roleId);
/**
* @dev Admin delay for a given `target` will be updated to `delay` when `since` is reached.
*/
event TargetAdminDelayUpdated(address indexed target, uint32 delay, uint48 since);
error AccessManagerAlreadyScheduled(bytes32 operationId);
error AccessManagerNotScheduled(bytes32 operationId);
error AccessManagerNotReady(bytes32 operationId);
error AccessManagerExpired(bytes32 operationId);
error AccessManagerLockedRole(uint64 roleId);
error AccessManagerBadConfirmation();
error AccessManagerUnauthorizedAccount(address msgsender, uint64 roleId);
error AccessManagerUnauthorizedCall(address caller, address target, bytes4 selector);
error AccessManagerUnauthorizedConsume(address target);
error AccessManagerUnauthorizedCancel(address msgsender, address caller, address target, bytes4 selector);
error AccessManagerInvalidInitialAdmin(address initialAdmin);
/**
* @dev Check if an address (`caller`) is authorised to call a given function on a given contract directly (with
* no restriction). Additionally, it returns the delay needed to perform the call indirectly through the {schedule}
* & {execute} workflow.
*
* This function is usually called by the targeted contract to control immediate execution of restricted functions.
* Therefore we only return true if the call can be performed without any delay. If the call is subject to a
* previously set delay (not zero), then the function should return false and the caller should schedule the operation
* for future execution.
*
* If `immediate` is true, the delay can be disregarded and the operation can be immediately executed, otherwise
* the operation can be executed if and only if delay is greater than 0.
*
* NOTE: The IAuthority interface does not include the `uint32` delay. This is an extension of that interface that
* is backward compatible. Some contracts may thus ignore the second return argument. In that case they will fail
* to identify the indirect workflow, and will consider calls that require a delay to be forbidden.
*
* NOTE: This function does not report the permissions of the admin functions in the manager itself. These are defined by the
* {AccessManager} documentation.
*/
function canCall(
address caller,
address target,
bytes4 selector
) external view returns (bool allowed, uint32 delay);
/**
* @dev Expiration delay for scheduled proposals. Defaults to 1 week.
*
* IMPORTANT: Avoid overriding the expiration with 0. Otherwise every contract proposal will be expired immediately,
* disabling any scheduling usage.
*/
function expiration() external view returns (uint32);
/**
* @dev Minimum setback for all delay updates, with the exception of execution delays. It
* can be increased without setback (and reset via {revokeRole} in the case event of an
* accidental increase). Defaults to 5 days.
*/
function minSetback() external view returns (uint32);
/**
* @dev Get whether the contract is closed disabling any access. Otherwise role permissions are applied.
*
* NOTE: When the manager itself is closed, admin functions are still accessible to avoid locking the contract.
*/
function isTargetClosed(address target) external view returns (bool);
/**
* @dev Get the role required to call a function.
*/
function getTargetFunctionRole(address target, bytes4 selector) external view returns (uint64);
/**
* @dev Get the admin delay for a target contract. Changes to contract configuration are subject to this delay.
*/
function getTargetAdminDelay(address target) external view returns (uint32);
/**
* @dev Get the id of the role that acts as an admin for the given role.
*
* The admin permission is required to grant the role, revoke the role and update the execution delay to execute
* an operation that is restricted to this role.
*/
function getRoleAdmin(uint64 roleId) external view returns (uint64);
/**
* @dev Get the role that acts as a guardian for a given role.
*
* The guardian permission allows canceling operations that have been scheduled under the role.
*/
function getRoleGuardian(uint64 roleId) external view returns (uint64);
/**
* @dev Get the role current grant delay.
*
* Its value may change at any point without an event emitted following a call to {setGrantDelay}.
* Changes to this value, including effect timepoint are notified in advance by the {RoleGrantDelayChanged} event.
*/
function getRoleGrantDelay(uint64 roleId) external view returns (uint32);
/**
* @dev Get the access details for a given account for a given role. These details include the timepoint at which
* membership becomes active, and the delay applied to all operation by this user that requires this permission
* level.
*
* Returns:
* [0] Timestamp at which the account membership becomes valid. 0 means role is not granted.
* [1] Current execution delay for the account.
* [2] Pending execution delay for the account.
* [3] Timestamp at which the pending execution delay will become active. 0 means no delay update is scheduled.
*/
function getAccess(
uint64 roleId,
address account
) external view returns (uint48 since, uint32 currentDelay, uint32 pendingDelay, uint48 effect);
/**
* @dev Check if a given account currently has the permission level corresponding to a given role. Note that this
* permission might be associated with an execution delay. {getAccess} can provide more details.
*/
function hasRole(uint64 roleId, address account) external view returns (bool isMember, uint32 executionDelay);
/**
* @dev Give a label to a role, for improved role discoverability by UIs.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {RoleLabel} event.
*/
function labelRole(uint64 roleId, string calldata label) external;
/**
* @dev Add `account` to `roleId`, or change its execution delay.
*
* This gives the account the authorization to call any function that is restricted to this role. An optional
* execution delay (in seconds) can be set. If that delay is non 0, the user is required to schedule any operation
* that is restricted to members of this role. The user will only be able to execute the operation after the delay has
* passed, before it has expired. During this period, admin and guardians can cancel the operation (see {cancel}).
*
* If the account has already been granted this role, the execution delay will be updated. This update is not
* immediate and follows the delay rules. For example, if a user currently has a delay of 3 hours, and this is
* called to reduce that delay to 1 hour, the new delay will take some time to take effect, enforcing that any
* operation executed in the 3 hours that follows this update was indeed scheduled before this update.
*
* Requirements:
*
* - the caller must be an admin for the role (see {getRoleAdmin})
* - granted role must not be the `PUBLIC_ROLE`
*
* Emits a {RoleGranted} event.
*/
function grantRole(uint64 roleId, address account, uint32 executionDelay) external;
/**
* @dev Remove an account from a role, with immediate effect. If the account does not have the role, this call has
* no effect.
*
* Requirements:
*
* - the caller must be an admin for the role (see {getRoleAdmin})
* - revoked role must not be the `PUBLIC_ROLE`
*
* Emits a {RoleRevoked} event if the account had the role.
*/
function revokeRole(uint64 roleId, address account) external;
/**
* @dev Renounce role permissions for the calling account with immediate effect. If the sender is not in
* the role this call has no effect.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* Emits a {RoleRevoked} event if the account had the role.
*/
function renounceRole(uint64 roleId, address callerConfirmation) external;
/**
* @dev Change admin role for a given role.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {RoleAdminChanged} event
*/
function setRoleAdmin(uint64 roleId, uint64 admin) external;
/**
* @dev Change guardian role for a given role.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {RoleGuardianChanged} event
*/
function setRoleGuardian(uint64 roleId, uint64 guardian) external;
/**
* @dev Update the delay for granting a `roleId`.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {RoleGrantDelayChanged} event.
*/
function setGrantDelay(uint64 roleId, uint32 newDelay) external;
/**
* @dev Set the role required to call functions identified by the `selectors` in the `target` contract.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {TargetFunctionRoleUpdated} event per selector.
*/
function setTargetFunctionRole(address target, bytes4[] calldata selectors, uint64 roleId) external;
/**
* @dev Set the delay for changing the configuration of a given target contract.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {TargetAdminDelayUpdated} event.
*/
function setTargetAdminDelay(address target, uint32 newDelay) external;
/**
* @dev Set the closed flag for a contract.
*
* Closing the manager itself won't disable access to admin methods to avoid locking the contract.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {TargetClosed} event.
*/
function setTargetClosed(address target, bool closed) external;
/**
* @dev Return the timepoint at which a scheduled operation will be ready for execution. This returns 0 if the
* operation is not yet scheduled, has expired, was executed, or was canceled.
*/
function getSchedule(bytes32 id) external view returns (uint48);
/**
* @dev Return the nonce for the latest scheduled operation with a given id. Returns 0 if the operation has never
* been scheduled.
*/
function getNonce(bytes32 id) external view returns (uint32);
/**
* @dev Schedule a delayed operation for future execution, and return the operation identifier. It is possible to
* choose the timestamp at which the operation becomes executable as long as it satisfies the execution delays
* required for the caller. The special value zero will automatically set the earliest possible time.
*
* Returns the `operationId` that was scheduled. Since this value is a hash of the parameters, it can reoccur when
* the same parameters are used; if this is relevant, the returned `nonce` can be used to uniquely identify this
* scheduled operation from other occurrences of the same `operationId` in invocations of {execute} and {cancel}.
*
* Emits a {OperationScheduled} event.
*
* NOTE: It is not possible to concurrently schedule more than one operation with the same `target` and `data`. If
* this is necessary, a random byte can be appended to `data` to act as a salt that will be ignored by the target
* contract if it is using standard Solidity ABI encoding.
*/
function schedule(
address target,
bytes calldata data,
uint48 when
) external returns (bytes32 operationId, uint32 nonce);
/**
* @dev Execute a function that is delay restricted, provided it was properly scheduled beforehand, or the
* execution delay is 0.
*
* Returns the nonce that identifies the previously scheduled operation that is executed, or 0 if the
* operation wasn't previously scheduled (if the caller doesn't have an execution delay).
*
* Emits an {OperationExecuted} event only if the call was scheduled and delayed.
*/
function execute(address target, bytes calldata data) external payable returns (uint32);
/**
* @dev Cancel a scheduled (delayed) operation. Returns the nonce that identifies the previously scheduled
* operation that is cancelled.
*
* Requirements:
*
* - the caller must be the proposer, a guardian of the targeted function, or a global admin
*
* Emits a {OperationCanceled} event.
*/
function cancel(address caller, address target, bytes calldata data) external returns (uint32);
/**
* @dev Consume a scheduled operation targeting the caller. If such an operation exists, mark it as consumed
* (emit an {OperationExecuted} event and clean the state). Otherwise, throw an error.
*
* This is useful for contract that want to enforce that calls targeting them were scheduled on the manager,
* with all the verifications that it implies.
*
* Emit a {OperationExecuted} event.
*/
function consumeScheduledOp(address caller, bytes calldata data) external;
/**
* @dev Hashing function for delayed operations.
*/
function hashOperation(address caller, address target, bytes calldata data) external view returns (bytes32);
/**
* @dev Changes the authority of a target managed by this manager instance.
*
* Requirements:
*
* - the caller must be a global admin
*/
function updateAuthority(address target, address newAuthority) external;
}
"
},
"lib/openzeppelin-contracts/contracts/access/manager/IAccessManaged.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/IAccessManaged.sol)
pragma solidity ^0.8.20;
interface IAccessManaged {
/**
* @dev Authority that manages this contract was updated.
*/
event AuthorityUpdated(address authority);
error AccessManagedUnauthorized(address caller);
error AccessManagedRequiredDelay(address caller, uint32 delay);
error AccessManagedInvalidAuthority(address authority);
/**
* @dev Returns the current authority.
*/
function authority() external view returns (address);
/**
* @dev Transfers control to a new authority. The caller must be the current authority.
*/
function setAuthority(address) external;
/**
* @dev Returns true only in the context of a delayed restricted call, at the moment that the scheduled operation is
* being consumed. Prevents denial of service for delayed restricted calls in the case that the contract performs
* attacker controlled calls.
*/
function isConsumingScheduledOp() external view returns (bytes4);
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}
"
},
"lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol": {
"content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reinitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initi
Submitted on: 2025-09-17 17:10:25
Comments
Log in to comment.
No comments yet.