ETH Price: $3,322.04 (-2.49%)
 

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

There are no matching entries

1 Internal Transaction and > 10 Token Transfers found.

Latest 1 internal transaction

Parent Transaction Hash Block From To
212367242024-10-18 14:46:35384 days ago1729262795  Contract Creation0 ETH

Cross-Chain Transactions
Loading...
Loading

Similar Match Source Code
This contract matches the deployed Bytecode of the Source Code for Contract 0xf5b70a97...7E0dd75B9
The constructor portion of the code might be different and could alter the actual behaviour of the contract

Contract Name:
TokenHandler

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion

Contract Source Code (Solidity Standard Json-Input format)

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {AccessManaged} from "@openzeppelin/contracts/access/manager/AccessManaged.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

import {Amount, AmountLib} from "../type/Amount.sol";
import {ContractLib} from "../shared/ContractLib.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {NftId} from "../type/NftId.sol";
import {SERVICE} from "../type/ObjectType.sol";


/// @dev Token specific transfer helper base contract.
/// A default token contract is provided via contract constructor.
/// Relies internally on OpenZeppelin SafeERC20.safeTransferFrom.
/// This base contract simplifies writing tests.
contract TokenHandlerBase {

    // _setWallet
    event LogTokenHandlerWalletAddressChanged(NftId componentNftId, address oldWallet, address newWallet);
    event LogTokenHandlerWalletTokensTransferred(NftId componentNftId, address oldWallet, address newWallet, Amount amount);

    // _approveTokenHandler
    event LogTokenHandlerTokenApproved(NftId nftId, address tokenHandler, address token, Amount amount, bool isMaxAmount);

    // _transfer
    event LogTokenHandlerTokenTransfer(address token, address from, address to, Amount amount);

    // constructor
    error ErrorTokenHandlerNotRegistry(address registry);
    error ErrorTokenHandlerComponentNotRegistered(address component);
    error ErrorTokenHandlerTokenAddressZero();

    // _setWallet
    error ErrorTokenHandlerNewWalletAddressZero();
    error ErrorTokenHandlerAddressIsSameAsCurrent();

    // _approveTokenHandler
    error ErrorTokenHandlerNotWallet(NftId nftId, address tokenHandler, address wallet);

    // _checkPreconditions
    error ErrorTokenHandlerBalanceTooLow(address token, address from, uint256 balance, uint256 expectedBalance);
    error ErrorTokenHandlerAllowanceTooSmall(address token, address from, address spender, uint256 allowance, uint256 expectedAllowance);
    error ErrorTokenHandlerAmountIsZero();

    IRegistry public immutable REGISTRY;
    IERC20Metadata public immutable TOKEN;
    address public immutable COMPONENT;
    NftId public immutable NFT_ID;

    address internal _wallet;

    constructor(
        address registry,
        address component,
        address token
    )
    {
        if (!ContractLib.isRegistry(registry)) {
            revert ErrorTokenHandlerNotRegistry(registry);
        }

        if (token == address(0)) {
            revert ErrorTokenHandlerTokenAddressZero();
        }

        REGISTRY = IRegistry(registry);
        COMPONENT = component;
        NFT_ID = REGISTRY.getNftIdForAddress(component);

        if (NFT_ID.eqz()) {
            revert ErrorTokenHandlerComponentNotRegistered(component);
        }

        TOKEN = IERC20Metadata(token);

        // self approval of token handler to max amount
        _approve(TOKEN, AmountLib.max());
    }


    /// @dev Checks the balance and allowance for the from address and amount.
    /// When requiring amount > 0 set checkAmount to true.
    function checkBalanceAndAllowance(
        address from,
        Amount amount,
        bool checkAmount
    )
        external
        view
    {
        _checkBalanceAndAllowance(from, amount, checkAmount);
    }


    /// @dev Returns the wallet linked to this TokenHandler.
    function getWallet()
        public
        view 
        returns (address wallet)
    {
        if (_wallet == address(0)) {
            return address(this);
        }

        return _wallet;
    }


    /// @dev Approves token handler to spend up to the specified amount of tokens.
    /// Sets spending limit to type(uint256).max for AmountLib.max().
    /// Reverts if wallet is not token handler itself.
    /// Sets approvel using SareERC20.forceApprove internally.
    function _approve(
        IERC20Metadata token, 
        Amount amount
    )
        internal
    {
        // check that wallet is token handler contract itself
        if(_wallet != address(0)) {
            revert ErrorTokenHandlerNotWallet(NFT_ID, address(this), _wallet);
        }

        // update spending limit for AmountLib.max() to type(uint256).max
        uint256 amountInt = amount.toInt();
        bool isMaxAmount = false;
        if (amount == AmountLib.max()) {
            amountInt = type(uint256).max;
            isMaxAmount = true;
        }

        emit LogTokenHandlerTokenApproved(NFT_ID, address(this), address(token), amount, isMaxAmount);

        // execute approval
        SafeERC20.forceApprove(
            token, 
            address(this), 
            amountInt);
    }


    function _setWallet(address newWallet)
        internal
    {
        address oldWallet = _wallet;
        if (newWallet == oldWallet) {
            revert ErrorTokenHandlerAddressIsSameAsCurrent();
        }

        // effects
        address oldWalletForBalance = getWallet();
        _wallet = newWallet;

        emit LogTokenHandlerWalletAddressChanged(NFT_ID, oldWallet, newWallet);

        // interactions
        Amount balanceAmount = AmountLib.toAmount(
            TOKEN.balanceOf(oldWalletForBalance));

        if (balanceAmount.gtz()) {
            // move tokens from old to new wallet 
            emit LogTokenHandlerWalletTokensTransferred(NFT_ID, oldWallet, newWallet, balanceAmount);

            if (oldWallet == address(0)) {
                // transfer from the component requires an allowance
                _transfer(address(this), newWallet, balanceAmount, true);
            } else if (newWallet == address(0)) {
                _transfer(oldWallet, address(this), balanceAmount, true);
            } else {
                _transfer(oldWallet, newWallet, balanceAmount, true);
            }
        }
    }


    function _pullToken(address from, Amount amount)
        internal
    {
        _transfer(from, getWallet(), amount, true);
    }


    function _pushToken(address to, Amount amount)
        internal
    {
        _transfer(getWallet(), to, amount, true);
    }


    function _transfer(
        address from,
        address to,
        Amount amount,
        bool checkPreconditions
    )
        internal
    {
        if (checkPreconditions) {
            bool checkAmount = true;
            _checkBalanceAndAllowance(from, amount, checkAmount);
        }

        // transfer the tokens
        emit LogTokenHandlerTokenTransfer(address(TOKEN), from, to, amount);

        SafeERC20.safeTransferFrom(
            TOKEN, 
            from, 
            to, 
            amount.toInt());
    }


    function _checkBalanceAndAllowance(
        address from,
        Amount amount,
        bool checkAmount
    ) 
        internal
        view
    {
        // amount must be greater than zero
        if (checkAmount && amount.eqz()) {
            revert ErrorTokenHandlerAmountIsZero();
        }

        // allowance must be >= amount
        uint256 allowance = TOKEN.allowance(from, address(this));
        if (allowance < amount.toInt()) {
            revert ErrorTokenHandlerAllowanceTooSmall(address(TOKEN), from, address(this), allowance, amount.toInt());
        }

        // balance must be >= amount
        uint256 balance = TOKEN.balanceOf(from);
        if (balance < amount.toInt()) {
            revert ErrorTokenHandlerBalanceTooLow(address(TOKEN), from, balance, amount.toInt());
        }
    }
}


/// @dev Token specific transfer helper.
/// Contract is derived from TokenHandlerBase and adds 
/// authorization based on OpenZeppelin AccessManaged.
contract TokenHandler is
    AccessManaged,
    TokenHandlerBase
{

    // onlyService
    error ErrorTokenHandlerNotService(address service);

    modifier onlyService() {
        if (!REGISTRY.isObjectType(msg.sender, SERVICE())) {
            revert ErrorTokenHandlerNotService(msg.sender);
        }
        _;
    }

    constructor(
        address registry,
        address component,
        address token, 
        address authority
    )
        TokenHandlerBase(registry, component, token)
        AccessManaged(authority)
    { }

    /// @dev Sets the wallet address for the component.
    /// Seeting the new wallet address to address(0) will set the wallet to the tokenHandler contract itself.
    /// If the current wallet has tokens, these will be transferred.
    /// If the new wallet address is externally owned, an approval from the 
    /// owner of the external wallet to the tokenhandler of the component that 
    /// covers the current component balance must exist.
    function setWallet(address newWallet)
        external
        restricted()
        onlyService()
    {
        _setWallet(newWallet);
    }


    /// @dev Approves token handler to spend up to the specified amount of tokens.
    /// Sets spending limit to type(uint256).max for AmountLib.max().
    /// Reverts if component wallet is not component itself.
    /// Sets approvel using SareERC20.forceApprove internally.
    function approve(
        IERC20Metadata token, 
        Amount amount
    )
        external
        restricted()
        onlyService()
    {
        _approve(token, amount);
    }

    /// @dev Collect tokens from outside of GIF and transfer them to the wallet.
    /// This method also checks balance and allowance and makes sure the amount is greater than zero.
    function pullToken(
        address from,
        Amount amount
    )
        external
        restricted()
        onlyService()
    {
        _pullToken(from, amount);
    }


    /// @dev Distribute tokens from a wallet within the scope of gif to some address.
    function pushToken(
        address to,
        Amount amount
    )
        external
        restricted()
        onlyService()
    {
        _pushToken(to, amount);
    }


    /// @dev Distribute fee tokens from a wallet within the scope of gif to some address.
    /// Separate push function for component service.
    function pushFeeToken(
        address to,
        Amount amount
    )
        external
        restricted()
        onlyService()
    {
        _pushToken(to, amount);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/AccessManaged.sol)

pragma solidity ^0.8.20;

import {IAuthority} from "./IAuthority.sol";
import {AuthorityUtils} from "./AuthorityUtils.sol";
import {IAccessManager} from "./IAccessManager.sol";
import {IAccessManaged} from "./IAccessManaged.sol";
import {Context} from "../../utils/Context.sol";

/**
 * @dev This contract module makes available a {restricted} modifier. Functions decorated with this modifier will be
 * permissioned according to an "authority": a contract like {AccessManager} that follows the {IAuthority} interface,
 * implementing a policy that allows certain callers to access certain functions.
 *
 * IMPORTANT: The `restricted` modifier should never be used on `internal` functions, judiciously used in `public`
 * functions, and ideally only used in `external` functions. See {restricted}.
 */
abstract contract AccessManaged is Context, IAccessManaged {
    address private _authority;

    bool private _consumingSchedule;

    /**
     * @dev Initializes the contract connected to an initial authority.
     */
    constructor(address initialAuthority) {
        _setAuthority(initialAuthority);
    }

    /**
     * @dev Restricts access to a function as defined by the connected Authority for this contract and the
     * caller and selector of the function that entered the contract.
     *
     * [IMPORTANT]
     * ====
     * In general, this modifier should only be used on `external` functions. It is okay to use it on `public`
     * functions that are used as external entry points and are not called internally. Unless you know what you're
     * doing, it should never be used on `internal` functions. Failure to follow these rules can have critical security
     * implications! This is because the permissions are determined by the function that entered the contract, i.e. the
     * function at the bottom of the call stack, and not the function where the modifier is visible in the source code.
     * ====
     *
     * [WARNING]
     * ====
     * Avoid adding this modifier to the https://docs.soliditylang.org/en/v0.8.20/contracts.html#receive-ether-function[`receive()`]
     * function or the https://docs.soliditylang.org/en/v0.8.20/contracts.html#fallback-function[`fallback()`]. These
     * functions are the only execution paths where a function selector cannot be unambiguosly determined from the calldata
     * since the selector defaults to `0x00000000` in the `receive()` function and similarly in the `fallback()` function
     * if no calldata is provided. (See {_checkCanCall}).
     *
     * The `receive()` function will always panic whereas the `fallback()` may panic depending on the calldata length.
     * ====
     */
    modifier restricted() {
        _checkCanCall(_msgSender(), _msgData());
        _;
    }

    /// @inheritdoc IAccessManaged
    function authority() public view virtual returns (address) {
        return _authority;
    }

    /// @inheritdoc IAccessManaged
    function setAuthority(address newAuthority) public virtual {
        address caller = _msgSender();
        if (caller != authority()) {
            revert AccessManagedUnauthorized(caller);
        }
        if (newAuthority.code.length == 0) {
            revert AccessManagedInvalidAuthority(newAuthority);
        }
        _setAuthority(newAuthority);
    }

    /// @inheritdoc IAccessManaged
    function isConsumingScheduledOp() public view returns (bytes4) {
        return _consumingSchedule ? this.isConsumingScheduledOp.selector : bytes4(0);
    }

    /**
     * @dev Transfers control to a new authority. Internal function with no access restriction. Allows bypassing the
     * permissions set by the current authority.
     */
    function _setAuthority(address newAuthority) internal virtual {
        _authority = newAuthority;
        emit AuthorityUpdated(newAuthority);
    }

    /**
     * @dev Reverts if the caller is not allowed to call the function identified by a selector. Panics if the calldata
     * is less than 4 bytes long.
     */
    function _checkCanCall(address caller, bytes calldata data) internal virtual {
        (bool immediate, uint32 delay) = AuthorityUtils.canCallWithDelay(
            authority(),
            caller,
            address(this),
            bytes4(data[0:4])
        );
        if (!immediate) {
            if (delay > 0) {
                _consumingSchedule = true;
                IAccessManager(authority()).consumeScheduledOp(caller, data);
                _consumingSchedule = false;
            } else {
                revert AccessManagedUnauthorized(caller);
            }
        }
    }
}

File 3 of 47 : AuthorityUtils.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/AuthorityUtils.sol)

pragma solidity ^0.8.20;

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

