ETH Price: $2,063.75 (+5.77%)
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Approve231210252025-08-11 23:16:47178 days ago1754954207IN
0x953Ca96b...9b1352341
0 ETH0.000108412.35826813
Approve231197132025-08-11 18:51:47178 days ago1754938307IN
0x953Ca96b...9b1352341
0 ETH0.000133472.90340679
Approve231170592025-08-11 9:57:47179 days ago1754906267IN
0x953Ca96b...9b1352341
0 ETH0.00002240.48753032
Approve231170222025-08-11 9:50:23179 days ago1754905823IN
0x953Ca96b...9b1352341
0 ETH0.000115042.50265093
Approve231169112025-08-11 9:28:11179 days ago1754904491IN
0x953Ca96b...9b1352341
0 ETH0.000018330.39911567
Approve230986352025-08-08 20:10:23181 days ago1754683823IN
0x953Ca96b...9b1352341
0 ETH0.000123612.68889298
Approve230921302025-08-07 22:21:59182 days ago1754605319IN
0x953Ca96b...9b1352341
0 ETH0.000109372.37913016

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x60c08060229767512025-07-22 19:18:23198 days ago1753211903  Contract Creation0 ETH
Loading...
Loading
Loading...
Loading
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
MultiDepositorVault

Compiler Version
v0.8.29+commit.ab55807c

Optimization Enabled:
Yes with 100000 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

import { IERC20 } from "@oz/interfaces/IERC20.sol";
import { ERC20 } from "@oz/token/ERC20/ERC20.sol";

import { SafeERC20 } from "@oz/token/ERC20/utils/SafeERC20.sol";
import { FeeVault } from "src/core/FeeVault.sol";

import { IBeforeTransferHook } from "src/core/interfaces/IBeforeTransferHook.sol";
import { IMultiDepositorVault } from "src/core/interfaces/IMultiDepositorVault.sol";
import { IMultiDepositorVaultFactory } from "src/core/interfaces/IMultiDepositorVaultFactory.sol";
import { IProvisioner } from "src/core/interfaces/IProvisioner.sol";

/// @title MultiDepositorVault
/// @notice A vault that allows users to deposit and withdraw multiple tokens. This contract just mints and burns unit
/// tokens and all logic and validation is handled by the provisioner
contract MultiDepositorVault is IMultiDepositorVault, ERC20, FeeVault {
    using SafeERC20 for IERC20;

    ////////////////////////////////////////////////////////////
    //                        Storage                         //
    ////////////////////////////////////////////////////////////

    /// @notice Hooks contract called before unit transfers/mints/burns
    IBeforeTransferHook public beforeTransferHook;

    /// @notice Role that can mint/burn vault units
    address public provisioner;

    /// @notice Ensures caller is the provisioner
    modifier onlyProvisioner() {
        // Requirements: check that the caller is the provisioner
        require(msg.sender == provisioner, Aera__CallerIsNotProvisioner());
        _;
    }

    ////////////////////////////////////////////////////////////
    //                      Constructor                       //
    ////////////////////////////////////////////////////////////

    constructor()
        ERC20(
            IMultiDepositorVaultFactory(msg.sender).getERC20Name(),
            IMultiDepositorVaultFactory(msg.sender).getERC20Symbol()
        )
        FeeVault()
    {
        // Interactions: get the before transfer hook contract
        IBeforeTransferHook beforeTransferHook_ =
            IMultiDepositorVaultFactory(msg.sender).multiDepositorVaultParameters();

        // Effects: set the before transfer hook contract
        _setBeforeTransferHook(beforeTransferHook_);
    }

    ////////////////////////////////////////////////////////////
    //              Public / External Functions               //
    ////////////////////////////////////////////////////////////

    /// @inheritdoc IMultiDepositorVault
    function enter(address sender, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, address recipient)
        external
        whenNotPaused
        onlyProvisioner
    {
        // Interactions: pull tokens from the sender
        if (tokenAmount > 0) token.safeTransferFrom(sender, address(this), tokenAmount);

        // Effects: mint units to the recipient
        _mint(recipient, unitsAmount);

        // Log the enter event
        emit Enter(sender, recipient, token, tokenAmount, unitsAmount);
    }

    /// @inheritdoc IMultiDepositorVault
    function exit(address sender, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, address recipient)
        external
        whenNotPaused
        onlyProvisioner
    {
        // Effects: burn units from the sender
        _burn(sender, unitsAmount);

        // Interactions: transfer tokens to the recipient
        if (tokenAmount > 0) token.safeTransfer(recipient, tokenAmount);

        // Log the exit event
        emit Exit(sender, recipient, token, tokenAmount, unitsAmount);
    }

    function setProvisioner(address provisioner_) external requiresAuth {
        // Effects: set the provisioner
        _setProvisioner(provisioner_);
    }

    /// @inheritdoc IMultiDepositorVault
    function setBeforeTransferHook(IBeforeTransferHook hook) external requiresAuth {
        // Effects: set the transfer hook
        _setBeforeTransferHook(hook);
    }

    ////////////////////////////////////////////////////////////
    //              Private / Internal Functions              //
    ////////////////////////////////////////////////////////////

    /// @inheritdoc ERC20
    function _update(address from, address to, uint256 amount) internal override {
        IBeforeTransferHook hook = beforeTransferHook;
        if (address(hook) != address(0)) {
            // Requirements: perform before transfer checks
            hook.beforeTransfer(from, to, provisioner);
        }

        // Requirements: check that the from address does not have its units locked
        // from == address(0) is to allow minting further units for user with locked units
        // to == address(0) is to allow burning units in refundDeposit
        require(
            from == address(0) || to == address(0) || !IProvisioner(provisioner).areUserUnitsLocked(from),
            Aera__UnitsLocked()
        );

        // Effects: transfer the tokens
        return super._update(from, to, amount);
    }

    /// @notice Set the transfer hook
    /// @param hook_ The transfer hook address
    function _setBeforeTransferHook(IBeforeTransferHook hook_) internal {
        // Effects: set the transfer hook contract
        beforeTransferHook = hook_;

        // Log that the transfer hook contract has been set
        emit BeforeTransferHookSet(address(hook_));
    }

    /// @notice Set the provisioner
    /// @param provisioner_ The provisioner address
    function _setProvisioner(address provisioner_) internal {
        // Requirements: check that the provisioner is not zero
        require(provisioner_ != address(0), Aera__ZeroAddressProvisioner());

        // Effects: set the provisioner
        provisioner = provisioner_;

        // Log that provisioner was set
        emit ProvisionerSet(provisioner_);
    }
}