library AuthorityUtils {
    /**
     * @dev Since `AccessManager` implements an extended IAuthority interface, invoking `canCall` with backwards compatibility
     * for the preexisting `IAuthority` interface requires special care to avoid reverting on insufficient return data.
     * This helper function takes care of invoking `canCall` in a backwards compatible way without reverting.
     */
    function canCallWithDelay(
        address authority,
        address caller,
        address target,
        bytes4 selector
    ) internal view returns (bool immediate, uint32 delay) {
        (bool success, bytes memory data) = authority.staticcall(
            abi.encodeCall(IAuthority.canCall, (caller, target, selector))
        );
        if (success) {
            if (data.length >= 0x40) {
                (immediate, delay) = abi.decode(data, (bool, uint32));
            } else if (data.length >= 0x20) {
                immediate = abi.decode(data, (bool));
            }
        }
        return (immediate, delay);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/IAccessManaged.sol)

pragma solidity ^0.8.20;

interface IAccessManaged {
    /**
     * @dev Authority that manages this contract was updated.
     */
    event AuthorityUpdated(address authority);

    error AccessManagedUnauthorized(address caller);
    error AccessManagedRequiredDelay(address caller, uint32 delay);
    error AccessManagedInvalidAuthority(address authority);

    /**
     * @dev Returns the current authority.
     */
    function authority() external view returns (address);

    /**
     * @dev Transfers control to a new authority. The caller must be the current authority.
     */
    function setAuthority(address) external;

    /**
     * @dev Returns true only in the context of a delayed restricted call, at the moment that the scheduled operation is
     * being consumed. Prevents denial of service for delayed restricted calls in the case that the contract performs
     * attacker controlled calls.
     */
    function isConsumingScheduledOp() external view returns (bytes4);
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/IAccessManager.sol)

pragma solidity ^0.8.20;

import {IAccessManaged} from "./IAccessManaged.sol";
import {Time} from "../../utils/types/Time.sol";

interface IAccessManager {
    /**
     * @dev A delayed operation was scheduled.
     */
    event OperationScheduled(
        bytes32 indexed operationId,
        uint32 indexed nonce,
        uint48 schedule,
        address caller,
        address target,
        bytes data
    );

    /**
     * @dev A scheduled operation was executed.
     */
    event OperationExecuted(bytes32 indexed operationId, uint32 indexed nonce);

    /**
     * @dev A scheduled operation was canceled.
     */
    event OperationCanceled(bytes32 indexed operationId, uint32 indexed nonce);

    /**
     * @dev Informational labelling for a roleId.
     */
    event RoleLabel(uint64 indexed roleId, string label);

    /**
     * @dev Emitted when `account` is granted `roleId`.
     *
     * NOTE: The meaning of the `since` argument depends on the `newMember` argument.
     * If the role is granted to a new member, the `since` argument indicates when the account becomes a member of the role,
     * otherwise it indicates the execution delay for this account and roleId is updated.
     */
    event RoleGranted(uint64 indexed roleId, address indexed account, uint32 delay, uint48 since, bool newMember);

    /**
     * @dev Emitted when `account` membership or `roleId` is revoked. Unlike granting, revoking is instantaneous.
     */
    event RoleRevoked(uint64 indexed roleId, address indexed account);

    /**
     * @dev Role acting as admin over a given `roleId` is updated.
     */
    event RoleAdminChanged(uint64 indexed roleId, uint64 indexed admin);

    /**
     * @dev Role acting as guardian over a given `roleId` is updated.
     */
    event RoleGuardianChanged(uint64 indexed roleId, uint64 indexed guardian);

    /**
     * @dev Grant delay for a given `roleId` will be updated to `delay` when `since` is reached.
     */
    event RoleGrantDelayChanged(uint64 indexed roleId, uint32 delay, uint48 since);

    /**
     * @dev Target mode is updated (true = closed, false = open).
     */
    event TargetClosed(address indexed target, bool closed);

    /**
     * @dev Role required to invoke `selector` on `target` is updated to `roleId`.
     */
    event TargetFunctionRoleUpdated(address indexed target, bytes4 selector, uint64 indexed roleId);

    /**
     * @dev Admin delay for a given `target` will be updated to `delay` when `since` is reached.
     */
    event TargetAdminDelayUpdated(address indexed target, uint32 delay, uint48 since);

    error AccessManagerAlreadyScheduled(bytes32 operationId);
    error AccessManagerNotScheduled(bytes32 operationId);
    error AccessManagerNotReady(bytes32 operationId);
    error AccessManagerExpired(bytes32 operationId);
    error AccessManagerLockedAccount(address account);
    error AccessManagerLockedRole(uint64 roleId);
    error AccessManagerBadConfirmation();
    error AccessManagerUnauthorizedAccount(address msgsender, uint64 roleId);
    error AccessManagerUnauthorizedCall(address caller, address target, bytes4 selector);
    error AccessManagerUnauthorizedConsume(address target);
    error AccessManagerUnauthorizedCancel(address msgsender, address caller, address target, bytes4 selector);
    error AccessManagerInvalidInitialAdmin(address initialAdmin);

    /**
     * @dev Check if an address (`caller`) is authorised to call a given function on a given contract directly (with
     * no restriction). Additionally, it returns the delay needed to perform the call indirectly through the {schedule}
     * & {execute} workflow.
     *
     * This function is usually called by the targeted contract to control immediate execution of restricted functions.
     * Therefore we only return true if the call can be performed without any delay. If the call is subject to a
     * previously set delay (not zero), then the function should return false and the caller should schedule the operation
     * for future execution.
     *
     * If `immediate` is true, the delay can be disregarded and the operation can be immediately executed, otherwise
     * the operation can be executed if and only if delay is greater than 0.
     *
     * NOTE: The IAuthority interface does not include the `uint32` delay. This is an extension of that interface that
     * is backward compatible. Some contracts may thus ignore the second return argument. In that case they will fail
     * to identify the indirect workflow, and will consider calls that require a delay to be forbidden.
     *
     * NOTE: This function does not report the permissions of this manager itself. These are defined by the
     * {_canCallSelf} function instead.
     */
    function canCall(
        address caller,
        address target,
        bytes4 selector
    ) external view returns (bool allowed, uint32 delay);

    /**
     * @dev Expiration delay for scheduled proposals. Defaults to 1 week.
     *
     * IMPORTANT: Avoid overriding the expiration with 0. Otherwise every contract proposal will be expired immediately,
     * disabling any scheduling usage.
     */
    function expiration() external view returns (uint32);

    /**
     * @dev Minimum setback for all delay updates, with the exception of execution delays. It
     * can be increased without setback (and reset via {revokeRole} in the case event of an
     * accidental increase). Defaults to 5 days.
     */
    function minSetback() external view returns (uint32);

    /**
     * @dev Get whether the contract is closed disabling any access. Otherwise role permissions are applied.
     */
    function isTargetClosed(address target) external view returns (bool);

    /**
     * @dev Get the role required to call a function.
     */
    function getTargetFunctionRole(address target, bytes4 selector) external view returns (uint64);

    /**
     * @dev Get the admin delay for a target contract. Changes to contract configuration are subject to this delay.
     */
    function getTargetAdminDelay(address target) external view returns (uint32);

    /**
     * @dev Get the id of the role that acts as an admin for the given role.
     *
     * The admin permission is required to grant the role, revoke the role and update the execution delay to execute
     * an operation that is restricted to this role.
     */
    function getRoleAdmin(uint64 roleId) external view returns (uint64);

    /**
     * @dev Get the role that acts as a guardian for a given role.
     *
     * The guardian permission allows canceling operations that have been scheduled under the role.
     */
    function getRoleGuardian(uint64 roleId) external view returns (uint64);

    /**
     * @dev Get the role current grant delay.
     *
     * Its value may change at any point without an event emitted following a call to {setGrantDelay}.
     * Changes to this value, including effect timepoint are notified in advance by the {RoleGrantDelayChanged} event.
     */
    function getRoleGrantDelay(uint64 roleId) external view returns (uint32);

    /**
     * @dev Get the access details for a given account for a given role. These details include the timepoint at which
     * membership becomes active, and the delay applied to all operation by this user that requires this permission
     * level.
     *
     * Returns:
     * [0] Timestamp at which the account membership becomes valid. 0 means role is not granted.
     * [1] Current execution delay for the account.
     * [2] Pending execution delay for the account.
     * [3] Timestamp at which the pending execution delay will become active. 0 means no delay update is scheduled.
     */
    function getAccess(uint64 roleId, address account) external view returns (uint48, uint32, uint32, uint48);

    /**
     * @dev Check if a given account currently has the permission level corresponding to a given role. Note that this
     * permission might be associated with an execution delay. {getAccess} can provide more details.
     */
    function hasRole(uint64 roleId, address account) external view returns (bool, uint32);

    /**
     * @dev Give a label to a role, for improved role discoverability by UIs.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {RoleLabel} event.
     */
    function labelRole(uint64 roleId, string calldata label) external;

    /**
     * @dev Add `account` to `roleId`, or change its execution delay.
     *
     * This gives the account the authorization to call any function that is restricted to this role. An optional
     * execution delay (in seconds) can be set. If that delay is non 0, the user is required to schedule any operation
     * that is restricted to members of this role. The user will only be able to execute the operation after the delay has
     * passed, before it has expired. During this period, admin and guardians can cancel the operation (see {cancel}).
     *
     * If the account has already been granted this role, the execution delay will be updated. This update is not
     * immediate and follows the delay rules. For example, if a user currently has a delay of 3 hours, and this is
     * called to reduce that delay to 1 hour, the new delay will take some time to take effect, enforcing that any
     * operation executed in the 3 hours that follows this update was indeed scheduled before this update.
     *
     * Requirements:
     *
     * - the caller must be an admin for the role (see {getRoleAdmin})
     * - granted role must not be the `PUBLIC_ROLE`
     *
     * Emits a {RoleGranted} event.
     */
    function grantRole(uint64 roleId, address account, uint32 executionDelay) external;

    /**
     * @dev Remove an account from a role, with immediate effect. If the account does not have the role, this call has
     * no effect.
     *
     * Requirements:
     *
     * - the caller must be an admin for the role (see {getRoleAdmin})
     * - revoked role must not be the `PUBLIC_ROLE`
     *
     * Emits a {RoleRevoked} event if the account had the role.
     */
    function revokeRole(uint64 roleId, address account) external;

    /**
     * @dev Renounce role permissions for the calling account with immediate effect. If the sender is not in
     * the role this call has no effect.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * Emits a {RoleRevoked} event if the account had the role.
     */
    function renounceRole(uint64 roleId, address callerConfirmation) external;

    /**
     * @dev Change admin role for a given role.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {RoleAdminChanged} event
     */
    function setRoleAdmin(uint64 roleId, uint64 admin) external;

    /**
     * @dev Change guardian role for a given role.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {RoleGuardianChanged} event
     */
    function setRoleGuardian(uint64 roleId, uint64 guardian) external;

    /**
     * @dev Update the delay for granting a `roleId`.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {RoleGrantDelayChanged} event.
     */
    function setGrantDelay(uint64 roleId, uint32 newDelay) external;

    /**
     * @dev Set the role required to call functions identified by the `selectors` in the `target` contract.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {TargetFunctionRoleUpdated} event per selector.
     */
    function setTargetFunctionRole(address target, bytes4[] calldata selectors, uint64 roleId) external;

    /**
     * @dev Set the delay for changing the configuration of a given target contract.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {TargetAdminDelayUpdated} event.
     */
    function setTargetAdminDelay(address target, uint32 newDelay) external;

    /**
     * @dev Set the closed flag for a contract.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {TargetClosed} event.
     */
    function setTargetClosed(address target, bool closed) external;

    /**
     * @dev Return the timepoint at which a scheduled operation will be ready for execution. This returns 0 if the
     * operation is not yet scheduled, has expired, was executed, or was canceled.
     */
    function getSchedule(bytes32 id) external view returns (uint48);

    /**
     * @dev Return the nonce for the latest scheduled operation with a given id. Returns 0 if the operation has never
     * been scheduled.
     */
    function getNonce(bytes32 id) external view returns (uint32);

    /**
     * @dev Schedule a delayed operation for future execution, and return the operation identifier. It is possible to
     * choose the timestamp at which the operation becomes executable as long as it satisfies the execution delays
     * required for the caller. The special value zero will automatically set the earliest possible time.
     *
     * Returns the `operationId` that was scheduled. Since this value is a hash of the parameters, it can reoccur when
     * the same parameters are used; if this is relevant, the returned `nonce` can be used to uniquely identify this
     * scheduled operation from other occurrences of the same `operationId` in invocations of {execute} and {cancel}.
     *
     * Emits a {OperationScheduled} event.
     *
     * NOTE: It is not possible to concurrently schedule more than one operation with the same `target` and `data`. If
     * this is necessary, a random byte can be appended to `data` to act as a salt that will be ignored by the target
     * contract if it is using standard Solidity ABI encoding.
     */
    function schedule(address target, bytes calldata data, uint48 when) external returns (bytes32, uint32);

    /**
     * @dev Execute a function that is delay restricted, provided it was properly scheduled beforehand, or the
     * execution delay is 0.
     *
     * Returns the nonce that identifies the previously scheduled operation that is executed, or 0 if the
     * operation wasn't previously scheduled (if the caller doesn't have an execution delay).
     *
     * Emits an {OperationExecuted} event only if the call was scheduled and delayed.
     */
    function execute(address target, bytes calldata data) external payable returns (uint32);

    /**
     * @dev Cancel a scheduled (delayed) operation. Returns the nonce that identifies the previously scheduled
     * operation that is cancelled.
     *
     * Requirements:
     *
     * - the caller must be the proposer, a guardian of the targeted function, or a global admin
     *
     * Emits a {OperationCanceled} event.
     */
    function cancel(address caller, address target, bytes calldata data) external returns (uint32);

    /**
     * @dev Consume a scheduled operation targeting the caller. If such an operation exists, mark it as consumed
     * (emit an {OperationExecuted} event and clean the state). Otherwise, throw an error.
     *
     * This is useful for contract that want to enforce that calls targeting them were scheduled on the manager,
     * with all the verifications that it implies.
     *
     * Emit a {OperationExecuted} event.
     */
    function consumeScheduledOp(address caller, bytes calldata data) external;

    /**
     * @dev Hashing function for delayed operations.
     */
    function hashOperation(address caller, address target, bytes calldata data) external view returns (bytes32);

    /**
     * @dev Changes the authority of a target managed by this manager instance.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     */
    function updateAuthority(address target, address newAuthority) external;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/IAuthority.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard interface for permissioning originally defined in Dappsys.
 */
interface IAuthority {
    /**
     * @dev Returns true if the caller can invoke on a target the function identified by a function selector.
     */
    function canCall(address caller, address target, bytes4 selector) external view returns (bool allowed);
}

// 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 ERC20 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.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view 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 ERC20 standard as defined in the EIP.
 */
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/utils/SafeERC20.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 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 {
    using Address for address;

    /**
     * @dev An operation with an ERC20 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.
     */
    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.
     */
    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.
     */
    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 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).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            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 silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
    }
}

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

pragma solidity ^0.8.20;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 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
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

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

    /**
     * @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 AddressInsufficientBalance(address(this));
        }

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

    /**
     * @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
     * {FailedInnerCall} 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 AddressInsufficientBalance(address(this));
        }
        (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 {FailedInnerCall}) 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 {FailedInnerCall} 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 {FailedInnerCall}.
     */
    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
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert FailedInnerCall();
        }
    }
}

// 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) (utils/introspection/ERC165Checker.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Library used to query support of an interface declared via {IERC165}.
 *
 * Note that these functions return the actual result of the query: they do not
 * `revert` if an interface is not supported. It is up to the caller to decide
 * what to do in these cases.
 */
library ERC165Checker {
    // As per the EIP-165 spec, no interface should ever match 0xffffffff
    bytes4 private constant INTERFACE_ID_INVALID = 0xffffffff;

    /**
     * @dev Returns true if `account` supports the {IERC165} interface.
     */
    function supportsERC165(address account) internal view returns (bool) {
        // Any contract that implements ERC165 must explicitly indicate support of
        // InterfaceId_ERC165 and explicitly indicate non-support of InterfaceId_Invalid
        return
            supportsERC165InterfaceUnchecked(account, type(IERC165).interfaceId) &&
            !supportsERC165InterfaceUnchecked(account, INTERFACE_ID_INVALID);
    }

    /**
     * @dev Returns true if `account` supports the interface defined by
     * `interfaceId`. Support for {IERC165} itself is queried automatically.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsInterface(address account, bytes4 interfaceId) internal view returns (bool) {
        // query support of both ERC165 as per the spec and support of _interfaceId
        return supportsERC165(account) && supportsERC165InterfaceUnchecked(account, interfaceId);
    }

    /**
     * @dev Returns a boolean array where each value corresponds to the
     * interfaces passed in and whether they're supported or not. This allows
     * you to batch check interfaces for a contract where your expectation
     * is that some interfaces may not be supported.
     *
     * See {IERC165-supportsInterface}.
     */
    function getSupportedInterfaces(
        address account,
        bytes4[] memory interfaceIds
    ) internal view returns (bool[] memory) {
        // an array of booleans corresponding to interfaceIds and whether they're supported or not
        bool[] memory interfaceIdsSupported = new bool[](interfaceIds.length);

        // query support of ERC165 itself
        if (supportsERC165(account)) {
            // query support of each interface in interfaceIds
            for (uint256 i = 0; i < interfaceIds.length; i++) {
                interfaceIdsSupported[i] = supportsERC165InterfaceUnchecked(account, interfaceIds[i]);
            }
        }

        return interfaceIdsSupported;
    }

    /**
     * @dev Returns true if `account` supports all the interfaces defined in
     * `interfaceIds`. Support for {IERC165} itself is queried automatically.
     *
     * Batch-querying can lead to gas savings by skipping repeated checks for
     * {IERC165} support.
     *
     * See {IERC165-supportsInterface}.
     */
    function supportsAllInterfaces(address account, bytes4[] memory interfaceIds) internal view returns (bool) {
        // query support of ERC165 itself
        if (!supportsERC165(account)) {
            return false;
        }

        // query support of each interface in interfaceIds
        for (uint256 i = 0; i < interfaceIds.length; i++) {
            if (!supportsERC165InterfaceUnchecked(account, interfaceIds[i])) {
                return false;
            }
        }

        // all interfaces supported
        return true;
    }

    /**
     * @notice Query if a contract implements an interface, does not check ERC165 support
     * @param account The address of the contract to query for support of an interface
     * @param interfaceId The interface identifier, as specified in ERC-165
     * @return true if the contract at account indicates support of the interface with
     * identifier interfaceId, false otherwise
     * @dev Assumes that account contains a contract that supports ERC165, otherwise
     * the behavior of this method is undefined. This precondition can be checked
     * with {supportsERC165}.
     *
     * Some precompiled contracts will falsely indicate support for a given interface, so caution
     * should be exercised when using this function.
     *
     * Interface identification is specified in ERC-165.
     */
    function supportsERC165InterfaceUnchecked(address account, bytes4 interfaceId) internal view returns (bool) {
        // prepare call
        bytes memory encodedParams = abi.encodeCall(IERC165.supportsInterface, (interfaceId));

        // perform static call
        bool success;
        uint256 returnSize;
        uint256 returnValue;
        assembly {
            success := staticcall(30000, account, add(encodedParams, 0x20), mload(encodedParams), 0x00, 0x20)
            returnSize := returndatasize()
            returnValue := mload(0x00)
        }

        return success && returnSize >= 0x20 && returnValue > 0;
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface 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[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

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

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
     * denominator == 0.
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
     * Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0 = x * y; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            if (denominator <= prod1) {
                revert MathOverflowedMulDiv();
            }

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator.
            // Always >= 1. See https://cs.stackexchange.com/q/138556/92363.

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
            // works in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
     * towards zero.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256 of a positive value rounded towards zero.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
        }
    }

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

File 17 of 47 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.20;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 */
library SafeCast {
    /**
     * @dev Value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);

    /**
     * @dev An int value doesn't fit in an uint of `bits` size.
     */
    error SafeCastOverflowedIntToUint(int256 value);

    /**
     * @dev Value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);

    /**
     * @dev An uint value doesn't fit in an int of `bits` size.
     */
    error SafeCastOverflowedUintToInt(uint256 value);

    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        if (value > type(uint248).max) {
            revert SafeCastOverflowedUintDowncast(248, value);
        }
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        if (value > type(uint240).max) {
            revert SafeCastOverflowedUintDowncast(240, value);
        }
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        if (value > type(uint232).max) {
            revert SafeCastOverflowedUintDowncast(232, value);
        }
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        if (value > type(uint224).max) {
            revert SafeCastOverflowedUintDowncast(224, value);
        }
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        if (value > type(uint216).max) {
            revert SafeCastOverflowedUintDowncast(216, value);
        }
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        if (value > type(uint208).max) {
            revert SafeCastOverflowedUintDowncast(208, value);
        }
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        if (value > type(uint200).max) {
            revert SafeCastOverflowedUintDowncast(200, value);
        }
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        if (value > type(uint192).max) {
            revert SafeCastOverflowedUintDowncast(192, value);
        }
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        if (value > type(uint184).max) {
            revert SafeCastOverflowedUintDowncast(184, value);
        }
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        if (value > type(uint176).max) {
            revert SafeCastOverflowedUintDowncast(176, value);
        }
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        if (value > type(uint168).max) {
            revert SafeCastOverflowedUintDowncast(168, value);
        }
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        if (value > type(uint160).max) {
            revert SafeCastOverflowedUintDowncast(160, value);
        }
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        if (value > type(uint152).max) {
            revert SafeCastOverflowedUintDowncast(152, value);
        }
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        if (value > type(uint144).max) {
            revert SafeCastOverflowedUintDowncast(144, value);
        }
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        if (value > type(uint136).max) {
            revert SafeCastOverflowedUintDowncast(136, value);
        }
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        if (value > type(uint128).max) {
            revert SafeCastOverflowedUintDowncast(128, value);
        }
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        if (value > type(uint120).max) {
            revert SafeCastOverflowedUintDowncast(120, value);
        }
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        if (value > type(uint112).max) {
            revert SafeCastOverflowedUintDowncast(112, value);
        }
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        if (value > type(uint104).max) {
            revert SafeCastOverflowedUintDowncast(104, value);
        }
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        if (value > type(uint96).max) {
            revert SafeCastOverflowedUintDowncast(96, value);
        }
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        if (value > type(uint88).max) {
            revert SafeCastOverflowedUintDowncast(88, value);
        }
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        if (value > type(uint80).max) {
            revert SafeCastOverflowedUintDowncast(80, value);
        }
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        if (value > type(uint72).max) {
            revert SafeCastOverflowedUintDowncast(72, value);
        }
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        if (value > type(uint64).max) {
            revert SafeCastOverflowedUintDowncast(64, value);
        }
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        if (value > type(uint56).max) {
            revert SafeCastOverflowedUintDowncast(56, value);
        }
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        if (value > type(uint48).max) {
            revert SafeCastOverflowedUintDowncast(48, value);
        }
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        if (value > type(uint40).max) {
            revert SafeCastOverflowedUintDowncast(40, value);
        }
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        if (value > type(uint32).max) {
            revert SafeCastOverflowedUintDowncast(32, value);
        }
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        if (value > type(uint24).max) {
            revert SafeCastOverflowedUintDowncast(24, value);
        }
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        if (value > type(uint16).max) {
            revert SafeCastOverflowedUintDowncast(16, value);
        }
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        if (value > type(uint8).max) {
            revert SafeCastOverflowedUintDowncast(8, value);
        }
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        if (value < 0) {
            revert SafeCastOverflowedIntToUint(value);
        }
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(248, value);
        }
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(240, value);
        }
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(232, value);
        }
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(224, value);
        }
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(216, value);
        }
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(208, value);
        }
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(200, value);
        }
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(192, value);
        }
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(184, value);
        }
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(176, value);
        }
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(168, value);
        }
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(160, value);
        }
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(152, value);
        }
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(144, value);
        }
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(136, value);
        }
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(128, value);
        }
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(120, value);
        }
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(112, value);
        }
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(104, value);
        }
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(96, value);
        }
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(88, value);
        }
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(80, value);
        }
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(72, value);
        }
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(64, value);
        }
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(56, value);
        }
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(48, value);
        }
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(40, value);
        }
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(32, value);
        }
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(24, value);
        }
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(16, value);
        }
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        if (downcasted != value) {
            revert SafeCastOverflowedIntDowncast(8, value);
        }
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        if (value > uint256(type(int256).max)) {
            revert SafeCastOverflowedUintToInt(value);
        }
        return int256(value);
    }
}

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

pragma solidity ^0.8.20;

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

// | string  | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   |
// | length  | 0x                                                              BB |
type ShortString is bytes32;

/**
 * @dev This library provides functions to convert short memory strings
 * into a `ShortString` type that can be used as an immutable variable.
 *
 * Strings of arbitrary length can be optimized using this library if
 * they are short enough (up to 31 bytes) by packing them with their
 * length (1 byte) in a single EVM word (32 bytes). Additionally, a
 * fallback mechanism can be used for every other case.
 *
 * Usage example:
 *
 * ```solidity
 * contract Named {
 *     using ShortStrings for *;
 *
 *     ShortString private immutable _name;
 *     string private _nameFallback;
 *
 *     constructor(string memory contractName) {
 *         _name = contractName.toShortStringWithFallback(_nameFallback);
 *     }
 *
 *     function name() external view returns (string memory) {
 *         return _name.toStringWithFallback(_nameFallback);
 *     }
 * }
 * ```
 */
library ShortStrings {
    // Used as an identifier for strings longer than 31 bytes.
    bytes32 private constant FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;

    error StringTooLong(string str);
    error InvalidShortString();

    /**
     * @dev Encode a string of at most 31 chars into a `ShortString`.
     *
     * This will trigger a `StringTooLong` error is the input string is too long.
     */
    function toShortString(string memory str) internal pure returns (ShortString) {
        bytes memory bstr = bytes(str);
        if (bstr.length > 31) {
            revert StringTooLong(str);
        }
        return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
    }

    /**
     * @dev Decode a `ShortString` back to a "normal" string.
     */
    function toString(ShortString sstr) internal pure returns (string memory) {
        uint256 len = byteLength(sstr);
        // using `new string(len)` would work locally but is not memory safe.
        string memory str = new string(32);
        /// @solidity memory-safe-assembly
        assembly {
            mstore(str, len)
            mstore(add(str, 0x20), sstr)
        }
        return str;
    }

    /**
     * @dev Return the length of a `ShortString`.
     */
    function byteLength(ShortString sstr) internal pure returns (uint256) {
        uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
        if (result > 31) {
            revert InvalidShortString();
        }
        return result;
    }

    /**
     * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
     */
    function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
        if (bytes(value).length < 32) {
            return toShortString(value);
        } else {
            StorageSlot.getStringSlot(store).value = value;
            return ShortString.wrap(FALLBACK_SENTINEL);
        }
    }

    /**
     * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     */
    function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
        if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
            return toString(value);
        } else {
            return store;
        }
    }

    /**
     * @dev Return the length of a string that was encoded to `ShortString` or written to storage using
     * {setWithFallback}.
     *
     * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
     * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
     */
    function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
        if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
            return byteLength(value);
        } else {
            return bytes(store).length;
        }
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(newImplementation.code.length > 0);
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }
}

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

pragma solidity ^0.8.20;

import {Math} from "../math/Math.sol";
import {SafeCast} from "../math/SafeCast.sol";

/**
 * @dev This library provides helpers for manipulating time-related objects.
 *
 * It uses the following types:
 * - `uint48` for timepoints
 * - `uint32` for durations
 *
 * While the library doesn't provide specific types for timepoints and duration, it does provide:
 * - a `Delay` type to represent duration that can be programmed to change value automatically at a given point
 * - additional helper functions
 */