File 2 of 48 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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 guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC-20
 * applications.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

    mapping(address account => mapping(address spender => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `value`.
     */
    function transfer(address to, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, value);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 value) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, value);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Skips emitting an {Approval} event indicating an allowance update. This is not
     * required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `value`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `value`.
     */
    function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, value);
        _transfer(from, to, value);
        return true;
    }

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _transfer(address from, address to, uint256 value) internal {
        if (from == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        if (to == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(from, to, value);
    }

    /**
     * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
     * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
     * this function.
     *
     * Emits a {Transfer} event.
     */
    function _update(address from, address to, uint256 value) internal virtual {
        if (from == address(0)) {
            // Overflow check required: The rest of the code assumes that totalSupply never overflows
            _totalSupply += value;
        } else {
            uint256 fromBalance = _balances[from];
            if (fromBalance < value) {
                revert ERC20InsufficientBalance(from, fromBalance, value);
            }
            unchecked {
                // Overflow not possible: value <= fromBalance <= totalSupply.
                _balances[from] = fromBalance - value;
            }
        }

        if (to == address(0)) {
            unchecked {
                // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
                _totalSupply -= value;
            }
        } else {
            unchecked {
                // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
                _balances[to] += value;
            }
        }

        emit Transfer(from, to, value);
    }

    /**
     * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
     * Relies on the `_update` mechanism
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead.
     */
    function _mint(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidReceiver(address(0));
        }
        _update(address(0), account, value);
    }

    /**
     * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
     * Relies on the `_update` mechanism.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * NOTE: This function is not virtual, {_update} should be overridden instead
     */
    function _burn(address account, uint256 value) internal {
        if (account == address(0)) {
            revert ERC20InvalidSender(address(0));
        }
        _update(account, address(0), value);
    }

    /**
     * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     *
     * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
     */
    function _approve(address owner, address spender, uint256 value) internal {
        _approve(owner, spender, value, true);
    }

    /**
     * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
     *
     * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
     * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
     * `Approval` event during `transferFrom` operations.
     *
     * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
     * true using the following override:
     *
     * ```solidity
     * function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
     *     super._approve(owner, spender, value, true);
     * }
     * ```
     *
     * Requirements are the same as {_approve}.
     */
    function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
        if (owner == address(0)) {
            revert ERC20InvalidApprover(address(0));
        }
        if (spender == address(0)) {
            revert ERC20InvalidSpender(address(0));
        }
        _allowances[owner][spender] = value;
        if (emitEvent) {
            emit Approval(owner, spender, value);
        }
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `value`.
     *
     * Does not update the allowance value in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Does not emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            if (currentAllowance < value) {
                revert ERC20InsufficientAllowance(spender, currentAllowance, value);
            }
            unchecked {
                _approve(owner, spender, currentAllowance - value, false);
            }
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC-20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    /**
     * @dev An operation with an ERC-20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        forceApprove(token, spender, oldAllowance + value);
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     *
     * IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
     * smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
     * this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
     * that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     *
     * NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
     * only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
     * set here.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            safeTransfer(token, to, value);
        } else if (!token.transferAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
     * has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * Reverts if the returned value is other than `true`.
     */
    function transferFromAndCallRelaxed(
        IERC1363 token,
        address from,
        address to,
        uint256 value,
        bytes memory data
    ) internal {
        if (to.code.length == 0) {
            safeTransferFrom(token, from, to, value);
        } else if (!token.transferFromAndCall(from, to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
     * code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
     * targeting contracts.
     *
     * NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
     * Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
     * once without retrying, and relies on the returned value to be true.
     *
     * Reverts if the returned value is other than `true`.
     */
    function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
        if (to.code.length == 0) {
            forceApprove(token, to, value);
        } else if (!token.approveAndCall(to, value, data)) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            // bubble errors
            if iszero(success) {
                let ptr := mload(0x40)
                returndatacopy(ptr, 0, returndatasize())
                revert(ptr, returndatasize())
            }
            returnSize := returndatasize()
            returnValue := mload(0)
        }

        if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silently catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly ("memory-safe") {
            success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0)
        }
        return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

import { IERC20 } from "@oz/interfaces/IERC20.sol";
import { SafeERC20 } from "@oz/token/ERC20/utils/SafeERC20.sol";

import { BaseVault } from "src/core/BaseVault.sol";
import { FeeVaultParameters } from "src/core/Types.sol";
import { IFeeCalculator } from "src/core/interfaces/IFeeCalculator.sol";
import { IFeeVault } from "src/core/interfaces/IFeeVault.sol";
import { IFeeVaultDeployer } from "src/core/interfaces/IFeeVaultDeployer.sol";

/// @title FeeVault
/// @notice This contract extends BaseVault with fee capabilities for vaults that have a single logical owner of all
/// assets. The vault relies on an external contract called the fee calculator which is shared across multiple vaults
/// The fee calculator is responsible for calculating the TVL and performance fees for the vault, but
/// the vault has control over those fees. Fee claims are initiated via the vault, which consults and updates the fee
/// calculator upon successful claims
abstract contract FeeVault is IFeeVault, BaseVault {
    using SafeERC20 for IERC20;

    ////////////////////////////////////////////////////////////
    //                       Immutables                       //
    ////////////////////////////////////////////////////////////

    /// @notice Address of the fee token
    IERC20 public immutable FEE_TOKEN;

    ////////////////////////////////////////////////////////////
    //                       Storage                          //
    ////////////////////////////////////////////////////////////

    /// @notice Address of the fee calculator contract
    IFeeCalculator public feeCalculator;

    /// @notice Address of the fee recipient
    address public feeRecipient;

    ////////////////////////////////////////////////////////////
    //                       Modifiers                        //
    ////////////////////////////////////////////////////////////

    /// @notice Modifier to check that the caller is the fee recipient
    modifier onlyFeeRecipient() {
        // Requirements: check that the caller is the fee recipient
        require(msg.sender == feeRecipient, Aera__CallerIsNotFeeRecipient());
        _;
    }

    constructor() BaseVault() {
        // Interactions: get the fee vault parameters
        FeeVaultParameters memory params = IFeeVaultDeployer(msg.sender).feeVaultParameters();

        IFeeCalculator feeCalculator_ = params.feeCalculator;
        IERC20 feeToken_ = params.feeToken;

        // Requirements: check that the fee calculator and fee token are not zero addresses
        require(address(feeCalculator_) != address(0), Aera__ZeroAddressFeeCalculator());
        require(address(feeToken_) != address(0), Aera__ZeroAddressFeeToken());

        // Interactions: register the vault with the fee calculator
        feeCalculator_.registerVault();

        address feeRecipient_ = params.feeRecipient;
        // Requirements: check that the fee recipient is not the zero address
        require(feeRecipient_ != address(0), Aera__ZeroAddressFeeRecipient());

        // Effects: set the fee recipient and the fee calculator
        feeRecipient = feeRecipient_;
        feeCalculator = feeCalculator_;

        // Effects: set the fee token immutable
        FEE_TOKEN = feeToken_;
    }

    ////////////////////////////////////////////////////////////
    //              Public / External Functions               //
    ////////////////////////////////////////////////////////////

    /// @inheritdoc IFeeVault
    function setFeeCalculator(IFeeCalculator newFeeCalculator) external requiresAuth {
        // Effects: set the new fee calculator
        feeCalculator = newFeeCalculator;
        // Log the fee calculator updated event
        emit FeeCalculatorUpdated(address(newFeeCalculator));

        // Interactions: register vault only if the new calculator is not address(0)
        if (address(newFeeCalculator) != address(0)) {
            newFeeCalculator.registerVault();
        }
    }

    /// @inheritdoc IFeeVault
    function setFeeRecipient(address newFeeRecipient) external requiresAuth {
        // Requirements: check that the new fee recipient is not the zero address
        require(newFeeRecipient != address(0), Aera__ZeroAddressFeeRecipient());

        // Effects: set the new fee recipient
        feeRecipient = newFeeRecipient;
        // Log the fee recipient updated event
        emit FeeRecipientUpdated(newFeeRecipient);
    }

    /// @inheritdoc IFeeVault
    function claimFees() external onlyFeeRecipient returns (uint256 feeRecipientFees, uint256 protocolFees) {
        address protocolFeeRecipient;

        // Interactions: claim the fees
        (feeRecipientFees, protocolFees, protocolFeeRecipient) =
            feeCalculator.claimFees(FEE_TOKEN.balanceOf(address(this)));

        // Requirements: check that the fee recipient has earned fees
        require(feeRecipientFees != 0, Aera__NoFeesToClaim());

        // Interactions: transfer the fees to the fee recipient
        FEE_TOKEN.safeTransfer(msg.sender, feeRecipientFees);
        // Log the fees claimed event
        emit FeesClaimed(msg.sender, feeRecipientFees);

        if (protocolFees != 0) {
            // Interactions: transfer the protocol fees to the protocol fee recipient
            FEE_TOKEN.safeTransfer(protocolFeeRecipient, protocolFees);
            // Log the protocol fees claimed event
            emit ProtocolFeesClaimed(protocolFeeRecipient, protocolFees);
        }
    }

    /// @inheritdoc IFeeVault
    function claimProtocolFees() external returns (uint256 protocolFees) {
        address protocolFeeRecipient;

        // Interactions: claim the protocol fees
        (protocolFees, protocolFeeRecipient) = feeCalculator.claimProtocolFees(FEE_TOKEN.balanceOf(address(this)));

        // Requirements: check that the caller is the protocol fee recipient
        require(msg.sender == protocolFeeRecipient, Aera__CallerIsNotProtocolFeeRecipient());

        // Requirements: check that the protocol has earned fees
        require(protocolFees != 0, Aera__NoFeesToClaim());

        // Interactions: transfer the protocol fees to the protocol fee recipient
        FEE_TOKEN.safeTransfer(protocolFeeRecipient, protocolFees);
        // Log the protocol fees claimed event
        emit ProtocolFeesClaimed(protocolFeeRecipient, protocolFees);
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

/// @title IBeforeTransferHook
/// @notice Interface for token transfer hooks used for vault units in multi-depositor vaults
interface IBeforeTransferHook {
    ////////////////////////////////////////////////////////////
    //                         Events                         //
    ////////////////////////////////////////////////////////////

    event VaultUnitTransferableSet(address indexed vault, bool isTransferable);

    ////////////////////////////////////////////////////////////
    //                         Errors                         //
    ////////////////////////////////////////////////////////////

    error Aera__NotVaultOwner();
    error Aera__VaultUnitsNotTransferable(address vault);

    ////////////////////////////////////////////////////////////
    //                       Functions                        //
    ////////////////////////////////////////////////////////////

    /// @notice Set whether vault units should be transferable
    /// @param vault The vault to update status for
    /// @param isTransferable Whether the vault units are transferable
    function setIsVaultUnitsTransferable(address vault, bool isTransferable) external;

    /// @notice Perform before transfer checks
    /// @param from Address that is sending the units
    /// @param to Address that is receiving the units
    /// @param transferAgent Address that is always allowed to transfer the units
    function beforeTransfer(address from, address to, address transferAgent) external view;
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

import { IERC20 } from "@oz/interfaces/IERC20.sol";
import { IBeforeTransferHook } from "src/core/interfaces/IBeforeTransferHook.sol";

/// @title IMultiDepositorVault
/// @notice Interface for vaults that can accept deposits from multiple addresses
interface IMultiDepositorVault {
    ////////////////////////////////////////////////////////////
    //                         Events                         //
    ////////////////////////////////////////////////////////////

    event BeforeTransferHookSet(address indexed beforeTransferHook);
    event ProvisionerSet(address indexed provisioner);
    event Enter(
        address indexed sender,
        address indexed recipient,
        IERC20 indexed token,
        uint256 tokenAmount,
        uint256 unitsAmount
    );
    event Exit(
        address indexed sender,
        address indexed recipient,
        IERC20 indexed token,
        uint256 tokenAmount,
        uint256 unitsAmount
    );

    ////////////////////////////////////////////////////////////
    //                         Errors                         //
    ////////////////////////////////////////////////////////////

    error Aera__UnitsLocked();
    error Aera__ZeroAddressProvisioner();
    error Aera__CallerIsNotProvisioner();

    ////////////////////////////////////////////////////////////
    //                       Functions                        //
    ////////////////////////////////////////////////////////////

    /// @notice Set the before transfer hooks
    /// @param hooks The before transfer hooks address
    function setBeforeTransferHook(IBeforeTransferHook hooks) external;

    /// @notice Deposit tokens into the vault and mint units
    /// @param sender The sender of the tokens
    /// @param token The token to deposit
    /// @param tokenAmount The amount of token to deposit
    /// @param unitsAmount The amount of units to mint
    /// @param recipient The recipient of the units
    function enter(address sender, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, address recipient)
        external;

    /// @notice Withdraw tokens from the vault and burn units
    /// @param sender The sender of the units
    /// @param token The token to withdraw
    /// @param tokenAmount The amount of token to withdraw
    /// @param unitsAmount The amount of units to burn
    /// @param recipient The recipient of the tokens
    function exit(address sender, IERC20 token, uint256 tokenAmount, uint256 unitsAmount, address recipient) external;
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

import { BaseVaultParameters, ERC20Parameters, FeeVaultParameters } from "src/core/Types.sol";
import { IBeforeTransferHook } from "src/core/interfaces/IBeforeTransferHook.sol";
import { IFeeVaultDeployer } from "src/core/interfaces/IFeeVaultDeployer.sol";

/// @title IMultiDepositorVaultFactory
/// @notice Interface for the multi depositor vault factory
interface IMultiDepositorVaultFactory is IFeeVaultDeployer {
    ////////////////////////////////////////////////////////////
    //                         Events                         //
    ////////////////////////////////////////////////////////////

    /// @notice Emitted when the vault is created
    /// @param vault Vault address
    /// @param owner Initial owner address
    /// @param hooks Vault hooks address
    /// @param erc20Params ERC20 parameters
    /// @param feeVaultParams Fee vault parameters
    /// @param beforeTransferHook Before transfer hooks
    /// @param description Vault description
    event VaultCreated(
        address indexed vault,
        address indexed owner,
        address hooks,
        ERC20Parameters erc20Params,
        FeeVaultParameters feeVaultParams,
        IBeforeTransferHook beforeTransferHook,
        string description
    );

    ////////////////////////////////////////////////////////////
    //                       Errors                           //
    ////////////////////////////////////////////////////////////

    /// @notice Thrown when deploy delegate is the zero address
    error Aera__ZeroAddressDeployDelegate();

    ////////////////////////////////////////////////////////////
    //                       Functions                        //
    ////////////////////////////////////////////////////////////

    /// @notice Create multi depositor vault
    /// @param salt The salt used to generate the vault address
    /// @param description Vault description
    /// @param erc20Params ERC20 parameters for deployment
    /// @param baseVaultParams Base vault parameters for deployment
    /// @param feeVaultParams Fee vault parameters for deployment
    /// @param beforeTransferHook Before transfer hooks for deployment
    /// @param expectedVaultAddress Expected vault address to check against deployed vault address
    /// @return deployedVault Deployed vault address
    function create(
        bytes32 salt,
        string calldata description,
        ERC20Parameters calldata erc20Params,
        BaseVaultParameters calldata baseVaultParams,
        FeeVaultParameters calldata feeVaultParams,
        IBeforeTransferHook beforeTransferHook,
        address expectedVaultAddress
    ) external returns (address deployedVault);

    /// @notice Get the ERC20 name of vault units
    /// @return name The name of the vault ERC20 token
    function getERC20Name() external view returns (string memory name);

    /// @notice Get the ERC20 symbol of vault units
    /// @return symbol The symbol of the vault ERC20 token
    function getERC20Symbol() external view returns (string memory symbol);

    /// @notice Get the vault parameters
    /// @return beforeTransferHook The hooks called before vault unit transfers
    function multiDepositorVaultParameters() external view returns (IBeforeTransferHook beforeTransferHook);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

import { IERC20 } from "@oz/token/ERC20/IERC20.sol";
import { Request, TokenDetails } from "src/core/Types.sol";

/// @title IProvisioner
/// @notice Interface for the contract that can mint and burn vault units in exchange for tokens
interface IProvisioner {
    ////////////////////////////////////////////////////////////
    //                         Events                         //
    ////////////////////////////////////////////////////////////

    /// @notice Emitted when a user deposits tokens directly into the vault
    /// @param user The address of the depositor
    /// @param token The token being deposited
    /// @param tokensIn The amount of tokens deposited
    /// @param unitsOut The amount of units minted
    /// @param depositHash Unique identifier for this deposit
    event Deposited(
        address indexed user, IERC20 indexed token, uint256 tokensIn, uint256 unitsOut, bytes32 depositHash
    );

    /// @notice Emitted when a deposit is refunded
    /// @param depositHash The hash of the deposit being refunded
    event DepositRefunded(bytes32 indexed depositHash);

    /// @notice Emitted when a direct (sync) deposit is refunded
    /// @param depositHash The hash of the deposit being refunded
    event DirectDepositRefunded(bytes32 indexed depositHash);

    /// @notice Emitted when a user creates a deposit request
    /// @param user The address requesting the deposit
    /// @param token The token being deposited
    /// @param tokensIn The amount of tokens to deposit
    /// @param minUnitsOut The minimum amount of units expected
    /// @param solverTip The tip offered to the solver in deposit token terms
    /// @param deadline Timestamp until which the request is valid
    /// @param maxPriceAge Maximum age of price data that solver can use
    /// @param isFixedPrice Whether the request is a fixed price request
    /// @param depositRequestHash The hash of the deposit request
    event DepositRequested(
        address indexed user,
        IERC20 indexed token,
        uint256 tokensIn,
        uint256 minUnitsOut,
        uint256 solverTip,
        uint256 deadline,
        uint256 maxPriceAge,
        bool isFixedPrice,
        bytes32 depositRequestHash
    );

    /// @notice Emitted when a user creates a redeem request
    /// @param user The address requesting the redemption
    /// @param token The token requested in return for units
    /// @param minTokensOut The minimum amount of tokens the user expects to receive
    /// @param unitsIn The amount of units being redeemed
    /// @param solverTip The tip offered to the solver in redeem token terms
    /// @param deadline The timestamp until which this request is valid
    /// @param maxPriceAge Maximum age of price data that solver can use
    /// @param isFixedPrice Whether the request is a fixed price request
    /// @param redeemRequestHash The hash of the redeem request
    event RedeemRequested(
        address indexed user,
        IERC20 indexed token,
        uint256 minTokensOut,
        uint256 unitsIn,
        uint256 solverTip,
        uint256 deadline,
        uint256 maxPriceAge,
        bool isFixedPrice,
        bytes32 redeemRequestHash
    );

    /// @notice Emitted when a deposit request is solved successfully
    /// @param depositHash The unique identifier of the deposit request that was solved
    event DepositSolved(bytes32 indexed depositHash);

    /// @notice Emitted when a redeem request is solved successfully
    /// @param redeemHash The unique identifier of the redeem request that was solved
    event RedeemSolved(bytes32 indexed redeemHash);

    /// @notice Emitted when an unrecognized async deposit hash is used
    /// @param depositHash The deposit hash that was not found in async records
    event InvalidRequestHash(bytes32 indexed depositHash);

    /// @notice Emitted when async deposits are disabled and a deposit request cannot be processed
    /// @param index The index of the deposit request that was rejected
    event AsyncDepositDisabled(uint256 indexed index);

    /// @notice Emitted when async redeems are disabled and a redeem request cannot be processed
    /// @param index The index of the redeem request that was rejected
    event AsyncRedeemDisabled(uint256 indexed index);

    /// @notice Emitted when the price age exceeds the maximum allowed for a request
    /// @param index The index of the request that was rejected
    event PriceAgeExceeded(uint256 indexed index);

    /// @notice Emitted when a deposit exceeds the vault's configured deposit cap
    /// @param index The index of the request that was rejected
    event DepositCapExceeded(uint256 indexed index);

    /// @notice Emitted when there are not enough tokens to cover the required solver tip
    /// @param index The index of the request that was rejected
    event InsufficientTokensForTip(uint256 indexed index);

    /// @notice Emitted when the output units are less than the amount requested
    /// @param index The index of the request that was rejected
    /// @param amount The actual amount
    /// @param bound The minimum amount
    event AmountBoundExceeded(uint256 indexed index, uint256 amount, uint256 bound);

    /// @notice Emitted when a redeem request is refunded due to expiration or cancellation
    /// @param redeemHash The unique identifier of the redeem request that was refunded
    event RedeemRefunded(bytes32 indexed redeemHash);

    /// @notice Emitted when the vault's deposit limits are updated
    /// @param depositCap The new maximum total value that can be deposited into the vault
    /// @param depositRefundTimeout The new time window during which deposits can be refunded
    event DepositDetailsUpdated(uint256 depositCap, uint256 depositRefundTimeout);

    /// @notice Emitted when a token's deposit/withdrawal settings are updated
    /// @param token The token whose settings are being updated
    /// @param tokensDetails The new token details
    event TokenDetailsSet(IERC20 indexed token, TokenDetails tokensDetails);

    /// @notice Emitted when a token is removed from the provisioner
    /// @param token The token that was removed
    event TokenRemoved(IERC20 indexed token);

    ////////////////////////////////////////////////////////////
    //                         Errors                         //
    ////////////////////////////////////////////////////////////

    error Aera__SyncDepositDisabled();
    error Aera__AsyncDepositDisabled();
    error Aera__AsyncRedeemDisabled();
    error Aera__DepositCapExceeded();
    error Aera__MinUnitsOutNotMet();
    error Aera__TokensInZero();
    error Aera__UnitsInZero();
    error Aera__UnitsOutZero();
    error Aera__MinUnitsOutZero();
    error Aera__MaxTokensInZero();
    error Aera__MaxTokensInExceeded();
    error Aera__MaxDepositRefundTimeoutExceeded();
    error Aera__DepositHashNotFound();
    error Aera__HashNotFound();
    error Aera__RefundPeriodExpired();
    error Aera__DeadlineInPast();
    error Aera__DeadlineTooFarInFuture();
    error Aera__DeadlineInFutureAndUnauthorized();
    error Aera__MinTokenOutZero();
    error Aera__HashCollision();
    error Aera__ZeroAddressPriceAndFeeCalculator();
    error Aera__ZeroAddressMultiDepositorVault();
    error Aera__DepositMultiplierTooLow();
    error Aera__DepositMultiplierTooHigh();
    error Aera__RedeemMultiplierTooLow();
    error Aera__RedeemMultiplierTooHigh();
    error Aera__DepositCapZero();
    error Aera__PriceAndFeeCalculatorVaultPaused();
    error Aera__AutoPriceSolveNotAllowed();
    error Aera__FixedPriceSolverTipNotAllowed();
    error Aera__TokenCantBePriced();
    error Aera__CallerIsVault();
    error Aera__InvalidToken();

    ////////////////////////////////////////////////////////////
    //                         Functions                      //
    ////////////////////////////////////////////////////////////

    /// @notice Deposit tokens directly into the vault
    /// @param token The token to deposit
    /// @param tokensIn The amount of tokens to deposit
    /// @param minUnitsOut The minimum amount of units expected
    /// @dev MUST revert if tokensIn is 0, minUnitsOut is 0, or sync deposits are disabled
    /// @return unitsOut The amount of shares minted to the receiver
    function deposit(IERC20 token, uint256 tokensIn, uint256 minUnitsOut) external returns (uint256 unitsOut);

    /// @notice Mint exact amount of units by depositing required tokens
    /// @param token The token to deposit
    /// @param unitsOut The exact amount of units to mint
    /// @param maxTokensIn Maximum amount of tokens willing to deposit
    /// @return tokensIn The amount of tokens used to mint the requested shares
    function mint(IERC20 token, uint256 unitsOut, uint256 maxTokensIn) external returns (uint256 tokensIn);

    /// @notice Refund a deposit within the refund period
    /// @param sender The original depositor
    /// @param token The deposited token
    /// @param tokenAmount The amount of tokens deposited
    /// @param unitsAmount The amount of units minted
    /// @param refundableUntil Timestamp until which refund is possible
    /// @dev Only callable by authorized addresses
    function refundDeposit(
        address sender,
        IERC20 token,
        uint256 tokenAmount,
        uint256 unitsAmount,
        uint256 refundableUntil
    ) external;

    /// @notice Refund an expired deposit or redeem request
    /// @param token The token involved in the request
    /// @param request The request to refund
    /// @dev Can only be called after request deadline has passed
    function refundRequest(IERC20 token, Request calldata request) external;

    /// @notice Create a new deposit request to be solved by solvers
    /// @param token The token to deposit
    /// @param tokensIn The amount of tokens to deposit
    /// @param minUnitsOut The minimum amount of units expected
    /// @param solverTip The tip offered to the solver
    /// @param deadline Duration in seconds for which the request is valid
    /// @param maxPriceAge Maximum age of price data that solver can use
    /// @param isFixedPrice Whether the request is a fixed price request
    function requestDeposit(
        IERC20 token,
        uint256 tokensIn,
        uint256 minUnitsOut,
        uint256 solverTip,
        uint256 deadline,
        uint256 maxPriceAge,
        bool isFixedPrice
    ) external;

    /// @notice Create a new redeem request to be solved by solvers
    /// @param token The token to receive
    /// @param unitsIn The amount of units to redeem
    /// @param minTokensOut The minimum amount of tokens expected
    /// @param solverTip The tip offered to the solver
    /// @param deadline Duration in seconds for which the request is valid
    /// @param maxPriceAge Maximum age of price data that solver can use
    function requestRedeem(
        IERC20 token,
        uint256 unitsIn,
        uint256 minTokensOut,
        uint256 solverTip,
        uint256 deadline,
        uint256 maxPriceAge,
        bool isFixedPrice
    ) external;

    /// @notice Solve multiple requests using vault's liquidity
    /// @param token The token for which to solve requests
    /// @param requests Array of requests to solve
    /// @dev Only callable by authorized addresses
    function solveRequestsVault(IERC20 token, Request[] calldata requests) external;

    /// @notice Solve multiple requests using solver's own liquidity
    /// @param token The token for which to solve requests
    /// @param requests Array of requests to solve
    function solveRequestsDirect(IERC20 token, Request[] calldata requests) external;

    /// @notice Update token parameters
    /// @param token The token to update
    /// @param tokensDetails The new token details
    function setTokenDetails(IERC20 token, TokenDetails calldata tokensDetails) external;

    /// @notice Removes token from provisioner
    /// @param token The token to be removed
    function removeToken(IERC20 token) external;

    /// @notice Update deposit parameters
    /// @param depositCap_ New maximum total value that can be deposited
    /// @param depositRefundTimeout_ New time window for deposit refunds
    function setDepositDetails(uint256 depositCap_, uint256 depositRefundTimeout_) external;

    /// @notice Return maximum amount that can still be deposited
    /// @return Amount of deposit capacity remaining
    function maxDeposit() external view returns (uint256);

    /// @notice Check if a user's units are currently locked
    /// @param user The address to check
    /// @return True if user's units are locked, false otherwise
    function areUserUnitsLocked(address user) external view returns (bool);

    /// @notice Computes the hash for a sync deposit
    /// @param user The address making the deposit
    /// @param token The token being deposited
    /// @param tokenAmount The amount of tokens to deposit
    /// @param unitsAmount Minimum amount of units to receive
    /// @param refundableUntil The timestamp until which the deposit is refundable
    /// @return The hash of the deposit
    function getDepositHash(
        address user,
        IERC20 token,
        uint256 tokenAmount,
        uint256 unitsAmount,
        uint256 refundableUntil
    ) external pure returns (bytes32);

    /// @notice Computes the hash for a generic request
    /// @param token The token involved in the request
    /// @param request The request struct
    /// @return The hash of the request
    function getRequestHash(IERC20 token, Request calldata request) external pure returns (bytes32);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC-20 standard.
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

// 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;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;

/**
 * @dev Standard ERC-20 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 tokens.
 */
interface IERC20Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC20InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC20InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     * @param allowance Amount of tokens a `spender` is allowed to operate with.
     * @param needed Minimum amount required to perform a transfer.
     */
    error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC20InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `spender` to be approved. Used in approvals.
     * @param spender Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC20InvalidSpender(address spender);
}

/**
 * @dev Standard ERC-721 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
 */
interface IERC721Errors {
    /**
     * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-20.
     * Used in balance queries.
     * @param owner Address of the current owner of a token.
     */
    error ERC721InvalidOwner(address owner);

    /**
     * @dev Indicates a `tokenId` whose `owner` is the zero address.
     * @param tokenId Identifier number of a token.
     */
    error ERC721NonexistentToken(uint256 tokenId);

    /**
     * @dev Indicates an error related to the ownership over a particular token. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param tokenId Identifier number of a token.
     * @param owner Address of the current owner of a token.
     */
    error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC721InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC721InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param tokenId Identifier number of a token.
     */
    error ERC721InsufficientApproval(address operator, uint256 tokenId);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC721InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC721InvalidOperator(address operator);
}

/**
 * @dev Standard ERC-1155 Errors
 * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 tokens.
 */
interface IERC1155Errors {
    /**
     * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     * @param balance Current balance for the interacting account.
     * @param needed Minimum amount required to perform a transfer.
     * @param tokenId Identifier number of a token.
     */
    error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId);

    /**
     * @dev Indicates a failure with the token `sender`. Used in transfers.
     * @param sender Address whose tokens are being transferred.
     */
    error ERC1155InvalidSender(address sender);

    /**
     * @dev Indicates a failure with the token `receiver`. Used in transfers.
     * @param receiver Address to which tokens are being transferred.
     */
    error ERC1155InvalidReceiver(address receiver);

    /**
     * @dev Indicates a failure with the `operator`’s approval. Used in transfers.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     * @param owner Address of the current owner of a token.
     */
    error ERC1155MissingApprovalForAll(address operator, address owner);

    /**
     * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals.
     * @param approver Address initiating an approval operation.
     */
    error ERC1155InvalidApprover(address approver);

    /**
     * @dev Indicates a failure with the `operator` to be approved. Used in approvals.
     * @param operator Address that may be allowed to operate on tokens without being their owner.
     */
    error ERC1155InvalidOperator(address operator);

    /**
     * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation.
     * Used in batch transfers.
     * @param idsLength Length of the array of token identifiers
     * @param valuesLength Length of the array of token amounts
     */
    error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1363.sol)

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";

/**
 * @title IERC1363
 * @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
 *
 * Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
 * after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
 */
interface IERC1363 is IERC20, IERC165 {
    /*
     * Note: the ERC-165 identifier for this interface is 0xb0202a11.
     * 0xb0202a11 ===
     *   bytes4(keccak256('transferAndCall(address,uint256)')) ^
     *   bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
     *   bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256)')) ^
     *   bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
     */

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
     * and then calls {IERC1363Receiver-onTransferReceived} on `to`.
     * @param from The address which you want to send tokens from.
     * @param to The address which you want to transfer to.
     * @param value The amount of tokens to be transferred.
     * @param data Additional data with no specified format, sent in call to `to`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value) external returns (bool);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
     * @param spender The address which will spend the funds.
     * @param value The amount of tokens to be spent.
     * @param data Additional data with no specified format, sent in call to `spender`.
     * @return A boolean value indicating whether the operation succeeded unless throwing.
     */
    function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.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, ) = recipient.call{value: amount}("");
        if (!success) {
            revert Errors.FailedCall();
        }
    }

    /**
     * @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") {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert Errors.FailedCall();
        }
    }
}

File 16 of 48 : BaseVault.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

import { CallbackHandler } from "src/core/CallbackHandler.sol";

// solhint-disable no-unused-import

import { IERC20 } from "@oz/interfaces/IERC20.sol";
import { IERC721Receiver } from "@oz/interfaces/IERC721Receiver.sol";
import { Pausable } from "@oz/utils/Pausable.sol";
import { ReentrancyGuardTransient } from "@oz/utils/ReentrancyGuardTransient.sol";
import { TransientSlot } from "@oz/utils/TransientSlot.sol";
import { MerkleProof } from "@oz/utils/cryptography/MerkleProof.sol";
import { EnumerableMap } from "@oz/utils/structs/EnumerableMap.sol";
import { Authority } from "@solmate/auth/Auth.sol";

import { Auth2Step } from "src/core/Auth2Step.sol";
import {
    ADDRESS_SIZE_BITS,
    AFTER_HOOK_MASK,
    BEFORE_HOOK_MASK,
    CONFIGURABLE_HOOKS_LENGTH_MASK,
    ERC20_SPENDER_OFFSET,
    HOOKS_FLAG_MASK,
    WORD_SIZE
} from "src/core/Constants.sol";
import { Approval, BaseVaultParameters, HookCallType, OperationContext, ReturnValueType } from "src/core/Types.sol";
import { IBaseVault } from "src/core/interfaces/IBaseVault.sol";
import { IBaseVaultFactory } from "src/core/interfaces/IBaseVaultFactory.sol";
import { ISubmitHooks } from "src/core/interfaces/ISubmitHooks.sol";
import { IWhitelist } from "src/core/interfaces/IWhitelist.sol";
import { CalldataExtractor } from "src/core/libraries/CalldataExtractor.sol";
import { CalldataReader, CalldataReaderLib } from "src/core/libraries/CalldataReader.sol";
import { Pipeline } from "src/core/libraries/Pipeline.sol";
import { IERC20WithAllowance } from "src/dependencies/openzeppelin/token/ERC20/IERC20WithAllowance.sol";

/// @title BaseVault
/// @notice This contract embeds core Aera platform functionality: the ability to enlist off-chain guardians to take
/// guarded actions on a vault. It is meant to either be extended with deposit/withdraw capabilities for users or used
/// directly. When used directly, a depositor can simply transfer assets to the vault and a guardian can transfer them
/// out when needed
///
/// Registered guardians call the submit function and trigger vault operations. The vault may run before and after
/// submit
/// hooks and revert if a guardian is using an unauthorized operation. Authorized operations are configured in an
/// off-chain merkle tree and guardians need to provide a merkle proof for each operation. In addition to validating
/// operation targets (the contract and function being called), the merkle tree can maintain custom per-operation hooks
/// that extract specific parts of the calldata for validation or even perform (possibly stateful) validation during
/// the submit call
contract BaseVault is IBaseVault, Pausable, CallbackHandler, ReentrancyGuardTransient, Auth2Step, IERC721Receiver {
    using Pipeline for bytes;
    using CalldataExtractor for bytes;
    using EnumerableMap for EnumerableMap.AddressToBytes32Map;
    using TransientSlot for *;

    ///////////////////////////////////////////////////////////
    //                       Constants                       //
    ///////////////////////////////////////////////////////////

    /// @notice ERC7201-compliant transient storage slot for the current hook call type flag
    /// @dev Equal to keccak256(abi.encode(uint256(keccak256("aera.basevault.hookCallType")) - 1)) &
    ///      ~bytes32(uint256(0xff));
    bytes32 internal constant HOOK_CALL_TYPE_SLOT = 0xb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e00;

    ////////////////////////////////////////////////////////////
    //                       Immutables                       //
    ////////////////////////////////////////////////////////////

    IWhitelist public immutable WHITELIST;

    ////////////////////////////////////////////////////////////
    //                        Storage                         //
    ////////////////////////////////////////////////////////////

    /// @notice Address of the submit hooks contract for vault-level operations
    ISubmitHooks public submitHooks;

    /// @notice Enumerable map of each guardian address to their merkle root
    EnumerableMap.AddressToBytes32Map internal guardianRoots;

    ////////////////////////////////////////////////////////////
    //                       Modifiers                        //
    ////////////////////////////////////////////////////////////

    /// @notice Ensures caller either has auth authorization requiresAuth (owner or authorized role) or is a guardian
    modifier onlyAuthOrGuardian() {
        require(
            isAuthorized(msg.sender, msg.sig) || guardianRoots.contains(msg.sender), Aera__CallerIsNotAuthOrGuardian()
        );
        _;
    }

    constructor() Pausable() Auth2Step(msg.sender, Authority(address(0))) {
        // Interactions: get initialization parameters from the factory
        BaseVaultParameters memory params = IBaseVaultFactory(msg.sender).baseVaultParameters();

        address initialOwner = params.owner;
        // Requirements: check that the owner address is not zero
        require(initialOwner != address(0), Aera__ZeroAddressOwner());
        // Effects: sets the pending owner via Auth2Step two-step process
        transferOwnership(initialOwner);

        if (params.authority != Authority(address(0))) {
            // Effects: set the authority
            setAuthority(params.authority);
        }

        // Effects: set the whitelist
        WHITELIST = params.whitelist;

        // Effects: set vault-level submit hooks
        ISubmitHooks submitHooks_ = params.submitHooks;
        if (address(submitHooks_) != address(0)) {
            _setSubmitHooks(submitHooks_);
        }
    }

    ////////////////////////////////////////////////////////////
    //              Public / External Functions               //
    ////////////////////////////////////////////////////////////

    /// @notice Receive function to allow the vault to receive native tokens
    receive() external payable { }

    /// @inheritdoc IBaseVault
    function submit(bytes calldata data) external whenNotPaused nonReentrant {
        (bool success, bytes32 root) = guardianRoots.tryGet(msg.sender);
        // Requirements: check that the caller is a guardian
        require(success, Aera__CallerIsNotGuardian());

        address submitHooks_ = address(submitHooks);
        // Requirements + Interactions: call the before submit hooks if defined
        _beforeSubmitHooks(submitHooks_, data);

        CalldataReader reader = CalldataReaderLib.from(data);
        CalldataReader end = reader.readBytesEnd(data);

        Approval[] memory approvals;
        uint256 approvalsLength;
        // Requirements + Interactions: execute operations
        (approvals, approvalsLength,, reader) = _executeSubmit(root, reader, false);

        // Requirements + Interactions: call the after submit hooks if defined
        _afterSubmitHooks(submitHooks_, data);

        // Invariants: verify no outgoing approvals are left
        _noPendingApprovalsInvariant(approvals, approvalsLength);

        // Invariants: check that the reader is at the end of the calldata
        reader.requireAtEndOf(end);
    }

    /// @inheritdoc IBaseVault
    function setGuardianRoot(address guardian, bytes32 root) external virtual requiresAuth {
        // Requirements + Effects: set the guardian root
        _setGuardianRoot(guardian, root);
    }

    /// @inheritdoc IBaseVault
    function removeGuardian(address guardian) external virtual requiresAuth {
        // Effects: set the guardian root to zero
        guardianRoots.remove(guardian);

        // Log emit guardian root set event
        emit GuardianRootSet(guardian, bytes32(0));
    }

    /// @inheritdoc IBaseVault
    function checkGuardianWhitelist(address guardian) external returns (bool isRemoved) {
        // Requirements: check that the guardian is not in the whitelist
        if (!WHITELIST.isWhitelisted(guardian)) {
            // Effects: set the guardian root to zero
            guardianRoots.remove(guardian);

            isRemoved = true;

            // Log guardian root set
            emit GuardianRootSet(guardian, bytes32(0));
        }
    }

    /// @inheritdoc IBaseVault
    function setSubmitHooks(ISubmitHooks newSubmitHooks) external virtual requiresAuth {
        // Requirements + Effects: set the submit hooks address
        _setSubmitHooks(newSubmitHooks);
    }

    /// @inheritdoc IBaseVault
    function pause() external onlyAuthOrGuardian {
        // Effects: pause the vault
        _pause();
    }

    /// @inheritdoc IBaseVault
    function unpause() external requiresAuth {
        // Effects: unpause the vault
        _unpause();
    }

    /// @inheritdoc IBaseVault
    function getActiveGuardians() external view returns (address[] memory) {
        return guardianRoots.keys();
    }

    /// @inheritdoc IBaseVault
    function getGuardianRoot(address guardian) external view returns (bytes32) {
        (bool success, bytes32 root) = guardianRoots.tryGet(guardian);
        return success ? root : bytes32(0);
    }

    /// @inheritdoc IBaseVault
    function getCurrentHookCallType() external view returns (HookCallType) {
        return HookCallType(HOOK_CALL_TYPE_SLOT.asUint256().tload());
    }

    /// @inheritdoc IERC721Receiver
    function onERC721Received(address, address, uint256, bytes calldata) external pure returns (bytes4) {
        return IERC721Receiver.onERC721Received.selector;
    }

    ////////////////////////////////////////////////////////////
    //              Private / Internal Functions              //
    ////////////////////////////////////////////////////////////

    /// @inheritdoc CallbackHandler
    function _handleCallbackOperations(bytes32 root, uint256 cursor)
        internal
        virtual
        override
        returns (bytes memory returnValue)
    {
        CalldataReader reader = CalldataReader.wrap(cursor);
        CalldataReader end = reader.readBytesEnd();

        Approval[] memory approvals;
        uint256 approvalsLength;
        bytes[] memory results;
        // Requirements + Interactions: execute vault operations received from the callback
        (approvals, approvalsLength, results, reader) = _executeSubmit(root, reader, true);

        // Effects: store the history of outgoing token approvals for later verification in submit
        _storeCallbackApprovals(approvals, approvalsLength);

        (reader, returnValue) = _getReturnValue(reader, results);

        // Invariants: check that the reader is at the end of the calldata
        reader.requireAtEndOf(end);

        return returnValue;
    }

    /// @notice Prepare for a callback if the guardian expects one
    /// @dev Writes to transient storage to encode callback expectations
    /// @param reader Current position in the calldata
    /// @param root The merkle root of the active guardian that triggered the callback
    /// @return Updated cursor position
    /// @return Packed callback data
    function _processExpectedCallback(CalldataReader reader, bytes32 root) internal returns (CalldataReader, uint208) {
        bool hasCallback;
        (reader, hasCallback) = reader.readBool();

        if (!hasCallback) {
            return (reader, 0);
        }

        uint208 packedCallbackData;
        (reader, packedCallbackData) = reader.readU208();

        // Requirements + Effects: allow the callback
        _allowCallback(root, packedCallbackData);

        return (reader, packedCallbackData);
    }

    /// @notice Call the before submit hooks if defined
    /// @param hooks Address of the submit hooks contract
    /// @dev Submit hooks passed as an argument to reduce storage loading
    function _beforeSubmitHooks(address hooks, bytes calldata data) internal {
        if (_hasBeforeHooks(hooks)) {
            // Interactions: call the before submit hooks
            (bool success, bytes memory result) =
                hooks.call(abi.encodeWithSelector(ISubmitHooks.beforeSubmit.selector, data, msg.sender));
            // Requirements: check that the hooks call succeeded
            require(success, Aera__BeforeSubmitHooksFailed(result));
        }
    }

    /// @notice Call the after submit hooks if defined
    /// @param hooks Address of the submit hooks contract
    /// @dev Submit hooks passed as an argument to reduce storage loading
    function _afterSubmitHooks(address hooks, bytes calldata data) internal {
        if (_hasAfterHooks(hooks)) {
            // Interactions: call the after submit hooks
            (bool success, bytes memory result) =
                hooks.call(abi.encodeWithSelector(ISubmitHooks.afterSubmit.selector, data, msg.sender));
            // Invariants: check that the hooks call succeeded
            require(success, Aera__AfterSubmitHooksFailed(result));
        }
    }

    /// @notice Call the before operation hooks if defined
    /// @param operationHooks Address of the operation-specific hooks
    /// @param data Operation calldata
    /// @param i Operation index
    /// @return result Result of the hooks call
    function _beforeOperationHooks(address operationHooks, bytes memory data, uint256 i)
        internal
        returns (bytes memory result)
    {
        if (_hasBeforeHooks(operationHooks)) {
            // Effects: set the hook call type to before
            _setHookCallType(HookCallType.BEFORE);

            // Interactions: call the before operation hooks
            (bool success, bytes memory returnValue) = operationHooks.call(data);
            // Requirements: check that the hooks call succeeded
            require(success, Aera__BeforeOperationHooksFailed(i, returnValue));

            // Requirements: check that the return data length is a multiple of 32
            require(returnValue.length % WORD_SIZE == 0, Aera__InvalidBeforeOperationHooksReturnDataLength());

            // Effects: set the hook call type to none
            _setHookCallType(HookCallType.NONE);

            // Return value is ABI encoded so we need to decode it to get to the actual
            // bytes value that we returned from the hooks
            (result) = abi.decode(returnValue, (bytes));
        }
    }

    /// @notice Call the after operation hooks if defined
    /// @param operationHooks Address of the operation-specific hooks
    /// @param data Operation calldata
    /// @param i Operation index
    function _afterOperationHooks(address operationHooks, bytes memory data, uint256 i) internal {
        if (_hasAfterHooks(operationHooks)) {
            // Effects: set the hook call type to after
            _setHookCallType(HookCallType.AFTER);

            // Interactions: call the after operation hooks
            (bool success, bytes memory result) = operationHooks.call(data);
            // Requirements: check that the hooks call succeeded
            require(success, Aera__AfterOperationHooksFailed(i, result));

            // Effects: set the hook call type to none
            _setHookCallType(HookCallType.NONE);
        }
    }

    /// @notice Executes a series of operations
    /// @param root The merkle root of the active guardian that triggered the callback
    /// @param reader Current position in the calldata
    /// @param isCalledFromCallback Whether the submit is called from a callback
    /// @return approvals Array of outgoing approvals created during execution
    /// @return approvalsLength Length of approvals array
    /// @return results Array of results from the operations
    /// @return newReader Updated cursor position
    /// @dev Approvals are tracked so we can verify if they have been zeroed out at the end of submit
    function _executeSubmit(bytes32 root, CalldataReader reader, bool isCalledFromCallback)
        internal
        returns (Approval[] memory approvals, uint256 approvalsLength, bytes[] memory results, CalldataReader newReader)
    {
        uint256 operationsLength;
        (reader, operationsLength) = reader.readU8();

        results = new bytes[](operationsLength);

        // There cannot be more approvals than operations
        approvals = new Approval[](operationsLength);

        // Safe to reuse the same variable because all its parameters get overwritten every time, except in static call
        // branch where we don't verify against the merkle root
        OperationContext memory ctx;
        for (uint256 i = 0; i < operationsLength; ++i) {
            (reader, ctx.target) = reader.readAddr();

            bytes memory callData;
            (reader, callData) = reader.readBytesToMemory();

            reader = callData.pipe(reader, results);

            bool isStaticCall;
            (reader, isStaticCall) = reader.readBool();
            if (isStaticCall) {
                // Interactions: perform external static call
                (bool success, bytes memory result) = ctx.target.staticcall(callData);
                // Requirements: verify static call succeeded
                require(success, Aera__SubmissionFailed(i, result));

                results[i] = result;
            } else {
                ctx.selector = bytes4(callData);
                if (_isAllowanceSelector(ctx.selector)) {
                    unchecked {
                        approvals[approvalsLength++] =
                            Approval({ token: ctx.target, spender: _extractApprovalSpender(callData) });
                    }
                }
                // Requirements + Effects: prepare to handle a callback if defined
                (reader, ctx.callbackData) = _processExpectedCallback(reader, root);

                bytes memory extractedData;
                // Requirements + possible Interactions: process the operation hooks
                (reader, extractedData, ctx.configurableOperationHooks, ctx.operationHooks) =
                    _processBeforeOperationHooks(reader, callData, i);

                bytes32[] memory proof;
                (reader, proof) = reader.readBytes32Array();

                (reader, ctx.value) = reader.readOptionalU256();

                // Requirements: verify merkle proof
                _verifyOperation(proof, root, _createMerkleLeaf(ctx, extractedData));

                //slither-disable-next-line arbitrary-send-eth
                (bool success, bytes memory result) = ctx.target.call{ value: ctx.value }(callData);
                // Requirements: check that the submission succeeded
                require(success, Aera__SubmissionFailed(i, result));

                if (ctx.callbackData != 0) {
                    // Requirements: check that the callback was received
                    require(_hasCallbackBeenCalled(), Aera__ExpectedCallbackNotReceived());

                    if (!isCalledFromCallback) {
                        // Effects: get the callback approvals and clear the transient storage
                        Approval[] memory callbackApprovals = _getCallbackApprovals();

                        // Invariants: verify no pending approvals from the callback
                        _noPendingApprovalsInvariant(callbackApprovals, callbackApprovals.length);
                    }
                }

                // possible Interactions + Requirements: call the after operation hooks if defined
                _afterOperationHooks(ctx.operationHooks, callData, i);

                results[i] = result;
            }
        }

        return (approvals, approvalsLength, results, reader);
    }

    /// @notice Processes all hooks for operation
    /// @notice Returns extracted data if configurable or contract before operation hooks is defined
    /// @param reader Current position in the calldata
    /// @param callData Operation calldata
    /// @param i Operation index
    /// @return reader Updated reader position
    /// @return extractedData Extracted chunks of calldata
    /// @return hooksConfigBytes hooks configuration bytes
    /// @return operationHooks Operation hooks address
    /// @dev Custom hooks (with contracts) can run before and after each operation but a configurable hooks
    /// can only run before an operation. This function processes all of the possible configurations of hooks
    /// which doesn't allow using both a custom before hook and a configurable before hook
    function _processBeforeOperationHooks(CalldataReader reader, bytes memory callData, uint256 i)
        internal
        returns (CalldataReader, bytes memory, uint256, address)
    {
        uint8 hooksConfigFlag;
        (reader, hooksConfigFlag) = reader.readU8();

        if (hooksConfigFlag == 0) {
            return (reader, "", 0, address(0));
        }

        uint256 calldataOffsetsCount = hooksConfigFlag & CONFIGURABLE_HOOKS_LENGTH_MASK;

        // Case A: Only configurable hooks defined
        if (hooksConfigFlag & HOOKS_FLAG_MASK == 0) {
            uint256 calldataOffsetsPacked;
            (reader, calldataOffsetsPacked) = reader.readU256();

            return (
                reader, callData.extract(calldataOffsetsPacked, calldataOffsetsCount), calldataOffsetsPacked, address(0)
            );
        }

        address operationHooks;
        // Case B: Both configurable and custom hooks defined
        if (calldataOffsetsCount != 0) {
            uint256 calldataOffsetsPacked;
            (reader, calldataOffsetsPacked) = reader.readU256();

            (reader, operationHooks) = reader.readAddr();
            // Requirements: check that the operation hooks is not a before submit hooks
            require(!_hasBeforeHooks(operationHooks), Aera__BeforeOperationHooksWithConfigurableHooks());

            return (
                reader,
                callData.extract(calldataOffsetsPacked, calldataOffsetsCount),
                calldataOffsetsPacked,
                operationHooks
            );
        }

        // Case C: only a custom hooks defined
        (reader, operationHooks) = reader.readAddr();

        return (
            reader,
            // Requirements + Interactions: call the before operation hooks if defined
            _beforeOperationHooks(operationHooks, callData, i),
            0,
            operationHooks
        );
    }

    /// @notice Set the submit hooks address
    /// @param submitHooks_ Address of the submit hooks contract
    function _setSubmitHooks(ISubmitHooks submitHooks_) internal {
        // Effects: set submit hooks address
        submitHooks = submitHooks_;

        // Log submit hooks set
        emit SubmitHooksSet(address(submitHooks_));
    }

    /// @notice Set the guardian root
    /// @param guardian Address of the guardian
    /// @param root Merkle root
    function _setGuardianRoot(address guardian, bytes32 root) internal virtual {
        // Requirements: check that the guardian address is not zero
        require(guardian != address(0), Aera__ZeroAddressGuardian());

        // Requirements: check that the guardian is whitelisted
        require(WHITELIST.isWhitelisted(guardian), Aera__GuardianNotWhitelisted());

        // Requirements: check that root is not zero
        require(root != bytes32(0), Aera__ZeroAddressMerkleRoot());

        // Effects: set guardian root
        guardianRoots.set(guardian, root);

        // Log guardian root set
        emit GuardianRootSet(guardian, root);
    }

    /// @notice Set the hook call type
    /// @param hookCallType The hook call type
    function _setHookCallType(HookCallType hookCallType) internal {
        // Effects: store the hook call type
        HOOK_CALL_TYPE_SLOT.asUint256().tstore(uint8(hookCallType));
    }

    /// @notice Verify no pending approvals remain at the end of a submit
    /// @param approvals Array of approvals to check
    /// @param approvalsLength Length of approvals array
    /// @dev We iterate backwards to avoid extra i variable
    /// @dev While loop is preferred over for(;approvalsLength != 0;)
    /// @dev Iterator variable is not used because it's not needed and decrement needs to be unchecked
    function _noPendingApprovalsInvariant(Approval[] memory approvals, uint256 approvalsLength) internal view {
        Approval memory approval;
        while (approvalsLength != 0) {
            unchecked {
                --approvalsLength;
            }

            approval = approvals[approvalsLength];

            // Requirements: verify allowance is zero
            require(
                // Interactions: get allowance
                IERC20(approval.token).allowance(address(this), approval.spender) == 0,
                Aera__AllowanceIsNotZero(approval.token, approval.spender)
            );
        }
    }

    /// @notice Get the return value from the operations
    /// @param reader Current position in the calldata
    /// @param results Array of results from the operations
    /// @return newReader Updated reader position
    /// @return returnValue Return value from the operations
    function _getReturnValue(CalldataReader reader, bytes[] memory results)
        internal
        pure
        returns (CalldataReader newReader, bytes memory returnValue)
    {
        uint8 returnTypeFlag;
        (reader, returnTypeFlag) = reader.readU8();

        if (returnTypeFlag == uint8(ReturnValueType.STATIC_RETURN)) {
            (reader, returnValue) = reader.readBytesToMemory();
        } else if (returnTypeFlag == uint8(ReturnValueType.DYNAMIC_RETURN)) {
            uint256 length = results.length;
            require(length > 0, Aera__NoResults());

            unchecked {
                returnValue = results[length - 1];
            }
        }

        return (reader, returnValue);
    }

    /// @notice Verify an operation by validating the merkle proof
    /// @param proof The merkle proof
    /// @param root The merkle root
    /// @param leaf The merkle leaf to verify
    function _verifyOperation(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure {
        require(MerkleProof.verify(proof, root, leaf), Aera__ProofVerificationFailed());
    }

    /// @notice Create a merkle leaf
    /// @param ctx The operation context
    /// @param extractedData The extracted data
    /// @return leaf The merkle leaf
    function _createMerkleLeaf(OperationContext memory ctx, bytes memory extractedData)
        internal
        pure
        returns (bytes32)
    {
        return keccak256(
            abi.encodePacked(
                ctx.target,
                ctx.selector,
                ctx.value > 0,
                ctx.operationHooks,
                ctx.configurableOperationHooks,
                ctx.callbackData,
                extractedData
            )
        );
    }

    /// @dev Extract spender address from approval data
    /// @param data Approval calldata
    /// @return spender Address of the spender
    function _extractApprovalSpender(bytes memory data) internal pure returns (address spender) {
        // Length check skipped intentionally, call reverts on misuse
        // "memory-safe" retained for compiler optimization
        assembly ("memory-safe") {
            let offset := add(data, ERC20_SPENDER_OFFSET)
            spender := mload(offset)
        }
    }

    /// @dev Check if hooks needs to be called before the submit/operation
    /// @param hooks Hooks address to check
    /// @return True if hooks needs to be called before the submit/operation
    function _hasBeforeHooks(address hooks) internal pure returns (bool) {
        /// least significant bit is 1 indicating it's a before hooks
        return uint160(hooks) & BEFORE_HOOK_MASK != 0;
    }

    /// @dev Check if submit hooks needs to be called after the submit/operation
    /// @param hooks Submit hooks address to check
    /// @return True if submit hooks needs to be called after the submit/operation
    function _hasAfterHooks(address hooks) internal pure returns (bool) {
        /// second least significant bit is 1 indicating it's a after hooks
        return uint160(hooks) & AFTER_HOOK_MASK != 0;
    }

    /// @dev Check if the selector is an allowance handling selector
    /// @param selector Selector to check
    /// @return True if the selector is an allowance handling selector
    function _isAllowanceSelector(bytes4 selector) internal pure returns (bool) {
        return selector == IERC20.approve.selector || selector == IERC20WithAllowance.increaseAllowance.selector;
    }
}

File 17 of 48 : Types.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

import { IERC20 } from "@oz/interfaces/IERC20.sol";
import { Authority } from "@solmate/auth/Auth.sol";

import { IFeeCalculator } from "src/core/interfaces/IFeeCalculator.sol";
import { ISubmitHooks } from "src/core/interfaces/ISubmitHooks.sol";
import { IWhitelist } from "src/core/interfaces/IWhitelist.sol";
import { IOracle } from "src/dependencies/oracles/IOracle.sol";

/// @notice Type of request: deposit/redeem and auto/fixed price
/// @dev
/// - The order is chosen so each bit encodes a property:
///   - Bit 0: 0 = deposit, 1 = redeem
///   - Bit 1: 0 = auto price, 1 = fixed price
enum RequestType {
    DEPOSIT_AUTO_PRICE, // 00: deposit, auto price
    REDEEM_AUTO_PRICE, // 01: redeem, auto price
    DEPOSIT_FIXED_PRICE, // 10: deposit, fixed price
    REDEEM_FIXED_PRICE // 11: redeem, fixed price

}

/// @notice Type of return value: no return, static return, dynamic return
/// @dev
/// - 00: no return
/// - 01: static return - hardcoded return data
/// - 10: dynamic return - return data is extracted from the results array
enum ReturnValueType {
    NO_RETURN,
    STATIC_RETURN,
    DYNAMIC_RETURN
}

/// @notice Type of hook call: before, after, or none
enum HookCallType {
    NONE,
    BEFORE,
    AFTER
}

/// @notice Operation struct for vault operations
/// @dev This struct is not used directly in core logic, but included for reference and clarity
///      It illustrates the full structure of an operation without storage packing
struct Operation {
    /// @notice Target contract address to call
    address target;
    /// @notice Calldata for the target contract
    bytes data;
    /// @notice Array of clipboard operations for copying return data
    Clipboard[] clipboards;
    /// @notice Whether to perform a static call
    bool isStaticCall;
    /// @notice Callback data for post-operation processing
    CallbackData callbackData;
    /// @notice Address of the hooks contract
    address hooks;
    /// @notice Array of offsets for extracting calldata
    uint16[] configurableHooksOffsets;
    /// @notice Merkle proof for operation verification
    bytes32[] proof;
    /// @notice ETH value to send with the call
    uint256 value;
}

/// @notice Operation execution context data
/// @dev Used to avoid stack too deep in BaseVault._executeSubmit function
struct OperationContext {
    /// @notice Address of the target contract to call
    address target;
    /// @notice Function selector extracted from calldata
    bytes4 selector;
    /// @notice Callback data packed
    uint208 callbackData;
    /// @notice ETH value to send with the call
    uint256 value;
    /// @notice Address of the operation-specific hooks contract
    address operationHooks;
    /// @notice Offset of the calldata extraction offsets packed in uint256
    uint256 configurableOperationHooks;
}

/// @notice Struct for payable operations
struct OperationPayable {
    /// @notice Target contract address
    address target;
    /// @notice Calldata for the target contract
    bytes data;
    /// @notice ETH value to send with the call
    uint256 value;
}

/// @notice Struct for token approvals
struct Approval {
    /// @notice Token address to approve
    address token;
    /// @notice Address to approve spending for
    address spender;
}

/// @notice Struct for token amounts
struct TokenAmount {
    /// @notice ERC20 token address
    IERC20 token;
    /// @notice Amount of tokens
    uint256 amount;
}

/// @notice Struct for clipboard operations
struct Clipboard {
    /// @notice Index of the result to copy from
    uint8 resultIndex;
    /// @notice Which word to copy from the result
    uint8 copyWord;
    /// @notice Offset to paste the copied data
    uint16 pasteOffset;
}

/// @notice Struct for callback data
struct CallbackData {
    /// @notice Address allowed to execute the callback
    address caller;
    /// @notice Function selector for the callback
    bytes4 selector;
    /// @notice Offset in calldata for the callback
    uint16 calldataOffset;
}

/// @notice Vault parameters for vault deployment
struct BaseVaultParameters {
    /// @notice Initial owner address
    address owner;
    /// @notice Initial authority address
    Authority authority;
    /// @notice Submit hooks address
    ISubmitHooks submitHooks;
    /// @notice Whitelist contract address
    IWhitelist whitelist;
}

/// @notice Parameters for fee vault deployment
struct FeeVaultParameters {
    /// @notice The fee calculator address
    IFeeCalculator feeCalculator;
    /// @notice The fee token address
    IERC20 feeToken;
    /// @notice The fee recipient address
    address feeRecipient;
}

/// @notice Parameters for ERC20 deployment
struct ERC20Parameters {
    /// @notice ERC20 token name
    string name;
    /// @notice ERC20 token symbol
    string symbol;
}

/// @notice Fee structure for TVL and performance fees
/// @dev All fees are in basis points (1/10000)
struct Fee {
    /// @notice TVL fee in basis points
    uint16 tvl;
    /// @notice Performance fee in basis points
    uint16 performance;
}

/// @notice Tracks fee configuration and accrued fees for a vault
struct VaultAccruals {
    /// @notice Current fee rates for the vault
    Fee fees;
    /// @notice Accrued fees for the vault fee recipient
    uint112 accruedFees;
    /// @notice Total protocol fees accrued but not claimed
    uint112 accruedProtocolFees;
}

/// @notice Complete state of a vault's fee configuration and accruals
struct VaultSnapshot {
    /// @notice Timestamp of last fee accrual
    uint32 lastFeeAccrual;
    /// @notice Timestamp when snapshot was taken
    uint32 timestamp;
    /// @notice Timestamp when snapshot is finalized after dispute period
    uint32 finalizedAt;
    /// @notice Average value of vault assets during snapshot period
    uint160 averageValue;
    /// @notice Highest profit achieved during snapshot period
    uint128 highestProfit;
    /// @notice Highest profit achieved in previous periods
    uint128 lastHighestProfit;
}

/// @notice Struct for target and calldata
struct TargetCalldata {
    /// @notice Target contract address
    address target;
    /// @notice Calldata for the target contract
    bytes data;
}

/// @notice Vault price information and configuration
struct VaultPriceState {
    /// @notice Whether vault price updates are paused
    bool paused;
    /// @notice Maximum age of price data in seconds before it is considered stale
    uint8 maxPriceAge;
    /// @notice Minimum time between price updates in minutes
    uint16 minUpdateIntervalMinutes;
    /// @notice Maximum allowed price increase ratio in basis points
    uint16 maxPriceToleranceRatio;
    /// @notice Minimum allowed price decrease ratio in basis points
    uint16 minPriceToleranceRatio;
    /// @notice Maximum allowed delay in price updates in days
    uint8 maxUpdateDelayDays;
    /// @notice Timestamp of last price update
    uint32 timestamp;
    /// @notice Seconds between last fee accrual and last price update
    uint24 accrualLag;
    /// @notice Current unit price
    uint128 unitPrice;
    /// @notice Highest historical unit price
    uint128 highestPrice;
    /// @notice Total supply at last price update
    uint128 lastTotalSupply;
}

/// @notice Token configuration for deposits and redemptions
struct TokenDetails {
    /// @notice Whether async deposits are enabled
    bool asyncDepositEnabled;
    /// @notice Whether async redemptions are enabled
    bool asyncRedeemEnabled;
    /// @notice Whether sync deposits are enabled
    bool syncDepositEnabled;
    /// @notice Premium multiplier applied to deposits in basis points (9999 = 0.1% premium)
    uint16 depositMultiplier;
    /// @notice Premium multiplier applied to redemptions in basis points (9999 = 0.1% premium)
    uint16 redeemMultiplier;
}

/// @notice Request parameters for deposits and redemptions
/// @dev
/// - For deposits:
///   - units: minimum units the user wants to receive (minUnitsOut)
///   - tokens: amount of tokens the user is providing (tokensIn)
/// - For redemptions:
///   - units: amount of units the user is redeeming (unitsIn)
///   - tokens: minimum tokens the user wants to receive (minTokensOut)
struct Request {
    /// @notice Request type(deposit/redeem + auto/fixed price)
    RequestType requestType;
    /// @notice User address making the request
    address user;
    /// @notice Amount of vault units
    uint256 units;
    /// @notice Amount of underlying tokens
    uint256 tokens;
    /// @notice Tip paid to solver, always in tokens
    uint256 solverTip;
    /// @notice Timestamp after which request expires
    uint256 deadline;
    /// @notice Maximum age of price data allowed
    uint256 maxPriceAge;
}

/// @notice Oracle data for a base/quote pair, including current, pending, and status flags
struct OracleData {
    /// @notice True if an oracle update is scheduled
    bool isScheduledForUpdate;
    /// @notice True if the current oracle is disabled
    bool isDisabled;
    /// @notice The currently active oracle
    IOracle oracle;
    /// @notice The pending oracle to be activated after delay
    IOracle pendingOracle;
    /// @notice Timestamp at which the pending oracle can be committed
    uint32 commitTimestamp;
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

/// @title IFeeCalculator
/// @notice Interface for a contract that calculates fees for a vault and protocol
interface IFeeCalculator {
    ////////////////////////////////////////////////////////////
    //                         Events                         //
    ////////////////////////////////////////////////////////////

    /// @notice Emitted when a new vault is registered
    /// @param vault The address of the registered vault
    event VaultRegistered(address indexed vault);

    ////////////////////////////////////////////////////////////
    //                         Errors                         //
    ////////////////////////////////////////////////////////////

    /// @notice Thrown when attempting to register an already registered vault
    error Aera__VaultAlreadyRegistered();

    ////////////////////////////////////////////////////////////
    //                       Functions                        //
    ////////////////////////////////////////////////////////////

    /// @notice Register a new vault with the fee calculator
    function registerVault() external;

    /// @notice Process a fee claim for a specific vault
    /// @param feeTokenBalance Available fee token balance to distribute
    /// @return earnedFees The amount of fees to be claimed by the fee recipient
    /// @return protocolEarnedFees The amount of protocol fees to be claimed by the protocol
    /// @return protocolFeeRecipient The address of the protocol fee recipient
    /// @dev Expected to be called by the vault only when claiming fees
    /// Only accrues fees and updates stored values; does not transfer tokens
    /// Caller must perform the actual transfers to avoid permanent fee loss
    function claimFees(uint256 feeTokenBalance) external returns (uint256, uint256, address);

    /// @notice Process a protocol fee claim for a vault
    /// @param feeTokenBalance Available fee token balance to distribute
    /// @return accruedFees The amount of protocol fees claimed
    /// @return protocolFeeRecipient The address of the protocol fee recipient
    /// @dev Expected to be called by the vault only when claiming protocol fees
    /// Only accrues protocol fees and updates stored values; does not transfer tokens
    /// Caller must perform the actual transfers to avoid permanent protocol fee loss
    function claimProtocolFees(uint256 feeTokenBalance) external returns (uint256, address);

    /// @notice Returns the current claimable fees for the given vault, as if a claim was made now
    /// @param vault The address of the vault to preview fees for
    /// @param feeTokenBalance Available fee token balance to distribute
    /// If set to `type(uint256).max`, the function returns all accrued fees
    /// If set to an actual balance, the result is capped to that claimable amount
    /// @return vaultFees The amount of claimable fees for the vault
    /// @return protocolFees The amount of claimable protocol fees
    function previewFees(address vault, uint256 feeTokenBalance)
        external
        view
        returns (uint256 vaultFees, uint256 protocolFees);

    /// @notice Returns the address that receives protocol fees
    /// @return The address that receives the protocol fees
    function protocolFeeRecipient() external view returns (address);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

import { IERC20 } from "@oz/interfaces/IERC20.sol";
import { IFeeCalculator } from "src/core/interfaces/IFeeCalculator.sol";

/// @title IFeeVault
/// @notice Interface for vaults that support fees but don't have multiple depositors
interface IFeeVault {
    ////////////////////////////////////////////////////////////
    //                         Events                         //
    ////////////////////////////////////////////////////////////

    event FeesClaimed(address indexed feeRecipient, uint256 fees);
    event ProtocolFeesClaimed(address indexed protocolFeeRecipient, uint256 protocolEarnedFees);
    event FeeRecipientUpdated(address indexed newFeeRecipient);
    event FeeCalculatorUpdated(address indexed newFeeCalculator);

    ////////////////////////////////////////////////////////////
    //                         Errors                         //
    ////////////////////////////////////////////////////////////

    error Aera__ZeroAddressFeeCalculator();
    error Aera__ZeroAddressFeeToken();
    error Aera__ZeroAddressFeeRecipient();
    error Aera__NoFeesToClaim();
    error Aera__CallerIsNotFeeRecipient();
    error Aera__CallerIsNotProtocolFeeRecipient();

    ////////////////////////////////////////////////////////////
    //                       Functions                        //
    ////////////////////////////////////////////////////////////

    /// @notice Set the fee recipient
    /// @param newFeeRecipient The new fee recipient address
    function setFeeRecipient(address newFeeRecipient) external;

    /// @notice Claim accrued fees for msg.sender
    /// @dev Automatically claims any earned protocol fees for the protocol
    /// @return feeRecipientFees The amount of fees to be claimed by the fee recipient
    /// @return protocolFees The amount of protocol fees to be claimed by the protocol
    function claimFees() external returns (uint256 feeRecipientFees, uint256 protocolFees);

    /// @notice Claim accrued protocol fees
    /// @return protocolFees The amount of protocol fees to be claimed by the protocol
    function claimProtocolFees() external returns (uint256 protocolFees);

    /// @notice Set the fee calculator
    /// @dev newFeeCalculator can be zero, which has the effect as disabling the fee calculator
    /// @param newFeeCalculator The new fee calculator
    function setFeeCalculator(IFeeCalculator newFeeCalculator) external;

    /// @notice Get the fee calculator
    // solhint-disable-next-line func-name-mixedcase
    function feeCalculator() external view returns (IFeeCalculator);

    /// @notice Get the fee token
    // solhint-disable-next-line func-name-mixedcase
    function FEE_TOKEN() external view returns (IERC20);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

import { FeeVaultParameters } from "src/core/Types.sol";
import { IBaseVaultDeployer } from "src/core/interfaces/IBaseVaultDeployer.sol";

/// @title IFeeVaultDeployer
/// @notice Interface for the fee vault deployer
interface IFeeVaultDeployer is IBaseVaultDeployer {
    ////////////////////////////////////////////////////////////
    //                       Functions                        //
    ////////////////////////////////////////////////////////////

    /// @notice Get the deployment parameters for the fee vault
    /// @return params Deployment parameters for the fee vault
    function feeVaultParameters() external view returns (FeeVaultParameters memory params);
}

File 21 of 48 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)

pragma solidity ^0.8.20;

import {IERC165} from "../utils/introspection/IERC165.sol";

File 22 of 48 : Errors.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

/**
 * @dev Collection of common custom errors used in multiple contracts
 *
 * IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
 * It is recommended to avoid relying on the error API for critical functionality.
 *
 * _Available since v5.1._
 */
library Errors {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedCall();

    /**
     * @dev The deployment failed.
     */
    error FailedDeployment();

    /**
     * @dev A necessary precompile is missing.
     */
    error MissingPrecompile(address);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

import { TransientSlot } from "@oz/utils/TransientSlot.sol";

import {
    ADDRESS_SIZE_BITS,
    CALLBACK_DATA_OFFSET,
    NO_CALLBACK_DATA,
    SELECTOR_OFFSET,
    // solhint-disable-next-line no-unused-import
    WORD_SIZE
} from "src/core/Constants.sol";
import { Approval } from "src/core/Types.sol";
import { ICallbackHandler } from "src/core/interfaces/ICallbackHandler.sol";

/// @title CallbackHandler
/// @notice Handles callback validation and execution for vault operations. This contract is designed to be
/// used as a mixin in BaseVault, providing the ability to register logic for safely handling callbacks during
/// guardian submissions. A common use case for handlers is receiving a flash loan. To receive a flashloan, the vault
/// has to cede control when requesting a flashloan and then atomically handle the callback to repay the flashloan
/// This requires two capabilities: the ability to register new handlers and the ability to initiate additional
/// operations in the handle while being restricted by the merkle tree constraints. The callback handler contract
/// achieves this by allowing guardians to "prepare" for a callback when they construct a given operation. If the
/// operation "has a callback" then the fallback function in this contract will handle it. It will use transient storage
/// to preserve information such as the expected callback caller, function selector of the callback and any
/// approvals that are created during the callback
/// @dev Uses transient storage to manage callback state and approvals

abstract contract CallbackHandler is ICallbackHandler {
    using TransientSlot for *;

    ////////////////////////////////////////////////////////////
    //                       Constants                        //
    ////////////////////////////////////////////////////////////

    /// @notice ERC7201-compliant transient storage slot for storing the next authorized selector + caller
    /// @dev Equal to keccak256(abi.encode(uint256(keccak256("aera.callbackHandler.call")) - 1)) &
    ///      ~bytes32(uint256(0xff));
    /// @custom:security Critical for callback validation
    bytes32 internal constant CALLBACK_CALL_SLOT = 0xa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f00;

    /// @notice ERC7201-compliant transient storage slot for storing the callback merkle root
    /// @dev Equal to keccak256(abi.encode(uint256(keccak256("aera.callbackHandler.merkleRoot")) - 1) &
    ///      ~bytes32(uint256(0xff));
    /// @custom:security Critical for callback validation
    bytes32 internal constant CALLBACK_MERKLE_ROOT_SLOT =
        0x30fb041442610fd0a22e4654f60ea1c715088ef7320b5ec0c4e75cbdd99dbe00;

    /// @notice ERC7201-compliant transient storage slot for storing the approval tracking
    /// @dev Equal to keccak256(abi.encode(uint256(keccak256("aera.callbackHandler.approvals")) - 1)) &
    ///      ~bytes32(uint256(0xff));
    /// @custom:security Critical for tracking token approvals during callbacks
    bytes32 internal constant APPROVALS_SLOT = 0xba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c00;

    ////////////////////////////////////////////////////////////
    //              Public / External Functions               //
    ////////////////////////////////////////////////////////////

    /// @notice Handle incoming callbacks and validates their authorization
    /// @dev Extracts callback data and forwards to _handleCallbackOperations if valid
    fallback(bytes calldata) external returns (bytes memory returnValue) {
        (address caller, bytes4 selector, uint16 userDataOffset) = _getAllowedCallback();
        // Requirements: check that the selector matches
        require(msg.sig == selector, Aera__UnauthorizedCallback());
        // Requirements: check that the caller matches
        require(msg.sender == caller, Aera__UnauthorizedCallback());

        bytes32 root = _getAllowedMerkleRoot();
        // Requirements: check that the merkle root is not zero
        require(root != bytes32(0), Aera__UnauthorizedCallback());

        // If NO_CALLBACK_DATA magic value is sent, we don't need to return anything
        if (userDataOffset == NO_CALLBACK_DATA) return bytes("");

        // Effects, Interactions: handle callback operations
        returnValue = _handleCallbackOperations(root, userDataOffset);
    }

    ////////////////////////////////////////////////////////////
    //              Private / Internal Functions              //
    ////////////////////////////////////////////////////////////

    /// @notice Internal handler for validated callbacks
    /// @dev Callback operations are like regular operations, but with a return value, which are encoded after
    /// operations array
    /// ┌─────────────────────────────┬─────────────────────────┬───────────────────────────────────────────────┐
    /// │ FIELDS                      │ SIZE                    │ DESCRIPTION                                   │
    /// ├─────────────────────────────┴─────────────────────────┴───────────────────────────────────────────────┤
    /// │  returnTypeFlag              1 byte                     0 = no return, 1 = static, 2 = dynamic        │
    /// │  [if returnTypeFlag == 1]:                                                                            │
    /// │     returnDataLength         2 bytes                    Length of return data                         │
    /// │     returnData               <returnDataLength> bytes   Static return data                            │
    /// └───────────────────────────────────────────────────────────────────────────────────────────────────────┘
    /// @param root The merkle root of the callback
    /// @param cursor The cursor to the callback data
    /// @return returnValue The return value of the callback
    function _handleCallbackOperations(bytes32 root, uint256 cursor)
        internal
        virtual
        returns (bytes memory returnValue);

    /// @notice Whitelist a function selector and caller as a valid callback
    /// @dev Uses transient storage to store the callback data
    /// @param root The merkle root of the callback
    /// @param packedCallbackData Packed data containing caller, selector, and offsets
    function _allowCallback(bytes32 root, uint256 packedCallbackData) internal {
        // Effects: store the callback data
        CALLBACK_CALL_SLOT.asUint256().tstore(packedCallbackData);

        // Effects: store the merkle root
        CALLBACK_MERKLE_ROOT_SLOT.asBytes32().tstore(root);
    }

    /// @notice Store approvals for the current callback context
    /// @param approvals Array of token approvals to store
    /// @param length Length of the array
    /// @dev Uses transient storage to track approvals during callback execution
    /// @dev Length of the array, packed with the token address will be stored in the first slot
    /// @dev All other elements are laid out sequentially after the first slot, taking 2 slots per approval
    /// @dev If there are existing approvals, we will update length in the slot zero and append new approvals
    function _storeCallbackApprovals(Approval[] memory approvals, uint256 length) internal {
        if (length == 0) return;

        uint256 existingApproval = APPROVALS_SLOT.asUint256().tload();
        uint256 existingLength = existingApproval >> ADDRESS_SIZE_BITS;

        uint256 i;
        uint256 currentSlot = uint256(APPROVALS_SLOT);
        Approval memory approval;
        if (existingLength == 0) {
            approval = approvals[0];
            unchecked {
                // Effects: store the token and spender
                /// @dev Store packed token and length in the zero slot, and spender in the second
                bytes32(currentSlot).asUint256().tstore(_packLengthAndToken(length, approval.token));
                bytes32(++currentSlot).asAddress().tstore(approval.spender);
            }

            i = 1;
        } else {
            unchecked {
                uint256 newLength = existingLength + length;
                // Effects: store the token and spender
                /// @dev Update the length and preserve the token in the zero slot
                bytes32(currentSlot).asUint256().tstore(
                    _packLengthAndToken(newLength, address(uint160(existingApproval)))
                );
                /// Minus one to compensate for pre-increment in upcoming storage loop
                currentSlot += existingLength * 2 - 1;
            }
        }

        for (; i < length; ++i) {
            approval = approvals[i];
            unchecked {
                // Effects: store the token and spender
                bytes32(++currentSlot).asAddress().tstore(approval.token);
                bytes32(++currentSlot).asAddress().tstore(approval.spender);
            }
        }
    }

    /// @notice Retrieve the currently allowed callback data
    /// @dev Unpacks data from transient storage
    /// @return caller The authorized caller address
    /// @return selector The authorized function selector
    /// @return userDataOffset The offset in calldata where user data begins
    /// @custom:security Critical for callback validation
    function _getAllowedCallback() internal returns (address caller, bytes4 selector, uint16 userDataOffset) {
        (caller, selector, userDataOffset) = _unpackCallbackData(CALLBACK_CALL_SLOT.asUint256().tload());

        // Effects: clear the transient storage slot
        CALLBACK_CALL_SLOT.asUint256().tstore(0);
    }

    /// @notice Retrieves the currently allowed merkle root
    /// @dev Unpacks data from transient storage
    /// @return root The authorized merkle root
    /// @custom:security Critical for callback validation
    function _getAllowedMerkleRoot() internal returns (bytes32 root) {
        root = CALLBACK_MERKLE_ROOT_SLOT.asBytes32().tload();
        // Effects: clear the merkle root slot
        CALLBACK_MERKLE_ROOT_SLOT.asBytes32().tstore(bytes32(0));
    }

    /// @notice Retrieves the current callback approvals
    /// @dev Decodes approvals from transient storage
    /// @return approvals Array of current token approvals
    /// @dev The first slot contains the length of the array, packed with the token address
    /// @dev All other elements are laid out sequentially after the first slot, taking 2 slots per approval
    /// @dev Only length slot is cleared, the rest of the approvals are left in the transient storage
    /// @dev This is safe because even if new approvals are added, old ones will be overwritten for length slots
    function _getCallbackApprovals() internal returns (Approval[] memory approvals) {
        uint256 lengthWithToken = APPROVALS_SLOT.asUint256().tload();
        uint256 length = lengthWithToken >> ADDRESS_SIZE_BITS;
        if (length == 0) return approvals;

        // Effects: clear the approvals length slot
        APPROVALS_SLOT.asUint256().tstore(0);

        approvals = new Approval[](length);

        address token = address(uint160(lengthWithToken));

        uint256 slotUint256 = uint256(APPROVALS_SLOT);
        address spender;
        unchecked {
            spender = bytes32(++slotUint256).asAddress().tload();
        }

        approvals[0] = Approval({ token: token, spender: spender });

        for (uint256 i = 1; i < length; ++i) {
            unchecked {
                token = bytes32(++slotUint256).asAddress().tload();
                spender = bytes32(++slotUint256).asAddress().tload();
            }
            approvals[i] = Approval({ token: token, spender: spender });
        }
    }

    /// @notice Checks if an expected callback has been called
    /// @dev If callback was expected but not received, CALLBACK_CALL_SLOT will not be reset to 0
    /// @return True if an expected callback has been called, false otherwise
    function _hasCallbackBeenCalled() internal view returns (bool) {
        return CALLBACK_CALL_SLOT.asUint256().tload() == 0;
    }

    /// @notice Unpacks callback data from a packed uint256
    /// @param packed The packed uint256 containing callback data
    /// @return target The target address
    /// @return selector The function selector
    /// @return dataOffset The offset in calldata where user data begins
    function _unpackCallbackData(uint256 packed)
        private
        pure
        returns (address target, bytes4 selector, uint16 dataOffset)
    {
        target = address(uint160(packed));
        selector = bytes4(bytes32(packed << SELECTOR_OFFSET));
        dataOffset = uint16(packed >> CALLBACK_DATA_OFFSET);
    }

    /// @notice Packs a token address and length into a uint256
    /// @dev Used in transient storage slot zero
    /// @dev `length` is required to be less than `type(uint96).max + 1`
    /// @param length The length of the approvals array
    /// @param token The token address
    /// @return packed The packed uint256
    function _packLengthAndToken(uint256 length, address token) private pure returns (uint256) {
        return uint160(token) | (length << ADDRESS_SIZE_BITS);
    }
}

File 24 of 48 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC721Receiver.sol)

pragma solidity ^0.8.20;

import {IERC721Receiver} from "../token/ERC721/IERC721Receiver.sol";

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    bool private _paused;

    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.24;

import {TransientSlot} from "./TransientSlot.sol";

/**
 * @dev Variant of {ReentrancyGuard} that uses transient storage.
 *
 * NOTE: This variant only works on networks where EIP-1153 is available.
 *
 * _Available since v5.1._
 */
abstract contract ReentrancyGuardTransient {
    using TransientSlot for *;

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant REENTRANCY_GUARD_STORAGE =
        0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_reentrancyGuardEntered()) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        REENTRANCY_GUARD_STORAGE.asBoolean().tstore(true);
    }

    function _nonReentrantAfter() private {
        REENTRANCY_GUARD_STORAGE.asBoolean().tstore(false);
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return REENTRANCY_GUARD_STORAGE.asBoolean().tload();
    }
}

// SPDX-License-Identifier: MIT
// This file was procedurally generated from scripts/generate/templates/TransientSlot.js.

pragma solidity ^0.8.24;

/**
 * @dev Library for reading and writing value-types to specific transient storage slots.
 *
 * Transient slots are often used to store temporary values that are removed after the current transaction.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 *  * Example reading and writing values using transient storage:
 * ```solidity
 * contract Lock {
 *     using TransientSlot for *;
 *
 *     // Define the slot. Alternatively, use the SlotDerivation library to derive the slot.
 *     bytes32 internal constant _LOCK_SLOT = 0xf4678858b2b588224636b8522b729e7722d32fc491da849ed75b3fdf3c84f542;
 *
 *     modifier locked() {
 *         require(!_LOCK_SLOT.asBoolean().tload());
 *
 *         _LOCK_SLOT.asBoolean().tstore(true);
 *         _;
 *         _LOCK_SLOT.asBoolean().tstore(false);
 *     }
 * }
 * ```
 *
 * TIP: Consider using this library along with {SlotDerivation}.
 */
library TransientSlot {
    /**
     * @dev UDVT that represent a slot holding a address.
     */
    type AddressSlot is bytes32;

    /**
     * @dev Cast an arbitrary slot to a AddressSlot.
     */
    function asAddress(bytes32 slot) internal pure returns (AddressSlot) {
        return AddressSlot.wrap(slot);
    }

    /**
     * @dev UDVT that represent a slot holding a bool.
     */
    type BooleanSlot is bytes32;

    /**
     * @dev Cast an arbitrary slot to a BooleanSlot.
     */
    function asBoolean(bytes32 slot) internal pure returns (BooleanSlot) {
        return BooleanSlot.wrap(slot);
    }

    /**
     * @dev UDVT that represent a slot holding a bytes32.
     */
    type Bytes32Slot is bytes32;

    /**
     * @dev Cast an arbitrary slot to a Bytes32Slot.
     */
    function asBytes32(bytes32 slot) internal pure returns (Bytes32Slot) {
        return Bytes32Slot.wrap(slot);
    }

    /**
     * @dev UDVT that represent a slot holding a uint256.
     */
    type Uint256Slot is bytes32;

    /**
     * @dev Cast an arbitrary slot to a Uint256Slot.
     */
    function asUint256(bytes32 slot) internal pure returns (Uint256Slot) {
        return Uint256Slot.wrap(slot);
    }

    /**
     * @dev UDVT that represent a slot holding a int256.
     */
    type Int256Slot is bytes32;

    /**
     * @dev Cast an arbitrary slot to a Int256Slot.
     */
    function asInt256(bytes32 slot) internal pure returns (Int256Slot) {
        return Int256Slot.wrap(slot);
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(AddressSlot slot) internal view returns (address value) {
        assembly ("memory-safe") {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(AddressSlot slot, address value) internal {
        assembly ("memory-safe") {
            tstore(slot, value)
        }
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(BooleanSlot slot) internal view returns (bool value) {
        assembly ("memory-safe") {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(BooleanSlot slot, bool value) internal {
        assembly ("memory-safe") {
            tstore(slot, value)
        }
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(Bytes32Slot slot) internal view returns (bytes32 value) {
        assembly ("memory-safe") {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(Bytes32Slot slot, bytes32 value) internal {
        assembly ("memory-safe") {
            tstore(slot, value)
        }
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(Uint256Slot slot) internal view returns (uint256 value) {
        assembly ("memory-safe") {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(Uint256Slot slot, uint256 value) internal {
        assembly ("memory-safe") {
            tstore(slot, value)
        }
    }

    /**
     * @dev Load the value held at location `slot` in transient storage.
     */
    function tload(Int256Slot slot) internal view returns (int256 value) {
        assembly ("memory-safe") {
            value := tload(slot)
        }
    }

    /**
     * @dev Store `value` at location `slot` in transient storage.
     */
    function tstore(Int256Slot slot, int256 value) internal {
        assembly ("memory-safe") {
            tstore(slot, value)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MerkleProof.sol)
// This file was procedurally generated from scripts/generate/templates/MerkleProof.js.

pragma solidity ^0.8.20;

import {Hashes} from "./Hashes.sol";

/**
 * @dev These functions deal with verification of Merkle Tree proofs.
 *
 * The tree and the proofs can be generated using our
 * https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
 * You will find a quickstart guide in the readme.
 *
 * WARNING: You should avoid using leaf values that are 64 bytes long prior to
 * hashing, or use a hash function other than keccak256 for hashing leaves.
 * This is because the concatenation of a sorted pair of internal nodes in
 * the Merkle tree could be reinterpreted as a leaf value.
 * OpenZeppelin's JavaScript library generates Merkle trees that are safe
 * against this attack out of the box.
 *
 * IMPORTANT: Consider memory side-effects when using custom hashing functions
 * that access memory in an unsafe way.
 *
 * NOTE: This library supports proof verification for merkle trees built using
 * custom _commutative_ hashing functions (i.e. `H(a, b) == H(b, a)`). Proving
 * leaf inclusion in trees built using non-commutative hashing functions requires
 * additional logic that is not supported by this library.
 */
library MerkleProof {
    /**
     *@dev The multiproof provided is not valid.
     */
    error MerkleProofInvalidMultiproof();

    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     *
     * This version handles proofs in memory with the default hashing function.
     */
    function verify(bytes32[] memory proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProof(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leaves & pre-images are assumed to be sorted.
     *
     * This version handles proofs in memory with the default hashing function.
     */
    function processProof(bytes32[] memory proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     *
     * This version handles proofs in memory with a custom hashing function.
     */
    function verify(
        bytes32[] memory proof,
        bytes32 root,
        bytes32 leaf,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bool) {
        return processProof(proof, leaf, hasher) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leaves & pre-images are assumed to be sorted.
     *
     * This version handles proofs in memory with a custom hashing function.
     */
    function processProof(
        bytes32[] memory proof,
        bytes32 leaf,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = hasher(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     *
     * This version handles proofs in calldata with the default hashing function.
     */
    function verifyCalldata(bytes32[] calldata proof, bytes32 root, bytes32 leaf) internal pure returns (bool) {
        return processProofCalldata(proof, leaf) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leaves & pre-images are assumed to be sorted.
     *
     * This version handles proofs in calldata with the default hashing function.
     */
    function processProofCalldata(bytes32[] calldata proof, bytes32 leaf) internal pure returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = Hashes.commutativeKeccak256(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if a `leaf` can be proved to be a part of a Merkle tree
     * defined by `root`. For this, a `proof` must be provided, containing
     * sibling hashes on the branch from the leaf to the root of the tree. Each
     * pair of leaves and each pair of pre-images are assumed to be sorted.
     *
     * This version handles proofs in calldata with a custom hashing function.
     */
    function verifyCalldata(
        bytes32[] calldata proof,
        bytes32 root,
        bytes32 leaf,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bool) {
        return processProofCalldata(proof, leaf, hasher) == root;
    }

    /**
     * @dev Returns the rebuilt hash obtained by traversing a Merkle tree up
     * from `leaf` using `proof`. A `proof` is valid if and only if the rebuilt
     * hash matches the root of the tree. When processing the proof, the pairs
     * of leaves & pre-images are assumed to be sorted.
     *
     * This version handles proofs in calldata with a custom hashing function.
     */
    function processProofCalldata(
        bytes32[] calldata proof,
        bytes32 leaf,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bytes32) {
        bytes32 computedHash = leaf;
        for (uint256 i = 0; i < proof.length; i++) {
            computedHash = hasher(computedHash, proof[i]);
        }
        return computedHash;
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * This version handles multiproofs in memory with the default hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
     * The `leaves` must be validated independently. See {processMultiProof}.
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProof(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * This version handles multiproofs in memory with the default hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
     * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
     * validating the leaves elsewhere.
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofFlagsLen = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proof.length != proofFlagsLen + 1) {
            revert MerkleProofInvalidMultiproof();
        }

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](proofFlagsLen);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < proofFlagsLen; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = Hashes.commutativeKeccak256(a, b);
        }

        if (proofFlagsLen > 0) {
            if (proofPos != proof.length) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[proofFlagsLen - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * This version handles multiproofs in memory with a custom hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
     * The `leaves` must be validated independently. See {processMultiProof}.
     */
    function multiProofVerify(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32 root,
        bytes32[] memory leaves,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bool) {
        return processMultiProof(proof, proofFlags, leaves, hasher) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * This version handles multiproofs in memory with a custom hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
     * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
     * validating the leaves elsewhere.
     */
    function processMultiProof(
        bytes32[] memory proof,
        bool[] memory proofFlags,
        bytes32[] memory leaves,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofFlagsLen = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proof.length != proofFlagsLen + 1) {
            revert MerkleProofInvalidMultiproof();
        }

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](proofFlagsLen);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < proofFlagsLen; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = hasher(a, b);
        }

        if (proofFlagsLen > 0) {
            if (proofPos != proof.length) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[proofFlagsLen - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * This version handles multiproofs in calldata with the default hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
     * The `leaves` must be validated independently. See {processMultiProofCalldata}.
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves
    ) internal pure returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * This version handles multiproofs in calldata with the default hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
     * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
     * validating the leaves elsewhere.
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves
    ) internal pure returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofFlagsLen = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proof.length != proofFlagsLen + 1) {
            revert MerkleProofInvalidMultiproof();
        }

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](proofFlagsLen);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < proofFlagsLen; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = Hashes.commutativeKeccak256(a, b);
        }

        if (proofFlagsLen > 0) {
            if (proofPos != proof.length) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[proofFlagsLen - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }

    /**
     * @dev Returns true if the `leaves` can be simultaneously proven to be a part of a Merkle tree defined by
     * `root`, according to `proof` and `proofFlags` as described in {processMultiProof}.
     *
     * This version handles multiproofs in calldata with a custom hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. See {processMultiProof} for details.
     *
     * NOTE: Consider the case where `root == proof[0] && leaves.length == 0` as it will return `true`.
     * The `leaves` must be validated independently. See {processMultiProofCalldata}.
     */
    function multiProofVerifyCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32 root,
        bytes32[] memory leaves,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bool) {
        return processMultiProofCalldata(proof, proofFlags, leaves, hasher) == root;
    }

    /**
     * @dev Returns the root of a tree reconstructed from `leaves` and sibling nodes in `proof`. The reconstruction
     * proceeds by incrementally reconstructing all inner nodes by combining a leaf/inner node with either another
     * leaf/inner node or a proof sibling node, depending on whether each `proofFlags` item is true or false
     * respectively.
     *
     * This version handles multiproofs in calldata with a custom hashing function.
     *
     * CAUTION: Not all Merkle trees admit multiproofs. To use multiproofs, it is sufficient to ensure that: 1) the tree
     * is complete (but not necessarily perfect), 2) the leaves to be proven are in the opposite order they are in the
     * tree (i.e., as seen from right to left starting at the deepest layer and continuing at the next layer).
     *
     * NOTE: The _empty set_ (i.e. the case where `proof.length == 1 && leaves.length == 0`) is considered a no-op,
     * and therefore a valid multiproof (i.e. it returns `proof[0]`). Consider disallowing this case if you're not
     * validating the leaves elsewhere.
     */
    function processMultiProofCalldata(
        bytes32[] calldata proof,
        bool[] calldata proofFlags,
        bytes32[] memory leaves,
        function(bytes32, bytes32) view returns (bytes32) hasher
    ) internal view returns (bytes32 merkleRoot) {
        // This function rebuilds the root hash by traversing the tree up from the leaves. The root is rebuilt by
        // consuming and producing values on a queue. The queue starts with the `leaves` array, then goes onto the
        // `hashes` array. At the end of the process, the last hash in the `hashes` array should contain the root of
        // the Merkle tree.
        uint256 leavesLen = leaves.length;
        uint256 proofFlagsLen = proofFlags.length;

        // Check proof validity.
        if (leavesLen + proof.length != proofFlagsLen + 1) {
            revert MerkleProofInvalidMultiproof();
        }

        // The xxxPos values are "pointers" to the next value to consume in each array. All accesses are done using
        // `xxx[xxxPos++]`, which return the current value and increment the pointer, thus mimicking a queue's "pop".
        bytes32[] memory hashes = new bytes32[](proofFlagsLen);
        uint256 leafPos = 0;
        uint256 hashPos = 0;
        uint256 proofPos = 0;
        // At each step, we compute the next hash using two values:
        // - a value from the "main queue". If not all leaves have been consumed, we get the next leaf, otherwise we
        //   get the next hash.
        // - depending on the flag, either another value from the "main queue" (merging branches) or an element from the
        //   `proof` array.
        for (uint256 i = 0; i < proofFlagsLen; i++) {
            bytes32 a = leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++];
            bytes32 b = proofFlags[i]
                ? (leafPos < leavesLen ? leaves[leafPos++] : hashes[hashPos++])
                : proof[proofPos++];
            hashes[i] = hasher(a, b);
        }

        if (proofFlagsLen > 0) {
            if (proofPos != proof.length) {
                revert MerkleProofInvalidMultiproof();
            }
            unchecked {
                return hashes[proofFlagsLen - 1];
            }
        } else if (leavesLen > 0) {
            return leaves[0];
        } else {
            return proof[0];
        }
    }
}

File 29 of 48 : EnumerableMap.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableMap.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableMap.js.

pragma solidity ^0.8.20;

import {EnumerableSet} from "./EnumerableSet.sol";

/**
 * @dev Library for managing an enumerable variant of Solidity's
 * https://solidity.readthedocs.io/en/latest/types.html#mapping-types[`mapping`]
 * type.
 *
 * Maps have the following properties:
 *
 * - Entries are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Entries are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableMap for EnumerableMap.UintToAddressMap;
 *
 *     // Declare a set state variable
 *     EnumerableMap.UintToAddressMap private myMap;
 * }
 * ```
 *
 * The following map types are supported:
 *
 * - `uint256 -> address` (`UintToAddressMap`) since v3.0.0
 * - `address -> uint256` (`AddressToUintMap`) since v4.6.0
 * - `bytes32 -> bytes32` (`Bytes32ToBytes32Map`) since v4.6.0
 * - `uint256 -> uint256` (`UintToUintMap`) since v4.7.0
 * - `bytes32 -> uint256` (`Bytes32ToUintMap`) since v4.7.0
 * - `uint256 -> bytes32` (`UintToBytes32Map`) since v5.1.0
 * - `address -> address` (`AddressToAddressMap`) since v5.1.0
 * - `address -> bytes32` (`AddressToBytes32Map`) since v5.1.0
 * - `bytes32 -> address` (`Bytes32ToAddressMap`) since v5.1.0
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableMap, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableMap.
 * ====
 */
library EnumerableMap {
    using EnumerableSet for EnumerableSet.Bytes32Set;

    // To implement this library for multiple types with as little code repetition as possible, we write it in
    // terms of a generic Map type with bytes32 keys and values. The Map implementation uses private functions,
    // and user-facing implementations such as `UintToAddressMap` are just wrappers around the underlying Map.
    // This means that we can only create new EnumerableMaps for types that fit in bytes32.

    /**
     * @dev Query for a nonexistent map key.
     */
    error EnumerableMapNonexistentKey(bytes32 key);

    struct Bytes32ToBytes32Map {
        // Storage of keys
        EnumerableSet.Bytes32Set _keys;
        mapping(bytes32 key => bytes32) _values;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(Bytes32ToBytes32Map storage map, bytes32 key, bytes32 value) internal returns (bool) {
        map._values[key] = value;
        return map._keys.add(key);
    }

    /**
     * @dev Removes a key-value pair from a map. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(Bytes32ToBytes32Map storage map, bytes32 key) internal returns (bool) {
        delete map._values[key];
        return map._keys.remove(key);
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool) {
        return map._keys.contains(key);
    }

    /**
     * @dev Returns the number of key-value pairs in the map. O(1).
     */
    function length(Bytes32ToBytes32Map storage map) internal view returns (uint256) {
        return map._keys.length();
    }

    /**
     * @dev Returns the key-value pair stored at position `index` in the map. O(1).
     *
     * Note that there are no guarantees on the ordering of entries inside the
     * array, and it may change when more entries are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32ToBytes32Map storage map, uint256 index) internal view returns (bytes32 key, bytes32 value) {
        bytes32 atKey = map._keys.at(index);
        return (atKey, map._values[atKey]);
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bool exists, bytes32 value) {
        bytes32 val = map._values[key];
        if (val == bytes32(0)) {
            return (contains(map, key), bytes32(0));
        } else {
            return (true, val);
        }
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(Bytes32ToBytes32Map storage map, bytes32 key) internal view returns (bytes32) {
        bytes32 value = map._values[key];
        if (value == 0 && !contains(map, key)) {
            revert EnumerableMapNonexistentKey(key);
        }
        return value;
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(Bytes32ToBytes32Map storage map) internal view returns (bytes32[] memory) {
        return map._keys.values();
    }

    // UintToUintMap

    struct UintToUintMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(UintToUintMap storage map, uint256 key, uint256 value) internal returns (bool) {
        return set(map._inner, bytes32(key), bytes32(value));
    }

    /**
     * @dev Removes a value from a map. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(UintToUintMap storage map, uint256 key) internal returns (bool) {
        return remove(map._inner, bytes32(key));
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(UintToUintMap storage map, uint256 key) internal view returns (bool) {
        return contains(map._inner, bytes32(key));
    }

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(UintToUintMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the map. O(1).
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintToUintMap storage map, uint256 index) internal view returns (uint256 key, uint256 value) {
        (bytes32 atKey, bytes32 val) = at(map._inner, index);
        return (uint256(atKey), uint256(val));
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(UintToUintMap storage map, uint256 key) internal view returns (bool exists, uint256 value) {
        (bool success, bytes32 val) = tryGet(map._inner, bytes32(key));
        return (success, uint256(val));
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(UintToUintMap storage map, uint256 key) internal view returns (uint256) {
        return uint256(get(map._inner, bytes32(key)));
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(UintToUintMap storage map) internal view returns (uint256[] memory) {
        bytes32[] memory store = keys(map._inner);
        uint256[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // UintToAddressMap

    struct UintToAddressMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(UintToAddressMap storage map, uint256 key, address value) internal returns (bool) {
        return set(map._inner, bytes32(key), bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a map. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(UintToAddressMap storage map, uint256 key) internal returns (bool) {
        return remove(map._inner, bytes32(key));
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(UintToAddressMap storage map, uint256 key) internal view returns (bool) {
        return contains(map._inner, bytes32(key));
    }

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(UintToAddressMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the map. O(1).
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintToAddressMap storage map, uint256 index) internal view returns (uint256 key, address value) {
        (bytes32 atKey, bytes32 val) = at(map._inner, index);
        return (uint256(atKey), address(uint160(uint256(val))));
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(UintToAddressMap storage map, uint256 key) internal view returns (bool exists, address value) {
        (bool success, bytes32 val) = tryGet(map._inner, bytes32(key));
        return (success, address(uint160(uint256(val))));
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(UintToAddressMap storage map, uint256 key) internal view returns (address) {
        return address(uint160(uint256(get(map._inner, bytes32(key)))));
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(UintToAddressMap storage map) internal view returns (uint256[] memory) {
        bytes32[] memory store = keys(map._inner);
        uint256[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // UintToBytes32Map

    struct UintToBytes32Map {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(UintToBytes32Map storage map, uint256 key, bytes32 value) internal returns (bool) {
        return set(map._inner, bytes32(key), value);
    }

    /**
     * @dev Removes a value from a map. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(UintToBytes32Map storage map, uint256 key) internal returns (bool) {
        return remove(map._inner, bytes32(key));
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(UintToBytes32Map storage map, uint256 key) internal view returns (bool) {
        return contains(map._inner, bytes32(key));
    }

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(UintToBytes32Map storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the map. O(1).
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintToBytes32Map storage map, uint256 index) internal view returns (uint256 key, bytes32 value) {
        (bytes32 atKey, bytes32 val) = at(map._inner, index);
        return (uint256(atKey), val);
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(UintToBytes32Map storage map, uint256 key) internal view returns (bool exists, bytes32 value) {
        (bool success, bytes32 val) = tryGet(map._inner, bytes32(key));
        return (success, val);
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(UintToBytes32Map storage map, uint256 key) internal view returns (bytes32) {
        return get(map._inner, bytes32(key));
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(UintToBytes32Map storage map) internal view returns (uint256[] memory) {
        bytes32[] memory store = keys(map._inner);
        uint256[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // AddressToUintMap

    struct AddressToUintMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(AddressToUintMap storage map, address key, uint256 value) internal returns (bool) {
        return set(map._inner, bytes32(uint256(uint160(key))), bytes32(value));
    }

    /**
     * @dev Removes a value from a map. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(AddressToUintMap storage map, address key) internal returns (bool) {
        return remove(map._inner, bytes32(uint256(uint160(key))));
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(AddressToUintMap storage map, address key) internal view returns (bool) {
        return contains(map._inner, bytes32(uint256(uint160(key))));
    }

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(AddressToUintMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the map. O(1).
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressToUintMap storage map, uint256 index) internal view returns (address key, uint256 value) {
        (bytes32 atKey, bytes32 val) = at(map._inner, index);
        return (address(uint160(uint256(atKey))), uint256(val));
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(AddressToUintMap storage map, address key) internal view returns (bool exists, uint256 value) {
        (bool success, bytes32 val) = tryGet(map._inner, bytes32(uint256(uint160(key))));
        return (success, uint256(val));
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(AddressToUintMap storage map, address key) internal view returns (uint256) {
        return uint256(get(map._inner, bytes32(uint256(uint160(key)))));
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(AddressToUintMap storage map) internal view returns (address[] memory) {
        bytes32[] memory store = keys(map._inner);
        address[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // AddressToAddressMap

    struct AddressToAddressMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(AddressToAddressMap storage map, address key, address value) internal returns (bool) {
        return set(map._inner, bytes32(uint256(uint160(key))), bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a map. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(AddressToAddressMap storage map, address key) internal returns (bool) {
        return remove(map._inner, bytes32(uint256(uint160(key))));
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(AddressToAddressMap storage map, address key) internal view returns (bool) {
        return contains(map._inner, bytes32(uint256(uint160(key))));
    }

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(AddressToAddressMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the map. O(1).
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressToAddressMap storage map, uint256 index) internal view returns (address key, address value) {
        (bytes32 atKey, bytes32 val) = at(map._inner, index);
        return (address(uint160(uint256(atKey))), address(uint160(uint256(val))));
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(AddressToAddressMap storage map, address key) internal view returns (bool exists, address value) {
        (bool success, bytes32 val) = tryGet(map._inner, bytes32(uint256(uint160(key))));
        return (success, address(uint160(uint256(val))));
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(AddressToAddressMap storage map, address key) internal view returns (address) {
        return address(uint160(uint256(get(map._inner, bytes32(uint256(uint160(key)))))));
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(AddressToAddressMap storage map) internal view returns (address[] memory) {
        bytes32[] memory store = keys(map._inner);
        address[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // AddressToBytes32Map

    struct AddressToBytes32Map {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(AddressToBytes32Map storage map, address key, bytes32 value) internal returns (bool) {
        return set(map._inner, bytes32(uint256(uint160(key))), value);
    }

    /**
     * @dev Removes a value from a map. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(AddressToBytes32Map storage map, address key) internal returns (bool) {
        return remove(map._inner, bytes32(uint256(uint160(key))));
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(AddressToBytes32Map storage map, address key) internal view returns (bool) {
        return contains(map._inner, bytes32(uint256(uint160(key))));
    }

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(AddressToBytes32Map storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the map. O(1).
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressToBytes32Map storage map, uint256 index) internal view returns (address key, bytes32 value) {
        (bytes32 atKey, bytes32 val) = at(map._inner, index);
        return (address(uint160(uint256(atKey))), val);
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(AddressToBytes32Map storage map, address key) internal view returns (bool exists, bytes32 value) {
        (bool success, bytes32 val) = tryGet(map._inner, bytes32(uint256(uint160(key))));
        return (success, val);
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(AddressToBytes32Map storage map, address key) internal view returns (bytes32) {
        return get(map._inner, bytes32(uint256(uint160(key))));
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(AddressToBytes32Map storage map) internal view returns (address[] memory) {
        bytes32[] memory store = keys(map._inner);
        address[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // Bytes32ToUintMap

    struct Bytes32ToUintMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(Bytes32ToUintMap storage map, bytes32 key, uint256 value) internal returns (bool) {
        return set(map._inner, key, bytes32(value));
    }

    /**
     * @dev Removes a value from a map. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(Bytes32ToUintMap storage map, bytes32 key) internal returns (bool) {
        return remove(map._inner, key);
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool) {
        return contains(map._inner, key);
    }

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(Bytes32ToUintMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the map. O(1).
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32ToUintMap storage map, uint256 index) internal view returns (bytes32 key, uint256 value) {
        (bytes32 atKey, bytes32 val) = at(map._inner, index);
        return (atKey, uint256(val));
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(Bytes32ToUintMap storage map, bytes32 key) internal view returns (bool exists, uint256 value) {
        (bool success, bytes32 val) = tryGet(map._inner, key);
        return (success, uint256(val));
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(Bytes32ToUintMap storage map, bytes32 key) internal view returns (uint256) {
        return uint256(get(map._inner, key));
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(Bytes32ToUintMap storage map) internal view returns (bytes32[] memory) {
        bytes32[] memory store = keys(map._inner);
        bytes32[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // Bytes32ToAddressMap

    struct Bytes32ToAddressMap {
        Bytes32ToBytes32Map _inner;
    }

    /**
     * @dev Adds a key-value pair to a map, or updates the value for an existing
     * key. O(1).
     *
     * Returns true if the key was added to the map, that is if it was not
     * already present.
     */
    function set(Bytes32ToAddressMap storage map, bytes32 key, address value) internal returns (bool) {
        return set(map._inner, key, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a map. O(1).
     *
     * Returns true if the key was removed from the map, that is if it was present.
     */
    function remove(Bytes32ToAddressMap storage map, bytes32 key) internal returns (bool) {
        return remove(map._inner, key);
    }

    /**
     * @dev Returns true if the key is in the map. O(1).
     */
    function contains(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (bool) {
        return contains(map._inner, key);
    }

    /**
     * @dev Returns the number of elements in the map. O(1).
     */
    function length(Bytes32ToAddressMap storage map) internal view returns (uint256) {
        return length(map._inner);
    }

    /**
     * @dev Returns the element stored at position `index` in the map. O(1).
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32ToAddressMap storage map, uint256 index) internal view returns (bytes32 key, address value) {
        (bytes32 atKey, bytes32 val) = at(map._inner, index);
        return (atKey, address(uint160(uint256(val))));
    }

    /**
     * @dev Tries to returns the value associated with `key`. O(1).
     * Does not revert if `key` is not in the map.
     */
    function tryGet(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (bool exists, address value) {
        (bool success, bytes32 val) = tryGet(map._inner, key);
        return (success, address(uint160(uint256(val))));
    }

    /**
     * @dev Returns the value associated with `key`. O(1).
     *
     * Requirements:
     *
     * - `key` must be in the map.
     */
    function get(Bytes32ToAddressMap storage map, bytes32 key) internal view returns (address) {
        return address(uint160(uint256(get(map._inner, key))));
    }

    /**
     * @dev Return the an array containing all the keys
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the map grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function keys(Bytes32ToAddressMap storage map) internal view returns (bytes32[] memory) {
        bytes32[] memory store = keys(map._inner);
        bytes32[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }
}

// SPDX-License-Identifier: AGPL-3.0-only
pragma solidity 0.8.29;

/// @notice Provides a flexible and updatable auth pattern which is completely separate from application logic.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
abstract contract Auth {
    event OwnershipTransferred(address indexed user, address indexed newOwner);

    event AuthorityUpdated(address indexed user, Authority indexed newAuthority);

    address public owner;

    Authority public authority;

    constructor(address _owner, Authority _authority) {
        owner = _owner;
        authority = _authority;

        emit OwnershipTransferred(msg.sender, _owner);
        emit AuthorityUpdated(msg.sender, _authority);
    }

    modifier requiresAuth() virtual {
        require(isAuthorized(msg.sender, msg.sig), "UNAUTHORIZED");

        _;
    }

    function isAuthorized(address user, bytes4 functionSig) internal view virtual returns (bool) {
        Authority auth = authority; // Memoizing authority saves us a warm SLOAD, around 100 gas.

        // Checking if the caller is the owner only after calling the authority saves gas in most cases, but be
        // aware that this makes protected functions uncallable even to the owner if the authority is out of order.
        return (address(auth) != address(0) && auth.canCall(user, address(this), functionSig)) || user == owner;
    }

    function setAuthority(Authority newAuthority) public virtual {
        // We check if the caller is the owner first because we want to ensure they can
        // always swap out the authority even if it's reverting or using up a lot of gas.
        require(msg.sender == owner || authority.canCall(msg.sender, address(this), msg.sig));

        authority = newAuthority;

        emit AuthorityUpdated(msg.sender, newAuthority);
    }

    function transferOwnership(address newOwner) public virtual requiresAuth {
        owner = newOwner;

        emit OwnershipTransferred(msg.sender, newOwner);
    }
}

/// @notice A generic interface for a contract which provides authorization data to an Auth instance.
/// @author Solmate (https://github.com/transmissions11/solmate/blob/main/src/auth/Auth.sol)
/// @author Modified from Dappsys (https://github.com/dapphub/ds-auth/blob/master/src/auth.sol)
interface Authority {
    function canCall(address user, address target, bytes4 functionSig) external view returns (bool);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

import { Auth, Authority } from "@solmate/auth/Auth.sol";
import { IAuth2Step } from "src/core/interfaces/IAuth2Step.sol";

/// @title Auth2Step
/// @notice An extension of Auth.sol that supports two-step ownership transfer
contract Auth2Step is IAuth2Step, Auth {
    ////////////////////////////////////////////////////////////
    //                        Storage                         //
    ////////////////////////////////////////////////////////////

    /// @notice Address of the pending owner
    address public pendingOwner;

    ////////////////////////////////////////////////////////////
    //                       Modifiers                        //
    ////////////////////////////////////////////////////////////

    modifier onlyOwner() virtual {
        require(msg.sender == owner, Aera__Unauthorized());
        _;
    }

    constructor(address newOwner_, Authority authority_) Auth(newOwner_, authority_) { }

    ////////////////////////////////////////////////////////////
    //              Public / External Functions               //
    ////////////////////////////////////////////////////////////

    /// @inheritdoc IAuth2Step
    function acceptOwnership() external virtual override {
        address pendingOwner_ = pendingOwner;

        // Requirements: the caller must be the pending owner
        require(msg.sender == pendingOwner_, Aera__Unauthorized());

        // Effects: set the owner to the pending owner and delete the pending owner
        owner = pendingOwner_;
        delete pendingOwner;

        // Log the ownership transfer
        emit OwnershipTransferred(msg.sender, pendingOwner_);
    }

    /// @notice Start the ownership transfer of the contract to a new account
    /// @param newOwner Address to transfer ownership to
    /// @dev Replaces the pending transfer if there is one
    /// @dev Overrides the `Auth` contract's `transferOwnership` function
    /// @dev Zero check is not needed because pendingOwner can always be overwritten
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        // Effects: set the pending owner
        //slither-disable-next-line missing-zero-check
        pendingOwner = newOwner;

        // Log the ownership transfer start
        emit OwnershipTransferStarted(owner, newOwner);
    }
}

File 32 of 48 : Constants.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

////////////////////////////////////////////////////////////
//                    Memory & Calldata                   //
////////////////////////////////////////////////////////////

// Size of a word in bytes
uint256 constant WORD_SIZE = 32;

// Size of a function selector in bytes
uint256 constant SELECTOR_SIZE = 4;

// Minimum valid calldata size (selector + one word = 36)
uint256 constant MINIMUM_CALLDATA_LENGTH = WORD_SIZE + SELECTOR_SIZE;

// Offset to skip selector and first word in calldata
uint256 constant CALLDATA_OFFSET = MINIMUM_CALLDATA_LENGTH;

// Offset for extracting spender address from approval calldata
uint256 constant ERC20_SPENDER_OFFSET = 36;

// Size of an address in bits
uint256 constant ADDRESS_SIZE_BITS = 160;

////////////////////////////////////////////////////////////
//                     Hooks Constants                    //
////////////////////////////////////////////////////////////

// Mask for a bit indicating whether a hooks has before submit call
uint256 constant BEFORE_HOOK_MASK = 1;

// Mask for a bit indicating whether a hooks has after submit call
uint256 constant AFTER_HOOK_MASK = 2;

// Mask for a bit indicating whether a hooks exists
uint256 constant HOOKS_FLAG_MASK = 0x80;

// Mask for 7 bits indicating the number of configurable hooks offsets
uint256 constant CONFIGURABLE_HOOKS_LENGTH_MASK = 0x7F;

////////////////////////////////////////////////////////////
//                     Bit Operations                     //
////////////////////////////////////////////////////////////

// Mask for extracting 8-bit values
uint256 constant MASK_8_BIT = 0xff;

// Mask for extracting 16-bit values
uint256 constant MASK_16_BIT = 0xffff;

////////////////////////////////////////////////////////////
//                    Pipeline Constants                  //
////////////////////////////////////////////////////////////

// Bit offset for results index in packed clipboard data
uint256 constant RESULTS_INDEX_OFFSET = 24;

// Bit offset for copy word position in packed clipboard data
uint256 constant COPY_WORD_OFFSET = 16;

////////////////////////////////////////////////////////////
//                   Extractor Constants                  //
////////////////////////////////////////////////////////////

// Number of bits per extraction offset
uint256 constant EXTRACT_OFFSET_SIZE_BITS = 16;

// Number of bits to shift to get the offset (256 - 16)
uint256 constant EXTRACTION_OFFSET_SHIFT_BITS = 240;

/// @dev Maximum number of extraction offsets(16) + 1
uint256 constant MAX_EXTRACT_OFFSETS_EXCLUSIVE = 17;

////////////////////////////////////////////////////////////
//                   Callback Constants                   //
////////////////////////////////////////////////////////////

// Maximum value for uint16, used to indicate no callback data
uint16 constant NO_CALLBACK_DATA = type(uint16).max;

// Offset for selector in callback data
uint256 constant SELECTOR_OFFSET = 48;

// Offset for callback data
uint256 constant CALLBACK_DATA_OFFSET = 160;

////////////////////////////////////////////////////////////
//                   Fee Constants                        //
////////////////////////////////////////////////////////////

// Basis points denominator (100%)
uint256 constant ONE_IN_BPS = 1e4;

// Maximum TVL fee
uint256 constant MAX_TVL_FEE = 2000; // 20%

// Maximum performance fee
uint256 constant MAX_PERFORMANCE_FEE = ONE_IN_BPS;

// Seconds in a year for fee calculations
uint256 constant SECONDS_PER_YEAR = 365 days;

// Maximum dispute period
uint256 constant MAX_DISPUTE_PERIOD = 30 days;

////////////////////////////////////////////////////////////
//                   Unit Price Constants                //
////////////////////////////////////////////////////////////

/// @dev Precision for unit price calculations (18 decimals)
uint256 constant UNIT_PRICE_PRECISION = 1e18;

/// @dev One minute in seconds
uint256 constant ONE_MINUTE = 1 minutes;

/// @dev One day in seconds
uint256 constant ONE_DAY = 1 days;

////////////////////////////////////////////////////////////
//                   Provisioner Constants                //
////////////////////////////////////////////////////////////

/// @dev Minimum deposit multiplier 50%
uint256 constant MIN_DEPOSIT_MULTIPLIER = 5000;

/// @dev Minimum redeem multiplier 50%
uint256 constant MIN_REDEEM_MULTIPLIER = 5000;

/// @dev Deposit/Redeem flag in RequestType enum
uint256 constant DEPOSIT_REDEEM_FLAG = 1;

/// @dev Auto/Fixed price flag in RequestType enum
uint256 constant AUTO_PRICE_FIXED_PRICE_FLAG = 2;

/// @dev One unit with 18 decimals
uint256 constant ONE_UNIT = 1e18;

/// @dev Maximum seconds between request deadline and current timestamp
uint256 constant MAX_SECONDS_TO_DEADLINE = 365 days;

/// @dev Upper bound for depositRefundTimeout to prevent indefinite user lockout
uint256 constant MAX_DEPOSIT_REFUND_TIMEOUT = 30 days;

////////////////////////////////////////////////////////////
//                   Whitelist Constants                  //
////////////////////////////////////////////////////////////

/// @dev Whitelist flag in AddressToUintMap
uint8 constant IS_WHITELISTED_FLAG = 1;

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

import { HookCallType } from "src/core/Types.sol";
import { ISubmitHooks } from "src/core/interfaces/ISubmitHooks.sol";

/// @notice Interface for the BaseVault
interface IBaseVault {
    ////////////////////////////////////////////////////////////
    //                         Events                         //
    ////////////////////////////////////////////////////////////

    event SubmitHooksSet(address indexed submitHooksAddress);
    event GuardianRootSet(address indexed guardian, bytes32 indexed root);

    ////////////////////////////////////////////////////////////
    //                         Errors                         //
    ////////////////////////////////////////////////////////////

    error Aera__ZeroAddressGuardian();
    error Aera__ZeroAddressOwner();
    error Aera__CallerIsNotGuardian();
    error Aera__CallerIsNotAuthOrGuardian();
    error Aera__SubmissionFailed(uint256 index, bytes result);
    error Aera__AllowanceIsNotZero(address token, address spender);
    error Aera__ZeroAddressMerkleRoot();
    error Aera__BeforeSubmitHooksFailed(bytes result);
    error Aera__AfterSubmitHooksFailed(bytes result);
    error Aera__BeforeOperationHooksFailed(uint256 index, bytes result);
    error Aera__AfterOperationHooksFailed(uint256 index, bytes result);
    error Aera__BeforeOperationHooksWithConfigurableHooks();
    error Aera__ProofVerificationFailed();
    error Aera__InvalidBeforeOperationHooksReturnDataLength();
    error Aera__GuardianNotWhitelisted();
    error Aera__ExpectedCallbackNotReceived();
    error Aera__NoResults();

    ////////////////////////////////////////////////////////////
    //                       Functions                        //
    ////////////////////////////////////////////////////////////

    /// @notice Submit a series of operations to the vault
    /// @param data Encoded array of operations to submit
    /// ┌─────────────────────────────┬─────────────────────────┬───────────────────────────────────────────────┐
    /// │ FIELDS                      │ SIZE                    │ DESCRIPTION                                   │
    /// ├─────────────────────────────┴─────────────────────────┴───────────────────────────────────────────────┤
    /// │ operationsLength              1 byte                    Number of operations in the array             │
    /// │                                                                                                       │
    /// │ [for each operation]:                                                                                 │
    /// │                                                                                                       │
    /// │   SIGNATURE                                                                                           │
    /// │   target                      20 bytes                  Target contract address                       │
    /// │   calldataLength              2 bytes                   Length of calldata                            │
    /// │   calldata                    <calldataLength> bytes    Calldata (before pipelining)                  │
    /// │                                                                                                       │
    /// │   CLIPBOARD                                                                                           │
    /// │   clipboardsLength            1 byte                    Number of clipboards                          │
    /// │   [for each clipboard entry]:                                                                         │
    /// │       resultIndex             1 byte                    Which operation to take from                  │
    /// │       copyWord                1 byte                    Which word to copy                            │
    /// │       pasteOffset             2 bytes                   What offset to paste it at                    │
    /// │                                                                                                       │
    /// │   CALL TYPE                                                                                           │
    /// │   isStaticCall                1 byte                    1 if static, 0 if a regular call              │
    /// │   [if isStaticCall == 0]:                                                                             │
    /// │                                                                                                       │
    /// │     CALLBACK HANDLING                                                                                 │
    /// │     hasCallback               1 byte                    Whether to allow callbacks during operation   │
    /// │     [if hasCallback == 1]:                                                                            │
    /// │       callbackData =          26 bytes                  Expected callback info                        │
    /// │       ┌────────────────────┬──────────────────────────┬───────────────────┐                           │
    /// │       │ selector (4 bytes) │ calldataOffset (2 bytes) │ caller (20 bytes) │                           │
    /// │       └────────────────────┴──────────────────────────┴───────────────────┘                           │
    /// │                                                                                                       │
    /// │     HOOKS                                                                                             │
    /// │     hookConfig =              1 byte                    Hook configuration                            │
    /// │     ┌─────────────────┬────────────────────────────────────────┐                                      │
    /// │     │ hasHook (1 bit) │ configurableHookOffsetsLength (7 bits) │                                      │
    /// │     └─────────────────┴────────────────────────────────────────┘                                      │
    /// │     if configurableHookOffsetsLength > 0:                                                             │
    /// │         configurableHookOffsets 32 bytes                Packed configurable hook offsets              │
    /// │     if hasHook == 1:                                                                                  │
    /// │         hook                 20 bytes                   Hook contract address                         │
    /// │                                                                                                       │
    /// │     MERKLE PROOF                                                                                      │
    /// │     proofLength              1 byte                     Merkle proof length                           │
    /// │     proof                    <proofLength> * 32 bytes   Merkle proof data                             │
    /// │                                                                                                       │
    /// │     PAYABILITY                                                                                        │
    /// │     hasValue                 1 byte                     Whether to send native token with the call    │
    /// │     [if hasValue == 1]:                                                                               │
    /// │       value                  32 bytes                   Amount of native token to send                │
    /// └───────────────────────────────────────────────────────────────────────────────────────────────────────┘
    function submit(bytes calldata data) external;

    /// @notice Set the merkle root for a guardian
    /// Used to add guardians and update their permissions
    /// @param guardian Address of the guardian
    /// @param root Merkle root
    function setGuardianRoot(address guardian, bytes32 root) external;

    /// @notice Removes a guardian from the vault
    /// @param guardian Address of the guardian
    function removeGuardian(address guardian) external;

    /// @notice Set the submit hooks address
    /// @param newSubmitHooks Address of the new submit hooks contract
    function setSubmitHooks(ISubmitHooks newSubmitHooks) external;

    /// @notice Pause the vault, halting the ability for guardians to submit
    function pause() external;

    /// @notice Unpause the vault, allowing guardians to submit operations
    function unpause() external;

    /// @notice Check if the guardian is whitelisted and set the root to zero if not
    /// Used to disable guardians who were removed from the whitelist
    /// after being selected as guardians
    /// @param guardian The guardian address
    /// @return isRemoved Whether the guardian was removed from the whitelist
    function checkGuardianWhitelist(address guardian) external returns (bool isRemoved);

    /// @notice Get all active guardians
    /// @return Array of active guardian addresses
    function getActiveGuardians() external view returns (address[] memory);

    /// @notice Get the guardian root for a guardian
    /// @param guardian The guardian address
    /// @return The guardian root
    function getGuardianRoot(address guardian) external view returns (bytes32);

    /// @notice Get the current hook call type
    /// @return The current hook call type
    function getCurrentHookCallType() external view returns (HookCallType);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

import { BaseVaultParameters } from "src/core/Types.sol";
import { IBaseVaultDeployer } from "src/core/interfaces/IBaseVaultDeployer.sol";

/// @title IBaseVaultFactory
/// @notice Interface for the base vault factory
interface IBaseVaultFactory is IBaseVaultDeployer {
    ////////////////////////////////////////////////////////////
    //                       Events                           //
    ////////////////////////////////////////////////////////////

    /// @notice Emitted when the vault is created
    /// @param vault Vault address
    /// @param owner Initial owner address
    /// @param submitHooks Submit hooks address
    /// @param description Vault description
    event VaultCreated(address indexed vault, address indexed owner, address submitHooks, string description);

    ////////////////////////////////////////////////////////////
    //                       Functions                        //
    ////////////////////////////////////////////////////////////

    /// @notice Create a new vault with the given parameters
    /// @param salt The salt value to use for create2
    /// @param description Vault description
    /// @param baseVaultParams Parameters for vault deployment
    /// @param expectedVaultAddress Expected address of the deployed vault
    /// @return deployedVault Address of the deployed vault
    function create(
        bytes32 salt,
        string calldata description,
        BaseVaultParameters calldata baseVaultParams,
        address expectedVaultAddress
    ) external returns (address deployedVault);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

/// @title ISubmitHooks
/// @notice Interface for hooks that execute before and after submit calls
interface ISubmitHooks {
    ////////////////////////////////////////////////////////////
    //                       Functions                        //
    ////////////////////////////////////////////////////////////

    /// @notice Called before a submit
    /// @param data Encoded data of the submit
    /// @param guardian Address of the guardian that submitted
    function beforeSubmit(bytes memory data, address guardian) external;

    /// @notice Called after a submit
    /// @param data Encoded data of the submit
    /// @param guardian Address of the guardian that submitted
    function afterSubmit(bytes memory data, address guardian) external;
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

interface IWhitelist {
    ////////////////////////////////////////////////////////////
    //                        Events                          //
    ////////////////////////////////////////////////////////////

    event WhitelistSet(address indexed addr, bool isAddressWhitelisted);

    ////////////////////////////////////////////////////////////
    //                       Functions                        //
    ////////////////////////////////////////////////////////////

    /// @notice Set the address whitelisted status
    /// @param addr The address to add/remove from the whitelist
    /// @param isAddressWhitelisted Whether address should be whitelisted going forward
    function setWhitelisted(address addr, bool isAddressWhitelisted) external;

    /// @notice Checks if the address is whitelisted
    /// @param addr The address to check
    /// @return True if the addr is whitelisted, false otherwise
    function isWhitelisted(address addr) external view returns (bool);

    /// @notice Get all whitelisted addresses
    /// @return An array of all whitelisted addresses
    function getAllWhitelisted() external view returns (address[] memory);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

import {
    EXTRACTION_OFFSET_SHIFT_BITS,
    EXTRACT_OFFSET_SIZE_BITS,
    MAX_EXTRACT_OFFSETS_EXCLUSIVE,
    MINIMUM_CALLDATA_LENGTH,
    SELECTOR_SIZE,
    WORD_SIZE
} from "src/core/Constants.sol";

/// @title CalldataExtractor
/// @notice Library for extracting specific chunks of calldata based on configured offsets
/// used in configurable hooks to extract 32 byte chunks from calldata and check them against
/// expected values in the merkle tree
library CalldataExtractor {
    ////////////////////////////////////////////////////////////
    //                         Errors                         //
    ////////////////////////////////////////////////////////////

    error Aera__ExtractionNumberTooLarge();
    error Aera__CalldataTooShort();
    error Aera__OffsetOutOfBounds();

    ////////////////////////////////////////////////////////////
    //              Private / Internal Functions              //
    ////////////////////////////////////////////////////////////

    /// @notice Extract 32-byte chunks from calldata based on config offsets
    /// @param callData The calldata to extract from
    /// @param calldataOffsetsPacked Packed 16-bit extraction offsets
    /// @param calldataOffsetsCount Number of extractions to perform
    /// @return result Concatenated byte values at specific offsets
    /// @dev Number of provided offsets must be <= 16 because that's how many fit in uint256
    /// @dev Calldata must be at least 36 bytes long to be considered valid
    /// @dev All math is unchecked because we validate everything before doing any operations
    function extract(bytes memory callData, uint256 calldataOffsetsPacked, uint256 calldataOffsetsCount)
        internal
        pure
        returns (bytes memory)
    {
        unchecked {
            // Check that the number of extractions is less than the maximum allowed
            require(calldataOffsetsCount < MAX_EXTRACT_OFFSETS_EXCLUSIVE, Aera__ExtractionNumberTooLarge());
            // Initialize result bytes array with the length being number of extractions times 32 bytes
            bytes memory result = new bytes(calldataOffsetsCount * WORD_SIZE);

            uint256 resultPtr;
            assembly ("memory-safe") {
                resultPtr := result
            }
            // Skip the dynamic array length word
            resultPtr += WORD_SIZE;

            uint256 callDataLength = callData.length;
            // Requirements: check that the calldata is at least 36 bytes long (selector + one word)
            require(callDataLength >= MINIMUM_CALLDATA_LENGTH, Aera__CalldataTooShort());

            // Max valid offset is the length of callData minus 36 bytes(selector + one word)
            uint256 maxValidOffset = callDataLength - WORD_SIZE;

            uint256 calldataPointer;
            assembly ("memory-safe") {
                calldataPointer := callData
            }
            // Skip the dynamic array length word
            calldataPointer += WORD_SIZE;

            uint256 resultWriteOffset;
            for (uint256 i = 0; i < calldataOffsetsCount; ++i) {
                uint256 extractionOffset = (calldataOffsetsPacked >> EXTRACTION_OFFSET_SHIFT_BITS) + SELECTOR_SIZE;

                // Requirements: check that the offset is within the calldata bounds
                require(extractionOffset <= maxValidOffset, Aera__OffsetOutOfBounds());

                uint256 calldataOffsetPointer = calldataPointer + extractionOffset;

                // Extract 32 bytes from calldata at offset
                // mload from callData pointer + extraction offset
                bytes32 extracted;
                assembly ("memory-safe") {
                    extracted := mload(calldataOffsetPointer)
                }

                // Store extracted value in the result
                // mstore extracted word to result pointer + current result offset
                uint256 resultOffsetPointer = resultPtr + resultWriteOffset;
                assembly ("memory-safe") {
                    mstore(resultOffsetPointer, extracted)
                }

                resultWriteOffset += WORD_SIZE;
                calldataOffsetsPacked = calldataOffsetsPacked << EXTRACT_OFFSET_SIZE_BITS;
            }

            return result;
        }
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

// slither-disable-start write-after-write,dead-code

/// @dev Represents a raw calldata offset
type CalldataReader is uint256;

using CalldataReaderLib for CalldataReader global;
using { neq as !=, eq as ==, gt as >, lt as <, ge as >=, le as <= } for CalldataReader global;

function neq(CalldataReader a, CalldataReader b) pure returns (bool) {
    return CalldataReader.unwrap(a) != CalldataReader.unwrap(b);
}

function eq(CalldataReader a, CalldataReader b) pure returns (bool) {
    return CalldataReader.unwrap(a) == CalldataReader.unwrap(b);
}

function gt(CalldataReader a, CalldataReader b) pure returns (bool) {
    return CalldataReader.unwrap(a) > CalldataReader.unwrap(b);
}

function lt(CalldataReader a, CalldataReader b) pure returns (bool) {
    return CalldataReader.unwrap(a) < CalldataReader.unwrap(b);
}

function ge(CalldataReader a, CalldataReader b) pure returns (bool) {
    return CalldataReader.unwrap(a) >= CalldataReader.unwrap(b);
}

function le(CalldataReader a, CalldataReader b) pure returns (bool) {
    return CalldataReader.unwrap(a) <= CalldataReader.unwrap(b);
}

/// @notice Modified version of the original CalldataReaderLib
/// @notice No functions were changed, only added new functions
/// @author Aera https://github.com/aera-finance
/// @author philogy <https://github.com/philogy>
library CalldataReaderLib {
    ////////////////////////////////////////////////////////////
    //                         Errors                         //
    ////////////////////////////////////////////////////////////

    error ReaderNotAtEnd();

    ////////////////////////////////////////////////////////////
    //              Private / Internal Functions              //
    ////////////////////////////////////////////////////////////

    function from(bytes calldata data) internal pure returns (CalldataReader reader) {
        assembly ("memory-safe") {
            reader := data.offset
        }
    }

    function requireAtEndOf(CalldataReader self, bytes calldata data) internal pure {
        assembly ("memory-safe") {
            let end := add(data.offset, data.length)
            if iszero(eq(self, end)) {
                mstore(0x00, 0x01842f8c /* ReaderNotAtEnd() */ )
                revert(0x1c, 0x04)
            }
        }
    }

    function requireAtEndOf(CalldataReader self, CalldataReader end) internal pure {
        if (self != end) revert ReaderNotAtEnd();
    }

    function offset(CalldataReader self) internal pure returns (uint256) {
        return CalldataReader.unwrap(self);
    }

    function readBool(CalldataReader self) internal pure returns (CalldataReader, bool value) {
        assembly ("memory-safe") {
            value := gt(byte(0, calldataload(self)), 0)
            self := add(self, 1)
        }
        return (self, value);
    }

    function readU8(CalldataReader self) internal pure returns (CalldataReader, uint8 value) {
        assembly ("memory-safe") {
            value := byte(0, calldataload(self))
            self := add(self, 1)
        }
        return (self, value);
    }

    function readU16(CalldataReader self) internal pure returns (CalldataReader, uint16 value) {
        assembly ("memory-safe") {
            value := shr(240, calldataload(self))
            self := add(self, 2)
        }
        return (self, value);
    }

    function readU32(CalldataReader self) internal pure returns (CalldataReader, uint32 value) {
        assembly ("memory-safe") {
            value := shr(224, calldataload(self))
            self := add(self, 4)
        }
        return (self, value);
    }

    function readI24(CalldataReader self) internal pure returns (CalldataReader, int24 value) {
        assembly ("memory-safe") {
            value := sar(232, calldataload(self))
            self := add(self, 3)
        }
        return (self, value);
    }

    function readU40(CalldataReader self) internal pure returns (CalldataReader, uint40 value) {
        assembly ("memory-safe") {
            value := shr(216, calldataload(self))
            self := add(self, 5)
        }
        return (self, value);
    }

    function readU64(CalldataReader self) internal pure returns (CalldataReader, uint64 value) {
        assembly ("memory-safe") {
            value := shr(192, calldataload(self))
            self := add(self, 8)
        }
        return (self, value);
    }

    function readU128(CalldataReader self) internal pure returns (CalldataReader, uint128 value) {
        assembly ("memory-safe") {
            value := shr(128, calldataload(self))
            self := add(self, 16)
        }
        return (self, value);
    }

    function readAddr(CalldataReader self) internal pure returns (CalldataReader, address addr) {
        assembly ("memory-safe") {
            addr := shr(96, calldataload(self))
            self := add(self, 20)
        }
        return (self, addr);
    }

    function readU256(CalldataReader self) internal pure returns (CalldataReader, uint256 value) {
        assembly ("memory-safe") {
            value := calldataload(self)
            self := add(self, 32)
        }
        return (self, value);
    }

    function readU24End(CalldataReader self) internal pure returns (CalldataReader, CalldataReader end) {
        assembly ("memory-safe") {
            let len := shr(232, calldataload(self))
            self := add(self, 3)
            end := add(self, len)
        }
        return (self, end);
    }

    function readBytes(CalldataReader self) internal pure returns (CalldataReader, bytes calldata slice) {
        assembly ("memory-safe") {
            slice.length := shr(232, calldataload(self))
            self := add(self, 3)
            slice.offset := self
            self := add(self, slice.length)
        }
        return (self, slice);
    }

    /// ADDED BY AERA

    function readU208(CalldataReader self) internal pure returns (CalldataReader, uint208 value) {
        assembly ("memory-safe") {
            value := shr(48, calldataload(self))
            self := add(self, 26)
        }
        return (self, value);
    }

    function readOptionalU256(CalldataReader reader) internal pure returns (CalldataReader, uint256 u256) {
        bool hasU256;
        (reader, hasU256) = reader.readBool();
        if (hasU256) {
            (reader, u256) = reader.readU256();
        }
        return (reader, u256);
    }

    function readBytes32Array(CalldataReader self) internal pure returns (CalldataReader, bytes32[] memory array) {
        uint256 length;
        (self, length) = readU8(self);
        array = new bytes32[](length);
        assembly ("memory-safe") {
            calldatacopy(add(array, 32), self, mul(length, 32))
            self := add(self, mul(length, 32))
        }
        return (self, array);
    }

    function readBytesEnd(CalldataReader self) internal pure returns (CalldataReader end) {
        assembly ("memory-safe") {
            let length := calldataload(sub(self, 32))
            end := add(self, length)
        }
    }

    function readBytesEnd(CalldataReader self, bytes calldata data) internal pure returns (CalldataReader end) {
        assembly ("memory-safe") {
            end := add(self, data.length)
        }
    }

    function readBytesToMemory(CalldataReader self) internal pure returns (CalldataReader, bytes memory data) {
        uint256 length;
        (self, length) = readU16(self);
        return readBytesToMemory(self, length);
    }

    function readBytesToMemory(CalldataReader self, uint256 length)
        internal
        pure
        returns (CalldataReader, bytes memory data)
    {
        data = new bytes(length);

        assembly ("memory-safe") {
            calldatacopy(add(data, 32), self, length)
            self := add(self, length)
        }

        return (self, data);
    }
}
// slither-disable-end write-after-write,dead-code

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

import {
    CALLDATA_OFFSET,
    COPY_WORD_OFFSET,
    MASK_16_BIT,
    MASK_8_BIT,
    RESULTS_INDEX_OFFSET,
    WORD_SIZE
} from "src/core/Constants.sol";
import { CalldataReader } from "src/core/libraries/CalldataReader.sol";

/// @title Pipeline
/// @notice Library for handling pipeline operations that copy and paste data between operations
/// @dev Uses bit manipulation and assembly for efficient data movement
library Pipeline {
    ////////////////////////////////////////////////////////////
    //                         Errors                         //
    ////////////////////////////////////////////////////////////

    /// @notice Thrown when trying to copy from an invalid position in source data
    error Aera__CopyOffsetOutOfBounds();
    /// @notice Thrown when trying to paste to an invalid position in target data
    error Aera__PasteOffsetOutOfBounds();

    ////////////////////////////////////////////////////////////
    //              Private / Internal Functions              //
    ////////////////////////////////////////////////////////////

    /// @notice Process pipeline operations by copying data between operations
    /// @param data The calldata to modify
    /// @param reader Current position in the calldata
    /// @param results Array of previous operation results to copy from
    function pipe(bytes memory data, CalldataReader reader, bytes[] memory results)
        internal
        pure
        returns (CalldataReader)
    {
        uint256 clipboardCount;
        (reader, clipboardCount) = reader.readU8();

        unchecked {
            for (; clipboardCount != 0; --clipboardCount) {
                uint256 clipboard;
                (reader, clipboard) = reader.readU32();

                uint256 resultIndex = clipboard >> RESULTS_INDEX_OFFSET;
                // Check that the result index is within the results bounds
                // will Panic(uint256) revert on out of bounds index
                bytes memory result = results[resultIndex];

                uint256 copyOffset = (clipboard >> COPY_WORD_OFFSET & MASK_8_BIT) * WORD_SIZE;
                // Check that the copy offset is within the result bounds
                require(copyOffset + WORD_SIZE <= result.length, Aera__CopyOffsetOutOfBounds());

                uint256 pasteOffset = clipboard & MASK_16_BIT;
                // Check that the paste offset is within the data bounds
                require(pasteOffset + WORD_SIZE <= data.length, Aera__PasteOffsetOutOfBounds());

                uint256 operationCalldataPointer;
                uint256 resultPointer;
                assembly ("memory-safe") {
                    // Since most operations don’t enter the loop, we avoid caching
                    // operationCalldataPointer upfront to save gas
                    operationCalldataPointer := data
                    resultPointer := result
                }

                uint256 pastePointer = operationCalldataPointer + pasteOffset + CALLDATA_OFFSET;
                uint256 copyPointer = resultPointer + WORD_SIZE + copyOffset;

                assembly ("memory-safe") {
                    mcopy(pastePointer, copyPointer, WORD_SIZE)
                }
            }
        }

        return reader;
    }
}

pragma solidity 0.8.29;

// Sampled from
// https://github.com/OpenZeppelin/openzeppelin-contracts/blob/63851f8de5a6e560e9774832d1a31c43645b73d2/contracts/token/ERC20/ERC20.sol

interface IERC20WithAllowance {
    function increaseAllowance(address spender, uint256 addedValue) external returns (bool);
    function decreaseAllowance(address spender, uint256 requestedDecrease) external returns (bool);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

/// @dev Implements the spec at https://eips.ethereum.org/EIPS/eip-7726
interface IOracle {
    ////////////////////////////////////////////////////////////
    //                       Functions                        //
    ////////////////////////////////////////////////////////////

    /// @notice Returns the value of `baseAmount` of `base` in `quote` terms
    /// @dev MUST round down towards 0
    /// MUST revert with `OracleUnsupportedPair` if not capable to provide data for the specified `base`
    /// and `quote` pair
    /// MUST revert with `OracleUntrustedData` if not capable to provide data within a degree of
    /// confidence publicly specified
    /// @param baseAmount The amount of `base` to convert
    /// @param base The asset that the user needs to know the value for
    /// @param quote The asset in which the user needs to value the base
    /// @return quoteAmount The value of `baseAmount` of `base` in `quote` terms
    function getQuote(uint256 baseAmount, address base, address quote) external view returns (uint256 quoteAmount);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

import { BaseVaultParameters } from "src/core/Types.sol";

/// @notice Interface for vault deployer
interface IBaseVaultDeployer {
    ////////////////////////////////////////////////////////////
    //                       Errors                           //
    ////////////////////////////////////////////////////////////

    /// @notice Thrown when vault description is empty
    error Aera__DescriptionIsEmpty();

    /// @notice Thrown when deployed vault address doesn't match expected address
    /// @param deployed Address of the deployed vault
    /// @param expected Expected address of the vault
    error Aera__VaultAddressMismatch(address deployed, address expected);

    ////////////////////////////////////////////////////////////
    //                       Functions                        //
    ////////////////////////////////////////////////////////////

    /// @notice Vault parameters for vault deployment
    /// @return parameters Parameters used for vault deployment, including owner, submit hooks, and whitelist
    /// @dev Necessary to support deterministic vault deployments
    function baseVaultParameters() external view returns (BaseVaultParameters memory);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

/**
 * @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);
}

File 44 of 48 : ICallbackHandler.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

/// @title ICallbackHandler
/// @notice Errors used in the CallbackHandler mixin
interface ICallbackHandler {
    ////////////////////////////////////////////////////////////
    //                         Errors                         //
    ////////////////////////////////////////////////////////////

    /// @notice Thrown when we receive a callback (or a regular call) that wasn't authorized
    error Aera__UnauthorizedCallback();
}

File 45 of 48 : IERC721Receiver.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol)

pragma solidity ^0.8.20;

/**
 * @title ERC-721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC-721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be
     * reverted.
     *
     * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.20;

/**
 * @dev Library of standard hash functions.
 *
 * _Available since v5.1._
 */
library Hashes {
    /**
     * @dev Commutative Keccak256 hash of a sorted pair of bytes32. Frequently used when working with merkle proofs.
     *
     * NOTE: Equivalent to the `standardNodeHash` in our https://github.com/OpenZeppelin/merkle-tree[JavaScript library].
     */
    function commutativeKeccak256(bytes32 a, bytes32 b) internal pure returns (bytes32) {
        return a < b ? _efficientKeccak256(a, b) : _efficientKeccak256(b, a);
    }

    /**
     * @dev Implementation of keccak256(abi.encode(a, b)) that doesn't allocate or expand memory.
     */
    function _efficientKeccak256(bytes32 a, bytes32 b) private pure returns (bytes32 value) {
        assembly ("memory-safe") {
            mstore(0x00, a)
            mstore(0x20, b)
            value := keccak256(0x00, 0x40)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol)
// This file was procedurally generated from scripts/generate/templates/EnumerableSet.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for managing
 * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive
 * types.
 *
 * Sets have the following properties:
 *
 * - Elements are added, removed, and checked for existence in constant time
 * (O(1)).
 * - Elements are enumerated in O(n). No guarantees are made on the ordering.
 *
 * ```solidity
 * contract Example {
 *     // Add the library methods
 *     using EnumerableSet for EnumerableSet.AddressSet;
 *
 *     // Declare a set state variable
 *     EnumerableSet.AddressSet private mySet;
 * }
 * ```
 *
 * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`)
 * and `uint256` (`UintSet`) are supported.
 *
 * [WARNING]
 * ====
 * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure
 * unusable.
 * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info.
 *
 * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an
 * array of EnumerableSet.
 * ====
 */
library EnumerableSet {
    // To implement this library for multiple types with as little code
    // repetition as possible, we write it in terms of a generic Set type with
    // bytes32 values.
    // The Set implementation uses private functions, and user-facing
    // implementations (such as AddressSet) are just wrappers around the
    // underlying Set.
    // This means that we can only create new EnumerableSets for types that fit
    // in bytes32.

    struct Set {
        // Storage of set values
        bytes32[] _values;
        // Position is the index of the value in the `values` array plus 1.
        // Position 0 is used to mean a value is not in the set.
        mapping(bytes32 value => uint256) _positions;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function _add(Set storage set, bytes32 value) private returns (bool) {
        if (!_contains(set, value)) {
            set._values.push(value);
            // The value is stored at length-1, but we add 1 to all indexes
            // and use 0 as a sentinel value
            set._positions[value] = set._values.length;
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function _remove(Set storage set, bytes32 value) private returns (bool) {
        // We cache the value's position to prevent multiple reads from the same storage slot
        uint256 position = set._positions[value];

        if (position != 0) {
            // Equivalent to contains(set, value)
            // To delete an element from the _values array in O(1), we swap the element to delete with the last one in
            // the array, and then remove the last element (sometimes called as 'swap and pop').
            // This modifies the order of the array, as noted in {at}.

            uint256 valueIndex = position - 1;
            uint256 lastIndex = set._values.length - 1;

            if (valueIndex != lastIndex) {
                bytes32 lastValue = set._values[lastIndex];

                // Move the lastValue to the index where the value to delete is
                set._values[valueIndex] = lastValue;
                // Update the tracked position of the lastValue (that was just moved)
                set._positions[lastValue] = position;
            }

            // Delete the slot where the moved value was stored
            set._values.pop();

            // Delete the tracked position for the deleted slot
            delete set._positions[value];

            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function _contains(Set storage set, bytes32 value) private view returns (bool) {
        return set._positions[value] != 0;
    }

    /**
     * @dev Returns the number of values on the set. O(1).
     */
    function _length(Set storage set) private view returns (uint256) {
        return set._values.length;
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function _at(Set storage set, uint256 index) private view returns (bytes32) {
        return set._values[index];
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function _values(Set storage set) private view returns (bytes32[] memory) {
        return set._values;
    }

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _add(set._inner, value);
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) {
        return _remove(set._inner, value);
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) {
        return _contains(set._inner, value);
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(Bytes32Set storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) {
        return _at(set._inner, index);
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(Bytes32Set storage set) internal view returns (bytes32[] memory) {
        bytes32[] memory store = _values(set._inner);
        bytes32[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(AddressSet storage set, address value) internal returns (bool) {
        return _add(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(AddressSet storage set, address value) internal returns (bool) {
        return _remove(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(AddressSet storage set, address value) internal view returns (bool) {
        return _contains(set._inner, bytes32(uint256(uint160(value))));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(AddressSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(AddressSet storage set, uint256 index) internal view returns (address) {
        return address(uint160(uint256(_at(set._inner, index))));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(AddressSet storage set) internal view returns (address[] memory) {
        bytes32[] memory store = _values(set._inner);
        address[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

    /**
     * @dev Add a value to a set. O(1).
     *
     * Returns true if the value was added to the set, that is if it was not
     * already present.
     */
    function add(UintSet storage set, uint256 value) internal returns (bool) {
        return _add(set._inner, bytes32(value));
    }

    /**
     * @dev Removes a value from a set. O(1).
     *
     * Returns true if the value was removed from the set, that is if it was
     * present.
     */
    function remove(UintSet storage set, uint256 value) internal returns (bool) {
        return _remove(set._inner, bytes32(value));
    }

    /**
     * @dev Returns true if the value is in the set. O(1).
     */
    function contains(UintSet storage set, uint256 value) internal view returns (bool) {
        return _contains(set._inner, bytes32(value));
    }

    /**
     * @dev Returns the number of values in the set. O(1).
     */
    function length(UintSet storage set) internal view returns (uint256) {
        return _length(set._inner);
    }

    /**
     * @dev Returns the value stored at position `index` in the set. O(1).
     *
     * Note that there are no guarantees on the ordering of values inside the
     * array, and it may change when more values are added or removed.
     *
     * Requirements:
     *
     * - `index` must be strictly less than {length}.
     */
    function at(UintSet storage set, uint256 index) internal view returns (uint256) {
        return uint256(_at(set._inner, index));
    }

    /**
     * @dev Return the entire set in an array
     *
     * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed
     * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that
     * this function has an unbounded cost, and using it as part of a state-changing function may render the function
     * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block.
     */
    function values(UintSet storage set) internal view returns (uint256[] memory) {
        bytes32[] memory store = _values(set._inner);
        uint256[] memory result;

        assembly ("memory-safe") {
            result := store
        }

        return result;
    }
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.29;

/// @title IAuth2Step
/// @notice Interface for the Auth2Step contract
interface IAuth2Step {
    ////////////////////////////////////////////////////////////
    //                         Events                         //
    ////////////////////////////////////////////////////////////

    /// @notice Emitted when ownership transfer is initiated
    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);

    ////////////////////////////////////////////////////////////
    //                         Errors                         //
    ////////////////////////////////////////////////////////////

    error Aera__ZeroAddressAuthority();
    error Aera__Unauthorized();

    ////////////////////////////////////////////////////////////
    //                       Functions                        //
    ////////////////////////////////////////////////////////////

    /// @notice Accept ownership transfer
    function acceptOwnership() external;
}

Settings
{
  "remappings": [
    "@oz/=lib/openzeppelin-contracts/contracts/",
    "@solmate/=src/dependencies/solmate/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 100000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "none",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "cancun",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"bytes","name":"result","type":"bytes"}],"name":"Aera__AfterOperationHooksFailed","type":"error"},{"inputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"name":"Aera__AfterSubmitHooksFailed","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"Aera__AllowanceIsNotZero","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"bytes","name":"result","type":"bytes"}],"name":"Aera__BeforeOperationHooksFailed","type":"error"},{"inputs":[],"name":"Aera__BeforeOperationHooksWithConfigurableHooks","type":"error"},{"inputs":[{"internalType":"bytes","name":"result","type":"bytes"}],"name":"Aera__BeforeSubmitHooksFailed","type":"error"},{"inputs":[],"name":"Aera__CalldataTooShort","type":"error"},{"inputs":[],"name":"Aera__CallerIsNotAuthOrGuardian","type":"error"},{"inputs":[],"name":"Aera__CallerIsNotFeeRecipient","type":"error"},{"inputs":[],"name":"Aera__CallerIsNotGuardian","type":"error"},{"inputs":[],"name":"Aera__CallerIsNotProtocolFeeRecipient","type":"error"},{"inputs":[],"name":"Aera__CallerIsNotProvisioner","type":"error"},{"inputs":[],"name":"Aera__CopyOffsetOutOfBounds","type":"error"},{"inputs":[],"name":"Aera__ExpectedCallbackNotReceived","type":"error"},{"inputs":[],"name":"Aera__ExtractionNumberTooLarge","type":"error"},{"inputs":[],"name":"Aera__GuardianNotWhitelisted","type":"error"},{"inputs":[],"name":"Aera__InvalidBeforeOperationHooksReturnDataLength","type":"error"},{"inputs":[],"name":"Aera__NoFeesToClaim","type":"error"},{"inputs":[],"name":"Aera__NoResults","type":"error"},{"inputs":[],"name":"Aera__OffsetOutOfBounds","type":"error"},{"inputs":[],"name":"Aera__PasteOffsetOutOfBounds","type":"error"},{"inputs":[],"name":"Aera__ProofVerificationFailed","type":"error"},{"inputs":[{"internalType":"uint256","name":"index","type":"uint256"},{"internalType":"bytes","name":"result","type":"bytes"}],"name":"Aera__SubmissionFailed","type":"error"},{"inputs":[],"name":"Aera__Unauthorized","type":"error"},{"inputs":[],"name":"Aera__UnauthorizedCallback","type":"error"},{"inputs":[],"name":"Aera__UnitsLocked","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressAuthority","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressFeeCalculator","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressFeeRecipient","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressFeeToken","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressGuardian","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressMerkleRoot","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressOwner","type":"error"},{"inputs":[],"name":"Aera__ZeroAddressProvisioner","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientAllowance","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"needed","type":"uint256"}],"name":"ERC20InsufficientBalance","type":"error"},{"inputs":[{"internalType":"address","name":"approver","type":"address"}],"name":"ERC20InvalidApprover","type":"error"},{"inputs":[{"internalType":"address","name":"receiver","type":"address"}],"name":"ERC20InvalidReceiver","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"ERC20InvalidSender","type":"error"},{"inputs":[{"internalType":"address","name":"spender","type":"address"}],"name":"ERC20InvalidSpender","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"ReaderNotAtEnd","type":"error"},{"inputs":[],"name":"ReentrancyGuardReentrantCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"AuthorityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"beforeTransferHook","type":"address"}],"name":"BeforeTransferHookSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unitsAmount","type":"uint256"}],"name":"Enter","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":true,"internalType":"contract IERC20","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"unitsAmount","type":"uint256"}],"name":"Exit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newFeeCalculator","type":"address"}],"name":"FeeCalculatorUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"newFeeRecipient","type":"address"}],"name":"FeeRecipientUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"feeRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"fees","type":"uint256"}],"name":"FeesClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"guardian","type":"address"},{"indexed":true,"internalType":"bytes32","name":"root","type":"bytes32"}],"name":"GuardianRootSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"protocolFeeRecipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"protocolEarnedFees","type":"uint256"}],"name":"ProtocolFeesClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"provisioner","type":"address"}],"name":"ProvisionerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"submitHooksAddress","type":"address"}],"name":"SubmitHooksSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"FEE_TOKEN","outputs":[{"internalType":"contract IERC20","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"WHITELIST","outputs":[{"internalType":"contract IWhitelist","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"contract Authority","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"beforeTransferHook","outputs":[{"internalType":"contract IBeforeTransferHook","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"guardian","type":"address"}],"name":"checkGuardianWhitelist","outputs":[{"internalType":"bool","name":"isRemoved","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimFees","outputs":[{"internalType":"uint256","name":"feeRecipientFees","type":"uint256"},{"internalType":"uint256","name":"protocolFees","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimProtocolFees","outputs":[{"internalType":"uint256","name":"protocolFees","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"unitsAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"enter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"uint256","name":"tokenAmount","type":"uint256"},{"internalType":"uint256","name":"unitsAmount","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"}],"name":"exit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feeCalculator","outputs":[{"internalType":"contract IFeeCalculator","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeRecipient","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getActiveGuardians","outputs":[{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentHookCallType","outputs":[{"internalType":"enum HookCallType","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"guardian","type":"address"}],"name":"getGuardianRoot","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"onERC721Received","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"provisioner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"guardian","type":"address"}],"name":"removeGuardian","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract Authority","name":"newAuthority","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IBeforeTransferHook","name":"hook","type":"address"}],"name":"setBeforeTransferHook","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract IFeeCalculator","name":"newFeeCalculator","type":"address"}],"name":"setFeeCalculator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newFeeRecipient","type":"address"}],"name":"setFeeRecipient","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"guardian","type":"address"},{"internalType":"bytes32","name":"root","type":"bytes32"}],"name":"setGuardianRoot","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"provisioner_","type":"address"}],"name":"setProvisioner","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"contract ISubmitHooks","name":"newSubmitHooks","type":"address"}],"name":"setSubmitHooks","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"submit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"submitHooks","outputs":[{"internalType":"contract ISubmitHooks","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]

60c0806040523461040f57638b6a7af160e01b81525f8082600481335afa918215610404575f926107e2575b5060405163bf7be0bb60e01b8152905f82600481335afa918215610404575f926107be575b5082516001600160401b0381116104b657600354600181811c911680156107b4575b60208210146106c557601f8111610751575b506020601f82116001146106ee57819293945f926106e3575b50508160011b915f199060031b1c1916176003555b81516001600160401b0381116104b657600454600181811c911680156106d9575b60208210146106c557601f8111610662575b50602092601f821160011461060157928192935f926105f6575b50508160011b915f199060031b1c1916176004555b600580546001600160a81b03191633600881901b610100600160a81b03169190911791829055600680546001600160a01b03191690819055604051929091807f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a35f335f5160206157615f395f51905f528280a363d2c0fc8360e01b8352608083600481335afa928315610404575f9361054a575b5082516001600160a01b031690811561053b5760081c6001600160a01b03163381900361052c57600780546001600160a01b031916831790557f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227005f80a360208201516001600160a01b0316908161050e575b505060608101516001600160a01b0390811660805260409091015116806104d2575b506040516360f1b94960e01b8152606081600481335afa908115610404575f91610431575b50805160208201516001600160a01b0390811692911690811561042257821561041357813b1561040f57604051630bb621bf60e11b81525f8160048183875af18015610404576103ef575b50604001516001600160a01b031680156103e057600d80546001600160a01b0319908116929092179055600c805490911691909117905560a05260405163cacf144b60e01b8152602081600481335afa9081156103d557829161038f575b50600e80546001600160a01b0319166001600160a01b03929092169182179055604051917f6b354c5f163626c6c1f2a0a7b551ee3cd06cbe5fd35e8fe74388b9c28f05d7689080a2614eba90816108a78239608051818181611be4015281816127d50152612dea015260a05181818161117e015281816125d101526128b90152f35b90506020813d6020116103cd575b816103aa602093836107fe565b810103126103c957516001600160a01b03811681036103c9575f61030d565b5080fd5b3d915061039d565b6040513d84823e3d90fd5b638d107ec560e01b8452600484fd5b6103fc9194505f906107fe565b5f925f6102af565b6040513d5f823e3d90fd5b5f80fd5b632cfbb22360e11b5f5260045ffd5b637cea400f60e01b5f5260045ffd5b90506060813d6060116104ca575b8161044c606093836107fe565b8101031261040f5760405190606082016001600160401b038111838210176104b65760405280516001600160a01b038116810361040f57825260208101516001600160a01b038116810361040f5760208301526104ab90604001610892565b60408201525f610264565b634e487b7160e01b5f52604160045260245ffd5b3d915061043f565b600880546001600160a01b031916821790557f783744567638c41aa4016f43e64e6bc0e58913f22df8da2ee0b6e8f52e4a06585f80a25f61023f565b8117600655335f5160206157615f395f51905f525f80a35f8061021d565b6312fd309160e21b5f5260045ffd5b6320955e0760e21b5f5260045ffd5b9092506080813d6080116105ee575b81610566608093836107fe565b8101031261040f5760405190608082016001600160401b038111838210176104b65760405261059481610892565b825260208101516001600160a01b038116810361040f5760208301526040810151906001600160a01b038216820361040f576040830191909152606001516001600160a01b038116810361040f576060820152915f6101ab565b3d9150610559565b015190505f806100ff565b601f1982169360045f52805f20915f5b86811061064a5750836001959610610632575b505050811b01600455610114565b01515f1960f88460031b161c191690555f8080610624565b91926020600181928685015181550194019201610611565b60045f527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b601f830160051c810191602084106106bb575b601f0160051c01905b8181106106b057506100e5565b5f81556001016106a3565b909150819061069a565b634e487b7160e01b5f52602260045260245ffd5b90607f16906100d3565b015190505f8061009d565b601f1982169060035f52805f20915f5b81811061073957509583600195969710610721575b505050811b016003556100b2565b01515f1960f88460031b161c191690555f8080610713565b9192602060018192868b0151815501940192016106fe565b60035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b601f830160051c810191602084106107aa575b601f0160051c01905b81811061079f5750610084565b5f8155600101610792565b9091508190610789565b90607f1690610072565b6107db9192503d805f833e6107d381836107fe565b810190610821565b905f610050565b6107f79192503d805f833e6107d381836107fe565b905f61002b565b601f909101601f19168101906001600160401b038211908210176104b657604052565b60208183031261040f578051906001600160401b03821161040f570181601f8201121561040f578051906001600160401b0382116104b65760405192610871601f8401601f1916602001856107fe565b8284526020838301011161040f57815f9260208093018386015e8301015290565b51906001600160a01b038216820361040f5756fe60e0806040526004361015610031575b50361561002f573461002b576100236134bf565b602081519101f35b5f80fd5b005b5f905f358060e01c91826306fdde031461307657508163095ea7b314612fc957816310173fbb14612ed6578163150b7a0214612e4957816318160ddd14612e0e5781631bb7cc9914612da057816323b872dd14612c0c578163313ce56714612bd35781633f4ba83a14612af15781634690484014612aa05781634a7d036914612870578163530e1f491461273d5781635c975abb146126fd57816370a082311461269b57816371404156146125f557816373717b081461258757816379ba50971461247d5781637a9e5e4b146122f75781637c554ed1146120b45781637e94eab9146120625781638456cb5914611f5d5781638929565f14611e8a578282638c66d04f14611d56575081638da5cb5b14611d015781639562029414611b1457816395d89b41146119ae57816397a175641461193d57816399588e2b1461186a578163a335811f14611818578163a9059cbb146117c8578163b00eb9fe14611776578163bca563df14611420578163bf7e214f146113ce578163d294f09314611114578163d72c994f1461102a578163dd62ed3e14610fb1578163e30c397814610f5f578163e74b981b14610e5c57508063ef7fa71b146103b8578063f2fde38b146102cc578063f5a689301461027a5763fd8fff570361000f573461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775760207fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005c6102668161330a565b604051906102738161330a565b8152f35b80fd5b503461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff60085416604051908152f35b50346102775760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610277576103046131ff565b73ffffffffffffffffffffffffffffffffffffffff60055460081c16908133036103905773ffffffffffffffffffffffffffffffffffffffff1690817fffffffffffffffffffffffff000000000000000000000000000000000000000060075416176007557f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b6004837f4bf4c244000000000000000000000000000000000000000000000000000000008152fd5b50346102775760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775760043567ffffffffffffffff8111610e5857610408903690600401613245565b91906104126141ce565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c610e305760017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d6104653361460e565b939015610e08576008549160018316610d5a575b8394600182019361048c8335871a614295565b9661049a60405198896133a6565b8335871a8089527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0906104cc90614295565b01875b818110610d495750506104e48435881a6142c5565b926040516080526104f660805161338a565b8760805152876020608051015287604060805101528760606080510152876080805101528760a06080510152875b8535891a811061064e575050965060028116610577575b50610551949561054a916143e0565b019061453f565b807f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d80f35b8580916040518273ffffffffffffffffffffffffffffffffffffffff60208301927f3a11805f0000000000000000000000000000000000000000000000000000000084526105fa816105ce338d8d60248501614202565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826133a6565b5193165af1610607614266565b9061053b5761064a906040519182917f4c760ca80000000000000000000000000000000000000000000000000000000083526020600484015260248301906131bc565b0390fd5b61066560148960019a3560601c608051520161463f565b9881019890358a1a805b610c8357506001890198358a1a156106db5790806106be8b8060019573ffffffffffffffffffffffffffffffffffffffff6080515116602082519201905afa6106b6614266565b9283916143a2565b6106c8828d614377565b526106d3818c614377565b505b01610524565b9392999581858b9a859c9a94989a507fffffffff00000000000000000000000000000000000000000000000000000000825160208401518281169160048110610c6e575b50501680602060805101527f095ea7b3000000000000000000000000000000000000000000000000000000008114908115610c44575b50610bc0575b9079ffffffffffffffffffffffffffffffffffffffffffffffffffff61079a73ffffffffffffffffffffffffffffffffffffffff976107aa9594614660565b91909116604060805101526146cb565b9a929a959091951660808051015260a060805101526107cb89358b1a614295565b986107d96040519a8b6133a6565b80358b1a808b526107e990614295565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe060208c01920136833780358c1a60051b80926001830190370193600185018b956002810191358d1a610bb3575b508560805160600152946080515191608051602001517fffffffff0000000000000000000000000000000000000000000000000000000016906080516080015160805160a001516080516040015191604051958695602087019860601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001689526034870152151560f81b603886015260601b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166039850152604d84015260301b7fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000016606d83015280516020819201608784015e81018d6087820152036087017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101825261096890826133a6565b519020948a955b8a518710156109b157610982878c614377565b5190818110156109a0578c52602052600160408c205b96019561096f565b908c52602052600160408c20610998565b909692989c95508b949a9b919793995003610b8b57898291610a0a828073ffffffffffffffffffffffffffffffffffffffff6080515116606060805101519085519160208701915af1610a02614266565b9485916143a2565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff6040608051015116610b21575b60808051015160028116610a63575b50505090600191610a52828d614377565b52610a5d818c614377565b506106d5565b90829160027fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d8273ffffffffffffffffffffffffffffffffffffffff60208451940192165af1610ab2614266565b9015610aeb5750906001918a7fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d909150895f80610a41565b8261064a6040519283927f6f9b57ff0000000000000000000000000000000000000000000000000000000084526004840161438b565b90507fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005c610b63578a90610b5e610b566148e4565b8051906143e0565b610a32565b60048b7f831579e8000000000000000000000000000000000000000000000000000000008152fd5b60048a7fdb87823d000000000000000000000000000000000000000000000000000000008152fd5b903595506022015f610838565b9150506080515173ffffffffffffffffffffffffffffffffffffffff16602487015160c05260405160a05260a051610bf790613341565b60a0515260c05173ffffffffffffffffffffffffffffffffffffffff1660a0516020015286868d6001019d808c60a05191610c3191614377565b52610c3c908c614377565b50909161075b565b7f39509351000000000000000000000000000000000000000000000000000000009150145f610755565b839250829060040360031b1b16165f8061071f565b60048a019935610c9760f882901c8e614377565b5190611fe08160eb1c169082516020830111610d215761ffff9060e01c169184516020840111610cf957916024602080937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff96950101918601015e018061066f565b60048e7f7ea37aee000000000000000000000000000000000000000000000000000000008152fd5b60048e7fc87025ed000000000000000000000000000000000000000000000000000000008152fd5b806060602080938d010152016104cf565b838060405160208101907f772cb3e6000000000000000000000000000000000000000000000000000000008252610d9a816105ce33898960248501614202565b51908273ffffffffffffffffffffffffffffffffffffffff88165af1610dbe614266565b9015610dca5750610479565b61064a906040519182917f072a446d0000000000000000000000000000000000000000000000000000000083526020600484015260248301906131bc565b6004837ff5185ed1000000000000000000000000000000000000000000000000000000008152fd5b6004827f3ee5aeb5000000000000000000000000000000000000000000000000000000008152fd5b5080fd5b905034610e585760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e585773ffffffffffffffffffffffffffffffffffffffff90610edf610eda7fffffffff00000000000000000000000000000000000000000000000000000000610ed26131ff565b931633613e1c565b6133e7565b168015610f3757807fffffffffffffffffffffffff0000000000000000000000000000000000000000600d541617600d557f7a7b5a0a132f9e0581eb8527f66eae9ee89c2a3e79d4ac7e41a1f1f4d48a7fc28280a280f35b6004827f8d107ec5000000000000000000000000000000000000000000000000000000008152fd5b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff60075416604051908152f35b82346102775760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775773ffffffffffffffffffffffffffffffffffffffff60406110006131ff565b928261100a613222565b9416815260016020522091165f52602052602060405f2054604051908152f35b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775760405180602060095491828152018091600985527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af90855b8181106110fe57505050826110a79103836133a6565b604051928392602084019060208552518091526040840192915b8181106110cf575050500390f35b825173ffffffffffffffffffffffffffffffffffffffff168452859450602093840193909201916001016110c1565b8254845260209093019260019283019201611091565b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775773ffffffffffffffffffffffffffffffffffffffff600d541633036113a65773ffffffffffffffffffffffffffffffffffffffff600c5416817f0000000000000000000000000000000000000000000000000000000000000000916040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff88165afa90811561136357839161136e575b5090602460609260405194859384927fac68a74800000000000000000000000000000000000000000000000000000000845260048401525af1801561136357839284928592611313575b5083156112eb576040945061125784338361416a565b84518481527f9493e5bbe4e8e0ac67284469a2d677403d0378a85a59e341d3abc433d0d9a20960203392a28280611298575b50505082519182526020820152f35b826112a29261416a565b7f0f58cb6262d88971b87878468debb7bc81f1714be597710e16a9fb84c66ff7b5602073ffffffffffffffffffffffffffffffffffffffff8651938585521692a2838082611289565b6004857f7c29a484000000000000000000000000000000000000000000000000000000008152fd5b93509150506060823d60601161135b575b81611331606093836133a6565b810103126113575781519061134d60406020850151940161344c565b9192919085611241565b8280fd5b3d9150611324565b6040513d85823e3d90fd5b919250506020813d60201161139e575b8161138b602093836133a6565b8101031261002b575183919060246111f7565b3d915061137e565b807f286713000000000000000000000000000000000000000000000000000000000060049252fd5b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff60065416604051908152f35b82346102775761142f36613273565b61143a9391936141ce565b73ffffffffffffffffffffffffffffffffffffffff600f5416330361174e5773ffffffffffffffffffffffffffffffffffffffff90836116f4575b169283156116c8578573ffffffffffffffffffffffffffffffffffffffff600e541680611638575b50600190611599575b15611571576002548181018091116115445773ffffffffffffffffffffffffffffffffffffffff7f59009eaf55f74c19a447f53174708d6c3b16e27d0dd94f6a2a8845f6728b1614936040938293600255878a5289602052848a20818154019055878a7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208851858152a38451968752602087015216951692a480f35b6024877f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6004867f60134b78000000000000000000000000000000000000000000000000000000008152fd5b506024602073ffffffffffffffffffffffffffffffffffffffff600f5416604051928380927f9428b68e0000000000000000000000000000000000000000000000000000000082528b60048301525afa90811561162d5787916115fe575b50156114a6565b611620915060203d602011611626575b61161881836133a6565b81019061346d565b876115f7565b503d61160e565b6040513d89823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff600f5416813b156113575782906064604051809481937fabd626b00000000000000000000000000000000000000000000000000000000083528560048401528b602484015260448301525afa80156116bd571561149d57816116ae916133a6565b6116b957858761149d565b8580fd5b6040513d84823e3d90fd5b6024867fec442f0500000000000000000000000000000000000000000000000000000000815280600452fd5b6117496040517f23b872dd0000000000000000000000000000000000000000000000000000000060208201528388166024820152306044820152856064820152606481526117436084826133a6565b8461456e565b611475565b6004867f18b2ef41000000000000000000000000000000000000000000000000000000008152fd5b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff600c5416604051908152f35b82346102775760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775761180d6118036131ff565b6024359033613f29565b602060405160018152f35b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff600e5416604051908152f35b905034610e585760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e58576004359073ffffffffffffffffffffffffffffffffffffffff821680920361135757610eda7fffffffff000000000000000000000000000000000000000000000000000000006118ec921633613e1c565b807fffffffffffffffffffffffff000000000000000000000000000000000000000060085416176008557f783744567638c41aa4016f43e64e6bc0e58913f22df8da2ee0b6e8f52e4a06588280a280f35b82346102775760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775760209061199673ffffffffffffffffffffffffffffffffffffffff6119906131ff565b1661460e565b9190156119a757505b604051908152f35b905061199f565b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610277576040519080600454908160011c91600181168015611b0a575b602084108114611add57838652908115611a985750600114611a3b575b611a3784611a23818603826133a6565b6040519182916020835260208301906131bc565b0390f35b600481527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b939250905b808210611a7e57509091508101602001611a2382611a13565b919260018160209254838588010152019101909291611a65565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208087019190915292151560051b85019092019250611a239150839050611a13565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526022600452fd5b92607f16926119f6565b905034610e585760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e585773ffffffffffffffffffffffffffffffffffffffff611b626131ff565b611b94610eda7fffffffff00000000000000000000000000000000000000000000000000000000602435951633613e1c565b168015611cd9576040517f3af32abf00000000000000000000000000000000000000000000000000000000815281600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa908115611cce578491611caf575b5015611c87578115611c5f57808352600b602052816040842055611c3781614e53565b507f4b06eaf2f723ef457d7e5a4bdd29d59455b133f37737541f209bbe963aba77fe8380a380f35b6004837fc8ddd4ba000000000000000000000000000000000000000000000000000000008152fd5b6004837feb1c650e000000000000000000000000000000000000000000000000000000008152fd5b611cc8915060203d6020116116265761161881836133a6565b5f611c14565b6040513d86823e3d90fd5b6004837fa9debd38000000000000000000000000000000000000000000000000000000008152fd5b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff60055460081c16604051908152f35b915034611e875760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112611e87576004359073ffffffffffffffffffffffffffffffffffffffff8216809203611e8557610eda7fffffffff00000000000000000000000000000000000000000000000000000000611dd8921633613e1c565b807fffffffffffffffffffffffff0000000000000000000000000000000000000000600c541617600c5560405190807f3e762c7e655633ce63121393b9694f9ca1883d14d18f48f1be55e5dc7a9fb6c18480a280611e34575050f35b803b15611e8557816004818580947f176c437e0000000000000000000000000000000000000000000000000000000083525af180156116bd57611e745750f35b81611e7e916133a6565b6102775780f35b505b50fd5b905034610e585760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e58576004359073ffffffffffffffffffffffffffffffffffffffff821680920361135757610eda7fffffffff00000000000000000000000000000000000000000000000000000000611f0c921633613e1c565b807fffffffffffffffffffffffff0000000000000000000000000000000000000000600e541617600e557f6b354c5f163626c6c1f2a0a7b551ee3cd06cbe5fd35e8fe74388b9c28f05d7688280a280f35b905034610e5857817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e58577fffffffff00000000000000000000000000000000000000000000000000000000611fb9911633613e1c565b801561204d575b1561202557611fcd6141ce565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060055416176005557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a180f35b807fdb78883e0000000000000000000000000000000000000000000000000000000060049252fd5b50338152600a60205260408120541515611fc0565b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff600f5416604051908152f35b823461002b576120c336613273565b936120d0939192936141ce565b73ffffffffffffffffffffffffffffffffffffffff600f541633036122cf5773ffffffffffffffffffffffffffffffffffffffff169182156122a35773ffffffffffffffffffffffffffffffffffffffff600e541680612217575b508286528560205260408620548181106121e35773ffffffffffffffffffffffffffffffffffffffff7ffb39f3e6cf68759732e3db2247713a4eca21e27eab5d056a07f276c35867f73f93836040948394888c528b60205203858b2055806002540360025589877fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208851858152a3876121d3575b8451978852602088015216951693a480f35b6121de888a8461416a565b6121c1565b8692506064937fe450d38c000000000000000000000000000000000000000000000000000000008452600452602452604452fd5b73ffffffffffffffffffffffffffffffffffffffff600f5416813b1561002b575f906064604051809481937fabd626b000000000000000000000000000000000000000000000000000000000835289600484015285602484015260448301525afa8015612298571561212b576122909196505f906133a6565b5f948661212b565b6040513d5f823e3d90fd5b7f96c6fd1e000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b7f18b2ef41000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b576004359073ffffffffffffffffffffffffffffffffffffffff821680920361002b5773ffffffffffffffffffffffffffffffffffffffff60055460081c1633149081156123c5575b501561002b57807fffffffffffffffffffffffff00000000000000000000000000000000000000006006541617600655337fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b763899801985f80a3005b6006546040517fb70096130000000000000000000000000000000000000000000000000000000081523360048201523060248201527fffffffff000000000000000000000000000000000000000000000000000000009290921660448301529091506020908290606490829073ffffffffffffffffffffffffffffffffffffffff165afa908115612298575f9161245e575b508261236e565b612477915060203d6020116116265761161881836133a6565b82612457565b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5760075473ffffffffffffffffffffffffffffffffffffffff81169081330361255f577fffffffffffffffffffffffff0000000000000000000000000000000000000000906005547fffffffffffffffffffffff0000000000000000000000000000000000000000ff74ffffffffffffffffffffffffffffffffffffffff008360081b1691161760055516600755337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b7f4bf4c244000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5773ffffffffffffffffffffffffffffffffffffffff5f9161266a610eda7fffffffff00000000000000000000000000000000000000000000000000000000610ed26131ff565b16612674816145f5565b507f4b06eaf2f723ef457d7e5a4bdd29d59455b133f37737541f209bbe963aba77fe8280a3005b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5773ffffffffffffffffffffffffffffffffffffffff6126e76131ff565b165f525f602052602060405f2054604051908152f35b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602060ff600554166040519015158152f35b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b576127746131ff565b5f73ffffffffffffffffffffffffffffffffffffffff604051927f3af32abf0000000000000000000000000000000000000000000000000000000084521680600484015260208360248173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000165afa8015612298576020935f91612853575b5015612819575b506040519015158152f35b9050612824816145f5565b505f6001917f4b06eaf2f723ef457d7e5a4bdd29d59455b133f37737541f209bbe963aba77fe8280a38261280e565b61286a9150843d86116116265761161881836133a6565b84612807565b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5773ffffffffffffffffffffffffffffffffffffffff600c54167f0000000000000000000000000000000000000000000000000000000000000000906040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff87165afa908115612298575f91612a6e575b505f916024604092835194859384927f01c621c100000000000000000000000000000000000000000000000000000000845260048401525af18015612298575f915f91612a2e575b5073ffffffffffffffffffffffffffffffffffffffff811690813303612a065782156129de57826129ad9160209561416a565b7f0f58cb6262d88971b87878468debb7bc81f1714be597710e16a9fb84c66ff7b583604051848152a2604051908152f35b7f7c29a484000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f3b4f2663000000000000000000000000000000000000000000000000000000005f5260045ffd5b9150506040813d604011612a66575b81612a4a604093836133a6565b8101031261002b57612a6060208251920161344c565b8361297a565b3d9150612a3d565b90506020813d602011612a98575b81612a89602093836133a6565b8101031261002b57515f612932565b3d9150612a7c565b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602073ffffffffffffffffffffffffffffffffffffffff600d5416604051908152f35b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57610eda7fffffffff00000000000000000000000000000000000000000000000000000000612b4e921633613e1c565b60055460ff811615612bab577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166005557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b7f8dfc202b000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602060405160128152f35b3461002b5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57612c436131ff565b612c4b613222565b6044359073ffffffffffffffffffffffffffffffffffffffff831692835f52600160205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f5260205260405f20547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8103612cc7575b5061180d9350613f29565b838110612d6c578415612d40573315612d145761180d945f52600160205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f526020528360405f209103905584612cbc565b7f94280d62000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b7fe602df05000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b83907ffb8f41b2000000000000000000000000000000000000000000000000000000005f523360045260245260445260645ffd5b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602060405173ffffffffffffffffffffffffffffffffffffffff7f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b576020600254604051908152f35b3461002b5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57612e806131ff565b50612e89613222565b5060643567ffffffffffffffff811161002b57612eaa903690600401613245565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5773ffffffffffffffffffffffffffffffffffffffff90612f4a610eda7fffffffff00000000000000000000000000000000000000000000000000000000610ed26131ff565b168015612fa157807fffffffffffffffffffffffff0000000000000000000000000000000000000000600f541617600f557f606753945c222bd6eb6533c506303e56af1df290ae8e1557219f83770cfa35155f80a2005b7f17c1ab6a000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461002b5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b576130006131ff565b602435903315612d405773ffffffffffffffffffffffffffffffffffffffff16908115612d1457335f52600160205260405f20825f526020528060405f20556040519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b575f6003548060011c906001811680156131b2575b6020831081146131855782855290811561314357506001146130e5575b611a3783611a23818503826133a6565b91905060035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b915f905b80821061312957509091508101602001611a236130d5565b919260018160209254838588010152019101909291613111565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b84019091019150611a2390506130d5565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f16916130b8565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361002b57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361002b57565b9181601f8401121561002b5782359167ffffffffffffffff831161002b576020838186019501011161002b57565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60a091011261002b5760043573ffffffffffffffffffffffffffffffffffffffff8116810361002b579060243573ffffffffffffffffffffffffffffffffffffffff8116810361002b5790604435906064359060843573ffffffffffffffffffffffffffffffffffffffff8116810361002b5790565b6003111561331457565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b6040810190811067ffffffffffffffff82111761335d57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60c0810190811067ffffffffffffffff82111761335d57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761335d57604052565b156133ee57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152fd5b519073ffffffffffffffffffffffffffffffffffffffff8216820361002b57565b9081602091031261002b5751801515810361002b5790565b67ffffffffffffffff811161335d57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b7fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005c5f7fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005d7fffffffff000000000000000000000000000000000000000000000000000000008160301b167fffffffff000000000000000000000000000000000000000000000000000000005f351603613df45773ffffffffffffffffffffffffffffffffffffffff81163303613df4577f30fb041442610fd0a22e4654f60ea1c715088ef7320b5ec0c4e75cbdd99dbe005c5f7f30fb041442610fd0a22e4654f60ea1c715088ef7320b5ec0c4e75cbdd99dbe005d8015613df45761ffff808360a01c1614613dde575f9060a083901c61ffff166001810191906135e69035841a614295565b926135f460405194856133a6565b60a085901c61ffff16355f1a8085527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09061362e90614295565b015f5b818110613dcd57505061364d61ffff8660a01c16355f1a6142c5565b9160405161365a8161338a565b5f81525f60208201525f60408201525f60608201525f60808201525f60a08201525f5b61ffff8860a01c16355f1a8110613791575050509061369c9190614a0e565b6060916001820191355f1a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810161371557505061371291506136de9061463f565b925b60a01c61ffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810135019061453f565b90565b600214613728575b5061371291926136e0565b80925051801561376957613712927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613762920190614377565b519161371d565b7f26779f3c000000000000000000000000000000000000000000000000000000005f5260045ffd5b6137a66014876001983560601c85520161463f565b9681019690355f1a805b613d0a57506001870196355f1a156138125790806137f55f8060019573ffffffffffffffffffffffffffffffffffffffff885116602082519201905afa6106b6614266565b6137ff828a614377565b5261380a8189614377565b505b0161367d565b818194999a9296939598978a7fffffffff00000000000000000000000000000000000000000000000000000000845160208601518281169160048110613cf5575b5050168060208a01527f095ea7b3000000000000000000000000000000000000000000000000000000008114908115613ccb575b50613c63575b61389691614660565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff1660408801526138c192906146cb565b73ffffffffffffffffffffffffffffffffffffffff16608088015260a08701529099906138f08b355f1a614295565b9a6040519b6138ff908d6133a6565b80355f1a808d5261390f90614295565b9060208d01917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00136833780355f1a60051b80926001830190370186600182015f926002810191355f1a151593613a67926087927fffffffffffffffffffffffffffffffffffffffff00000000000000000000000096613c55575b50602090826060870152967fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000008651937fffffffff00000000000000000000000000000000000000000000000000000000848901511697896080820151604060a0840151930151936040519b8c99898b019e8f9160601b16905260348a0152151560f81b603889015260601b166039870152604d86015260301b16606d8401528051918291018484015e81015f8382015203017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826133a6565b519020935f945b8c51861015613ab057613a81868e614377565b519081811015613a9f575f52602052600160405f205b950194613a6e565b905f52602052600160405f20613a97565b9097939b508a95929a91989996945003613c2d578190613b035f8073ffffffffffffffffffffffffffffffffffffffff87511660608801519085519160208701915af1613afb614266565b9384916143a2565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff604085015116613bde575b608084015160028116613b58575b505090600191613b47828a614377565b52613b528189614377565b5061380c565b5f91829160027fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d8273ffffffffffffffffffffffffffffffffffffffff60208451940192165af1613ba8614266565b9015610aeb5750906001915f7fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d90915f613b37565b7fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005c15613b29577f831579e8000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fdb87823d000000000000000000000000000000000000000000000000000000005f5260045ffd5b90359150602201602061398a565b875173ffffffffffffffffffffffffffffffffffffffff169a60248501516040519c613c8e8e613341565b8d5273ffffffffffffffffffffffffffffffffffffffff1660208d0152806001019b613cba8289614377565b52613cc59087614377565b5061388d565b7f39509351000000000000000000000000000000000000000000000000000000009150145f613887565b839250829060040360031b1b16165f80613853565b9660048135910197613d1f8260f81c8b614377565b51611fe08360eb1c169281516020850111613da55761ffff9060e01c1684516020820111613d7d57602460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9581940101918601015e01806137b0565b7f7ea37aee000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fc87025ed000000000000000000000000000000000000000000000000000000005f5260045ffd5b806060602080938901015201613631565b5050604051613dee6020826133a6565b5f815290565b7fc6248746000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff60065416918215159283613e71575b50508115613e4c575090565b905073ffffffffffffffffffffffffffffffffffffffff8060055460081c1691161490565b6040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201523060248201527fffffffff0000000000000000000000000000000000000000000000000000000092909216604483015291925090602090829060649082905afa908115612298575f91613f0a575b50905f80613e40565b613f23915060203d6020116116265761161881836133a6565b5f613f01565b9173ffffffffffffffffffffffffffffffffffffffff5f93169182156122a35773ffffffffffffffffffffffffffffffffffffffff1692831561413e5773ffffffffffffffffffffffffffffffffffffffff600e5416806140bf575b506024602073ffffffffffffffffffffffffffffffffffffffff600f5416604051928380927f9428b68e0000000000000000000000000000000000000000000000000000000082528860048301525afa9081156116bd5782916140a0575b50614078578281528060205260408120548281106140455791604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815280845220818154019055604051908152a3565b6064937fe450d38c0000000000000000000000000000000000000000000000000000000083949352600452602452604452fd5b807f60134b780000000000000000000000000000000000000000000000000000000060049252fd5b6140b9915060203d6020116116265761161881836133a6565b5f613fe3565b73ffffffffffffffffffffffffffffffffffffffff600f5416813b1561002b575f906064604051809481937fabd626b00000000000000000000000000000000000000000000000000000000083528960048401528a602484015260448301525afa80156122985715613f855761413791505f906133a6565b5f5f613f85565b7fec442f05000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b6141cc9273ffffffffffffffffffffffffffffffffffffffff604051937fa9059cbb0000000000000000000000000000000000000000000000000000000060208601521660248401526044830152604482526141c76064836133a6565b61456e565b565b60ff600554166141da57565b7fd93c0665000000000000000000000000000000000000000000000000000000005f5260045ffd5b9160607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8373ffffffffffffffffffffffffffffffffffffffff94602096999899604089528160408a0152858901375f84828901015201168401019416910152565b3d15614290573d9061427782613485565b9161428560405193846133a6565b82523d5f602084013e565b606090565b67ffffffffffffffff811161335d5760051b60200190565b604051906142ba82613341565b5f6020838281520152565b906142cf82614295565b6142dc60405191826133a6565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061430a8294614295565b01905f5b82811061431a57505050565b60209060405161432981613341565b5f81525f838201528282850101520161430e565b80511561434a5760200190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b805182101561434a5760209160051b010190565b6040906137129392815281602082015201906131bc565b156143ab575050565b61064a6040519283927f248ef89c0000000000000000000000000000000000000000000000000000000084526004840161438b565b90806143ea6142ad565b505b6143f4575050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff016144208183614377565b5173ffffffffffffffffffffffffffffffffffffffff815116906020810190602073ffffffffffffffffffffffffffffffffffffffff8351166044604051809681937fdd62ed3e00000000000000000000000000000000000000000000000000000000835230600484015260248301525afa928315612298575f936144f8575b5073ffffffffffffffffffffffffffffffffffffffff80915116915116916144ca575050806143ec565b7f715879af000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b9092506020813d8211614537575b81614513602093836133a6565b8101031261002b57519173ffffffffffffffffffffffffffffffffffffffff6144a0565b3d9150614506565b0361454657565b7f01842f8c000000000000000000000000000000000000000000000000000000005f5260045ffd5b905f602091828151910182855af115612298575f513d6145ec575073ffffffffffffffffffffffffffffffffffffffff81163b155b6145aa5750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b600114156145a3565b61371290805f52600b6020525f60408120556009614c98565b805f52600b60205260405f205480155f1461463757505f52600a60205260405f20541515905f90565b600192909150565b906002823560f01c61465081614b35565b9381838201602087013701019190565b9190916001810181355f1a156146c35790601b913560301c93847fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005d7f30fb041442610fd0a22e4654f60ea1c715088ef7320b5ec0c4e75cbdd99dbe005d019190565b92505f919050565b909291906001810181355f1a80156148c5576080607f82169116156148a9578061485457503560601c916060946001841661470d575b505060150192915f9190565b5f91929550819060017fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d60208151910182865af19061474b614266565b911561481f5750601f8151166147f7575f7fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d805181019060208181840193031261002b5760208101519067ffffffffffffffff821161002b57019080603f8301121561002b5760208201516147c081613485565b916147ce60405193846133a6565b8183526040848301011161002b576020815f926040601596018386015e8301015293905f614701565b7fbe72399a000000000000000000000000000000000000000000000000000000005f5260045ffd5b61064a6040519283927f883d36b40000000000000000000000000000000000000000000000000000000084526004840161438b565b93919492503592602185013560601c9260018416614881576035918561487992614b84565b940193929190565b7ffafeed10000000000000000000000000000000000000000000000000000000005f5260045ffd5b602192959493506148bc91358095614b84565b93019291905f90565b509293505050906040516148da6020826133a6565b5f8152905f905f90565b7fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c005c908160a01c8015614a07575f7fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c005d61493d816142c5565b927fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c019073ffffffffffffffffffffffffffffffffffffffff825c816040519361498585613341565b1683521660208201526149978561433d565b526149a18461433d565b5060015b8281106149b157505050565b60019060028284015c93019273ffffffffffffffffffffffffffffffffffffffff845c81604051936149e285613341565b1683521660208201526149f58288614377565b52614a008187614377565b50016149a5565b5060609150565b8115614b31577fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c00905f825c60a081901c614a466142ad565b5080614ad25750505073ffffffffffffffffffffffffffffffffffffffff60206001614a718461433d565b5194838651168760a01b17815d0193015116825d60015b838110614a955750505050565b8073ffffffffffffffffffffffffffffffffffffffff60206002614abb60019587614377565b5196838851168682015d0195015116845d01614a88565b9085947fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66bff93966c01fffffffffffffffffffffffe930160a01b73ffffffffffffffffffffffffffffffffffffffff831617905d609f1c16019192614a88565b5050565b90614b3f82613485565b614b4c60405191826133a6565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0614b7a8294613485565b0190602036910137565b90916011811015614c5b57614b9b8160051b614b35565b92825160248110614c33577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015f915f925b848410614bdd5750505050505090565b8160f01c836004820111614c0b5760019160246020928901015182828b010152019160101b93019290614bcd565b7f66a468ef000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f6809cb33000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f9bbf46dc000000000000000000000000000000000000000000000000000000005f5260045ffd5b805482101561434a575f5260205f2001905f90565b906001820191815f528260205260405f20548015155f14614e4b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101818111614e1e578254907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211614e1e57818103614db4575b50505080548015614d87577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190614d4a8282614c83565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82549160031b1b19169055555f526020525f6040812055600190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffd5b614e09614dc4614dd49386614c83565b90549060031b1c92839286614c83565b81939154907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060031b92831b921b19161790565b90555f528360205260405f20555f8080614d12565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b505050505f90565b805f52600a60205260405f2054155f14614ea8576009546801000000000000000081101561335d57614e91614dd48260018594016009556009614c83565b9055600954905f52600a60205260405f2055600190565b505f9056fea164736f6c634300081d000aa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b76389980198

Deployed Bytecode

0x60e0806040526004361015610031575b50361561002f573461002b576100236134bf565b602081519101f35b5f80fd5b005b5f905f358060e01c91826306fdde031461307657508163095ea7b314612fc957816310173fbb14612ed6578163150b7a0214612e4957816318160ddd14612e0e5781631bb7cc9914612da057816323b872dd14612c0c578163313ce56714612bd35781633f4ba83a14612af15781634690484014612aa05781634a7d036914612870578163530e1f491461273d5781635c975abb146126fd57816370a082311461269b57816371404156146125f557816373717b081461258757816379ba50971461247d5781637a9e5e4b146122f75781637c554ed1146120b45781637e94eab9146120625781638456cb5914611f5d5781638929565f14611e8a578282638c66d04f14611d56575081638da5cb5b14611d015781639562029414611b1457816395d89b41146119ae57816397a175641461193d57816399588e2b1461186a578163a335811f14611818578163a9059cbb146117c8578163b00eb9fe14611776578163bca563df14611420578163bf7e214f146113ce578163d294f09314611114578163d72c994f1461102a578163dd62ed3e14610fb1578163e30c397814610f5f578163e74b981b14610e5c57508063ef7fa71b146103b8578063f2fde38b146102cc578063f5a689301461027a5763fd8fff570361000f573461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775760207fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005c6102668161330a565b604051906102738161330a565b8152f35b80fd5b503461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff60085416604051908152f35b50346102775760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610277576103046131ff565b73ffffffffffffffffffffffffffffffffffffffff60055460081c16908133036103905773ffffffffffffffffffffffffffffffffffffffff1690817fffffffffffffffffffffffff000000000000000000000000000000000000000060075416176007557f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e227008380a380f35b6004837f4bf4c244000000000000000000000000000000000000000000000000000000008152fd5b50346102775760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775760043567ffffffffffffffff8111610e5857610408903690600401613245565b91906104126141ce565b7f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005c610e305760017f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d6104653361460e565b939015610e08576008549160018316610d5a575b8394600182019361048c8335871a614295565b9661049a60405198896133a6565b8335871a8089527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0906104cc90614295565b01875b818110610d495750506104e48435881a6142c5565b926040516080526104f660805161338a565b8760805152876020608051015287604060805101528760606080510152876080805101528760a06080510152875b8535891a811061064e575050965060028116610577575b50610551949561054a916143e0565b019061453f565b807f9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f005d80f35b8580916040518273ffffffffffffffffffffffffffffffffffffffff60208301927f3a11805f0000000000000000000000000000000000000000000000000000000084526105fa816105ce338d8d60248501614202565b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826133a6565b5193165af1610607614266565b9061053b5761064a906040519182917f4c760ca80000000000000000000000000000000000000000000000000000000083526020600484015260248301906131bc565b0390fd5b61066560148960019a3560601c608051520161463f565b9881019890358a1a805b610c8357506001890198358a1a156106db5790806106be8b8060019573ffffffffffffffffffffffffffffffffffffffff6080515116602082519201905afa6106b6614266565b9283916143a2565b6106c8828d614377565b526106d3818c614377565b505b01610524565b9392999581858b9a859c9a94989a507fffffffff00000000000000000000000000000000000000000000000000000000825160208401518281169160048110610c6e575b50501680602060805101527f095ea7b3000000000000000000000000000000000000000000000000000000008114908115610c44575b50610bc0575b9079ffffffffffffffffffffffffffffffffffffffffffffffffffff61079a73ffffffffffffffffffffffffffffffffffffffff976107aa9594614660565b91909116604060805101526146cb565b9a929a959091951660808051015260a060805101526107cb89358b1a614295565b986107d96040519a8b6133a6565b80358b1a808b526107e990614295565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe060208c01920136833780358c1a60051b80926001830190370193600185018b956002810191358d1a610bb3575b508560805160600152946080515191608051602001517fffffffff0000000000000000000000000000000000000000000000000000000016906080516080015160805160a001516080516040015191604051958695602087019860601b7fffffffffffffffffffffffffffffffffffffffff0000000000000000000000001689526034870152151560f81b603886015260601b7fffffffffffffffffffffffffffffffffffffffff000000000000000000000000166039850152604d84015260301b7fffffffffffffffffffffffffffffffffffffffffffffffffffff00000000000016606d83015280516020819201608784015e81018d6087820152036087017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08101825261096890826133a6565b519020948a955b8a518710156109b157610982878c614377565b5190818110156109a0578c52602052600160408c205b96019561096f565b908c52602052600160408c20610998565b909692989c95508b949a9b919793995003610b8b57898291610a0a828073ffffffffffffffffffffffffffffffffffffffff6080515116606060805101519085519160208701915af1610a02614266565b9485916143a2565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff6040608051015116610b21575b60808051015160028116610a63575b50505090600191610a52828d614377565b52610a5d818c614377565b506106d5565b90829160027fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d8273ffffffffffffffffffffffffffffffffffffffff60208451940192165af1610ab2614266565b9015610aeb5750906001918a7fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d909150895f80610a41565b8261064a6040519283927f6f9b57ff0000000000000000000000000000000000000000000000000000000084526004840161438b565b90507fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005c610b63578a90610b5e610b566148e4565b8051906143e0565b610a32565b60048b7f831579e8000000000000000000000000000000000000000000000000000000008152fd5b60048a7fdb87823d000000000000000000000000000000000000000000000000000000008152fd5b903595506022015f610838565b9150506080515173ffffffffffffffffffffffffffffffffffffffff16602487015160c05260405160a05260a051610bf790613341565b60a0515260c05173ffffffffffffffffffffffffffffffffffffffff1660a0516020015286868d6001019d808c60a05191610c3191614377565b52610c3c908c614377565b50909161075b565b7f39509351000000000000000000000000000000000000000000000000000000009150145f610755565b839250829060040360031b1b16165f8061071f565b60048a019935610c9760f882901c8e614377565b5190611fe08160eb1c169082516020830111610d215761ffff9060e01c169184516020840111610cf957916024602080937fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff96950101918601015e018061066f565b60048e7f7ea37aee000000000000000000000000000000000000000000000000000000008152fd5b60048e7fc87025ed000000000000000000000000000000000000000000000000000000008152fd5b806060602080938d010152016104cf565b838060405160208101907f772cb3e6000000000000000000000000000000000000000000000000000000008252610d9a816105ce33898960248501614202565b51908273ffffffffffffffffffffffffffffffffffffffff88165af1610dbe614266565b9015610dca5750610479565b61064a906040519182917f072a446d0000000000000000000000000000000000000000000000000000000083526020600484015260248301906131bc565b6004837ff5185ed1000000000000000000000000000000000000000000000000000000008152fd5b6004827f3ee5aeb5000000000000000000000000000000000000000000000000000000008152fd5b5080fd5b905034610e585760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e585773ffffffffffffffffffffffffffffffffffffffff90610edf610eda7fffffffff00000000000000000000000000000000000000000000000000000000610ed26131ff565b931633613e1c565b6133e7565b168015610f3757807fffffffffffffffffffffffff0000000000000000000000000000000000000000600d541617600d557f7a7b5a0a132f9e0581eb8527f66eae9ee89c2a3e79d4ac7e41a1f1f4d48a7fc28280a280f35b6004827f8d107ec5000000000000000000000000000000000000000000000000000000008152fd5b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff60075416604051908152f35b82346102775760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775773ffffffffffffffffffffffffffffffffffffffff60406110006131ff565b928261100a613222565b9416815260016020522091165f52602052602060405f2054604051908152f35b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775760405180602060095491828152018091600985527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af90855b8181106110fe57505050826110a79103836133a6565b604051928392602084019060208552518091526040840192915b8181106110cf575050500390f35b825173ffffffffffffffffffffffffffffffffffffffff168452859450602093840193909201916001016110c1565b8254845260209093019260019283019201611091565b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775773ffffffffffffffffffffffffffffffffffffffff600d541633036113a65773ffffffffffffffffffffffffffffffffffffffff600c5416817f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48916040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff88165afa90811561136357839161136e575b5090602460609260405194859384927fac68a74800000000000000000000000000000000000000000000000000000000845260048401525af1801561136357839284928592611313575b5083156112eb576040945061125784338361416a565b84518481527f9493e5bbe4e8e0ac67284469a2d677403d0378a85a59e341d3abc433d0d9a20960203392a28280611298575b50505082519182526020820152f35b826112a29261416a565b7f0f58cb6262d88971b87878468debb7bc81f1714be597710e16a9fb84c66ff7b5602073ffffffffffffffffffffffffffffffffffffffff8651938585521692a2838082611289565b6004857f7c29a484000000000000000000000000000000000000000000000000000000008152fd5b93509150506060823d60601161135b575b81611331606093836133a6565b810103126113575781519061134d60406020850151940161344c565b9192919085611241565b8280fd5b3d9150611324565b6040513d85823e3d90fd5b919250506020813d60201161139e575b8161138b602093836133a6565b8101031261002b575183919060246111f7565b3d915061137e565b807f286713000000000000000000000000000000000000000000000000000000000060049252fd5b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff60065416604051908152f35b82346102775761142f36613273565b61143a9391936141ce565b73ffffffffffffffffffffffffffffffffffffffff600f5416330361174e5773ffffffffffffffffffffffffffffffffffffffff90836116f4575b169283156116c8578573ffffffffffffffffffffffffffffffffffffffff600e541680611638575b50600190611599575b15611571576002548181018091116115445773ffffffffffffffffffffffffffffffffffffffff7f59009eaf55f74c19a447f53174708d6c3b16e27d0dd94f6a2a8845f6728b1614936040938293600255878a5289602052848a20818154019055878a7fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208851858152a38451968752602087015216951692a480f35b6024877f4e487b710000000000000000000000000000000000000000000000000000000081526011600452fd5b6004867f60134b78000000000000000000000000000000000000000000000000000000008152fd5b506024602073ffffffffffffffffffffffffffffffffffffffff600f5416604051928380927f9428b68e0000000000000000000000000000000000000000000000000000000082528b60048301525afa90811561162d5787916115fe575b50156114a6565b611620915060203d602011611626575b61161881836133a6565b81019061346d565b876115f7565b503d61160e565b6040513d89823e3d90fd5b73ffffffffffffffffffffffffffffffffffffffff600f5416813b156113575782906064604051809481937fabd626b00000000000000000000000000000000000000000000000000000000083528560048401528b602484015260448301525afa80156116bd571561149d57816116ae916133a6565b6116b957858761149d565b8580fd5b6040513d84823e3d90fd5b6024867fec442f0500000000000000000000000000000000000000000000000000000000815280600452fd5b6117496040517f23b872dd0000000000000000000000000000000000000000000000000000000060208201528388166024820152306044820152856064820152606481526117436084826133a6565b8461456e565b611475565b6004867f18b2ef41000000000000000000000000000000000000000000000000000000008152fd5b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff600c5416604051908152f35b82346102775760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775761180d6118036131ff565b6024359033613f29565b602060405160018152f35b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff600e5416604051908152f35b905034610e585760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e58576004359073ffffffffffffffffffffffffffffffffffffffff821680920361135757610eda7fffffffff000000000000000000000000000000000000000000000000000000006118ec921633613e1c565b807fffffffffffffffffffffffff000000000000000000000000000000000000000060085416176008557f783744567638c41aa4016f43e64e6bc0e58913f22df8da2ee0b6e8f52e4a06588280a280f35b82346102775760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126102775760209061199673ffffffffffffffffffffffffffffffffffffffff6119906131ff565b1661460e565b9190156119a757505b604051908152f35b905061199f565b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610277576040519080600454908160011c91600181168015611b0a575b602084108114611add57838652908115611a985750600114611a3b575b611a3784611a23818603826133a6565b6040519182916020835260208301906131bc565b0390f35b600481527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b939250905b808210611a7e57509091508101602001611a2382611a13565b919260018160209254838588010152019101909291611a65565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208087019190915292151560051b85019092019250611a239150839050611a13565b6024837f4e487b710000000000000000000000000000000000000000000000000000000081526022600452fd5b92607f16926119f6565b905034610e585760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e585773ffffffffffffffffffffffffffffffffffffffff611b626131ff565b611b94610eda7fffffffff00000000000000000000000000000000000000000000000000000000602435951633613e1c565b168015611cd9576040517f3af32abf00000000000000000000000000000000000000000000000000000000815281600482015260208160248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ddfd960a7150520548dd1f6e53cc2f201b364692165afa908115611cce578491611caf575b5015611c87578115611c5f57808352600b602052816040842055611c3781614e53565b507f4b06eaf2f723ef457d7e5a4bdd29d59455b133f37737541f209bbe963aba77fe8380a380f35b6004837fc8ddd4ba000000000000000000000000000000000000000000000000000000008152fd5b6004837feb1c650e000000000000000000000000000000000000000000000000000000008152fd5b611cc8915060203d6020116116265761161881836133a6565b5f611c14565b6040513d86823e3d90fd5b6004837fa9debd38000000000000000000000000000000000000000000000000000000008152fd5b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff60055460081c16604051908152f35b915034611e875760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112611e87576004359073ffffffffffffffffffffffffffffffffffffffff8216809203611e8557610eda7fffffffff00000000000000000000000000000000000000000000000000000000611dd8921633613e1c565b807fffffffffffffffffffffffff0000000000000000000000000000000000000000600c541617600c5560405190807f3e762c7e655633ce63121393b9694f9ca1883d14d18f48f1be55e5dc7a9fb6c18480a280611e34575050f35b803b15611e8557816004818580947f176c437e0000000000000000000000000000000000000000000000000000000083525af180156116bd57611e745750f35b81611e7e916133a6565b6102775780f35b505b50fd5b905034610e585760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e58576004359073ffffffffffffffffffffffffffffffffffffffff821680920361135757610eda7fffffffff00000000000000000000000000000000000000000000000000000000611f0c921633613e1c565b807fffffffffffffffffffffffff0000000000000000000000000000000000000000600e541617600e557f6b354c5f163626c6c1f2a0a7b551ee3cd06cbe5fd35e8fe74388b9c28f05d7688280a280f35b905034610e5857817ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610e58577fffffffff00000000000000000000000000000000000000000000000000000000611fb9911633613e1c565b801561204d575b1561202557611fcd6141ce565b60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0060055416176005557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586020604051338152a180f35b807fdb78883e0000000000000000000000000000000000000000000000000000000060049252fd5b50338152600a60205260408120541515611fc0565b823461027757807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261027757602073ffffffffffffffffffffffffffffffffffffffff600f5416604051908152f35b823461002b576120c336613273565b936120d0939192936141ce565b73ffffffffffffffffffffffffffffffffffffffff600f541633036122cf5773ffffffffffffffffffffffffffffffffffffffff169182156122a35773ffffffffffffffffffffffffffffffffffffffff600e541680612217575b508286528560205260408620548181106121e35773ffffffffffffffffffffffffffffffffffffffff7ffb39f3e6cf68759732e3db2247713a4eca21e27eab5d056a07f276c35867f73f93836040948394888c528b60205203858b2055806002540360025589877fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef60208851858152a3876121d3575b8451978852602088015216951693a480f35b6121de888a8461416a565b6121c1565b8692506064937fe450d38c000000000000000000000000000000000000000000000000000000008452600452602452604452fd5b73ffffffffffffffffffffffffffffffffffffffff600f5416813b1561002b575f906064604051809481937fabd626b000000000000000000000000000000000000000000000000000000000835289600484015285602484015260448301525afa8015612298571561212b576122909196505f906133a6565b5f948661212b565b6040513d5f823e3d90fd5b7f96c6fd1e000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b7f18b2ef41000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b576004359073ffffffffffffffffffffffffffffffffffffffff821680920361002b5773ffffffffffffffffffffffffffffffffffffffff60055460081c1633149081156123c5575b501561002b57807fffffffffffffffffffffffff00000000000000000000000000000000000000006006541617600655337fa3396fd7f6e0a21b50e5089d2da70d5ac0a3bbbd1f617a93f134b763899801985f80a3005b6006546040517fb70096130000000000000000000000000000000000000000000000000000000081523360048201523060248201527fffffffff000000000000000000000000000000000000000000000000000000009290921660448301529091506020908290606490829073ffffffffffffffffffffffffffffffffffffffff165afa908115612298575f9161245e575b508261236e565b612477915060203d6020116116265761161881836133a6565b82612457565b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5760075473ffffffffffffffffffffffffffffffffffffffff81169081330361255f577fffffffffffffffffffffffff0000000000000000000000000000000000000000906005547fffffffffffffffffffffff0000000000000000000000000000000000000000ff74ffffffffffffffffffffffffffffffffffffffff008360081b1691161760055516600755337f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e05f80a3005b7f4bf4c244000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48168152f35b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5773ffffffffffffffffffffffffffffffffffffffff5f9161266a610eda7fffffffff00000000000000000000000000000000000000000000000000000000610ed26131ff565b16612674816145f5565b507f4b06eaf2f723ef457d7e5a4bdd29d59455b133f37737541f209bbe963aba77fe8280a3005b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5773ffffffffffffffffffffffffffffffffffffffff6126e76131ff565b165f525f602052602060405f2054604051908152f35b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602060ff600554166040519015158152f35b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b576127746131ff565b5f73ffffffffffffffffffffffffffffffffffffffff604051927f3af32abf0000000000000000000000000000000000000000000000000000000084521680600484015260208360248173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ddfd960a7150520548dd1f6e53cc2f201b364692165afa8015612298576020935f91612853575b5015612819575b506040519015158152f35b9050612824816145f5565b505f6001917f4b06eaf2f723ef457d7e5a4bdd29d59455b133f37737541f209bbe963aba77fe8280a38261280e565b61286a9150843d86116116265761161881836133a6565b84612807565b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5773ffffffffffffffffffffffffffffffffffffffff600c54167f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48906040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173ffffffffffffffffffffffffffffffffffffffff87165afa908115612298575f91612a6e575b505f916024604092835194859384927f01c621c100000000000000000000000000000000000000000000000000000000845260048401525af18015612298575f915f91612a2e575b5073ffffffffffffffffffffffffffffffffffffffff811690813303612a065782156129de57826129ad9160209561416a565b7f0f58cb6262d88971b87878468debb7bc81f1714be597710e16a9fb84c66ff7b583604051848152a2604051908152f35b7f7c29a484000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f3b4f2663000000000000000000000000000000000000000000000000000000005f5260045ffd5b9150506040813d604011612a66575b81612a4a604093836133a6565b8101031261002b57612a6060208251920161344c565b8361297a565b3d9150612a3d565b90506020813d602011612a98575b81612a89602093836133a6565b8101031261002b57515f612932565b3d9150612a7c565b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602073ffffffffffffffffffffffffffffffffffffffff600d5416604051908152f35b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57610eda7fffffffff00000000000000000000000000000000000000000000000000000000612b4e921633613e1c565b60055460ff811615612bab577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00166005557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa6020604051338152a1005b7f8dfc202b000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602060405160128152f35b3461002b5760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57612c436131ff565b612c4b613222565b6044359073ffffffffffffffffffffffffffffffffffffffff831692835f52600160205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f5260205260405f20547fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8103612cc7575b5061180d9350613f29565b838110612d6c578415612d40573315612d145761180d945f52600160205260405f2073ffffffffffffffffffffffffffffffffffffffff33165f526020528360405f209103905584612cbc565b7f94280d62000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b7fe602df05000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b83907ffb8f41b2000000000000000000000000000000000000000000000000000000005f523360045260245260445260645ffd5b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57602060405173ffffffffffffffffffffffffffffffffffffffff7f000000000000000000000000ddfd960a7150520548dd1f6e53cc2f201b364692168152f35b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b576020600254604051908152f35b3461002b5760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b57612e806131ff565b50612e89613222565b5060643567ffffffffffffffff811161002b57612eaa903690600401613245565b505060206040517f150b7a02000000000000000000000000000000000000000000000000000000008152f35b3461002b5760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b5773ffffffffffffffffffffffffffffffffffffffff90612f4a610eda7fffffffff00000000000000000000000000000000000000000000000000000000610ed26131ff565b168015612fa157807fffffffffffffffffffffffff0000000000000000000000000000000000000000600f541617600f557f606753945c222bd6eb6533c506303e56af1df290ae8e1557219f83770cfa35155f80a2005b7f17c1ab6a000000000000000000000000000000000000000000000000000000005f5260045ffd5b3461002b5760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b576130006131ff565b602435903315612d405773ffffffffffffffffffffffffffffffffffffffff16908115612d1457335f52600160205260405f20825f526020528060405f20556040519081527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560203392a3602060405160018152f35b3461002b575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261002b575f6003548060011c906001811680156131b2575b6020831081146131855782855290811561314357506001146130e5575b611a3783611a23818503826133a6565b91905060035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b915f905b80821061312957509091508101602001611a236130d5565b919260018160209254838588010152019101909291613111565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff001660208086019190915291151560051b84019091019150611a2390506130d5565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602260045260245ffd5b91607f16916130b8565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f602080948051918291828752018686015e5f8582860101520116010190565b6004359073ffffffffffffffffffffffffffffffffffffffff8216820361002b57565b6024359073ffffffffffffffffffffffffffffffffffffffff8216820361002b57565b9181601f8401121561002b5782359167ffffffffffffffff831161002b576020838186019501011161002b57565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60a091011261002b5760043573ffffffffffffffffffffffffffffffffffffffff8116810361002b579060243573ffffffffffffffffffffffffffffffffffffffff8116810361002b5790604435906064359060843573ffffffffffffffffffffffffffffffffffffffff8116810361002b5790565b6003111561331457565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b6040810190811067ffffffffffffffff82111761335d57604052565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b60c0810190811067ffffffffffffffff82111761335d57604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761335d57604052565b156133ee57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600c60248201527f554e415554484f52495a454400000000000000000000000000000000000000006044820152fd5b519073ffffffffffffffffffffffffffffffffffffffff8216820361002b57565b9081602091031261002b5751801515810361002b5790565b67ffffffffffffffff811161335d57601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01660200190565b7fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005c5f7fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005d7fffffffff000000000000000000000000000000000000000000000000000000008160301b167fffffffff000000000000000000000000000000000000000000000000000000005f351603613df45773ffffffffffffffffffffffffffffffffffffffff81163303613df4577f30fb041442610fd0a22e4654f60ea1c715088ef7320b5ec0c4e75cbdd99dbe005c5f7f30fb041442610fd0a22e4654f60ea1c715088ef7320b5ec0c4e75cbdd99dbe005d8015613df45761ffff808360a01c1614613dde575f9060a083901c61ffff166001810191906135e69035841a614295565b926135f460405194856133a6565b60a085901c61ffff16355f1a8085527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe09061362e90614295565b015f5b818110613dcd57505061364d61ffff8660a01c16355f1a6142c5565b9160405161365a8161338a565b5f81525f60208201525f60408201525f60608201525f60808201525f60a08201525f5b61ffff8860a01c16355f1a8110613791575050509061369c9190614a0e565b6060916001820191355f1a7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff810161371557505061371291506136de9061463f565b925b60a01c61ffff167fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0810135019061453f565b90565b600214613728575b5061371291926136e0565b80925051801561376957613712927fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff613762920190614377565b519161371d565b7f26779f3c000000000000000000000000000000000000000000000000000000005f5260045ffd5b6137a66014876001983560601c85520161463f565b9681019690355f1a805b613d0a57506001870196355f1a156138125790806137f55f8060019573ffffffffffffffffffffffffffffffffffffffff885116602082519201905afa6106b6614266565b6137ff828a614377565b5261380a8189614377565b505b0161367d565b818194999a9296939598978a7fffffffff00000000000000000000000000000000000000000000000000000000845160208601518281169160048110613cf5575b5050168060208a01527f095ea7b3000000000000000000000000000000000000000000000000000000008114908115613ccb575b50613c63575b61389691614660565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff1660408801526138c192906146cb565b73ffffffffffffffffffffffffffffffffffffffff16608088015260a08701529099906138f08b355f1a614295565b9a6040519b6138ff908d6133a6565b80355f1a808d5261390f90614295565b9060208d01917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe00136833780355f1a60051b80926001830190370186600182015f926002810191355f1a151593613a67926087927fffffffffffffffffffffffffffffffffffffffff00000000000000000000000096613c55575b50602090826060870152967fffffffffffffffffffffffffffffffffffffffffffffffffffff0000000000008651937fffffffff00000000000000000000000000000000000000000000000000000000848901511697896080820151604060a0840151930151936040519b8c99898b019e8f9160601b16905260348a0152151560f81b603889015260601b166039870152604d86015260301b16606d8401528051918291018484015e81015f8382015203017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826133a6565b519020935f945b8c51861015613ab057613a81868e614377565b519081811015613a9f575f52602052600160405f205b950194613a6e565b905f52602052600160405f20613a97565b9097939b508a95929a91989996945003613c2d578190613b035f8073ffffffffffffffffffffffffffffffffffffffff87511660608801519085519160208701915af1613afb614266565b9384916143a2565b79ffffffffffffffffffffffffffffffffffffffffffffffffffff604085015116613bde575b608084015160028116613b58575b505090600191613b47828a614377565b52613b528189614377565b5061380c565b5f91829160027fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d8273ffffffffffffffffffffffffffffffffffffffff60208451940192165af1613ba8614266565b9015610aeb5750906001915f7fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d90915f613b37565b7fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005c15613b29577f831579e8000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fdb87823d000000000000000000000000000000000000000000000000000000005f5260045ffd5b90359150602201602061398a565b875173ffffffffffffffffffffffffffffffffffffffff169a60248501516040519c613c8e8e613341565b8d5273ffffffffffffffffffffffffffffffffffffffff1660208d0152806001019b613cba8289614377565b52613cc59087614377565b5061388d565b7f39509351000000000000000000000000000000000000000000000000000000009150145f613887565b839250829060040360031b1b16165f80613853565b9660048135910197613d1f8260f81c8b614377565b51611fe08360eb1c169281516020850111613da55761ffff9060e01c1684516020820111613d7d57602460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9581940101918601015e01806137b0565b7f7ea37aee000000000000000000000000000000000000000000000000000000005f5260045ffd5b7fc87025ed000000000000000000000000000000000000000000000000000000005f5260045ffd5b806060602080938901015201613631565b5050604051613dee6020826133a6565b5f815290565b7fc6248746000000000000000000000000000000000000000000000000000000005f5260045ffd5b73ffffffffffffffffffffffffffffffffffffffff60065416918215159283613e71575b50508115613e4c575090565b905073ffffffffffffffffffffffffffffffffffffffff8060055460081c1691161490565b6040517fb700961300000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff841660048201523060248201527fffffffff0000000000000000000000000000000000000000000000000000000092909216604483015291925090602090829060649082905afa908115612298575f91613f0a575b50905f80613e40565b613f23915060203d6020116116265761161881836133a6565b5f613f01565b9173ffffffffffffffffffffffffffffffffffffffff5f93169182156122a35773ffffffffffffffffffffffffffffffffffffffff1692831561413e5773ffffffffffffffffffffffffffffffffffffffff600e5416806140bf575b506024602073ffffffffffffffffffffffffffffffffffffffff600f5416604051928380927f9428b68e0000000000000000000000000000000000000000000000000000000082528860048301525afa9081156116bd5782916140a0575b50614078578281528060205260408120548281106140455791604082827fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef958760209652828652038282205586815280845220818154019055604051908152a3565b6064937fe450d38c0000000000000000000000000000000000000000000000000000000083949352600452602452604452fd5b807f60134b780000000000000000000000000000000000000000000000000000000060049252fd5b6140b9915060203d6020116116265761161881836133a6565b5f613fe3565b73ffffffffffffffffffffffffffffffffffffffff600f5416813b1561002b575f906064604051809481937fabd626b00000000000000000000000000000000000000000000000000000000083528960048401528a602484015260448301525afa80156122985715613f855761413791505f906133a6565b5f5f613f85565b7fec442f05000000000000000000000000000000000000000000000000000000005f525f60045260245ffd5b6141cc9273ffffffffffffffffffffffffffffffffffffffff604051937fa9059cbb0000000000000000000000000000000000000000000000000000000060208601521660248401526044830152604482526141c76064836133a6565b61456e565b565b60ff600554166141da57565b7fd93c0665000000000000000000000000000000000000000000000000000000005f5260045ffd5b9160607fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f8373ffffffffffffffffffffffffffffffffffffffff94602096999899604089528160408a0152858901375f84828901015201168401019416910152565b3d15614290573d9061427782613485565b9161428560405193846133a6565b82523d5f602084013e565b606090565b67ffffffffffffffff811161335d5760051b60200190565b604051906142ba82613341565b5f6020838281520152565b906142cf82614295565b6142dc60405191826133a6565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe061430a8294614295565b01905f5b82811061431a57505050565b60209060405161432981613341565b5f81525f838201528282850101520161430e565b80511561434a5760200190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b805182101561434a5760209160051b010190565b6040906137129392815281602082015201906131bc565b156143ab575050565b61064a6040519283927f248ef89c0000000000000000000000000000000000000000000000000000000084526004840161438b565b90806143ea6142ad565b505b6143f4575050565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff016144208183614377565b5173ffffffffffffffffffffffffffffffffffffffff815116906020810190602073ffffffffffffffffffffffffffffffffffffffff8351166044604051809681937fdd62ed3e00000000000000000000000000000000000000000000000000000000835230600484015260248301525afa928315612298575f936144f8575b5073ffffffffffffffffffffffffffffffffffffffff80915116915116916144ca575050806143ec565b7f715879af000000000000000000000000000000000000000000000000000000005f5260045260245260445ffd5b9092506020813d8211614537575b81614513602093836133a6565b8101031261002b57519173ffffffffffffffffffffffffffffffffffffffff6144a0565b3d9150614506565b0361454657565b7f01842f8c000000000000000000000000000000000000000000000000000000005f5260045ffd5b905f602091828151910182855af115612298575f513d6145ec575073ffffffffffffffffffffffffffffffffffffffff81163b155b6145aa5750565b73ffffffffffffffffffffffffffffffffffffffff907f5274afe7000000000000000000000000000000000000000000000000000000005f521660045260245ffd5b600114156145a3565b61371290805f52600b6020525f60408120556009614c98565b805f52600b60205260405f205480155f1461463757505f52600a60205260405f20541515905f90565b600192909150565b906002823560f01c61465081614b35565b9381838201602087013701019190565b9190916001810181355f1a156146c35790601b913560301c93847fa48fd101fc9f41f09dc754b3b14722487070ffbd61259b49558564a3296a3f005d7f30fb041442610fd0a22e4654f60ea1c715088ef7320b5ec0c4e75cbdd99dbe005d019190565b92505f919050565b909291906001810181355f1a80156148c5576080607f82169116156148a9578061485457503560601c916060946001841661470d575b505060150192915f9190565b5f91929550819060017fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d60208151910182865af19061474b614266565b911561481f5750601f8151166147f7575f7fb8706f504833578f7e830b12e31c3cfba31669a85b02596177f00c6a7faf6e005d805181019060208181840193031261002b5760208101519067ffffffffffffffff821161002b57019080603f8301121561002b5760208201516147c081613485565b916147ce60405193846133a6565b8183526040848301011161002b576020815f926040601596018386015e8301015293905f614701565b7fbe72399a000000000000000000000000000000000000000000000000000000005f5260045ffd5b61064a6040519283927f883d36b40000000000000000000000000000000000000000000000000000000084526004840161438b565b93919492503592602185013560601c9260018416614881576035918561487992614b84565b940193929190565b7ffafeed10000000000000000000000000000000000000000000000000000000005f5260045ffd5b602192959493506148bc91358095614b84565b93019291905f90565b509293505050906040516148da6020826133a6565b5f8152905f905f90565b7fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c005c908160a01c8015614a07575f7fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c005d61493d816142c5565b927fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c019073ffffffffffffffffffffffffffffffffffffffff825c816040519361498585613341565b1683521660208201526149978561433d565b526149a18461433d565b5060015b8281106149b157505050565b60019060028284015c93019273ffffffffffffffffffffffffffffffffffffffff845c81604051936149e285613341565b1683521660208201526149f58288614377565b52614a008187614377565b50016149a5565b5060609150565b8115614b31577fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66c00905f825c60a081901c614a466142ad565b5080614ad25750505073ffffffffffffffffffffffffffffffffffffffff60206001614a718461433d565b5194838651168760a01b17815d0193015116825d60015b838110614a955750505050565b8073ffffffffffffffffffffffffffffffffffffffff60206002614abb60019587614377565b5196838851168682015d0195015116845d01614a88565b9085947fba2cfcc1b17a97110b1fb218b61c42c0e510c6913e669a69d4ade619ace66bff93966c01fffffffffffffffffffffffe930160a01b73ffffffffffffffffffffffffffffffffffffffff831617905d609f1c16019192614a88565b5050565b90614b3f82613485565b614b4c60405191826133a6565b8281527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0614b7a8294613485565b0190602036910137565b90916011811015614c5b57614b9b8160051b614b35565b92825160248110614c33577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0015f915f925b848410614bdd5750505050505090565b8160f01c836004820111614c0b5760019160246020928901015182828b010152019160101b93019290614bcd565b7f66a468ef000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f6809cb33000000000000000000000000000000000000000000000000000000005f5260045ffd5b7f9bbf46dc000000000000000000000000000000000000000000000000000000005f5260045ffd5b805482101561434a575f5260205f2001905f90565b906001820191815f528260205260405f20548015155f14614e4b577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8101818111614e1e578254907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8201918211614e1e57818103614db4575b50505080548015614d87577fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0190614d4a8282614c83565b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff82549160031b1b19169055555f526020525f6040812055600190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603160045260245ffd5b614e09614dc4614dd49386614c83565b90549060031b1c92839286614c83565b81939154907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff9060031b92831b921b19161790565b90555f528360205260405f20555f8080614d12565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b505050505f90565b805f52600a60205260405f2054155f14614ea8576009546801000000000000000081101561335d57614e91614dd48260018594016009556009614c83565b9055600954905f52600a60205260405f2055600190565b505f9056fea164736f6c634300081d000a

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.