library Time {
    using Time for *;

    /**
     * @dev Get the block timestamp as a Timepoint.
     */
    function timestamp() internal view returns (uint48) {
        return SafeCast.toUint48(block.timestamp);
    }

    /**
     * @dev Get the block number as a Timepoint.
     */
    function blockNumber() internal view returns (uint48) {
        return SafeCast.toUint48(block.number);
    }

    // ==================================================== Delay =====================================================
    /**
     * @dev A `Delay` is a uint32 duration that can be programmed to change value automatically at a given point in the
     * future. The "effect" timepoint describes when the transitions happens from the "old" value to the "new" value.
     * This allows updating the delay applied to some operation while keeping some guarantees.
     *
     * In particular, the {update} function guarantees that if the delay is reduced, the old delay still applies for
     * some time. For example if the delay is currently 7 days to do an upgrade, the admin should not be able to set
     * the delay to 0 and upgrade immediately. If the admin wants to reduce the delay, the old delay (7 days) should
     * still apply for some time.
     *
     *
     * The `Delay` type is 112 bits long, and packs the following:
     *
     * ```
     *   | [uint48]: effect date (timepoint)
     *   |           | [uint32]: value before (duration)
     *   ↓           ↓       ↓ [uint32]: value after (duration)
     * 0xAAAAAAAAAAAABBBBBBBBCCCCCCCC
     * ```
     *
     * NOTE: The {get} and {withUpdate} functions operate using timestamps. Block number based delays are not currently
     * supported.
     */
    type Delay is uint112;

    /**
     * @dev Wrap a duration into a Delay to add the one-step "update in the future" feature
     */
    function toDelay(uint32 duration) internal pure returns (Delay) {
        return Delay.wrap(duration);
    }

    /**
     * @dev Get the value at a given timepoint plus the pending value and effect timepoint if there is a scheduled
     * change after this timepoint. If the effect timepoint is 0, then the pending value should not be considered.
     */
    function _getFullAt(Delay self, uint48 timepoint) private pure returns (uint32, uint32, uint48) {
        (uint32 valueBefore, uint32 valueAfter, uint48 effect) = self.unpack();
        return effect <= timepoint ? (valueAfter, 0, 0) : (valueBefore, valueAfter, effect);
    }

    /**
     * @dev Get the current value plus the pending value and effect timepoint if there is a scheduled change. If the
     * effect timepoint is 0, then the pending value should not be considered.
     */
    function getFull(Delay self) internal view returns (uint32, uint32, uint48) {
        return _getFullAt(self, timestamp());
    }

    /**
     * @dev Get the current value.
     */
    function get(Delay self) internal view returns (uint32) {
        (uint32 delay, , ) = self.getFull();
        return delay;
    }

    /**
     * @dev Update a Delay object so that it takes a new duration after a timepoint that is automatically computed to
     * enforce the old delay at the moment of the update. Returns the updated Delay object and the timestamp when the
     * new delay becomes effective.
     */
    function withUpdate(
        Delay self,
        uint32 newValue,
        uint32 minSetback
    ) internal view returns (Delay updatedDelay, uint48 effect) {
        uint32 value = self.get();
        uint32 setback = uint32(Math.max(minSetback, value > newValue ? value - newValue : 0));
        effect = timestamp() + setback;
        return (pack(value, newValue, effect), effect);
    }

    /**
     * @dev Split a delay into its components: valueBefore, valueAfter and effect (transition timepoint).
     */
    function unpack(Delay self) internal pure returns (uint32 valueBefore, uint32 valueAfter, uint48 effect) {
        uint112 raw = Delay.unwrap(self);

        valueAfter = uint32(raw);
        valueBefore = uint32(raw >> 32);
        effect = uint48(raw >> 64);

        return (valueBefore, valueAfter, effect);
    }

    /**
     * @dev pack the components into a Delay object.
     */
    function pack(uint32 valueBefore, uint32 valueAfter, uint48 effect) internal pure returns (Delay) {
        return Delay.wrap((uint112(effect) << 64) | (uint112(valueBefore) << 32) | uint112(valueAfter));
    }
}

File 21 of 47 : IAccess.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {Blocknumber} from "../type/Blocknumber.sol";
import {RoleId} from "../type/RoleId.sol";
import {Selector} from "../type/Selector.sol";
import {Str} from "../type/String.sol";
import {Timestamp} from "../type/Timestamp.sol";

interface IAccess {

    enum TargetType {
        Undefined, // no target must have this type
        Core, // GIF core contracts
        GenericService, // release independent service contracts
        Service, // service contracts
        Instance, // instance contracts
        Component, // instance contracts
        Contract, // normal contracts
        Custom // use case specific rules for contracts or normal accounts
    }

    struct RoleInfo {
        // slot 0
        RoleId adminRoleId;  // 64
        TargetType targetType; // ?
        uint32 maxMemberCount; // 32
        Timestamp createdAt; // 40
        Timestamp pausedAt; // 40
        Blocknumber lastUpdateIn; // 40
        // slot 1
        Str name; // 256
    }


    // TODO recalc slot allocation
    struct TargetInfo {
        Str name;
        TargetType targetType;
        RoleId roleId;
        Timestamp createdAt;
        Blocknumber lastUpdateIn;
    }

    struct FunctionInfo {
        // slot 0
        Str name; // function name
        // slot 1
        Selector selector; // function selector
        Timestamp createdAt;
        Blocknumber lastUpdateIn;
    }

    struct RoleNameInfo {
        // slot 0
        RoleId roleId;
        bool exists;
    }

    struct TargeNameInfo {
        // slot 0
        address target;
        bool exists;
    }

}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

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

import {IAccess} from "../authorization/IAccess.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {RoleId} from "../type/RoleId.sol";
import {Str} from "../type/String.sol";
import {VersionPart} from "../type/Version.sol";

interface IServiceAuthorization is 
     IERC165, 
     IAccess
{

     error ErrorAuthorizationMainTargetNameEmpty();
     error ErrorAuthorizationTargetDomainZero();
     error ErrorAuthorizationReleaseInvalid(uint8 release);
     error ErrorAuthorizationCommitHashInvalid(string commitHash);

     /// @dev Returns the main domain of the authorization.
     function getDomain() external view returns(ObjectType targetDomain);

     /// @dev Returns the release (VersionPart) for which the authorizations are defined by this contract.
     /// Matches with the release returned by the linked service authorization.
     function getRelease() external view returns(VersionPart release);

     /// @dev Returns the commit hash for the related GIF release.
     function getCommitHash() external view returns(string memory commitHash);

     /// @dev Returns the main target id name as string.
     /// This name is used to derive the target id and a corresponding target role name
     /// Overwrite this function to change the basic pool target name.
     function getMainTargetName() external view returns (string memory name);

     /// @dev Returns the main target.
     function getMainTarget() external view returns(Str target);

     /// @dev Returns the full list of service domains for this release.
     /// Services need to be registered for the release in revers order of this list.
     function getServiceDomains() external view returns(ObjectType[] memory serviceDomains);

     /// @dev Returns the service domain for the provided index.
     function getServiceDomain(uint256 idx) external view returns(ObjectType serviceDomain);

     /// @dev Returns the service target for the specified domain.
     function getServiceTarget(ObjectType serviceDomain) external view returns(Str serviceTarget);

     /// @dev Returns the service target for the specified domain.
     function getServiceRole(ObjectType serviceDomain) external view returns(RoleId serviceRoleId);

     /// @dev Returns the expected service address for the provided domain.
     function getServiceAddress(ObjectType serviceDomain) external view returns(address service);

     /// @dev Returns the role id associated with the target.
     /// If no role is associated with the target the zero role id is returned.
     function getTargetRole(Str target) external view returns(RoleId roleId);

     /// @dev Returns true iff the role exists.
     function roleExists(RoleId roleId) external view returns(bool exists);

     /// @dev Returns the list of involved roles.
     function getRoles() external view returns(RoleId[] memory roles);

     /// @dev Returns the role info for the provided role id.
     function getRoleInfo(RoleId roleId) external view returns (RoleInfo memory roleInfo);

     /// @dev Returns the name for the provided role id.
     function getRoleName(RoleId roleId) external view returns (string memory roleName);

     /// @dev For the given target the list of authorized role ids is returned
     function getAuthorizedRoles(Str target) external view returns(RoleId[] memory roleIds);

     /// @dev For the given target and role id the list of authorized functions is returned
     function getAuthorizedFunctions(Str target, RoleId roleId) external view returns(FunctionInfo[] memory authorizatedFunctions);
}

File 23 of 47 : IRegistry.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

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

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

import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {VersionPart} from "../type/Version.sol";

/// @title Chain Registry interface.
/// A chain registry holds all protocol relevant objects with basic metadata.
/// Registered objects include services, instances, products, pools, policies, bundles, stakes and more.
/// Registered objects are represented by NFTs.
/// When on mainnet registry is global and keeps arbitrary number of chain registries residing on different chain ids.
/// When not on mainnet registry keeps the only object residing on different chain id (on mainnet) - global registry.
interface IRegistry is
    IERC165
{

    event LogRegistryObjectRegistered(NftId nftId, NftId parentNftId, ObjectType objectType, bool isInterceptor, address objectAddress, address initialOwner);
    event LogRegistryServiceRegistered(VersionPart majorVersion, ObjectType domain);
    event LogRegistryChainRegistryRegistered(NftId nftId, uint256 chainId, address chainRegistryAddress);

    // initialize
    error ErrorRegistryCallerNotDeployer();

    // register()
    error ErrorRegistryObjectTypeNotSupported(ObjectType objectType);

    // registerRegistry()
    error ErrorRegistryNotOnMainnet(uint256 chainId);
    error ErrorRegistryChainRegistryChainIdZero(NftId nftId);
    error ErrorRegistryChainRegistryAddressZero(NftId nftId, uint256 chainId);
    error ErrorRegistryChainRegistryNftIdInvalid(NftId nftId, uint256 chainId);
    error ErrorRegistryChainRegistryAlreadyRegistered(NftId nftId, uint256 chainId);

    // registerService()
    error ErrorRegistryServiceAddressZero(); 
    error ErrorRegistryServiceVersionZero(address service);

    error ErrorRegistryServiceDomainZero(address service, VersionPart version);
    error ErrorRegistryNotService(address service, ObjectType objectType);
    error ErrorRegistryServiceParentNotRegistry(address service, VersionPart version, NftId parentNftId);
    error ErrorRegistryServiceDomainAlreadyRegistered(address service, VersionPart version, ObjectType domain);

    // registerWithCustomTypes()
    error ErrorRegistryCoreTypeRegistration();

    // _register()
    error ErrorRegistryGlobalRegistryAsParent(address objectAddress, ObjectType objectType);
    error ErrorRegistryTypeCombinationInvalid(address objectAddress, ObjectType objectType, ObjectType parentType);
    error ErrorRegistryContractAlreadyRegistered(address objectAddress);

    struct ObjectInfo {
        // slot 0
        NftId nftId;
        NftId parentNftId;
        ObjectType objectType;
        bool isInterceptor;
        // slot 1
        address objectAddress;
        // slot 2
        address initialOwner;
        // slot 3
        bytes data;
    }

    /// @dev Registers a registry contract for a specified chain.
    /// Only one chain registry may be registered per chain
    function registerRegistry(
        NftId nftId, 
        uint256 chainId, 
        address chainRegistryAddress
    ) external;

    /// @dev Register a service with using the provided domain and version.
    /// The function returns a newly minted service NFT ID.
    /// May only be used to register services.
    function registerService(
        ObjectInfo memory serviceInfo, 
        VersionPart serviceVersion, 
        ObjectType serviceDomain
    ) external returns(NftId nftId);

    /// @dev Register an object with a known core type.
    /// The function returns a newly minted object NFT ID.
    /// May not be used to register services.
    function register(ObjectInfo memory info) external returns (NftId nftId);

    /// @dev Register an object with a custom type.
    /// The function returns a newly minted object NFT ID.
    /// This function is reserved for GIF releases > 3.
    /// May not be used to register known core types.
    function registerWithCustomType(ObjectInfo memory info) external returns (NftId nftId);

    function getInitialRelease() external view returns (VersionPart);

    function getNextRelease() external view returns (VersionPart);

    function getLatestRelease() external view returns (VersionPart);

    function getReleaseInfo(VersionPart release) external view returns (IRelease.ReleaseInfo memory);

    /// @dev Returns the number of supported chains.
    function chainIds() external view returns (uint256);

    /// @dev Returns the chain id at the specified index.
    function getChainId(uint256 idx) external view returns (uint256);

    /// @dev Returns the NFT ID of the registry for the specified chain.
    function getRegistryNftId(uint256 chainId) external returns (NftId nftId); 

    function getObjectCount() external view returns (uint256);

    function getNftIdForAddress(address objectAddress) external view returns (NftId nftId);

    function ownerOf(NftId nftId) external view returns (address);

    function isOwnerOf(NftId nftId, address expectedOwner) external view returns (bool);

    function ownerOf(address contractAddress) external view returns (address);

    function getObjectInfo(NftId nftId) external view returns (ObjectInfo memory info);

    function getParentNftId(NftId nftId) external view returns (NftId parentNftId);

    function isObjectType(NftId nftId, ObjectType expectedObjectType) external view returns (bool);

    function isObjectType(address contractAddress, ObjectType expectedObjectType) external view returns (bool);

    function getObjectAddress(NftId nftId) external view returns (address objectAddress);

    /// @dev Returns the object info for the specified object address.
    //  MUST not be used with chain registry address (resides on different chan id)
    function getObjectInfo(address object) external view returns (ObjectInfo memory info);

    function isRegistered(NftId nftId) external view returns (bool);

    function isRegistered(address contractAddress) external view returns (bool);

    function isRegisteredService(address contractAddress) external view returns (bool);

    function isRegisteredComponent(address object) external view returns (bool);

    function isActiveRelease(VersionPart version) external view returns (bool);

    function getServiceAddress(
        ObjectType serviceDomain, 
        VersionPart releaseVersion
    ) external view returns (address serviceAddress);

    function getProtocolNftId() external view returns (NftId protocolNftId);

    function getNftId() external view returns (NftId nftId);

    function getOwner() external view returns (address);

    // TODO refactor the address getters below to contract getters
    function getChainNftAddress() external view returns (address);

    function getReleaseRegistryAddress() external view returns (address);

    function getStakingAddress() external view returns (address);

    function getTokenRegistryAddress() external view returns (address);

    function getRegistryAdminAddress() external view returns (address);

    function getAuthority() external view returns (address);
}

File 24 of 47 : IRelease.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {IServiceAuthorization} from "../authorization/IServiceAuthorization.sol";

import {StateId} from "../type/StateId.sol";
import {Timestamp} from "../type/Timestamp.sol";
import {VersionPart} from "../type/Version.sol";

/// @title Marks contracts that are linked to a specific GIF release version.
interface IRelease {

    struct ReleaseInfo {
        // slot 0
        address releaseAdmin;
        StateId state;
        VersionPart version;
        Timestamp activatedAt;
        Timestamp disabledAt;
        // slot 1
        IServiceAuthorization auth;
        // slot 2
        bytes32 salt;
    }

    /// @dev Registers a registry contract for a specified chain.
    /// Only one chain registry may be registered per chain
    function getRelease() external view returns (VersionPart release);
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {ERC165Checker} from "@openzeppelin/contracts/utils/introspection/ERC165Checker.sol";
import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol";
import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";

import {IPolicyHolder} from "../shared/IPolicyHolder.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {IService} from "../shared/IService.sol";

import {ChainId} from "../type/ChainId.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType, INSTANCE, PRODUCT, DISTRIBUTION, ORACLE, POOL} from "../type/ObjectType.sol";
import {VersionPart} from "../type/Version.sol";

interface ITargetHelper {
    function isTargetLocked(address target) external view returns (bool);
}

interface IInstanceAdminHelper {
    function getInstanceAdmin() external view returns (ITargetHelper);
}

interface ITokenRegistryHelper {
    function isActive(ChainId chainId, address token, VersionPart release) external view returns (bool);
}

library ContractLib {

    error ErrorContractLibCallerNotRegistered(address target);
    error ErrorContractLibCallerNotComponent(NftId componentNftId, ObjectType objectType);
    error ErrorContractLibParentNotInstance(NftId componentNftId, NftId parentNftId);
    error ErrorContractLibParentNotProduct(NftId componentNftId, NftId parentNftId);
    error ErrorContractLibComponentTypeMismatch(NftId componentNftId, ObjectType expectedType, ObjectType actualType);
    error ErrorContractLibComponentInactive(NftId componentNftId);


    function getInfoAndInstance(
        IRegistry registry,
        NftId componentNftId,
        bool onlyActive
    )
        external
        view
        returns (
            IRegistry.ObjectInfo memory info, 
            address instance
        )
    {
        info = registry.getObjectInfo(componentNftId);
        return _getAndVerifyComponentAndInstance(registry, info, info.objectType, onlyActive);
    }


    function getAndVerifyAnyComponent(
        IRegistry registry, 
        address caller,
        bool onlyActive
    )
        external
        view
        returns (
            IRegistry.ObjectInfo memory callerInfo, 
            address instance
        )
    {
        // check caller is component
        callerInfo = _getAndVerifyObjectInfo(registry, caller);
        if(!(callerInfo.objectType == PRODUCT()
            || callerInfo.objectType == POOL()
            || callerInfo.objectType == DISTRIBUTION()
            || callerInfo.objectType == ORACLE())
        ) {
            revert ErrorContractLibCallerNotComponent(
                callerInfo.nftId,
                callerInfo.objectType);
        }

        return _getAndVerifyComponentAndInstance(registry, callerInfo, callerInfo.objectType, onlyActive);
    }


    function getAndVerifyComponent(
        IRegistry registry, 
        address caller,
        ObjectType expectedType,
        bool onlyActive
    )
        external
        view
        returns (
            IRegistry.ObjectInfo memory info, 
            address instance
        )
    {
        info = _getAndVerifyObjectInfo(registry, caller);
        return _getAndVerifyComponentAndInstance(registry, info, expectedType, onlyActive);
    }


    function getInstanceForComponent(
        IRegistry registry, 
        NftId componentNftId
    )
        public
        view
        returns (address instance)
    {
        NftId productNftId = registry.getParentNftId(componentNftId);
        NftId instanceNftId = registry.getParentNftId(productNftId);
        return registry.getObjectInfo(instanceNftId).objectAddress;
    }


    function isActiveToken(
        address tokenRegistryAddress,
        ChainId chainId, 
        address token,
        VersionPart release
    )
        external 
        view 
        returns (bool)
    {
        return ITokenRegistryHelper(
            tokenRegistryAddress).isActive(
                chainId, token, release);
    }


    function isPolicyHolder(address target) external view returns (bool) {
        return ERC165Checker.supportsInterface(target, type(IPolicyHolder).interfaceId);
    }


    function isAuthority(address authority) public view returns (bool) {
        if (!isContract(authority)) {
            return false;
        }

        return supportsInterface(authority, type(IAccessManager).interfaceId);
    }


    function isAccessManaged(address target)
        public
        view
        returns (bool)
    {
        if (!isContract(target)) {
            return false;
        }

        (bool success, ) = target.staticcall(
            abi.encodeWithSelector(
                IAccessManaged.authority.selector));

        return success;
    }


    function isRegistered(address registry, address caller, ObjectType expectedType) public view returns (bool) {
        NftId nftId = IRegistry(registry).getNftIdForAddress(caller);
        if (nftId.eqz()) {
            return false;
        }

        return IRegistry(registry).getObjectInfo(nftId).objectType == expectedType;
    }


    function isService(address service) public view returns (bool) {
        if (!isContract(service)) {
            return false;
        }

        return supportsInterface(service, type(IService).interfaceId);
    }


    function isRegistry(address registry) public view returns (bool) {
        if (!isContract(registry)) {
            return false;
        }

        return supportsInterface(registry, type(IRegistry).interfaceId);
    }


    function isContract(address target) public view returns (bool) {
        if (target == address(0)) {
            return false;
        }

        uint256 size;
        assembly {
            size := extcodesize(target)
        }
        return size > 0;
    }

    function supportsInterface(address target, bytes4 interfaceId)  public view returns (bool) {
        return ERC165Checker.supportsInterface(target, interfaceId);
    }


    function _getAndVerifyComponentAndInstance(
        IRegistry registry, 
        IRegistry.ObjectInfo memory info,
        ObjectType expectedType,
        bool onlyActive
    )
        internal
        view
        returns (
            IRegistry.ObjectInfo memory, 
            address instance
        )
    {
        if(info.objectType != expectedType) {
            revert ErrorContractLibComponentTypeMismatch(
                info.nftId,
                expectedType,
                info.objectType);
        }

        // get instance and check component is active
        instance = getAndVerifyInstance(registry, info);
        _checkComponentActive(instance, info.objectAddress, info.nftId, onlyActive);

        return (
            info,
            instance
        );
    }


    function _checkComponentActive(
        address instance, 
        address target, 
        NftId componentNftId, 
        bool onlyActive
    )
        internal
        view
    {
        if (onlyActive) {
            if (IInstanceAdminHelper(
                instance).getInstanceAdmin().isTargetLocked(
                    target)
            ) {
                revert ErrorContractLibComponentInactive(componentNftId);
            }
        }
    }


    /// @dev Given an object info the function returns the instance address.
    /// The info may represent a product or any other component.
    /// If the parent of the provided info is not registered with the correct type, the function reverts.
    function getAndVerifyInstance(
        IRegistry registry,
        IRegistry.ObjectInfo memory info
    )
        public
        view
        returns (address instance)
    {
        // get instance for product case
        if (info.objectType == PRODUCT()) {
            // verify that parent of product is registered instance
            IRegistry.ObjectInfo memory instanceInfo = registry.getObjectInfo(info.parentNftId);
            if (instanceInfo.objectType != INSTANCE()) {
                revert ErrorContractLibParentNotInstance(
                    info.nftId,
                    info.parentNftId);
            }

            // we have verified that parent object is a registerd instance -> we return the instance address
            return instanceInfo.objectAddress;
        }

        // not product: verify parent is registered product
        info = registry.getObjectInfo(info.parentNftId);
        if (info.objectType != PRODUCT()) {
            revert ErrorContractLibParentNotProduct(
                info.nftId,
                info.parentNftId);
        }

        // we have verified that parent is registerd product -> we can rely on registry that its parent is an instance
        return registry.getObjectAddress(info.parentNftId);
    }


    function _getAndVerifyObjectInfo(
        IRegistry registry, 
        address caller
    )
        internal
        view
        returns (IRegistry.ObjectInfo memory info)
    {
        NftId componentNftId = registry.getNftIdForAddress(caller);
        if (componentNftId.eqz()) {
            revert ErrorContractLibCallerNotRegistered(caller);
        }

        info = registry.getObjectInfo(componentNftId);
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

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

import {IRegistryLinked} from "./IRegistryLinked.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";

interface INftOwnable is
    IERC165,
    IRegistryLinked
{
    event LogNftOwnableNftLinkedToAddress(NftId nftId, address owner);
    
    error ErrorNftOwnableInitialOwnerZero();
    error ErrorNftOwnableNotOwner(address account);
    error ErrorNftOwnableInvalidType(NftId nftId, ObjectType expectedObjectType);

    error ErrorNftOwnableAlreadyLinked(NftId nftId);
    error ErrorNftOwnableContractNotRegistered(address contractAddress);

    function linkToRegisteredNftId() external returns (NftId);

    function getNftId() external view returns (NftId);
    function getOwner() external view returns (address);
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol";

import {Amount} from "../type/Amount.sol";
import {ClaimId} from "../type/ClaimId.sol";
import {IRegistryLinked} from "../shared/IRegistryLinked.sol";
import {NftId} from "../type/NftId.sol";
import {PayoutId} from "../type/PayoutId.sol";
import {Timestamp} from "../type/Timestamp.sol";

/// @dev Generic interface for contracts that need to hold policies and receive payouts.
/// The framework notifies policy holder contracts for policy creation/expiry, claim confirmation and payout execution
interface IPolicyHolder is
    IERC165,
    IERC721Receiver,
    IRegistryLinked
{

    /// @dev Callback function that will be called after successful policy activation.
    /// Active policies may open claims under the activated policy.
    function policyActivated(NftId policyNftId, Timestamp activatedAt) external;

    /// @dev Callback function to indicate the specified policy has expired.
    /// expired policies no longer accept new claims.
    function policyExpired(NftId policyNftId, Timestamp expiredAt) external;

    /// @dev Callback function to notify the confirmation of the specified claim.
    function claimConfirmed(NftId policyNftId, ClaimId claimId, Amount amount) external;

    /// @dev Callback function to notify the successful payout.
    function payoutExecuted(NftId policyNftId, PayoutId payoutId, Amount amount, address beneficiary) external;
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";

import {INftOwnable} from "./INftOwnable.sol";
import {IRelease} from "../registry/IRelease.sol";
import {IRegistry} from "../registry/IRegistry.sol";

/// @title IRegisterable
/// @dev Marks contracts that are intended to be registered in the registry.
/// 
interface IRegisterable is
    IAccessManaged,
    INftOwnable,
    IRelease
{
    // __Registerable_init
    error ErrorAuthorityInvalid(address authority);

    // onlyActive()
    error ErrorRegisterableNotActive();

    /// @dev Returns true iff this contract managed by its authority is active.
    /// Queries the IAccessManaged.authority().
    function isActive() external view returns (bool active);

    /// @dev retuns the object info relevant for registering for this contract 
    /// IMPORTANT information returned by this function may only be used
    /// before the contract is registered in the registry.
    /// Once registered this information MUST only be accessed via the registry.
    function getInitialInfo() 
        external 
        view
        returns (IRegistry.ObjectInfo memory);
}

File 29 of 47 : IRegistryLinked.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {IRegistry} from "../registry/IRegistry.sol";

interface IRegistryLinked {

    error ErrorNotRegistry(address registryAddress);

    function getRegistry() external view returns (IRegistry);
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";

import {IRegisterable} from "./IRegisterable.sol";
import {IVersionable} from "../upgradeability/IVersionable.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {RoleId} from "../type/RoleId.sol";

interface IService is 
    IAccessManaged,
    IRegisterable,
    IVersionable
{
    /// @dev returns the domain for this service.
    /// In any GIF release only one service for any given domain may be deployed.
    function getDomain() external pure returns(ObjectType serviceDomain);

    /// @dev returns the GIF release specific role id.
    /// These role ids are used to authorize service to service communication.
    function getRoleId() external view returns(RoleId serviceRoleId);
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {UFixed, UFixedLib} from "./UFixed.sol";

/// @dev Targets: 100 trillion (worlds GDP) with 6 decimal places
/// 3 trillion USD (total crypto market cap) with 12 decimal places.
/// 2023 100 trillion USD => 100e12 * 1e6 = 1e20
/// 2024 2 trillion crypto market cap => 2e12 * 1e18 = 2e30
type Amount is uint96;

using {
    addAmount as +,
    subAmount as -,
    eqAmount as ==,
    nqAmount as !=,
    ltAmount as <,
    ltAmount as <=,
    gtAmount as >,
    gteAmount as >=,
    AmountLib.add,
    AmountLib.eq,
    AmountLib.eqz,
    AmountLib.gtz,
    AmountLib.gt,
    AmountLib.gte,
    AmountLib.multiplyWith,
    AmountLib.toInt,
    AmountLib.toUFixed
} for Amount global;

function addAmount(Amount a, Amount b) pure returns (Amount) {
    return AmountLib.add(a, b);
}

function subAmount(Amount a, Amount b) pure returns (Amount) {
    return AmountLib.sub(a, b);
}

function eqAmount(Amount a, Amount b) pure returns (bool) {
    return AmountLib.eq(a, b);
}

function nqAmount(Amount a, Amount b) pure returns (bool) {
    return !AmountLib.eq(a, b);
}

function ltAmount(Amount a, Amount b) pure returns (bool) {
    return AmountLib.lt(a, b);
}

function lteAmount(Amount a, Amount b) pure returns (bool) {
    return AmountLib.lte(a, b);
}

function gtAmount(Amount a, Amount b) pure returns (bool) {
    return AmountLib.gt(a, b);
}

function gteAmount(Amount a, Amount b) pure returns (bool) {
    return AmountLib.gte(a, b);
}

library AmountLib {

    error ErrorAmountLibValueTooBig(uint256 amount);

    function zero() public pure returns (Amount) {
        return Amount.wrap(0);
    }

    function max() public pure returns (Amount) {
        return Amount.wrap(_max());
    }

    /// @dev converts the uint amount into Amount
    /// function reverts if value is exceeding max Amount value
    function toAmount(uint256 amount) public pure returns (Amount) {
        if(amount > _max()) {
            revert ErrorAmountLibValueTooBig(amount);
        }

        return Amount.wrap(uint96(amount));
    }

    /// @dev return true if amount equals 0
    function eqz(Amount amount) public pure returns (bool) {
        return Amount.unwrap(amount) == 0;
    }

    /// @dev return true if amount1 equals amount2
    function eq(Amount amount1, Amount amount2) public pure returns (bool) {
        return Amount.unwrap(amount1) == Amount.unwrap(amount2);
    }

    /// @dev return true if amount a1 is less than a2
    function lt(Amount a1, Amount a2) public pure returns (bool) {
        return Amount.unwrap(a1) < Amount.unwrap(a2);
    }

    /// @dev return true if amount a1 is less or equal than a2
    function lte(Amount a1, Amount a2) public pure returns (bool) {
        return Amount.unwrap(a1) <= Amount.unwrap(a2);
    }

    /// @dev return true if amount a1 is greater than a2
    function gt(Amount a1, Amount a2) public pure returns (bool) {
        return Amount.unwrap(a1) > Amount.unwrap(a2);
    }

    /// @dev return true if amount a1 is greater or equal than a2
    function gte(Amount a1, Amount a2) public pure returns (bool) {
        return Amount.unwrap(a1) >= Amount.unwrap(a2);
    }

    /// @dev return minimum of a1 and a2.
    function min(Amount a1, Amount a2) public pure returns (Amount) {
        if (Amount.unwrap(a1) < Amount.unwrap(a2)) {
            return a1;
        }

        return a2;
    }

    /// @dev return true if amount is larger than 0
    function gtz(Amount amount) public pure returns (bool) {
        return Amount.unwrap(amount) > 0;
    }

    function add(Amount a1, Amount a2) public pure returns (Amount) {
        return Amount.wrap(Amount.unwrap(a1) + Amount.unwrap(a2));
    }

    function sub(Amount a1, Amount a2) public pure returns (Amount) {
        return Amount.wrap(Amount.unwrap(a1) - Amount.unwrap(a2));
    }

    function toInt(Amount amount) public pure returns (uint256) {
        return uint256(uint96(Amount.unwrap(amount)));
    }

    function toUFixed(Amount amount) public pure returns (UFixed) {
        return UFixedLib.toUFixed(Amount.unwrap(amount));
    }

    function multiplyWith(Amount amount, UFixed factor) public pure returns (Amount) {
        return toAmount((factor * UFixedLib.toUFixed(Amount.unwrap(amount))).toInt());
    }

    function _max() internal pure returns (uint96) {
        // IMPORTANT: type nees to match with actual definition for Amount
        return type(uint96).max;
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

/// @dev Target: Cover 10 years with 1 ms block times.
/// Typical block times are a few seconds.
type Blocknumber is uint40;

using {
    gtBlocknumber as >,
    gteBlocknumber as >=,
    ltBlocknumber as <,
    lteBlocknumber as <=,
    eqBlocknumber as ==,
    neBlocknumber as !=,
    BlocknumberLib.toInt,
    BlocknumberLib.eq,
    BlocknumberLib.ne,
    BlocknumberLib.eqz,
    BlocknumberLib.gtz,
    BlocknumberLib.gt,
    BlocknumberLib.gte,
    BlocknumberLib.lt,
    BlocknumberLib.lte
} for Blocknumber global;

/// @dev return true if Blocknumber a is greater than Blocknumber b
function gtBlocknumber(Blocknumber a, Blocknumber b) pure returns (bool) {
    return Blocknumber.unwrap(a) > Blocknumber.unwrap(b);
}

/// @dev return true if Blocknumber a is greater than or equal to Blocknumber b
function gteBlocknumber(Blocknumber a, Blocknumber b) pure returns (bool) {
    return Blocknumber.unwrap(a) >= Blocknumber.unwrap(b);
}

/// @dev return true if Blocknumber a is less than Blocknumber b
function ltBlocknumber(Blocknumber a, Blocknumber b) pure returns (bool) {
    return Blocknumber.unwrap(a) < Blocknumber.unwrap(b);
}

/// @dev return true if Blocknumber a is less than or equal to Blocknumber b
function lteBlocknumber(Blocknumber a, Blocknumber b) pure returns (bool) {
    return Blocknumber.unwrap(a) <= Blocknumber.unwrap(b);
}

/// @dev return true if Blocknumber a is equal to Blocknumber b
function eqBlocknumber(Blocknumber a, Blocknumber b) pure returns (bool) {
    return Blocknumber.unwrap(a) == Blocknumber.unwrap(b);
}

/// @dev return true if Blocknumber a is not equal to Blocknumber b
function neBlocknumber(Blocknumber a, Blocknumber b) pure returns (bool) {
    return Blocknumber.unwrap(a) != Blocknumber.unwrap(b);
}


library BlocknumberLib {

    function zero() public pure returns (Blocknumber) {
        return Blocknumber.wrap(0);
    }

    function max() public pure returns (Blocknumber) {
        return Blocknumber.wrap(type(uint40).max);
    }

    function current() public view returns (Blocknumber) {
        return Blocknumber.wrap(uint40(block.number));
    }

    function toBlocknumber(uint256 blocknum) public pure returns (Blocknumber) {
        return Blocknumber.wrap(uint32(blocknum));
    }

    /// @dev return true iff blocknumber is 0
    function eqz(Blocknumber blocknumber) public pure returns (bool) {
        return Blocknumber.unwrap(blocknumber) == 0;
    }

    /// @dev return true iff blocknumber is > 0
    function gtz(Blocknumber blocknumber) public pure returns (bool) {
        return Blocknumber.unwrap(blocknumber) > 0;
    }

    /// @dev return true if Blocknumber a is greater than Blocknumber b
    function gt(
        Blocknumber a,
        Blocknumber b
    ) public pure returns (bool isAfter) {
        return gtBlocknumber(a, b);
    }

    /// @dev return true if Blocknumber a is greater than or equal to Blocknumber b
    function gte(
        Blocknumber a,
        Blocknumber b
    ) public pure returns (bool isAfterOrSame) {
        return gteBlocknumber(a, b);
    }

    /// @dev return true if Blocknumber a is less than Blocknumber b
    function lt(
        Blocknumber a,
        Blocknumber b
    ) public pure returns (bool isBefore) {
        return ltBlocknumber(a, b);
    }

    /// @dev return true if Blocknumber a is less than or equal to Blocknumber b
    function lte(
        Blocknumber a,
        Blocknumber b
    ) public pure returns (bool isBeforeOrSame) {
        return lteBlocknumber(a, b);
    }

    /// @dev return true if Blocknumber a is equal to Blocknumber b
    function eq(
        Blocknumber a,
        Blocknumber b
    ) public pure returns (bool isSame) {
        return eqBlocknumber(a, b);
    }

    /// @dev return true if Blocknumber a is not equal to Blocknumber b
    function ne(
        Blocknumber a,
        Blocknumber b
    ) public pure returns (bool isDifferent) {
        return neBlocknumber(a, b);
    }

    /// @dev converts the Blocknumber to a uint256
    function toInt(Blocknumber blocknumber) public pure returns (uint256) {
        return uint256(uint32(Blocknumber.unwrap(blocknumber)));
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

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

/// @dev Target: Cover chain IDs up to 26 decimal places.
/// Current longest chain ID seems to be DCHAIN Testnet: 2713017997578000 with 16 decimal places 
type ChainId is uint96;

using {
    eqChainId as ==,
    neChainId as !=,
    ChainIdLib.toInt,
    ChainIdLib.eqz,
    ChainIdLib.gtz
} for ChainId global;


/// @dev return true if ChainId a is equal to ChainId b
function eqChainId(ChainId a, ChainId b) pure returns (bool) {
    return ChainId.unwrap(a) == ChainId.unwrap(b);
}

/// @dev return true if ChainId a is not equal to ChainId b
function neChainId(ChainId a, ChainId b) pure returns (bool) {
    return ChainId.unwrap(a) != ChainId.unwrap(b);
}


library ChainIdLib {

    error ErrorChainIdLibValueTooBig(uint256 chainId);


    function zero() public pure returns (ChainId) {
        return ChainId.wrap(0);
    }


    function max() public pure returns (ChainId) {
        return ChainId.wrap(_max());
    }


    function current() public view returns (ChainId) {
        return toChainId(block.chainid);
    }


    /// @dev return true iff chainId is 0
    function eqz(ChainId chainId) public pure returns (bool) {
        return ChainId.unwrap(chainId) == 0;
    }


    /// @dev return true iff chainId is > 0
    function gtz(ChainId chainId) public pure returns (bool) {
        return ChainId.unwrap(chainId) > 0;
    }


    /// @dev converts the uint into ChainId
    /// function reverts if value is exceeding max ChainId value
    function toChainId(uint256 chainId) public pure returns (ChainId) {
        if(chainId > _max()) {
            revert ErrorChainIdLibValueTooBig(chainId);
        }

        return ChainId.wrap(uint96(chainId));
    }


    /// @dev returns true iff NFT ID is from the current chain.
    function isCurrentChain(NftId nftId) public view returns (bool) {
        return _fromNftId(nftId) == block.chainid;
    }


    function fromNftId(NftId nftId) public pure returns (ChainId) {
        return toChainId(_fromNftId(nftId));
    }


    /// @dev converts the ChainId to a uint256
    function toInt(ChainId chainId) public pure returns (uint256) {
        return uint256(uint96(ChainId.unwrap(chainId)));
    }


    function _fromNftId(NftId nftId) internal pure returns (uint256 chainIdInt) {
        uint256 nftIdInt = nftId.toInt();
        uint256 chainIdDigits = nftIdInt % 100; // Extract the last two digits
        chainIdInt = nftIdInt % 10**(chainIdDigits + 2) / 100; // Extract the chainId
    }


    function _max() internal pure returns (uint96) {
        // IMPORTANT: type nees to match with actual definition for Amount
        return type(uint96).max;
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

// uint16 allows for 65'535 claims per policy
type ClaimId is uint16;

import {CLAIM} from "./ObjectType.sol";
import {Key32, KeyId, Key32Lib} from "./Key32.sol";
import {NftId} from "./NftId.sol";

// type bindings
using {
    eqClaimId as ==, 
    neClaimId as !=, 
    ClaimIdLib.eq,
    ClaimIdLib.eqz,
    ClaimIdLib.gtz,
    ClaimIdLib.toInt,
    ClaimIdLib.toKey32
} for ClaimId global;


// pure free functions for operators
function eqClaimId(ClaimId a, ClaimId b) pure returns (bool isSame) {
    return ClaimIdLib.eq(a, b);
}

function neClaimId(ClaimId a, ClaimId b) pure returns (bool isDifferent) {
    return ClaimId.unwrap(a) != ClaimId.unwrap(b);
}

// library functions that operate on user defined type
library ClaimIdLib {
    /// @dev claim id min value (0), use only for non-initialized values
    function zero() public pure returns (ClaimId) {
        return ClaimId.wrap(0);
    }
    /// @dev claim id max value (2**16-1), use only for non-initialized values
    function max() public pure returns (ClaimId) {
        return ClaimId.wrap(type(uint16).max);
    }

    /// @dev Converts an uint into a ClaimId.
    function toClaimId(uint256 a) public pure returns (ClaimId) {
        return ClaimId.wrap(uint16(a));
    }

    /// @dev Converts the ClaimId to a uint.
    function toInt(ClaimId a) public pure returns (uint16) {
        return uint16(ClaimId.unwrap(a));
    }

    /// @dev Converts the ClaimId and NftId to a Key32.
    function toKey32(ClaimId claimId, NftId policyNftId) public pure returns (Key32) {
        return Key32Lib.toKey32(CLAIM(), toKeyId(claimId, policyNftId));
    }

    /// @dev Converts the ClaimId and NftId to a Key32.
    function toKeyId(ClaimId claimId, NftId policyNftId) public pure returns (KeyId) {
        return KeyId.wrap(
            bytes31(
                bytes14(
                    uint112(
                        NftId.unwrap(policyNftId) << 16 + ClaimId.unwrap(claimId)))));
    }

    /// @dev Returns true if the value is non-zero (> 0).
    function gtz(ClaimId a) public pure returns (bool) {
        return ClaimId.unwrap(a) > 0;
    }

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

    /// @dev Returns true if the value is zero (== 0).
    function eqz(ClaimId a) public pure returns (bool) {
        return ClaimId.unwrap(a) == 0;
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

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

type Key32 is bytes32;
type KeyId is bytes31;

// type bindings
using {
    eqKey32 as ==, 
    neKey32 as !=,
    Key32Lib.toKeyId,
    Key32Lib.toObjectType
} for Key32 global;

// @dev Returns true iff keys are identical
function eqKey32(Key32 a, Key32 b) pure returns (bool isSame) {
    return Key32.unwrap(a) == Key32.unwrap(b);
}

// @dev Returns true iff keys are different
function neKey32(Key32 a, Key32 b) pure returns (bool isDifferent) {
    return Key32.unwrap(a) != Key32.unwrap(b);
}

library Key32Lib {

    uint8 public constant TYPE_SHIFT = 31 * 8;
    uint8 public constant ID_SHIFT = uint8(32 * 8 - TYPE_SHIFT);
    bytes32 public constant TYPE_MASK = bytes32(bytes1(type(uint8).max)); // [32] byte in bytes32
    bytes32 public constant ID_MASK = bytes32(~TYPE_MASK); // [0..31] bytes in bytes32

    function toKey32(ObjectType objectType, KeyId id) public pure returns (Key32) {
        uint256 uintObjectType = ObjectType.unwrap(objectType);
        uint256 uintId = uint248(KeyId.unwrap(id));
        uint256 uintKey = (uintObjectType << TYPE_SHIFT) + uintId;
        return Key32.wrap(bytes32(uintKey));
    }

    function toObjectType(Key32 key) public pure returns (ObjectType objectType) {
        bytes32 key32 = Key32.unwrap(key);
        objectType = ObjectType.wrap(uint8(uint256(key32 & TYPE_MASK) >> TYPE_SHIFT));
    }

    function toKeyId(Key32 key) public pure returns (KeyId id) {
        bytes32 key32 = Key32.unwrap(key);
        id = KeyId.wrap(bytes31((key32 & ID_MASK) << ID_SHIFT));
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {Key32, KeyId, Key32Lib} from "./Key32.sol";
import {ObjectType} from "./ObjectType.sol";

// uint96 allows for chain ids up to 13 digits
type NftId is uint96;

// type bindings
using {
    eqNftId as ==, 
    neNftId as !=, 
    NftIdLib.toInt,
    NftIdLib.gtz,
    NftIdLib.eqz,
    NftIdLib.eq,
    NftIdLib.toKeyId,
    NftIdLib.toKey32
} for NftId global;

// pure free functions for operators
function eqNftId(NftId a, NftId b) pure returns (bool isSame) {
    return NftIdLib.eq(a, b);
}

function neNftId(NftId a, NftId b) pure returns (bool isDifferent) {
    return NftIdLib.ne(a, b);
}

// library functions that operate on user defined type
library NftIdLib {

    /// @dev the zero nft id
    /// is never a valid nft id and implies a non-initialized value
    function zero() public pure returns (NftId) {
        return NftId.wrap(0);
    }

    /// @dev Converts the uint256 to a NftId.
    function toNftId(uint256 id) public pure returns (NftId) {
        return NftId.wrap(uint96(id));
    }

    /// @dev Converts the NftId to a uint256.
    function toInt(NftId nftId) public pure returns (uint96) {
        return uint96(NftId.unwrap(nftId));
    }

    /// @dev Returns true if the value is non-zero (> 0).
    function gtz(NftId a) public pure returns (bool) {
        return NftId.unwrap(a) > 0;
    }

    /// @dev Returns true if the value is zero (== 0).
    function eqz(NftId a) public pure returns (bool) {
        return NftId.unwrap(a) == 0;
    }

    /// @dev Returns true if the values are equal (==).
    function eq(NftId a, NftId b) public pure returns (bool isSame) {
        return NftId.unwrap(a) == NftId.unwrap(b);
    }

    /// @dev Returns true if the values are not equal (!=).
    function ne(NftId a, NftId b) public pure returns (bool isSame) {
        return NftId.unwrap(a) != NftId.unwrap(b);
    }

    /// @dev Returns the key32 value for the specified nft id and object type.
    function toKey32(NftId id, ObjectType objectType) public pure returns (Key32 key) {
        return Key32Lib.toKey32(objectType, toKeyId(id));
    }

    /// @dev Returns the key id value for the specified nft id
    function toKeyId(NftId id) public pure returns (KeyId keyId) {
        return KeyId.wrap(bytes31(uint248(NftId.unwrap(id))));
    }

    function toNftId(KeyId keyId) public pure returns (NftId nftId) {
        uint248 keyIdInt = uint248(bytes31(KeyId.unwrap(keyId)));
        assert(keyIdInt < type(uint96).max);
        return NftId.wrap(uint96(keyIdInt));
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {StrLib} from "./String.sol";
import {VersionPart} from "./Version.sol";

type ObjectType is uint8;

// type bindings
using {
    eqObjectType as ==,
    neObjectType as !=,
    ObjectTypeLib.toInt,
    ObjectTypeLib.toName,
    ObjectTypeLib.eqz,
    ObjectTypeLib.eq,
    ObjectTypeLib.gtz
} for ObjectType global;


//--- GIF object types/domains (rage: 1 - 99) -------------------------------//

function PROTOCOL() pure returns (ObjectType) {
    return ObjectType.wrap(1);
}

function REGISTRY() pure returns (ObjectType) {
    return ObjectType.wrap(2);
}

function STAKING() pure returns (ObjectType) {
    return ObjectType.wrap(3);
}

function RELEASE() pure returns (ObjectType) {
    return ObjectType.wrap(6);
}

function ROLE() pure returns (ObjectType) {
    return ObjectType.wrap(7);
}

function SERVICE() pure returns (ObjectType) {
    return ObjectType.wrap(8);
}

function INSTANCE() pure returns (ObjectType) {
    return ObjectType.wrap(10);
}

/// @dev Generic component object type.
/// Component role id range is 11-19.
/// Stick to this range for new component object types.
function COMPONENT() pure returns (ObjectType) {
    return ObjectType.wrap(11);
}

/// @dev Product object type.
/// IMPORTANT the actual value has an influence on the corresponding role id (RoleIdLib.sol). 
/// Do not change this value without updating the corresponding role id calculation.
function PRODUCT() pure returns (ObjectType) {
    return ObjectType.wrap(12);
}

function ORACLE() pure returns (ObjectType) {
    return ObjectType.wrap(13);
}

function DISTRIBUTION() pure returns (ObjectType) {
    return ObjectType.wrap(14);
}

function POOL() pure returns (ObjectType) {
    return ObjectType.wrap(15);
}

/// @dev Application object type.
/// Range for NFT objects created thorugh components is 20-29.
function APPLICATION() pure returns (ObjectType) {
    return ObjectType.wrap(20);
}

function POLICY() pure returns (ObjectType) {
    return ObjectType.wrap(21);
}

function BUNDLE() pure returns (ObjectType) {
    return ObjectType.wrap(22);
}

function DISTRIBUTOR() pure returns (ObjectType) {
    return ObjectType.wrap(23);
}

/// @dev Stake object type.
/// NFT object type is 30
function STAKE() pure returns (ObjectType) {
    return ObjectType.wrap(30);
}

/// @dev Staking target object type.
function TARGET() pure returns (ObjectType) {
    return ObjectType.wrap(31);
}

/// @dev Accounting object type.
/// Range for non-NFT types created through components is 40+
function ACCOUNTING() pure returns (ObjectType) {
    return ObjectType.wrap(40);
}

function FEE() pure returns (ObjectType) {
    return ObjectType.wrap(41);
}

function PRICE() pure returns (ObjectType) {
    return ObjectType.wrap(42);
}

function PREMIUM() pure returns (ObjectType) {
    return ObjectType.wrap(43);
}

function RISK() pure returns (ObjectType) {
    return ObjectType.wrap(44);
}

function CLAIM() pure returns (ObjectType) {
    return ObjectType.wrap(45);
}

function PAYOUT() pure returns (ObjectType) {
    return ObjectType.wrap(46); 
}

function REQUEST() pure returns (ObjectType) {
    return ObjectType.wrap(47);
}

function DISTRIBUTOR_TYPE() pure returns (ObjectType) {
    return ObjectType.wrap(48);
}

function REFERRAL() pure returns (ObjectType) {
    return ObjectType.wrap(49);
}

/// @dev Object type for GIF core target roles.
function CORE() pure returns (ObjectType) {
    return ObjectType.wrap(97);
}

/// @dev Object type for target roles of contracts outside the GIF framework.
/// Example: Custom supporting contracts for a product component.
function CUSTOM() pure returns (ObjectType) {
    return ObjectType.wrap(98);
}

/// @dev Object type that includes any other object type.
/// Note that eq()/'==' does not take this property into account.
function ALL() pure returns (ObjectType) {
    return ObjectType.wrap(99);
}

// other pure free functions for operators
function eqObjectType(ObjectType a, ObjectType b) pure returns (bool isSame) {
    return ObjectType.unwrap(a) == ObjectType.unwrap(b);
}

function neObjectType(ObjectType a, ObjectType b) pure returns (bool isSame) {
    return ObjectType.unwrap(a) != ObjectType.unwrap(b);
}

// library functions that operate on user defined type
library ObjectTypeLib {

    function zero() public pure returns (ObjectType) {
        return ObjectType.wrap(0);
    }

    /// @dev Converts the uint256 into ObjectType.
    function toObjectType(uint256 objectType) public pure returns (ObjectType) {
        return ObjectType.wrap(uint8(objectType));
    }

    /// @dev Converts the NftId to a uint256.
    function toInt(ObjectType objectType) public pure returns (uint96) {
        return uint96(ObjectType.unwrap(objectType));
    }

    /// @dev Returns true if the value is non-zero (> 0).
    function gtz(ObjectType a) public pure returns (bool) {
        return ObjectType.unwrap(a) > 0;
    }

    /// @dev Returns true if the value is zero (== 0).
    function eqz(ObjectType a) public pure returns (bool) {
        return ObjectType.unwrap(a) == 0;
    }

    /// @dev Returns true if the values are equal (==).
    function eq(ObjectType a, ObjectType b) public pure returns (bool isSame) {
        return ObjectType.unwrap(a) == ObjectType.unwrap(b);
    }

    /// @dev Returns true if the values are not equal (!=).
    function ne(ObjectType a, ObjectType b) public pure returns (bool isSame) {
        return ObjectType.unwrap(a) != ObjectType.unwrap(b);
    }

    /// @dev Returns the type/domain name for the provided object type
    function toName(ObjectType objectType) public pure returns (string memory name) {
        if (objectType == REGISTRY()) {
            return "Registry";
        } else if (objectType == STAKING()) {
            return "Staking";
        } else if (objectType == RELEASE()) {
            return "Release";
        } else if (objectType == INSTANCE()) {
            return "Instance";
        } else if (objectType == COMPONENT()) {
            return "Component";
        } else if (objectType == PRODUCT()) {
            return "Product";
        } else if (objectType == ORACLE()) {
            return "Oracle";
        } else if (objectType == DISTRIBUTION()) {
            return "Distribution";
        } else if (objectType == POOL()) {
            return "Pool";
        } else if (objectType == APPLICATION()) {
            return "Application";
        } else if (objectType == POLICY()) {
            return "Policy";
        } else if (objectType == CLAIM()) {
            return "Claim";
        } else if (objectType == PRICE()) {
            return "Price";
        } else if (objectType == BUNDLE()) {
            return "Bundle";
        } else if (objectType == RISK()) {
            return "Risk";
        } else if (objectType == ACCOUNTING()) {
            return "Accounting";
        }

        // fallback: ObjectType<obect-type-int>
        return string(
            abi.encodePacked(
                "ObjectType",
                StrLib.uintToString(
                    toInt(objectType))));
    }

    // TODO move to IService
    function toVersionedName(
        string memory name, 
        string memory suffix, 
        VersionPart release
    )
        external
        pure
        returns (string memory versionedName)
    {
        string memory versionName = "V0";

        if (release.toInt() >= 10) {
            versionName = "V";
        }

        versionedName = string(
            abi.encodePacked(
                name,
                suffix,
                versionName,
                release.toString()));
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

// uint40 allows for 65'535 claims with 16'777'216 payouts each per policy
type PayoutId is uint40;

import {ClaimId} from "./ClaimId.sol";
import {PAYOUT} from "./ObjectType.sol";
import {Key32, KeyId, Key32Lib} from "./Key32.sol";
import {NftId} from "./NftId.sol";

// type bindings
using {
    eqPayoutId as ==, 
    nePayoutId as !=, 
    PayoutIdLib.eqz,
    PayoutIdLib.gtz,
    PayoutIdLib.toInt,
    PayoutIdLib.toClaimId,
    PayoutIdLib.toPayoutNo,
    PayoutIdLib.toKey32
} for PayoutId global;


// pure free functions for operators
function eqPayoutId(PayoutId a, PayoutId b) pure returns (bool isSame) {
    return PayoutId.unwrap(a) == PayoutId.unwrap(b);
}

function nePayoutId(PayoutId a, PayoutId b) pure returns (bool isDifferent) {
    return PayoutId.unwrap(a) != PayoutId.unwrap(b);
}

// library functions that operate on user defined type
library PayoutIdLib {
    /// @dev Converts the PayoutId to a uint.
    function zero() public pure returns (PayoutId) {
        return PayoutId.wrap(0);
    }

    /// @dev Converts an uint into a PayoutId.
    function toPayoutId(ClaimId claimId, uint24 payoutNo) public pure returns (PayoutId) {
        return PayoutId.wrap((uint40(ClaimId.unwrap(claimId)) << 24) + payoutNo);
    }

    function toClaimId(PayoutId payoutId) public pure returns (ClaimId) {
        return ClaimId.wrap(uint16(PayoutId.unwrap(payoutId) >> 24));
    }

    function toPayoutNo(PayoutId payoutId) public pure returns (uint24) {
        return uint24(PayoutId.unwrap(payoutId) & 16777215);
    }

    /// @dev Converts the PayoutId to a uint.
    function toInt(PayoutId a) public pure returns (uint40) {
        return PayoutId.unwrap(a);
    }

    /// @dev Returns true if the value is non-zero (> 0).
    function gtz(PayoutId a) public pure returns (bool) {
        return PayoutId.unwrap(a) > 0;
    }

    /// @dev Returns true if the value is zero (== 0).
    function eqz(PayoutId a) public pure returns (bool) {
        return PayoutId.unwrap(a) == 0;
    }

    /// @dev Converts the PayoutId and NftId to a Key32.
    function toKey32(PayoutId payoutId, NftId policyNftId) public pure returns (Key32) {
        return Key32Lib.toKey32(PAYOUT(), toKeyId(payoutId, policyNftId));
    }

    /// @dev Converts the PayoutId and NftId to a Key32.
    function toKeyId(PayoutId payoutId, NftId policyNftId) public pure returns (KeyId) {
        return KeyId.wrap(
            bytes31(
                bytes15(
                    uint120(
                        (NftId.unwrap(policyNftId) << 40) + PayoutId.unwrap(payoutId)))));
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {Key32, KeyId, Key32Lib} from "./Key32.sol";
import {ObjectType, ROLE} from "./ObjectType.sol";
import {VersionPart, VersionPartLib} from "./Version.sol";

type RoleId is uint64;

// type bindings
using {
    eqRoleId as ==, 
    neRoleId as !=,
    RoleIdLib.toInt,
    RoleIdLib.isServiceRole,
    RoleIdLib.eqz,
    RoleIdLib.gtz
} for RoleId global;

// general pure free functions

// @dev Returns true iff role ids a and b are identical
function eqRoleId(RoleId a, RoleId b) pure returns (bool isSame) {
    return RoleId.unwrap(a) == RoleId.unwrap(b);
}

// @dev Returns true iff role ids a and b are different
function neRoleId(RoleId a, RoleId b) pure returns (bool isDifferent) {
    return RoleId.unwrap(a) != RoleId.unwrap(b);
}

//--- OpenZeppelin provided roles -------------------------------------------//

/// @dev Role ID needs to match with oz AccessManager.ADMIN_ROLE
function ADMIN_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(type(uint64).min); }

/// @dev Role ID needs to match with oz AccessManager.PUBLIC_ROLE
function PUBLIC_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(type(uint64).max); }

//--- GIF roles (range: 1-99) ----------------------------------------------//

/// @dev cental role for gif release management.
/// this role is necessary to call ReleaseManager.createNextRelease/activateNextRelease
/// the actual deployment of a release requires the GIF_MANAGER_ROLE.
/// GIF_ADMIN_ROLE is the admin of the GIF_MANAGER_ROLE.
/// only a single holder may hold this role at any time
function GIF_ADMIN_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(1); } 

/// @dev role for token whith/blacklisting, deploying and registering the services for a new major release
/// registering services for a new major release is only possible after a new initial release has been created by the GIF_ADMIN_ROLE
/// token white/blacklisting is possible for any active release
function GIF_MANAGER_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(2); } 

/// @dev role for registering remote staking targets and reporting remote total value locked amounts.
function GIF_REMOTE_MANAGER_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(3); } 

// TODO check if/where this is really needed
/// @dev role assigned to release registry, release specfic to lock/unlock a release
function RELEASE_REGISTRY_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(4); }

/// @dev role assigned to every instance owner
function INSTANCE_OWNER_ROLE() pure returns (RoleId) { return RoleIdLib.toRoleId(5); }  

// TODO upate role id ranges
//--- GIF core contract roles (range: 200 - 9'900) --------------------------//
// created and assigned during initial deployment for registry and staking
// granting for instances and components in instance service
// object type * 100 + 0, examples:
// - registry contract role: 200 
// - staking contract role: 300 
// - instance contract role: 1000

//--- GIF service roles (range 201 - 9'9xx) ---------------------------------//
// created and assigned by release manager contract
// object type * 100 + 1/major version, examples:
// - registry service role (any version): 299
// - registry service role (version 3): 203
// - registry service role (any version): 399
// - staking service role: (version 3): 303
// - application service role (version 3): 2003

//--- GIF component contract roles (range 12'001 - 19'099) ------------------//
// the min value of 12'001 is based on the following calculation:
// object type * 1000 + 1 where the lowest object type is 12 (product) 
// assigned at component registration time
// object type * 1000 + instane specific component counter
// on any instance a maximum number of 999 components may be deployed
// examples:
// - 1st pool on instance: 15001
// - 1st distribution on instance: 14002
// - 1st product on instance: 12003
// - 2nd pool on instance: 15004
// - 2nd distribution on instance: 14005
// - 2nd product on instance: 12006


//--- Custom roles (range >= 1'000'000) -------------------------------------//

function CUSTOM_ROLE_MIN() pure returns (RoleId) { return RoleIdLib.toRoleId(1000000); }

library RoleIdLib {

    error ErrorRoleIdTooBig(uint256 roleId);

    // constant values need to match with AccessAdminLib.SERVICE_ROLE_*
    uint64 public constant SERVICE_ROLE_MIN =  1000;
    uint64 public constant SERVICE_ROLE_MAX = 99099; // 99 (max object type) * 1000 + 99

    uint64 public constant SERVICE_ROLE_FACTOR = 1000;

    /// @dev Converts the RoleId to a uint.
    function zero() public pure returns (RoleId) {
        return RoleId.wrap(0);
    }


    /// @dev Converts an uint into a role id.
    function toRoleId(uint256 a) public pure returns (RoleId) {
        if (a > type(uint64).max) {
            revert ErrorRoleIdTooBig(a);
        }

        return RoleId.wrap(uint64(a));
    }


    function isServiceRole(RoleId roleId)
        public
        pure
        returns (bool)
    {
        uint256 roleIdInt = RoleId.unwrap(roleId);
        return roleIdInt >= SERVICE_ROLE_MIN && roleIdInt <= SERVICE_ROLE_MAX;
    }


    function toGenericServiceRoleId(
        ObjectType objectType
    )
        public 
        pure 
        returns (RoleId)
    {
        return toServiceRoleId(
            objectType, 
            VersionPartLib.releaseMax());
    }


    function toServiceRoleId(
        ObjectType serviceDomain, 
        VersionPart release
    )
        public 
        pure 
        returns (RoleId serviceRoleId)
    {
        uint256 serviceRoleIdInt = 
            SERVICE_ROLE_MIN 
            + SERVICE_ROLE_FACTOR * (serviceDomain.toInt() - 1)
            + release.toInt();

        return toRoleId(serviceRoleIdInt);
    }

    /// @dev Converts the role id to a uint.
    function toInt(RoleId a) public pure returns (uint64) {
        return uint64(RoleId.unwrap(a));
    }

    /// @dev Returns true if the value is non-zero (> 0).
    function gtz(RoleId a) public pure returns (bool) {
        return RoleId.unwrap(a) > 0;
    }

    /// @dev Returns true if the value is zero (== 0).
    function eqz(RoleId a) public pure returns (bool) {
        return RoleId.unwrap(a) == 0;
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

/// @dev Target: Cover durations of 1000 years.
type Seconds is uint40;

using {
    SecondsEq as ==,
    SecondsLt as <,
    SecondsGt as >,
    SecondsAdd as +,
    SecondsLib.eqz,
    SecondsLib.gtz,
    SecondsLib.eq,
    SecondsLib.gt,
    SecondsLib.lt,
    SecondsLib.toInt,
    SecondsLib.add
} for Seconds global;

function SecondsEq(Seconds duration1, Seconds duration2) pure returns (bool) {
    return SecondsLib.eq(duration1, duration2);
}

function SecondsLt(Seconds duration1, Seconds duration2) pure returns (bool) {
    return SecondsLib.lt(duration1, duration2);
}

function SecondsGt(Seconds duration1, Seconds duration2) pure returns (bool) {
    return SecondsLib.gt(duration1, duration2);
}

function SecondsAdd(Seconds duration1, Seconds duration2) pure returns (Seconds) {
    return SecondsLib.add(duration1, duration2);
}


library SecondsLib {

    error ErrorSecondsLibDurationTooBig(uint256 duration);

    function zero() public pure returns (Seconds) {
        return Seconds.wrap(0);
    }

    function max() public pure returns (Seconds) {
        return Seconds.wrap(_max());
    }

    function fromHours(uint32 numberOfHours) public pure returns (Seconds duration) {
        return Seconds.wrap(numberOfHours * 3600);
    }

    function oneDay() public pure returns (Seconds duration) {
        return Seconds.wrap(24 * 3600);
    }

    function fromDays(uint32 numberOfDays) public pure returns (Seconds duration) {
        return Seconds.wrap(numberOfDays * 24 * 3600);
    }

    function oneYear() public pure returns (Seconds duration) {
        return Seconds.wrap(365 * 24 * 3600);
    }

    /// @dev converts the uint duration into Seconds
    /// function reverts if duration is exceeding max Seconds value
    function toSeconds(uint256 duration) public pure returns (Seconds) {
        // if(duration > type(uint40).max) {
        if(duration > _max()) {
            revert ErrorSecondsLibDurationTooBig(duration);
        }

        return Seconds.wrap(uint40(duration));
    }

    /// @dev return true if duration equals 0
    function eqz(Seconds duration) public pure returns (bool) {
        return Seconds.unwrap(duration) == 0;
    }

    /// @dev return true if duration is larger than 0
    function gtz(Seconds duration) public pure returns (bool) {
        return Seconds.unwrap(duration) > 0;
    }

    /// @dev return true iff duration1 and duration2 are the same
    function eq(Seconds duration1, Seconds duration2) public pure returns (bool) {
        return Seconds.unwrap(duration1) == Seconds.unwrap(duration2);
    }

    /// @dev return true if duration1 is larger than duration2
    function gt(Seconds duration1, Seconds duration2) public pure returns (bool) {
        return Seconds.unwrap(duration1) > Seconds.unwrap(duration2);
    }

    /// @dev return true if duration1 is smaller than duration2
    function lt(Seconds duration1, Seconds duration2) public pure returns (bool) {
        return Seconds.unwrap(duration1) < Seconds.unwrap(duration2);
    }

    /// @dev returns the smaller of the duration
    function min(Seconds duration1, Seconds duration2) public pure returns (Seconds) {
        if (Seconds.unwrap(duration1) < Seconds.unwrap(duration2)) {
            return duration1;
        } 
        
        return duration2;
    }   

    /// @dev return add duration1 and duration2
    function add(Seconds duration1, Seconds duration2) public pure returns (Seconds) {
        return Seconds.wrap(Seconds.unwrap(duration1) + Seconds.unwrap(duration2));
    }

    function toInt(Seconds duration) public pure returns (uint256) {
        return uint256(uint40(Seconds.unwrap(duration)));
    }

    function _max() internal pure returns (uint40) {
        // IMPORTANT: type nees to match with actual definition for Seconds
        return type(uint40).max;
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

type Selector is bytes4;

// type bindings
using {
    eqSelector as ==, 
    neSelector as !=, 
    SelectorLib.toBytes4,
    SelectorLib.toString,
    SelectorLib.eqz
} for Selector global;

// pure free functions for operators
function eqSelector(Selector s1, Selector s2) pure returns (bool isSame) {
    return SelectorLib.eq(s1, s2);
}

function neSelector(Selector s1, Selector s2) pure returns (bool isDifferent) {
    return SelectorLib.ne(s1, s2);
}

// library functions that operate on user defined type
library SelectorLib {

    function zero() public pure returns (Selector) {
        return Selector.wrap("");
    }

    function eqz(Selector s) public pure returns (bool) {
        return Selector.unwrap(s) == "";
    }

    function eq(Selector s1, Selector s2) public pure returns (bool isSame) {
        return Selector.unwrap(s1) == Selector.unwrap(s2);
    }

    function ne(Selector s1, Selector s2) public pure returns (bool isDifferent) {
        return Selector.unwrap(s1) != Selector.unwrap(s2);
    }

    function toSelector(bytes4 selector) public pure returns (Selector) {
        return Selector.wrap(selector);
    }

    function toBytes4(Selector s) public pure returns (bytes4) {
        return Selector.unwrap(s);
    }

    function toString(Selector s) public pure returns (string memory) {
        return string(abi.encode(Selector.unwrap(s)));
    }
}

// selector specific set library
// based on https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/utils/structs/EnumerableSet.sol
library SelectorSetLib {

    struct Set {
        Selector[] selectors;
        mapping(Selector selector => uint256 index) at;
    }

    function add(Set storage set, Selector selector) external {
        // selector already in set
        if (set.at[selector] > 0) { return; }

        set.selectors.push(selector);
        set.at[selector] = set.selectors.length;
    }

    function remove(Set storage set, Selector selector) external {
        uint256 selectorIndex = set.at[selector];

        // selector not in set
        if (selectorIndex == 0) {return; }

        uint256 toDeleteIndex = selectorIndex - 1;
        uint256 lastIndex = set.selectors.length - 1;

        if (lastIndex != toDeleteIndex) {
            Selector lastSelector = set.selectors[lastIndex];
            set.selectors[toDeleteIndex] = lastSelector;
            set.at[lastSelector] = selectorIndex; // Replace lastValue's index to valueIndex
        }

        set.selectors.pop();
        delete set.at[selector];
    }

    function isEmpty(Set storage set) external view returns(bool empty) {
        return set.selectors.length == 0;
    }

    function contains(Set storage set, Selector selector) external view returns(bool inSet) {
        return set.at[selector] > 0;
    }

    function size(Set storage set) external view returns(uint256 length) {
        return set.selectors.length;
    }

    function at(Set storage set, uint256 index) external view returns(Selector selector) {
        return set.selectors[index];
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

// uint96 allows for chain ids up to 13 digits
type StateId is uint8;

// type bindings
using {
    eqStateId as ==, 
    neStateId as !=, 
    StateIdLib.eqz,
    StateIdLib.eq,
    StateIdLib.gtz,
    StateIdLib.toInt
} for StateId global;

// general pure free functions

function INITIAL() pure returns (StateId) {
    return toStateId(1);
}

function SCHEDULED() pure returns (StateId) {
    return toStateId(2);
}

function DEPLOYING() pure returns (StateId) {
    return toStateId(3);
}

function DEPLOYED() pure returns (StateId) {
    return toStateId(4);
}

function ACTIVE() pure returns (StateId) {
    return toStateId(5);
}

function SKIPPED() pure returns (StateId) {
    return toStateId(6);
}

function APPLIED() pure returns (StateId) {
    return toStateId(10);
}

function REVOKED() pure returns (StateId) {
    return toStateId(20);
}

function DECLINED() pure returns (StateId) {
    return toStateId(30);
}

function COLLATERALIZED() pure returns (StateId) {
    return toStateId(40);
}

function SUBMITTED() pure returns (StateId) {
    return toStateId(50);
}

function CONFIRMED() pure returns (StateId) {
    return toStateId(51);
}

function EXPECTED() pure returns (StateId) {
    return toStateId(60);
}

function FULFILLED() pure returns (StateId) {
    return toStateId(70);
}

function FAILED() pure returns (StateId) {
    return toStateId(7);
}

function CANCELLED() pure returns (StateId) {
    return toStateId(72);
}

function PAUSED() pure returns (StateId) {
    return toStateId(110);
}

function CLOSED() pure returns (StateId) {
    return toStateId(200);
}

function ARCHIVED() pure returns (StateId) {
    return toStateId(210);
}

function PAID() pure returns (StateId) {
    return toStateId(220);
}

function KEEP_STATE() pure returns (StateId) {
    return toStateId(type(uint8).max);
}

/// @dev Converts the uint8 to a StateId.
function toStateId(uint256 id) pure returns (StateId) {
    return StateId.wrap(uint8(id));
}

// TODO move to StateIdLib and rename to zero()
/// @dev Return the StateId zero (0)
function zeroStateId() pure returns (StateId) {
    return StateId.wrap(0);
}

// pure free functions for operators
function eqStateId(StateId a, StateId b) pure returns (bool isSame) {
    return StateId.unwrap(a) == StateId.unwrap(b);
}

function neStateId(StateId a, StateId b) pure returns (bool isDifferent) {
    return StateId.unwrap(a) != StateId.unwrap(b);
}

// library functions that operate on user defined type
library StateIdLib {

    function zero() public pure returns (StateId) {
        return StateId.wrap(0);
    }

    /// @dev Converts the NftId to a uint256.
    function toInt(StateId stateId) public pure returns (uint96) {
        return uint96(StateId.unwrap(stateId));
    }

    /// @dev Returns true if the value is non-zero (> 0).
    function gtz(StateId a) public pure returns (bool) {
        return StateId.unwrap(a) > 0;
    }

    /// @dev Returns true if the value is zero (== 0).
    function eqz(StateId a) public pure returns (bool) {
        return StateId.unwrap(a) == 0;
    }

    /// @dev Returns true if the values are equal (==).
    function eq(StateId a, StateId b) public pure returns (bool isSame) {
        return eqStateId(a, b);
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {ShortString, ShortStrings} from "@openzeppelin/contracts/utils/ShortStrings.sol";

type Str is bytes32;

using {
    StrEq as ==,
    StrNe as !=,
    StrLib.toString,
    StrLib.length
} for Str global;

// pure free function needed for the operator overloading
function StrEq(Str s1, Str s2) pure returns (bool) {
    return StrLib.eq(s1, s2);
}

// pure free function needed for the operator overloading
function StrNe(Str s1, Str s2) pure returns (bool) {
    return StrLib.ne(s1, s2);
}

library StrLib {


    /// @dev converts the provided string into a short string.
    /// uses ShortStrings.toShortString
    function toStr(string memory str) public pure returns (Str) {
        return Str.wrap(ShortString.unwrap(ShortStrings.toShortString(str)));
    }

    /// @dev return true iff s1 equals s2
    function eq(Str s1, Str s2) public pure returns (bool) {
        return Str.unwrap(s1) == Str.unwrap(s2);
    }

    /// @dev return true iff s1 differs from s2
    function ne(Str s1, Str s2) public pure returns (bool) {
        return Str.unwrap(s1) != Str.unwrap(s2);
    }

    /// @dev return true iff s1 equals from s2
    function eq(string memory s1, string memory s2) public pure returns (bool) {
        return keccak256(bytes(s1)) == keccak256(bytes(s2));
    }

    /// @dev return true iff s1 differs s2
    function ne(string memory s1, string memory s2) public pure returns (bool) {
        return !eq(s1, s2);
    }

    /// @dev converts the provided short string into a string.
    /// uses ShortStrings.toString
    function toString(Str str) public pure returns (string memory) {
        return ShortStrings.toString(ShortString.wrap(Str.unwrap(str)));
    }

    /// @dev converts the provided short string into a string.
    /// uses ShortStrings.byteLength
    function length(Str str) public pure returns (uint256 byteLength) {
        return ShortStrings.byteLength(ShortString.wrap(Str.unwrap(str)));
    }

    /// @dev Returns the provied int as a string
    function uintToString(uint256 value) public pure returns (string memory name) {

        if (value == 0) {
            return "0";
        }

        uint256 temp = value;
        uint256 digits = 0;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }

        bytes memory buffer = new bytes(digits);
        uint256 index = digits - 1;

        temp = value;
        while (temp != 0) {
            buffer[index] = bytes1(uint8(48 + temp % 10));
            temp /= 10;

            if (index > 0) {
                index--;
            }
        }

        return string(buffer);
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

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

/// @dev Target: Cover 10 years with 1 ms block time resolution.
/// Typical block time resolution is 1s.
type Timestamp is uint40;

using {
    gtTimestamp as >,
    gteTimestamp as >=,
    ltTimestamp as <,
    lteTimestamp as <=,
    eqTimestamp as ==,
    neTimestamp as !=,
    TimestampLib.eq,
    TimestampLib.ne,
    TimestampLib.gt,
    TimestampLib.gte,
    TimestampLib.lt,
    TimestampLib.lte,
    TimestampLib.gtz,
    TimestampLib.eqz,
    TimestampLib.addSeconds,
    TimestampLib.subtractSeconds,
    TimestampLib.toInt
} for Timestamp global;

/// @dev return true if Timestamp a is after Timestamp b
function gtTimestamp(Timestamp a, Timestamp b) pure returns (bool) {
    return Timestamp.unwrap(a) > Timestamp.unwrap(b);
}

/// @dev return true if Timestamp a is after or equal to Timestamp b
function gteTimestamp(Timestamp a, Timestamp b) pure returns (bool) {
    return Timestamp.unwrap(a) >= Timestamp.unwrap(b);
}

/// @dev return true if Timestamp a is before Timestamp b
function ltTimestamp(Timestamp a, Timestamp b) pure returns (bool) {
    return Timestamp.unwrap(a) < Timestamp.unwrap(b);
}

/// @dev return true if Timestamp a is before or equal to Timestamp b
function lteTimestamp(Timestamp a, Timestamp b) pure returns (bool) {
    return Timestamp.unwrap(a) <= Timestamp.unwrap(b);
}

/// @dev return true if Timestamp a is equal to Timestamp b
function eqTimestamp(Timestamp a, Timestamp b) pure returns (bool) {
    return Timestamp.unwrap(a) == Timestamp.unwrap(b);
}

/// @dev return true if Timestamp a is not equal to Timestamp b
function neTimestamp(Timestamp a, Timestamp b) pure returns (bool) {
    return Timestamp.unwrap(a) != Timestamp.unwrap(b);
}

// TODO move to TimestampLib and rename to zero()
/// @dev Return the Timestamp zero (0)
function zeroTimestamp() pure returns (Timestamp) {
    return Timestamp.wrap(0);
}

library TimestampLib {

    function zero() public pure returns (Timestamp) {
        return Timestamp.wrap(0);
    }

    function max() public pure returns (Timestamp) {
        return Timestamp.wrap(type(uint40).max);
    }

    function current() public view returns (Timestamp) {
        return Timestamp.wrap(uint40(block.timestamp));
    }

    function toTimestamp(uint256 timestamp) public pure returns (Timestamp) {
        return Timestamp.wrap(uint40(timestamp));
    }
    
    /// @dev return true if Timestamp a is after Timestamp b
    function gt(Timestamp a, Timestamp b) public pure returns (bool isAfter) {
        return gtTimestamp(a, b);
    }

    /// @dev return true if Timestamp a is after or the same than Timestamp b
    function gte(
        Timestamp a,
        Timestamp b
    ) public pure returns (bool isAfterOrSame) {
        return gteTimestamp(a, b);
    }

    /// @dev return true if Timestamp a is before Timestamp b
    function lt(Timestamp a, Timestamp b) public pure returns (bool isBefore) {
        return ltTimestamp(a, b);
    }

    /// @dev return true if Timestamp a is before or the same than Timestamp b
    function lte(
        Timestamp a,
        Timestamp b
    ) public pure returns (bool isBeforeOrSame) {
        return lteTimestamp(a, b);
    }

    /// @dev return true if Timestamp a is equal to Timestamp b
    function eq(Timestamp a, Timestamp b) public pure returns (bool isSame) {
        return eqTimestamp(a, b);
    }

    /// @dev return true if Timestamp a is not equal to Timestamp b
    function ne(
        Timestamp a,
        Timestamp b
    ) public pure returns (bool isDifferent) {
        return neTimestamp(a, b);
    }

    /// @dev return true if Timestamp equals 0
    function eqz(Timestamp timestamp) public pure returns (bool) {
        return Timestamp.unwrap(timestamp) == 0;
    }

    /// @dev return true if Timestamp is larger than 0
    function gtz(Timestamp timestamp) public pure returns (bool) {
        return Timestamp.unwrap(timestamp) > 0;
    }

    /// @dev return a new timestamp that is duration seconds later than the provided timestamp.
    function addSeconds(
        Timestamp timestamp,
        Seconds duration
    ) public pure returns (Timestamp) {
        return toTimestamp(Timestamp.unwrap(timestamp) + duration.toInt());
    }

    /// @dev return a new timestamp that is duration seconds earlier than the provided timestamp.
    function subtractSeconds(
        Timestamp timestamp,
        Seconds duration
    ) public pure returns (Timestamp) {
        return toTimestamp(Timestamp.unwrap(timestamp) - duration.toInt());
    }

    function toInt(Timestamp timestamp) public pure returns (uint256) {
        return uint256(uint40(Timestamp.unwrap(timestamp)));
    }
}

File 45 of 47 : UFixed.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";

/// @dev UFixed is a 160-bit fixed point number with 15 decimals precision.
type UFixed is uint160; 

using {
    addUFixed as +,
    subUFixed as -,
    mulUFixed as *,
    divUFixed as /,
    gtUFixed as >,
    gteUFixed as >=,
    ltUFixed as <,
    lteUFixed as <=,
    eqUFixed as ==,
    neUFixed as !=,
    UFixedLib.gt,
    UFixedLib.eqz,
    UFixedLib.gtz,
    UFixedLib.toInt,
    UFixedLib.toInt1000
} for UFixed global;

// TODO move to UFixedLib and rename to zero()
function zeroUFixed() pure returns (UFixed zero) {
    return UFixed.wrap(0);
}

function addUFixed(UFixed a, UFixed b) pure returns (UFixed) {
    return UFixed.wrap(UFixed.unwrap(a) + UFixed.unwrap(b));
}

function subUFixed(UFixed a, UFixed b) pure returns (UFixed) {
    if (a < b) {
        revert UFixedLib.UFixedLibNegativeResult();
    }
    return UFixed.wrap(UFixed.unwrap(a) - UFixed.unwrap(b));
}

function mulUFixed(UFixed a, UFixed b) pure returns (UFixed) {
    return
        UFixed.wrap(uint160(Math.mulDiv(UFixed.unwrap(a), UFixed.unwrap(b), 10 ** 15)));
}

function divUFixed(UFixed a, UFixed b) pure returns (UFixed) {
    if (UFixed.unwrap(b) == 0) {
        revert UFixedLib.UFixedLibDivisionByZero();
    }
    
    return
        UFixed.wrap(uint160(Math.mulDiv(UFixed.unwrap(a), 10 ** 15, UFixed.unwrap(b))));
}

function gtUFixed(UFixed a, UFixed b) pure returns (bool isGreaterThan) {
    return UFixed.unwrap(a) > UFixed.unwrap(b);
}

function gteUFixed(UFixed a, UFixed b) pure returns (bool isGreaterThan) {
    return UFixed.unwrap(a) >= UFixed.unwrap(b);
}

function ltUFixed(UFixed a, UFixed b) pure returns (bool isGreaterThan) {
    return UFixed.unwrap(a) < UFixed.unwrap(b);
}

function lteUFixed(UFixed a, UFixed b) pure returns (bool isGreaterThan) {
    return UFixed.unwrap(a) <= UFixed.unwrap(b);
}

function eqUFixed(UFixed a, UFixed b) pure returns (bool isEqual) {
    return UFixed.unwrap(a) == UFixed.unwrap(b);
}

function neUFixed(UFixed a, UFixed b) pure returns (bool isEqual) {
    return UFixed.unwrap(a) != UFixed.unwrap(b);
}

function gtzUFixed(UFixed a) pure returns (bool isZero) {
    return UFixed.unwrap(a) > 0;
}

function eqzUFixed(UFixed a) pure returns (bool isZero) {
    return UFixed.unwrap(a) == 0;
}

function deltaUFixed(UFixed a, UFixed b) pure returns (UFixed) {
    if (a > b) {
        return a - b;
    }

    return b - a;
}

library UFixedLib {
    error UFixedLibNegativeResult();
    error UFixedLibDivisionByZero();

    error UFixedLibExponentTooSmall(int8 exp);
    error UFixedLibExponentTooLarge(int8 exp);

    error UFixedLibNumberTooLarge(uint256 number);

    int8 public constant EXP = 15;
    uint256 public constant MULTIPLIER = 10 ** uint256(int256(EXP));
    uint256 public constant MULTIPLIER_HALF = MULTIPLIER / 2;

    /// @dev returns the rounding mode DOWN - 0.4 becomes 0, 0.5 becomes 0, 0.6 becomes 0
    function ROUNDING_DOWN() public pure returns (uint8) {
        return uint8(0);
    }

    /// @dev returns the rounding mode UP - 0.4 becomes 1, 0.5 becomes 1, 0.6 becomes 1
    function ROUNDING_UP() public pure returns (uint8) {
        return uint8(1);
    }

    /// @dev returns the rounding mode HALF_UP - 0.4 becomes 0, 0.5 becomes 1, 0.6 becomes 1
    function ROUNDING_HALF_UP() public pure returns (uint8) {
        return uint8(2);
    }

    /// @dev Converts the uint256 to a uint160 based UFixed. 
    /// This method reverts if the number is too large to fit in a uint160.
    function toUFixed(uint256 a) public pure returns (UFixed) {
        uint256 n = a * MULTIPLIER;
        if (n > type(uint160).max) {
            revert UFixedLibNumberTooLarge(a);
        }
        return UFixed.wrap(uint160(n));
    }

    /// @dev Converts the uint256 to a UFixed with given exponent.
    function toUFixed(uint256 a, int8 exp) public pure returns (UFixed) {
        if (EXP + exp < 0) {
            revert UFixedLibExponentTooSmall(exp);
        }
        if (EXP + exp > 48) {
            revert UFixedLibExponentTooLarge(exp);
        }
        
        uint256 n = a * 10 ** uint8(EXP + exp);

        if (n > type(uint160).max) {
            revert UFixedLibNumberTooLarge(n);
        }

        return UFixed.wrap(uint160(n));
    }

    /// @dev returns the decimals precision of the UFixed type
    function decimals() public pure returns (uint256) {
        return uint8(EXP);
    }

    /// @dev Converts a UFixed to a uint256.
    function toInt(UFixed a) public pure returns (uint256) {
        return toIntWithRounding(a, ROUNDING_HALF_UP());
    }

    /// @dev Converts a UFixed to a uint256.
    function toInt1000(UFixed a) public pure returns (uint256) {
        return toIntWithRounding(toUFixed(1000) * a, ROUNDING_HALF_UP());
    }

    /// @dev Converts a UFixed to a uint256 with given rounding mode.
    function toIntWithRounding(UFixed a, uint8 rounding) public pure returns (uint256) {
        if (rounding == ROUNDING_HALF_UP()) {
            return
                Math.mulDiv(
                    UFixed.unwrap(a) + MULTIPLIER_HALF,
                    1,
                    MULTIPLIER,
                    Math.Rounding.Floor
                );
        } else if (rounding == ROUNDING_DOWN()) {
            return
                Math.mulDiv(
                    UFixed.unwrap(a),
                    1,
                    MULTIPLIER,
                    Math.Rounding.Floor
                );
        } else {
            return
                Math.mulDiv(UFixed.unwrap(a), 1, MULTIPLIER, Math.Rounding.Ceil);
        }
    }

    /// @dev adds two UFixed numbers
    function add(UFixed a, UFixed b) public pure returns (UFixed) {
        return addUFixed(a, b);
    }

    /// @dev subtracts two UFixed numbers
    function sub(UFixed a, UFixed b) public pure returns (UFixed) {
        return subUFixed(a, b);
    }

    /// @dev multiplies two UFixed numbers
    function mul(UFixed a, UFixed b) public pure returns (UFixed) {
        return mulUFixed(a, b);
    }

    /// @dev divides two UFixed numbers
    function div(UFixed a, UFixed b) public pure returns (UFixed) {
        return divUFixed(a, b);
    }

    /// @dev return true if UFixed a is greater than UFixed b
    function gt(UFixed a, UFixed b) public pure returns (bool isGreaterThan) {
        return gtUFixed(a, b);
    }

    /// @dev return true if UFixed a is greater than or equal to UFixed b
    function gte(UFixed a, UFixed b) public pure returns (bool isGreaterThan) {
        return gteUFixed(a, b);
    }

    /// @dev return true if UFixed a is less than UFixed b
    function lt(UFixed a, UFixed b) public pure returns (bool isGreaterThan) {
        return ltUFixed(a, b);
    }

    /// @dev return true if UFixed a is less than or equal to UFixed b
    function lte(UFixed a, UFixed b) public pure returns (bool isGreaterThan) {
        return lteUFixed(a, b);
    }

    /// @dev return true if UFixed a is equal to UFixed b
    function eq(UFixed a, UFixed b) public pure returns (bool isEqual) {
        return eqUFixed(a, b);
    }

    /// @dev return true if UFixed a is not zero
    function gtz(UFixed a) public pure returns (bool isZero) {
        return gtzUFixed(a);
    }

    /// @dev return true if UFixed a is zero
    function eqz(UFixed a) public pure returns (bool isZero) {
        return eqzUFixed(a);
    }

    function zero() public pure returns (UFixed) {
        return UFixed.wrap(0);
    }

    function one() public pure returns (UFixed) {
        return UFixed.wrap(uint160(MULTIPLIER));
    }

    function max() public pure returns (UFixed) {
        return UFixed.wrap(type(uint160).max);
    }

    /// @dev return the absolute delta between two UFixed numbers
    function delta(UFixed a, UFixed b) public pure returns (UFixed) {
        return deltaUFixed(a, b);
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

type VersionPart is uint8;

using {
    versionPartGt as >,
    versionPartEq as ==,
    versionPartNe as !=,
    VersionPartLib.eqz,
    VersionPartLib.gtz,
    VersionPartLib.toInt,
    VersionPartLib.toString,
    VersionPartLib.isValidRelease
}
    for VersionPart global;

function versionPartGt(VersionPart a, VersionPart b) pure returns(bool isGreaterThan) { return VersionPart.unwrap(a) > VersionPart.unwrap(b); }
function versionPartEq(VersionPart a, VersionPart b) pure returns(bool isSame) { return VersionPart.unwrap(a) == VersionPart.unwrap(b); }
function versionPartNe(VersionPart a, VersionPart b) pure returns(bool isSame) { return VersionPart.unwrap(a) != VersionPart.unwrap(b); }

library VersionPartLib {

    error ErrorReleaseTooBig(VersionPart releaseMax, VersionPart release);

    function releaseMin() public pure returns (VersionPart) { return toVersionPart(3); }
    function releaseMax() public pure returns (VersionPart) { return toVersionPart(99); }

    function isValidRelease(VersionPart release) external pure returns(bool) { 
        uint256 releaseInt = VersionPart.unwrap(release);
        return 3 <= releaseInt && releaseInt <= 99; 
    } 

    function toString(VersionPart a) external pure returns (string memory) {
        if (a > releaseMax()) {
            revert ErrorReleaseTooBig(releaseMax(), a);
        }

        uint256 value = VersionPart.unwrap(a);
        if (value == 0) {
            return "0";
        }

        uint256 temp = value;
        uint256 digits = 0;
        while (temp != 0) {
            digits++;
            temp /= 10;
        }

        bytes memory buffer = new bytes(digits);
        uint index = digits - 1;

        temp = value;
        while (temp != 0) {
            buffer[index] = bytes1(uint8(48 + temp % 10));
            temp /= 10;

            if (index > 0) {
                index--;
            }
        }

        return string(buffer);
    }

    function eqz(VersionPart a) external pure returns(bool) { return VersionPart.unwrap(a) == 0; }
    function gtz(VersionPart a) external pure returns(bool) { return VersionPart.unwrap(a) > 0; }
    function toInt(VersionPart a) external pure returns(uint256) { return VersionPart.unwrap(a); }
    function toVersionPart(uint256 a) public pure returns(VersionPart) { return VersionPart.wrap(uint8(a)); }
}

type Version is uint24; // contains major,minor,patch version parts

using {
    versionGt as >,
    versionEq as ==,
    VersionLib.toInt,
    VersionLib.toUint64,
    VersionLib.toMajorPart,
    VersionLib.toVersionParts
}
    for Version global;

function versionGt(Version a, Version b) pure returns(bool isGreaterThan) { return Version.unwrap(a) > Version.unwrap(b); }
function versionEq(Version a, Version b) pure returns(bool isSame) { return Version.unwrap(a) == Version.unwrap(b); }

library VersionLib {

    function toInt(Version version) external pure returns(uint) { return Version.unwrap(version); }

    function toUint64(Version version) external pure returns(uint64) { return Version.unwrap(version); }

    function toMajorPart(Version version)
        external    
        pure 
        returns(VersionPart major)
    { 
        uint24 versionInt = Version.unwrap(version);
        uint8 majorInt = uint8(versionInt >> 16);
        return VersionPart.wrap(majorInt);
    }

    function toVersionParts(Version version)
        external
        pure
        returns(
            VersionPart major,
            VersionPart minor,
            VersionPart patch
        )
    {
        uint24 versionInt = Version.unwrap(version);
        uint8 majorInt = uint8(versionInt >> 16);

        versionInt -= majorInt << 16;
        uint8 minorInt = uint8(versionInt >> 8);
        uint8 patchInt = uint8(versionInt - (minorInt << 8));

        return (
            VersionPart.wrap(majorInt),
            VersionPart.wrap(minorInt),
            VersionPart.wrap(patchInt)
        );
    }

    // function toVersionPart(uint256 versionPart) public pure returns(VersionPart) { 
    //     return VersionPart.wrap(uint8(versionPart)); 
    // }

    function toVersion(
        uint256 major,
        uint256 minor,
        uint256 patch
    )
        external
        pure
        returns(Version)
    {
        require(
            major < 256 && minor < 256 && patch < 256,
            "ERROR:VRS-010:VERSION_PART_TOO_BIG");

        return Version.wrap(
            uint24(
                (major << 16) + (minor << 8) + patch));
    }

    // TODO check for overflow?
    function toVersion(uint64 versionNumber) external pure returns(Version) {
        //assert(versionNumber <= type(Version).max);
        return Version.wrap(uint24(versionNumber));
    }

    // TODO rename to zero()
    function zeroVersion() external pure returns(Version) {
        return Version.wrap(0);
    }
}

// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {Version} from "../type/Version.sol";

/// IMPORTANT
// Upgradeable contract MUST:
// 1) inherit from Versionable
// 2) implement version() function
// 3) implement internal _initialize() function with onlyInitializing modifier 
// 4) implement internal _upgrade() function with onlyInitializing modifier (1st version MUST revert)
// 5) have onlyInitialising modifier for each function callable inside _initialize()/_upgrade() (MUST use different functions for initialization/upgrade and normal operations)
// 6) use default empty constructor -> _disableInitializer() is called from Versionable contructor
// 7) use namespace storage (should this be needed)
// 8) since now inheritance is used for upgradability, contract MUST BE inherited ONLY by the next version 
// Upgradeable contract SHOULD:
// 9) define all non private methods as virtual (in order to be able to upgrade them latter)
//    otherwise, it is still possible to upgrade contract, but everyone who is using it will have to switch to a new fucntions
//    in some cases this ok but not in the others...
//
// IMPORTANT
// If introducting/amending storage related to Versionable version MUST:
// 1) define namespace storage struct if accessing storage
//      - DO NOT use structs inside, except
//      - CAN use structs ONLY inside mappings
// 2) ALWAYS define private getter if accessing storage
//      - MUST use default implementation, CAN change ONLY return type

interface IVersionable {

    error ErrorVersionableInitializeNotImplemented();
    error ErrorVersionableUpgradeNotImplemented();

    /**
     * @dev IMPORTANT
     * implementation MUST be guarded by initializer modifier
     * new version MUST inherit from previous version
     */
    function initializeVersionable(address activatedBy, bytes memory activationData) external;

    /**
     * @dev
     * implementation MUST be guarded by reinitializer(version().toUint64()) modifier
     * new version MUST inherit from previous version
     * the first verion MUST revert 
     */
    function upgradeVersionable(bytes memory upgradeData) external;

    /**
     * @dev returns version of this contract
     * each new implementation MUST implement this function
     * version number MUST increase 
     */
    function getVersion() external pure returns(Version);

}

Settings
{
  "evmVersion": "cancun",
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {
    "contracts/shared/ContractLib.sol": {
      "ContractLib": "0x0Dca08c2260A16010538655b2894c4d7FeD280fA"
    },
    "contracts/type/NftId.sol": {
      "NftIdLib": "0xb6fE977f3e63D01a734DD238bA5FC05bf45BF32c"
    },
    "contracts/type/Amount.sol": {
      "AmountLib": "0x82860d6202c009092d0d7507380ba8ef8f4fd567"
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"registry","type":"address"},{"internalType":"address","name":"component","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"authority","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"authority","type":"address"}],"name":"AccessManagedInvalidAuthority","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"uint32","name":"delay","type":"uint32"}],"name":"AccessManagedRequiredDelay","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"AccessManagedUnauthorized","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"ErrorTokenHandlerAddressIsSameAsCurrent","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"allowance","type":"uint256"},{"internalType":"uint256","name":"expectedAllowance","type":"uint256"}],"name":"ErrorTokenHandlerAllowanceTooSmall","type":"error"},{"inputs":[],"name":"ErrorTokenHandlerAmountIsZero","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"balance","type":"uint256"},{"internalType":"uint256","name":"expectedBalance","type":"uint256"}],"name":"ErrorTokenHandlerBalanceTooLow","type":"error"},{"inputs":[{"internalType":"address","name":"component","type":"address"}],"name":"ErrorTokenHandlerComponentNotRegistered","type":"error"},{"inputs":[],"name":"ErrorTokenHandlerNewWalletAddressZero","type":"error"},{"inputs":[{"internalType":"address","name":"registry","type":"address"}],"name":"ErrorTokenHandlerNotRegistry","type":"error"},{"inputs":[{"internalType":"address","name":"service","type":"address"}],"name":"ErrorTokenHandlerNotService","type":"error"},{"inputs":[{"internalType":"NftId","name":"nftId","type":"uint96"},{"internalType":"address","name":"tokenHandler","type":"address"},{"internalType":"address","name":"wallet","type":"address"}],"name":"ErrorTokenHandlerNotWallet","type":"error"},{"inputs":[],"name":"ErrorTokenHandlerTokenAddressZero","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"authority","type":"address"}],"name":"AuthorityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"NftId","name":"nftId","type":"uint96"},{"indexed":false,"internalType":"address","name":"tokenHandler","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"Amount","name":"amount","type":"uint96"},{"indexed":false,"internalType":"bool","name":"isMaxAmount","type":"bool"}],"name":"LogTokenHandlerTokenApproved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"Amount","name":"amount","type":"uint96"}],"name":"LogTokenHandlerTokenTransfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"NftId","name":"componentNftId","type":"uint96"},{"indexed":false,"internalType":"address","name":"oldWallet","type":"address"},{"indexed":false,"internalType":"address","name":"newWallet","type":"address"}],"name":"LogTokenHandlerWalletAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"NftId","name":"componentNftId","type":"uint96"},{"indexed":false,"internalType":"address","name":"oldWallet","type":"address"},{"indexed":false,"internalType":"address","name":"newWallet","type":"address"},{"indexed":false,"internalType":"Amount","name":"amount","type":"uint96"}],"name":"LogTokenHandlerWalletTokensTransferred","type":"event"},{"inputs":[],"name":"COMPONENT","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NFT_ID","outputs":[{"internalType":"NftId","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REGISTRY","outputs":[{"internalType":"contract IRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"TOKEN","outputs":[{"internalType":"contract IERC20Metadata","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"token","type":"address"},{"internalType":"Amount","name":"amount","type":"uint96"}],"name":"approve","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"Amount","name":"amount","type":"uint96"},{"internalType":"bool","name":"checkAmount","type":"bool"}],"name":"checkBalanceAndAllowance","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWallet","outputs":[{"internalType":"address","name":"wallet","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isConsumingScheduledOp","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"Amount","name":"amount","type":"uint96"}],"name":"pullToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"Amount","name":"amount","type":"uint96"}],"name":"pushFeeToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"Amount","name":"amount","type":"uint96"}],"name":"pushToken","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAuthority","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newWallet","type":"address"}],"name":"setWallet","outputs":[],"stateMutability":"nonpayable","type":"function"}]

0x610100604052348015610010575f80fd5b5060405161231238038061231283398101604081905261002f9161084e565b8383838361003c816102c6565b50604051633334d16b60e01b81526001600160a01b0384166004820152730dca08c2260a16010538655b2894c4d7fed280fa90633334d16b90602401602060405180830381865af4158015610093573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906100b7919061089f565b6100e457604051633c165c4d60e01b81526001600160a01b03841660048201526024015b60405180910390fd5b6001600160a01b03811661010b57604051630d0482ff60e31b815260040160405180910390fd5b6001600160a01b03838116608081905290831660c0819052604051632f2a35f760e11b81526004810191909152635e546bee90602401602060405180830381865afa15801561015c573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061018091906108d2565b6001600160601b031660e0819052604051630a4d29dd60e31b8152600481019190915273b6fe977f3e63d01a734dd238ba5fc05bf45bf32c906352694ee890602401602060405180830381865af41580156101dd573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610201919061089f565b1561022a57604051634fd16de160e11b81526001600160a01b03831660048201526024016100db565b806001600160a01b031660a0816001600160a01b0316815250506102ba60a0517382860d6202c009092d0d7507380ba8ef8f4fd567636ac5db196040518163ffffffff1660e01b8152600401602060405180830381865af4158015610291573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102b591906108d2565b610319565b5050505050505061091a565b5f80546001600160a01b0319166001600160a01b0383169081179091556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad9060200160405180910390a150565b6001546001600160a01b0316156103695760e05160015460405163266a456b60e11b81526001600160601b0390921660048301523060248301526001600160a01b031660448201526064016100db565b60405163046e44af60e11b81526001600160601b03821660048201525f907382860d6202c009092d0d7507380ba8ef8f4fd567906308dc895e90602401602060405180830381865af41580156103c1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103e591906108ed565b90505f61045c837382860d6202c009092d0d7507380ba8ef8f4fd567636ac5db196040518163ffffffff1660e01b8152600401602060405180830381865af4158015610433573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061045791906108d2565b6104dd565b1561046957505f19905060015b60e051604080516001600160601b0392831681523060208201526001600160a01b0387168183015291851660608301528215156080830152517f87afdceff4fac7ed560df07efe2dbb4585adc663dd5fab969376f20d70cb7a449181900360a00190a16104d7843084610568565b50505050565b60405163c4cade9d60e01b81526001600160601b038084166004830152821660248201525f907382860d6202c009092d0d7507380ba8ef8f4fd5679063c4cade9d90604401602060405180830381865af415801561053d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610561919061089f565b9392505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b179091526105c0908590839061062616565b6104d757604080516001600160a01b03851660248201525f6044808301919091528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b1790915261061c9186916106c716565b6104d784826106c7565b5f805f846001600160a01b0316846040516106419190610904565b5f604051808303815f865af19150503d805f811461067a576040519150601f19603f3d011682016040523d82523d5f602084013e61067f565b606091505b50915091508180156106a95750805115806106a95750808060200190518101906106a9919061089f565b80156106be57505f856001600160a01b03163b115b95945050505050565b5f6106db6001600160a01b0384168361072d565b905080515f141580156106ff5750808060200190518101906106fd919061089f565b155b1561072857604051635274afe760e01b81526001600160a01b03841660048201526024016100db565b505050565b606061056183835f845f80856001600160a01b031684866040516107519190610904565b5f6040518083038185875af1925050503d805f811461078b576040519150601f19603f3d011682016040523d82523d5f602084013e610790565b606091505b5090925090506107a18683836107ab565b9695505050505050565b6060826107c0576107bb82610807565b610561565b81511580156107d757506001600160a01b0384163b155b1561080057604051639996b31560e01b81526001600160a01b03851660048201526024016100db565b5080610561565b8051156108175780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b50565b80516001600160a01b0381168114610849575f80fd5b919050565b5f805f8060808587031215610861575f80fd5b61086a85610833565b935061087860208601610833565b925061088660408601610833565b915061089460608601610833565b905092959194509250565b5f602082840312156108af575f80fd5b81518015158114610561575f80fd5b6001600160601b0381168114610830575f80fd5b5f602082840312156108e2575f80fd5b8151610561816108be565b5f602082840312156108fd575f80fd5b5051919050565b5f82518060208501845e5f920191825250919050565b60805160a05160c05160e05161195d6109b55f395f818161014f0152818161083201528181610a1c01528181610b0a0152610c5301525f61020c01525f81816101b4015281816108cc01528181610da001528181610ead01528181610f95015281816110a20152818161127f015261131601525f818160ee015281816102b901528181610388015281816104e401526105ac015261195d5ff3fe608060405234801561000f575f80fd5b50600436106100e5575f3560e01c806382bfefc811610088578063ce0f75de11610063578063ce0f75de14610207578063deaa59df1461022e578063ea0b3c7314610241578063f14ac2b914610254575f80fd5b806382bfefc8146101af5780638fb36037146101d6578063bf7e214f146101f7575f80fd5b806367ed5f36116100c357806367ed5f3614610135578063707ddfae1461014a57806379fc35e8146101895780637a9e5e4b1461019c575f80fd5b806306433b1b146100e9578063132996041461012d5780633fd3433314610135575b5f80fd5b6101107f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b610110610267565b61014861014336600461174a565b61028e565b005b6101717f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160601b039091168152602001610124565b61014861019736600461174a565b610360565b6101486101aa366004611781565b610426565b6101107f000000000000000000000000000000000000000000000000000000000000000081565b6101de61049a565b6040516001600160e01b03199091168152602001610124565b5f546001600160a01b0316610110565b6101107f000000000000000000000000000000000000000000000000000000000000000081565b61014861023c366004611781565b6104bc565b61014861024f36600461174a565b610584565b6101486102623660046117a9565b61064a565b6001545f906001600160a01b031661027e57503090565b506001546001600160a01b031690565b61029a335b5f3661065a565b60408051631703989960e21b81523360048201526008602482015290517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691635c0e62649160448083019260209291908290030181865afa15801561030a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061032e91906117f1565b61035257604051630542d5c160e01b81523360048201526024015b60405180910390fd5b61035c8282610750565b5050565b61036933610293565b60408051631703989960e21b81523360048201526008602482015290517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691635c0e62649160448083019260209291908290030181865afa1580156103d9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103fd91906117f1565b61041c57604051630542d5c160e01b8152336004820152602401610349565b61035c8282610764565b5f5433906001600160a01b0316811461045c5760405162d1953b60e31b81526001600160a01b0382166004820152602401610349565b816001600160a01b03163b5f03610491576040516361798f2f60e11b81526001600160a01b0383166004820152602401610349565b61035c82610778565b5f8054600160a01b900460ff166104b057505f90565b50638fb3603760e01b90565b6104c533610293565b60408051631703989960e21b81523360048201526008602482015290517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691635c0e62649160448083019260209291908290030181865afa158015610535573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061055991906117f1565b61057857604051630542d5c160e01b8152336004820152602401610349565b610581816107cb565b50565b61058d33610293565b60408051631703989960e21b81523360048201526008602482015290517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691635c0e62649160448083019260209291908290030181865afa1580156105fd573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061062191906117f1565b61064057604051630542d5c160e01b8152336004820152602401610349565b61035c8282610adf565b610655838383610cd9565b505050565b5f8061068d6106705f546001600160a01b031690565b863061067f60045f898b61180c565b61068891611833565b61115c565b91509150816107495763ffffffff811615610726575f805460ff60a01b198116600160a01b17909155604051634a63ebf760e11b81526001600160a01b03909116906394c7d7ee906106e79088908890889060040161186b565b5f604051808303815f87803b1580156106fe575f80fd5b505af1158015610710573d5f803e3d5ffd5b50505f805460ff60a01b19169055506107499050565b60405162d1953b60e31b81526001600160a01b0386166004820152602401610349565b5050505050565b61035c61075b610267565b83836001611264565b61035c82610770610267565b836001611264565b5f80546001600160a01b0319166001600160a01b0383169081179091556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad9060200160405180910390a150565b6001546001600160a01b039081169082168190036107fc5760405163b779402d60e01b815260040160405180910390fd5b5f610805610267565b600180546001600160a01b0319166001600160a01b03868116918217909255604080516001600160601b037f000000000000000000000000000000000000000000000000000000000000000016815292861660208401528201529091507fe17b63a940799420dfdbb7bc7d4f264ef924329d7a5135cb82b1162c86ea02f59060600160405180910390a16040516370a0823160e01b81526001600160a01b0382811660048301525f917382860d6202c009092d0d7507380ba8ef8f4fd56791634c41dd96917f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa158015610913573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061093791906118aa565b6040518263ffffffff1660e01b815260040161095591815260200190565b602060405180830381865af4158015610970573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061099491906118c1565b6040516330b8415f60e01b81526001600160601b03821660048201529091507382860d6202c009092d0d7507380ba8ef8f4fd567906330b8415f90602401602060405180830381865af41580156109ed573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a1191906117f1565b15610ad957604080517f00000000000000000000000000000000000000000000000000000000000000006001600160601b0390811682526001600160a01b0386811660208401528716828401528316606082015290517f85bc564e97c299d12e3a61734d4f9ca9aff69b15059d1b177825a456c8a317d99181900360800190a16001600160a01b038316610ab157610aac3085836001611264565b610ad9565b6001600160a01b038416610acc57610aac8330836001611264565b610ad98385836001611264565b50505050565b6001546001600160a01b031615610b4d5760015460405163266a456b60e11b81526001600160601b037f00000000000000000000000000000000000000000000000000000000000000001660048201523060248201526001600160a01b039091166044820152606401610349565b60405163046e44af60e11b81526001600160601b03821660048201525f907382860d6202c009092d0d7507380ba8ef8f4fd567906308dc895e90602401602060405180830381865af4158015610ba5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bc991906118aa565b90505f610c40837382860d6202c009092d0d7507380ba8ef8f4fd567636ac5db196040518163ffffffff1660e01b8152600401602060405180830381865af4158015610c17573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c3b91906118c1565b61139d565b15610c4d57505f19905060015b604080517f00000000000000000000000000000000000000000000000000000000000000006001600160601b0390811682523060208301526001600160a01b0387168284015285166060820152821515608082015290517f87afdceff4fac7ed560df07efe2dbb4585adc663dd5fab969376f20d70cb7a449181900360a00190a1610ad9843084611428565b808015610d5b5750604051630a4d29dd60e31b81526001600160601b03831660048201527382860d6202c009092d0d7507380ba8ef8f4fd567906352694ee890602401602060405180830381865af4158015610d37573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d5b91906117f1565b15610d795760405163ba9cf25b60e01b815260040160405180910390fd5b604051636eb1769f60e11b81526001600160a01b0384811660048301523060248301525f917f00000000000000000000000000000000000000000000000000000000000000009091169063dd62ed3e90604401602060405180830381865afa158015610de7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e0b91906118aa565b60405163046e44af60e11b81526001600160601b03851660048201529091507382860d6202c009092d0d7507380ba8ef8f4fd567906308dc895e90602401602060405180830381865af4158015610e64573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e8891906118aa565b811015610f745760405163046e44af60e11b81526001600160601b03841660048201527f0000000000000000000000000000000000000000000000000000000000000000908590309084907382860d6202c009092d0d7507380ba8ef8f4fd567906308dc895e90602401602060405180830381865af4158015610f0d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f3191906118aa565b6040516319d2916b60e21b81526001600160a01b03958616600482015293851660248501529390911660448301526064820152608481019190915260a401610349565b6040516370a0823160e01b81526001600160a01b0385811660048301525f917f0000000000000000000000000000000000000000000000000000000000000000909116906370a0823190602401602060405180830381865afa158015610fdc573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061100091906118aa565b60405163046e44af60e11b81526001600160601b03861660048201529091507382860d6202c009092d0d7507380ba8ef8f4fd567906308dc895e90602401602060405180830381865af4158015611059573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061107d91906118aa565b8110156107495760405163046e44af60e11b81526001600160601b03851660048201527f000000000000000000000000000000000000000000000000000000000000000090869083907382860d6202c009092d0d7507380ba8ef8f4fd567906308dc895e90602401602060405180830381865af4158015611100573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061112491906118aa565b604051631288e44360e21b81526001600160a01b03948516600482015293909216602484015260448301526064820152608401610349565b6040516001600160a01b03848116602483015283811660448301526001600160e01b0319831660648301525f9182918291829189169060840160408051601f198184030181529181526020820180516001600160e01b031663b700961360e01b179052516111ca91906118dc565b5f60405180830381855afa9150503d805f8114611202576040519150601f19603f3d011682016040523d82523d5f602084013e611207565b606091505b50915091508115611259576040815110611239578080602001905181019061122f91906118f2565b9094509250611259565b6020815110611259578080602001905181019061125691906117f1565b93505b505094509492505050565b8015611279576001611277858483610cd9565b505b604080517f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03908116825286811660208301528516818301526001600160601b038416606082015290517fd8dad5de52bc07319fbcdbf42f954f497c3a267ce0cc264584726fa7a3b322079181900360800190a160405163046e44af60e11b81526001600160601b0383166004820152610ad9907f000000000000000000000000000000000000000000000000000000000000000090869086907382860d6202c009092d0d7507380ba8ef8f4fd567906308dc895e90602401602060405180830381865af4158015611374573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061139891906118aa565b6114e5565b60405163c4cade9d60e01b81526001600160601b038084166004830152821660248201525f907382860d6202c009092d0d7507380ba8ef8f4fd5679063c4cade9d90604401602060405180830381865af41580156113fd573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061142191906117f1565b9392505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052611479848261151e565b610ad9576040516001600160a01b0384811660248301525f60448301526114db91869182169063095ea7b3906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506115bf565b610ad984826115bf565b6040516001600160a01b038481166024830152838116604483015260648201839052610ad99186918216906323b872dd906084016114a9565b5f805f846001600160a01b03168460405161153991906118dc565b5f604051808303815f865af19150503d805f8114611572576040519150601f19603f3d011682016040523d82523d5f602084013e611577565b606091505b50915091508180156115a15750805115806115a15750808060200190518101906115a191906117f1565b80156115b657505f856001600160a01b03163b115b95945050505050565b5f6115d36001600160a01b03841683611620565b905080515f141580156115f75750808060200190518101906115f591906117f1565b155b1561065557604051635274afe760e01b81526001600160a01b0384166004820152602401610349565b606061142183835f845f80856001600160a01b0316848660405161164491906118dc565b5f6040518083038185875af1925050503d805f811461167e576040519150601f19603f3d011682016040523d82523d5f602084013e611683565b606091505b509150915061169386838361169d565b9695505050505050565b6060826116b2576116ad826116f9565b611421565b81511580156116c957506001600160a01b0384163b155b156116f257604051639996b31560e01b81526001600160a01b0385166004820152602401610349565b5080611421565b8051156117095780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b0381168114610581575f80fd5b6001600160601b0381168114610581575f80fd5b5f806040838503121561175b575f80fd5b823561176681611722565b9150602083013561177681611736565b809150509250929050565b5f60208284031215611791575f80fd5b813561142181611722565b8015158114610581575f80fd5b5f805f606084860312156117bb575f80fd5b83356117c681611722565b925060208401356117d681611736565b915060408401356117e68161179c565b809150509250925092565b5f60208284031215611801575f80fd5b81516114218161179c565b5f808585111561181a575f80fd5b83861115611826575f80fd5b5050820193919092039150565b80356001600160e01b03198116906004841015611864576001600160e01b0319600485900360031b81901b82161691505b5092915050565b6001600160a01b03841681526040602082018190528101829052818360608301375f818301606090810191909152601f909201601f1916010192915050565b5f602082840312156118ba575f80fd5b5051919050565b5f602082840312156118d1575f80fd5b815161142181611736565b5f82518060208501845e5f920191825250919050565b5f8060408385031215611903575f80fd5b825161190e8161179c565b602084015190925063ffffffff81168114611776575f80fdfea2646970667358221220834d9e16c5bab34fc3f8ae5f3a7c8796c3d3673b0928702c083ea27f1b712e7e64736f6c634300081a003300000000000000000000000048b24f2908bba1fd1a7d68395980e4c9eabbb4d30000000000000000000000002c948ff1f45f933d5263800e351823560454c20d000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913000000000000000000000000f89bd41848d2edc7a5231c65e38db71ef66d3539

Deployed Bytecode

0x608060405234801561000f575f80fd5b50600436106100e5575f3560e01c806382bfefc811610088578063ce0f75de11610063578063ce0f75de14610207578063deaa59df1461022e578063ea0b3c7314610241578063f14ac2b914610254575f80fd5b806382bfefc8146101af5780638fb36037146101d6578063bf7e214f146101f7575f80fd5b806367ed5f36116100c357806367ed5f3614610135578063707ddfae1461014a57806379fc35e8146101895780637a9e5e4b1461019c575f80fd5b806306433b1b146100e9578063132996041461012d5780633fd3433314610135575b5f80fd5b6101107f00000000000000000000000048b24f2908bba1fd1a7d68395980e4c9eabbb4d381565b6040516001600160a01b0390911681526020015b60405180910390f35b610110610267565b61014861014336600461174a565b61028e565b005b6101717f0000000000000000000000000000000000000000000000000000000003a06cf881565b6040516001600160601b039091168152602001610124565b61014861019736600461174a565b610360565b6101486101aa366004611781565b610426565b6101107f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291381565b6101de61049a565b6040516001600160e01b03199091168152602001610124565b5f546001600160a01b0316610110565b6101107f0000000000000000000000002c948ff1f45f933d5263800e351823560454c20d81565b61014861023c366004611781565b6104bc565b61014861024f36600461174a565b610584565b6101486102623660046117a9565b61064a565b6001545f906001600160a01b031661027e57503090565b506001546001600160a01b031690565b61029a335b5f3661065a565b60408051631703989960e21b81523360048201526008602482015290517f00000000000000000000000048b24f2908bba1fd1a7d68395980e4c9eabbb4d36001600160a01b031691635c0e62649160448083019260209291908290030181865afa15801561030a573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061032e91906117f1565b61035257604051630542d5c160e01b81523360048201526024015b60405180910390fd5b61035c8282610750565b5050565b61036933610293565b60408051631703989960e21b81523360048201526008602482015290517f00000000000000000000000048b24f2908bba1fd1a7d68395980e4c9eabbb4d36001600160a01b031691635c0e62649160448083019260209291908290030181865afa1580156103d9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103fd91906117f1565b61041c57604051630542d5c160e01b8152336004820152602401610349565b61035c8282610764565b5f5433906001600160a01b0316811461045c5760405162d1953b60e31b81526001600160a01b0382166004820152602401610349565b816001600160a01b03163b5f03610491576040516361798f2f60e11b81526001600160a01b0383166004820152602401610349565b61035c82610778565b5f8054600160a01b900460ff166104b057505f90565b50638fb3603760e01b90565b6104c533610293565b60408051631703989960e21b81523360048201526008602482015290517f00000000000000000000000048b24f2908bba1fd1a7d68395980e4c9eabbb4d36001600160a01b031691635c0e62649160448083019260209291908290030181865afa158015610535573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061055991906117f1565b61057857604051630542d5c160e01b8152336004820152602401610349565b610581816107cb565b50565b61058d33610293565b60408051631703989960e21b81523360048201526008602482015290517f00000000000000000000000048b24f2908bba1fd1a7d68395980e4c9eabbb4d36001600160a01b031691635c0e62649160448083019260209291908290030181865afa1580156105fd573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061062191906117f1565b61064057604051630542d5c160e01b8152336004820152602401610349565b61035c8282610adf565b610655838383610cd9565b505050565b5f8061068d6106705f546001600160a01b031690565b863061067f60045f898b61180c565b61068891611833565b61115c565b91509150816107495763ffffffff811615610726575f805460ff60a01b198116600160a01b17909155604051634a63ebf760e11b81526001600160a01b03909116906394c7d7ee906106e79088908890889060040161186b565b5f604051808303815f87803b1580156106fe575f80fd5b505af1158015610710573d5f803e3d5ffd5b50505f805460ff60a01b19169055506107499050565b60405162d1953b60e31b81526001600160a01b0386166004820152602401610349565b5050505050565b61035c61075b610267565b83836001611264565b61035c82610770610267565b836001611264565b5f80546001600160a01b0319166001600160a01b0383169081179091556040519081527f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad9060200160405180910390a150565b6001546001600160a01b039081169082168190036107fc5760405163b779402d60e01b815260040160405180910390fd5b5f610805610267565b600180546001600160a01b0319166001600160a01b03868116918217909255604080516001600160601b037f0000000000000000000000000000000000000000000000000000000003a06cf816815292861660208401528201529091507fe17b63a940799420dfdbb7bc7d4f264ef924329d7a5135cb82b1162c86ea02f59060600160405180910390a16040516370a0823160e01b81526001600160a01b0382811660048301525f917382860d6202c009092d0d7507380ba8ef8f4fd56791634c41dd96917f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913909116906370a0823190602401602060405180830381865afa158015610913573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061093791906118aa565b6040518263ffffffff1660e01b815260040161095591815260200190565b602060405180830381865af4158015610970573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061099491906118c1565b6040516330b8415f60e01b81526001600160601b03821660048201529091507382860d6202c009092d0d7507380ba8ef8f4fd567906330b8415f90602401602060405180830381865af41580156109ed573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a1191906117f1565b15610ad957604080517f0000000000000000000000000000000000000000000000000000000003a06cf86001600160601b0390811682526001600160a01b0386811660208401528716828401528316606082015290517f85bc564e97c299d12e3a61734d4f9ca9aff69b15059d1b177825a456c8a317d99181900360800190a16001600160a01b038316610ab157610aac3085836001611264565b610ad9565b6001600160a01b038416610acc57610aac8330836001611264565b610ad98385836001611264565b50505050565b6001546001600160a01b031615610b4d5760015460405163266a456b60e11b81526001600160601b037f0000000000000000000000000000000000000000000000000000000003a06cf81660048201523060248201526001600160a01b039091166044820152606401610349565b60405163046e44af60e11b81526001600160601b03821660048201525f907382860d6202c009092d0d7507380ba8ef8f4fd567906308dc895e90602401602060405180830381865af4158015610ba5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bc991906118aa565b90505f610c40837382860d6202c009092d0d7507380ba8ef8f4fd567636ac5db196040518163ffffffff1660e01b8152600401602060405180830381865af4158015610c17573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c3b91906118c1565b61139d565b15610c4d57505f19905060015b604080517f0000000000000000000000000000000000000000000000000000000003a06cf86001600160601b0390811682523060208301526001600160a01b0387168284015285166060820152821515608082015290517f87afdceff4fac7ed560df07efe2dbb4585adc663dd5fab969376f20d70cb7a449181900360a00190a1610ad9843084611428565b808015610d5b5750604051630a4d29dd60e31b81526001600160601b03831660048201527382860d6202c009092d0d7507380ba8ef8f4fd567906352694ee890602401602060405180830381865af4158015610d37573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d5b91906117f1565b15610d795760405163ba9cf25b60e01b815260040160405180910390fd5b604051636eb1769f60e11b81526001600160a01b0384811660048301523060248301525f917f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029139091169063dd62ed3e90604401602060405180830381865afa158015610de7573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e0b91906118aa565b60405163046e44af60e11b81526001600160601b03851660048201529091507382860d6202c009092d0d7507380ba8ef8f4fd567906308dc895e90602401602060405180830381865af4158015610e64573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e8891906118aa565b811015610f745760405163046e44af60e11b81526001600160601b03841660048201527f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913908590309084907382860d6202c009092d0d7507380ba8ef8f4fd567906308dc895e90602401602060405180830381865af4158015610f0d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f3191906118aa565b6040516319d2916b60e21b81526001600160a01b03958616600482015293851660248501529390911660448301526064820152608481019190915260a401610349565b6040516370a0823160e01b81526001600160a01b0385811660048301525f917f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda02913909116906370a0823190602401602060405180830381865afa158015610fdc573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061100091906118aa565b60405163046e44af60e11b81526001600160601b03861660048201529091507382860d6202c009092d0d7507380ba8ef8f4fd567906308dc895e90602401602060405180830381865af4158015611059573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061107d91906118aa565b8110156107495760405163046e44af60e11b81526001600160601b03851660048201527f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291390869083907382860d6202c009092d0d7507380ba8ef8f4fd567906308dc895e90602401602060405180830381865af4158015611100573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061112491906118aa565b604051631288e44360e21b81526001600160a01b03948516600482015293909216602484015260448301526064820152608401610349565b6040516001600160a01b03848116602483015283811660448301526001600160e01b0319831660648301525f9182918291829189169060840160408051601f198184030181529181526020820180516001600160e01b031663b700961360e01b179052516111ca91906118dc565b5f60405180830381855afa9150503d805f8114611202576040519150601f19603f3d011682016040523d82523d5f602084013e611207565b606091505b50915091508115611259576040815110611239578080602001905181019061122f91906118f2565b9094509250611259565b6020815110611259578080602001905181019061125691906117f1565b93505b505094509492505050565b8015611279576001611277858483610cd9565b505b604080517f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda029136001600160a01b03908116825286811660208301528516818301526001600160601b038416606082015290517fd8dad5de52bc07319fbcdbf42f954f497c3a267ce0cc264584726fa7a3b322079181900360800190a160405163046e44af60e11b81526001600160601b0383166004820152610ad9907f000000000000000000000000833589fcd6edb6e08f4c7c32d4f71b54bda0291390869086907382860d6202c009092d0d7507380ba8ef8f4fd567906308dc895e90602401602060405180830381865af4158015611374573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061139891906118aa565b6114e5565b60405163c4cade9d60e01b81526001600160601b038084166004830152821660248201525f907382860d6202c009092d0d7507380ba8ef8f4fd5679063c4cade9d90604401602060405180830381865af41580156113fd573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061142191906117f1565b9392505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052611479848261151e565b610ad9576040516001600160a01b0384811660248301525f60448301526114db91869182169063095ea7b3906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506115bf565b610ad984826115bf565b6040516001600160a01b038481166024830152838116604483015260648201839052610ad99186918216906323b872dd906084016114a9565b5f805f846001600160a01b03168460405161153991906118dc565b5f604051808303815f865af19150503d805f8114611572576040519150601f19603f3d011682016040523d82523d5f602084013e611577565b606091505b50915091508180156115a15750805115806115a15750808060200190518101906115a191906117f1565b80156115b657505f856001600160a01b03163b115b95945050505050565b5f6115d36001600160a01b03841683611620565b905080515f141580156115f75750808060200190518101906115f591906117f1565b155b1561065557604051635274afe760e01b81526001600160a01b0384166004820152602401610349565b606061142183835f845f80856001600160a01b0316848660405161164491906118dc565b5f6040518083038185875af1925050503d805f811461167e576040519150601f19603f3d011682016040523d82523d5f602084013e611683565b606091505b509150915061169386838361169d565b9695505050505050565b6060826116b2576116ad826116f9565b611421565b81511580156116c957506001600160a01b0384163b155b156116f257604051639996b31560e01b81526001600160a01b0385166004820152602401610349565b5080611421565b8051156117095780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b0381168114610581575f80fd5b6001600160601b0381168114610581575f80fd5b5f806040838503121561175b575f80fd5b823561176681611722565b9150602083013561177681611736565b809150509250929050565b5f60208284031215611791575f80fd5b813561142181611722565b8015158114610581575f80fd5b5f805f606084860312156117bb575f80fd5b83356117c681611722565b925060208401356117d681611736565b915060408401356117e68161179c565b809150509250925092565b5f60208284031215611801575f80fd5b81516114218161179c565b5f808585111561181a575f80fd5b83861115611826575f80fd5b5050820193919092039150565b80356001600160e01b03198116906004841015611864576001600160e01b0319600485900360031b81901b82161691505b5092915050565b6001600160a01b03841681526040602082018190528101829052818360608301375f818301606090810191909152601f909201601f1916010192915050565b5f602082840312156118ba575f80fd5b5051919050565b5f602082840312156118d1575f80fd5b815161142181611736565b5f82518060208501845e5f920191825250919050565b5f8060408385031215611903575f80fd5b825161190e8161179c565b602084015190925063ffffffff81168114611776575f80fdfea2646970667358221220834d9e16c5bab34fc3f8ae5f3a7c8796c3d3673b0928702c083ea27f1b712e7e64736f6c634300081a0033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ 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.