ETH Price: $3,302.52 (-3.55%)
 

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
Create Policy Wi...244221392024-12-31 8:27:05310 days ago1735633625IN
0x0A413B84...78E05F838
0 ETH0.000025860.00274891
Create Policy Wi...244221112024-12-31 8:26:09310 days ago1735633569IN
0x0A413B84...78E05F838
0 ETH0.000024210.00275292
Create Policy Wi...244220252024-12-31 8:23:17310 days ago1735633397IN
0x0A413B84...78E05F838
0 ETH0.000035660.00270154
Create Policy Wi...229969562024-11-28 8:40:59343 days ago1732783259IN
0x0A413B84...78E05F838
0 ETH0.000150730.04364037
Create Policy Wi...229966472024-11-28 8:30:41343 days ago1732782641IN
0x0A413B84...78E05F838
0 ETH0.000111260.03990693
Create Policy Wi...229965992024-11-28 8:29:05343 days ago1732782545IN
0x0A413B84...78E05F838
0 ETH0.000153930.03947819
Create Policy Wi...223589942024-11-13 14:15:35358 days ago1731507335IN
0x0A413B84...78E05F838
0 ETH0.000046420.01444497
Create Policy Wi...223555132024-11-13 12:19:33358 days ago1731500373IN
0x0A413B84...78E05F838
0 ETH0.00003420.01076189
Create Policy Wi...223553652024-11-13 12:14:37358 days ago1731500077IN
0x0A413B84...78E05F838
0 ETH0.000036030.01132508
Create Policy Wi...223553552024-11-13 12:14:17358 days ago1731500057IN
0x0A413B84...78E05F838
0 ETH0.000035910.01135957
Create Policy Wi...223553442024-11-13 12:13:55358 days ago1731500035IN
0x0A413B84...78E05F838
0 ETH0.000036370.01144075
Create Policy Wi...223553292024-11-13 12:13:25358 days ago1731500005IN
0x0A413B84...78E05F838
0 ETH0.000036590.01149287
Create Policy Wi...223553172024-11-13 12:13:01358 days ago1731499981IN
0x0A413B84...78E05F838
0 ETH0.000036580.01147655
Create Policy Wi...223551672024-11-13 12:08:01358 days ago1731499681IN
0x0A413B84...78E05F838
0 ETH0.0000370.01167714
Create Policy Wi...223551552024-11-13 12:07:37358 days ago1731499657IN
0x0A413B84...78E05F838
0 ETH0.000037320.01172097
Create Policy Wi...223551402024-11-13 12:07:07358 days ago1731499627IN
0x0A413B84...78E05F838
0 ETH0.000038140.01180814
Create Policy Wi...223551062024-11-13 12:05:59358 days ago1731499559IN
0x0A413B84...78E05F838
0 ETH0.000038170.01199663
Create Policy Wi...223550682024-11-13 12:04:43358 days ago1731499483IN
0x0A413B84...78E05F838
0 ETH0.000038130.01204761
Create Policy Wi...223550262024-11-13 12:03:19358 days ago1731499399IN
0x0A413B84...78E05F838
0 ETH0.000038370.01207316
Create Policy Wi...223550032024-11-13 12:02:33358 days ago1731499353IN
0x0A413B84...78E05F838
0 ETH0.000038640.01215273
Create Policy Wi...223549822024-11-13 12:01:51358 days ago1731499311IN
0x0A413B84...78E05F838
0 ETH0.000038720.01217818
Create Policy Wi...223549572024-11-13 12:01:01358 days ago1731499261IN
0x0A413B84...78E05F838
0 ETH0.000038690.01222584
Create Policy Wi...223548922024-11-13 11:58:51358 days ago1731499131IN
0x0A413B84...78E05F838
0 ETH0.000038950.01221097
Create Policy Wi...223548652024-11-13 11:57:57358 days ago1731499077IN
0x0A413B84...78E05F838
0 ETH0.000038220.01200092
Create Policy Wi...223548502024-11-13 11:57:27358 days ago1731499047IN
0x0A413B84...78E05F838
0 ETH0.000038140.01196411
View all transactions

Parent Transaction Hash Block From To
View All Internal Transactions

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
FlightProduct

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 200 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {ERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

import {IAuthorization} from "../../authorization/IAuthorization.sol";
import {IComponents} from "../../instance/module/IComponents.sol";
import {IPolicy} from "../../instance/module/IPolicy.sol";

import {Amount, AmountLib} from "../../type/Amount.sol";
import {ClaimId} from "../../type/ClaimId.sol";
import {Component} from "../../shared/Component.sol";
import {FeeLib} from "../../type/Fee.sol";
import {FlightLib} from "./FlightLib.sol";
import {FlightMessageVerifier} from "./FlightMessageVerifier.sol";
import {FlightOracle} from "./FlightOracle.sol";
import {InstanceReader} from "../../instance/InstanceReader.sol";
import {NftId, NftIdLib} from "../../type/NftId.sol";
import {PayoutId} from "../../type/PayoutId.sol";
import {Product} from "../../product/Product.sol";
import {ReferralLib} from "../../type/Referral.sol";
import {RiskId, RiskIdLib} from "../../type/RiskId.sol";
import {RequestId} from "../../type/RequestId.sol";
import {Seconds, SecondsLib} from "../../type/Seconds.sol";
import {Str, StrLib} from "../../type/String.sol";
import {Timestamp, TimestampLib} from "../../type/Timestamp.sol";


/// @dev FlightProduct implements the flight delay product.
contract FlightProduct is
    Product
{

    event LogFlightPolicyPurchased(NftId policyNftId, string flightData, Amount premiumAmount);
    event LogFlightPolicyClosed(NftId policyNftId, Amount payoutAmount);

    event LogFlightStatusProcessed(RequestId requestId, RiskId riskId, bytes1 status, int256 delayMinutes, uint8 payoutOption);
    event LogFlightPoliciesProcessed(RiskId riskId, uint8 payoutOption, uint256 policiesProcessed, uint256 policiesRemaining);

    // solhint-disable
    Amount public MIN_PREMIUM;
    Amount public MAX_PREMIUM;
    Amount public MAX_PAYOUT;
    Amount public MAX_TOTAL_PAYOUT; // Maximum risk per flight/risk

    // Minimum time before departure for applying
    Seconds public MIN_TIME_BEFORE_DEPARTURE;
    // Maximum time before departure for applying
    Seconds public MAX_TIME_BEFORE_DEPARTURE;
    // Maximum duration of flight
    Seconds public MAX_FLIGHT_DURATION;
    // Max time to process claims after departure
    Seconds public LIFETIME;

    // ['observations','late15','late30','late45','cancelled','diverted']
    // no payouts for delays of 30' or less
    uint8[6] public WEIGHT_PATTERN;
    // Minimum number of observations for valid prediction/premium calculation
    uint256 public MIN_OBSERVATIONS;
    // Maximum cumulated weighted premium per risk
    uint256 public MARGIN_PERCENT;
    // Maximum number of policies to process in one callback
    uint8 public MAX_POLICIES_TO_PROCESS;
    // solhint-enable

    bool internal _testMode;

    mapping(RiskId riskId => RequestId requestId) internal _requests;

    // GIF V3 specifics
    NftId internal _defaultBundleNftId;
    NftId internal _oracleNftId;


    struct FlightRisk {
        Str flightData; // example: "LX 180 ZRH BKK 20241104"
        Timestamp departureTime;
        // this field contains static data required by the frontend and is not directly used by the product
        string departureTimeLocal; // example "2024-10-14T10:10:00.000 Asia/Seoul"
        Timestamp arrivalTime; 
        // this field contains static data required by the frontend and is not directly used by the product
        string arrivalTimeLocal; // example "2024-10-14T10:10:00.000 Asia/Seoul"
        Amount sumOfSumInsuredAmounts;
        bytes1 status; // 'L'ate, 'C'ancelled, 'D'iverted, ...
        int256 delayMinutes;
        uint8 payoutOption;
        Timestamp statusUpdatedAt;
    }


    struct ApplicationData {
        string flightData;
        Timestamp departureTime;
        string departureTimeLocal;
        Timestamp arrivalTime;
        string arrivalTimeLocal;
        Amount premiumAmount;
        uint256[6] statistics;
    }


    struct PermitData {
        address owner;
        address spender;
        uint256 value;
        uint256 deadline;
        uint8 v;
        bytes32 r;
        bytes32 s;
    }


    constructor(
        address registry,
        NftId instanceNftId,
        string memory componentName,
        IAuthorization authorization
    )
    {
        address initialOwner = msg.sender;

        _initialize(
            registry,
            instanceNftId,
            componentName,
            authorization,
            initialOwner);
    }


    //--- external functions ------------------------------------------------//
    //--- unpermissioned functions ------------------------------------------//

    function setOracleNftId()
        external
    {
        _oracleNftId = _getInstanceReader().getProductInfo(
            getNftId()).oracleNftId[0];
    }


    function calculatePayoutAmounts(
        FlightProduct flightProduct,
        Amount premium, 
        uint256[6] memory statistics
    ) 
        external
        view
        returns (
            uint256 weight, 
            Amount[5] memory payoutAmounts,
            Amount sumInsuredAmount // simply the max of payoutAmounts 
        ) 
    {
        return FlightLib.calculatePayoutAmounts(
            flightProduct,
            premium,
            statistics);
    }

    /// @dev Creates a policy using a permit for the policy holder.
    /// The policy holder is defined as the owner parameter of the permit data.
    /// NOTE: This function makes the assumption that the product token
    /// supports permits. This assumption is not verfied.
    function createPolicyWithPermit(
        PermitData memory permit,
        ApplicationData memory application
    )
        external
        virtual
        restricted()
        returns (
            RiskId riskId,
            NftId policyNftId
        )
    {
        // process permit data
        _processPermit(permit);

        // create policy
        address policyHolder = permit.owner;
        (
            riskId,
            policyNftId
        ) = _createPolicy(
            policyHolder,
            StrLib.toStr(application.flightData),
            application.departureTime,
            application.departureTimeLocal,
            application.arrivalTime,
            application.arrivalTimeLocal,
            application.premiumAmount,
            application.statistics);
    }


    /// @dev Callback for flight status oracle.
    /// Function may only be alled by oracle service.
    function flightStatusCallback(
        RequestId requestId,
        bytes memory responseData
    )
        external
        virtual
        restricted()
    {
        FlightOracle.FlightStatusResponse memory response = abi.decode(
            responseData, (FlightOracle.FlightStatusResponse));

        _processFlightStatus(
            requestId, 
            response.riskId, 
            response.status, 
            response.delayMinutes);
    }


    function resendRequest(RequestId requestId)
        external
        virtual
        restricted()
    {
        _resendRequest(requestId);
    }


    /// @dev Manual fallback function for product owner.
    function processPayoutsAndClosePolicies(
        RiskId riskId, 
        uint8 maxPoliciesToProcess
    )
        external
        virtual
        restricted()
        onlyOwner()
    {
        _processPayoutsAndClosePolicies(
            riskId, 
            maxPoliciesToProcess);
    }


    //--- owner functions ---------------------------------------------------//

    /// @dev Call after product registration with the instance
    /// when the product token/tokenhandler is available
    function setConstants(
        Amount minPremium,
        Amount maxPremium,
        Amount maxPayout,
        Amount maxTotalPayout,
        Seconds minTimeBeforeDeparture,
        Seconds maxTimeBeforeDeparture,
        uint8 maxPoliciesToProcess
    )
        external
        virtual
        restricted()
        onlyOwner()
    {
        MIN_PREMIUM = minPremium; 
        MAX_PREMIUM = maxPremium; 
        MAX_PAYOUT = maxPayout; 
        MAX_TOTAL_PAYOUT = maxTotalPayout;

        MIN_TIME_BEFORE_DEPARTURE = minTimeBeforeDeparture;
        MAX_TIME_BEFORE_DEPARTURE = maxTimeBeforeDeparture;
        MAX_POLICIES_TO_PROCESS = maxPoliciesToProcess;
    }

    function setDefaultBundle(NftId bundleNftId) external restricted() onlyOwner() { _defaultBundleNftId = bundleNftId; }
    function setTestMode(bool testMode) external restricted() onlyOwner() { _testMode = testMode; }

    function approveTokenHandler(IERC20Metadata token, Amount amount) external restricted() onlyOwner() { _approveTokenHandler(token, amount); }
    function setLocked(bool locked) external onlyOwner() { _setLocked(locked); }
    function setWallet(address newWallet) external restricted() onlyOwner() { _setWallet(newWallet); }


    //--- view functions ----------------------------------------------------//

    function calculateNetPremium(
        Amount, // sumInsuredAmount: not used in this product
        RiskId, // riskId: not used in this product
        Seconds, // lifetime: not used in this product, a flight is a one time risk
        bytes memory applicationData // holds the premium amount the customer is willing to pay
    )
        external
        virtual override
        view 
        returns (Amount netPremiumAmount)
    {
        (netPremiumAmount, ) = abi.decode(applicationData, (Amount, Amount[5]));
    }


    function getOracleNftId() public view returns (NftId oracleNftId) { return _oracleNftId; }
    function isTestMode() public view returns (bool) { return _testMode; }


    function getFlightRisk(
        RiskId riskId,
        bool requireRiskExists
    )
        public
        view
        returns (
            bool exists,
            FlightRisk memory flightRisk
        )
    {
        (exists, flightRisk) = FlightLib.getFlightRisk(
            _getInstanceReader(), 
            getNftId(), 
            riskId, 
            requireRiskExists);
    }

    function getRequestForRisk(RiskId riskId) public view returns (RequestId requestId) { return _requests[riskId]; }
    function decodeFlightRiskData(bytes memory data) public pure returns (FlightRisk memory) { return abi.decode(data, (FlightRisk)); }

    //--- internal functions ------------------------------------------------//


    function _processPermit(
        PermitData memory permit
    )
        internal
        virtual
        restricted()
    {
        address tokenAddress = address(getToken());

        // process permit data
        ERC20Permit(tokenAddress).permit(
            permit.owner, 
            permit.spender, 
            permit.value, 
            permit.deadline, 
            permit.v, 
            permit.r, 
            permit.s); 
    }


    function _createPolicy(
        address policyHolder,
        Str flightData, 
        Timestamp departureTime,
        string memory departureTimeLocal,
        Timestamp arrivalTime,
        string memory arrivalTimeLocal,
        Amount premiumAmount,
        uint256[6] memory statistics
    )
        internal
        virtual
        returns (
            RiskId riskId,
            NftId policyNftId
        )
    {
        // checks
        // disabled for now - using rbac for security
        FlightLib.checkApplicationData(
            this,
            flightData,
            departureTime,
            arrivalTime,
            premiumAmount);

        (riskId, policyNftId) = _prepareApplication(
            policyHolder, 
            flightData,
            departureTime,
            departureTimeLocal,
            arrivalTime,
            arrivalTimeLocal,
            premiumAmount,
            statistics);

        _createPolicy(
            policyNftId, 
            TimestampLib.zero(), // do not ativate yet 
            premiumAmount); // max premium amount

        // interactions (token transfer + callback to token holder, if contract)
        _collectPremium(
            policyNftId, 
            departureTime); // activate at scheduled departure time of flight

        // send oracle request for for new risk to obtain flight status (interacts with flight oracle contract)
        if (_requests[riskId].eqz()) {
            _requests[riskId] = _sendRequest(
                _oracleNftId, 
                abi.encode(
                    FlightOracle.FlightStatusRequest(
                        riskId,
                        flightData,
                        departureTime)),
                // allow up to 30 days to process the claim
                arrivalTime.addSeconds(SecondsLib.fromDays(30)), 
                "flightStatusCallback");
        }

        emit LogFlightPolicyPurchased(policyNftId, flightData.toString(), premiumAmount);
    }


    function _prepareApplication(
        address policyHolder,
        Str flightData, 
        Timestamp departureTime,
        string memory departureTimeLocal,
        Timestamp arrivalTime,
        string memory arrivalTimeLocal,
        Amount premiumAmount,
        uint256[6] memory statistics
    )
        internal
        virtual
        returns (
            RiskId riskId,
            NftId policyNftId
        )
    {
        Amount[5] memory payoutAmounts;
        Amount sumInsuredAmount;

        (
            riskId, 
            payoutAmounts,
            sumInsuredAmount
        ) = _createRiskAndPayoutAmounts(
            flightData,
            departureTime,
            departureTimeLocal,
            arrivalTime,
            arrivalTimeLocal,
            premiumAmount,
            statistics);

        policyNftId = _createApplication(
            policyHolder, 
            riskId, 
            sumInsuredAmount,
            premiumAmount,
            LIFETIME,
            _defaultBundleNftId, 
            ReferralLib.zero(), 
            abi.encode(
                premiumAmount,
                payoutAmounts)); // application data
    }


    function _createRiskAndPayoutAmounts(
        Str flightData, 
        Timestamp departureTime,
        string memory departureTimeLocal,
        Timestamp arrivalTime,
        string memory arrivalTimeLocal,
        Amount premiumAmount,
        uint256[6] memory statistics
    )
        internal
        virtual
        returns (
            RiskId riskId,
            Amount[5] memory payoutAmounts,
            Amount sumInsuredAmount
        )
    {
        uint256 weight;

        (
            weight, 
            payoutAmounts,            
            sumInsuredAmount
        ) = FlightLib.calculatePayoutAmounts(
            this,
            premiumAmount, 
            statistics);

        riskId = _checkAndUpdateFlightRisk(
            flightData,
            departureTime,
            departureTimeLocal,
            arrivalTime,
            arrivalTimeLocal,
            sumInsuredAmount,
            weight);
    }


    function _checkAndUpdateFlightRisk(
        Str flightData,
        Timestamp departureTime,
        string memory departureTimeLocal,
        Timestamp arrivalTime,
        string memory arrivalTimeLocal,
        Amount sumInsuredAmount,
        uint256 weight
    )
        internal
        virtual
        returns (RiskId riskId)
    {
        bool exists;
        FlightRisk memory flightRisk;
        (riskId, exists, flightRisk) = FlightLib.getFlightRisk(
            _getInstanceReader(), 
            getNftId(), 
            flightData, 
            departureTime, 
            departureTimeLocal,
            arrivalTime,
            arrivalTimeLocal);

        // create risk, if new
        if (!exists) {
            bytes32 riskKey = FlightLib.getRiskKey(flightData);
            _createRisk(riskKey, abi.encode(flightRisk));
        }

        FlightLib.checkClusterRisk(
            flightRisk.sumOfSumInsuredAmounts, 
            sumInsuredAmount, 
            MAX_TOTAL_PAYOUT);

        // update existing risk with additional sum insured amount
        flightRisk.sumOfSumInsuredAmounts = flightRisk.sumOfSumInsuredAmounts + sumInsuredAmount;
        _updateRisk(riskId, abi.encode(flightRisk));
    }


    function _processFlightStatus(
        RequestId requestId,
        RiskId riskId, 
        bytes1 status, 
        int256 delayMinutes
    )
        internal
        virtual
    {
        // check risk exists
        (, FlightRisk memory flightRisk) = getFlightRisk(riskId, true);

        // update status, if not yet set
        if (flightRisk.statusUpdatedAt.eqz()) {
            flightRisk.statusUpdatedAt = TimestampLib.current();
            flightRisk.status = status;
            flightRisk.delayMinutes = delayMinutes;
            flightRisk.payoutOption = FlightLib.checkAndGetPayoutOption(
                requestId, riskId, status, delayMinutes);

            _updateRisk(riskId, abi.encode(flightRisk));
        }

        (,, uint8 payoutOption) = _processPayoutsAndClosePolicies(
            riskId, 
            MAX_POLICIES_TO_PROCESS);

        // logging
        emit LogFlightStatusProcessed(requestId, riskId, status, delayMinutes, payoutOption);
    }


    function _processPayoutsAndClosePolicies(
        RiskId riskId, 
        uint8 maxPoliciesToProcess
    )
        internal
        virtual
        returns (
            bool riskExists, 
            bool statusAvailable,
            uint8 payoutOption
        )
    {
        // determine numbers of policies to process
        InstanceReader reader = _getInstanceReader();
        (riskExists, statusAvailable, payoutOption) = FlightLib.getPayoutOption(reader, getNftId(), riskId);

        // return with default values if risk does not exist or status is not yet available
        if (!riskExists || !statusAvailable) {
            return (riskExists, statusAvailable, payoutOption);
        }

        uint256 policiesToProcess = reader.policiesForRisk(riskId);
        uint256 policiesProcessed = policiesToProcess < maxPoliciesToProcess ? policiesToProcess : maxPoliciesToProcess;

        // assemble array with policies to process
        NftId [] memory policies = new NftId[](policiesProcessed);
        for (uint256 i = 0; i < policiesProcessed; i++) {
            policies[i] = reader.getPolicyForRisk(riskId, i);
        }

        // go through policies
        for (uint256 i = 0; i < policiesProcessed; i++) {
            NftId policyNftId = policies[i];
            Amount payoutAmount = FlightLib.getPayoutAmount(
                reader.getPolicyInfo(policyNftId).applicationData, 
                payoutOption);

            // create claim/payout (if applicable)
            _resolvePayout(
                policyNftId, 
                payoutAmount); 

            // expire and close policy
            _expire(policyNftId, TimestampLib.current());
            _close(policyNftId);

            emit LogFlightPolicyClosed(policyNftId, payoutAmount);
        }

        // logging
        emit LogFlightPoliciesProcessed(riskId, payoutOption, policiesProcessed, policiesToProcess - policiesProcessed);
    }


    function _resolvePayout(
        NftId policyNftId,
        Amount payoutAmount
    )
        internal
        virtual
    {
        // no action if no payout
        if (payoutAmount.eqz()) {
            return;
        }

        // create confirmed claim
        ClaimId claimId = _submitClaim(policyNftId, payoutAmount, "");
        _confirmClaim(policyNftId, claimId, payoutAmount, "");

        // create and execute payout
        PayoutId payoutId = _createPayout(policyNftId, claimId, payoutAmount, "");
        _processPayout(policyNftId, payoutId);
    }


    function _initialize(
        address registry,
        NftId instanceNftId,
        string memory componentName,
        IAuthorization authorization,
        address initialOwner
    )
        internal
        initializer()
    {
        __Product_init(
            registry,
            instanceNftId,
            componentName,
            IComponents.ProductInfo({
                isProcessingFundedClaims: false,
                isInterceptingPolicyTransfers: false,
                hasDistribution: false,
                expectedNumberOfOracles: 1,
                numberOfOracles: 0,
                poolNftId: NftIdLib.zero(),
                distributionNftId: NftIdLib.zero(),
                oracleNftId: new NftId[](0)
            }), 
            IComponents.FeeInfo({
                productFee: FeeLib.zero(),
                processingFee: FeeLib.zero(),
                distributionFee: FeeLib.zero(),
                minDistributionOwnerFee: FeeLib.zero(),
                poolFee: FeeLib.zero(),
                stakingFee: FeeLib.zero(),
                performanceFee: FeeLib.zero()
            }),
            authorization,
            initialOwner);  // number of oracles

        MAX_FLIGHT_DURATION = SecondsLib.fromDays(2);
        LIFETIME = SecondsLib.fromDays(30);
        WEIGHT_PATTERN = [0, 0, 0, 30, 50, 50];
        MIN_OBSERVATIONS = 10;
        MARGIN_PERCENT = 30;
    }
}

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

pragma solidity ^0.8.20;

import {IAuthority} from "@openzeppelin/contracts/access/manager/IAuthority.sol";
import {AuthorityUtils} from "@openzeppelin/contracts/access/manager/AuthorityUtils.sol";
import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol";
import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

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

        bool _consumingSchedule;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessManaged")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant AccessManagedStorageLocation = 0xf3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00;

    function _getAccessManagedStorage() private pure returns (AccessManagedStorage storage $) {
        assembly {
            $.slot := AccessManagedStorageLocation
        }
    }

    /**
     * @dev Initializes the contract connected to an initial authority.
     */
    function __AccessManaged_init(address initialAuthority) internal onlyInitializing {
        __AccessManaged_init_unchained(initialAuthority);
    }

    function __AccessManaged_init_unchained(address initialAuthority) internal onlyInitializing {
        _setAuthority(initialAuthority);
    }

    /**
     * @dev Restricts access to a function as defined by the connected Authority for this contract and the
     * caller and selector of the function that entered the contract.
     *
     * [IMPORTANT]
     * ====
     * In general, this modifier should only be used on `external` functions. It is okay to use it on `public`
     * functions that are used as external entry points and are not called internally. Unless you know what you're
     * doing, it should never be used on `internal` functions. Failure to follow these rules can have critical security
     * implications! This is because the permissions are determined by the function that entered the contract, i.e. the
     * function at the bottom of the call stack, and not the function where the modifier is visible in the source code.
     * ====
     *
     * [WARNING]
     * ====
     * Avoid adding this modifier to the https://docs.soliditylang.org/en/v0.8.20/contracts.html#receive-ether-function[`receive()`]
     * function or the https://docs.soliditylang.org/en/v0.8.20/contracts.html#fallback-function[`fallback()`]. These
     * functions are the only execution paths where a function selector cannot be 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) {
        AccessManagedStorage storage $ = _getAccessManagedStorage();
        return $._authority;
    }

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

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

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

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

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

pragma solidity ^0.8.20;

import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol";
import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";
import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {MulticallUpgradeable} from "../../utils/MulticallUpgradeable.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {Time} from "@openzeppelin/contracts/utils/types/Time.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";

/**
 * @dev AccessManager is a central contract to store the permissions of a system.
 *
 * A smart contract under the control of an AccessManager instance is known as a target, and will inherit from the
 * {AccessManaged} contract, be connected to this contract as its manager and implement the {AccessManaged-restricted}
 * modifier on a set of functions selected to be permissioned. Note that any function without this setup won't be
 * effectively restricted.
 *
 * The restriction rules for such functions are defined in terms of "roles" identified by an `uint64` and scoped
 * by target (`address`) and function selectors (`bytes4`). These roles are stored in this contract and can be
 * configured by admins (`ADMIN_ROLE` members) after a delay (see {getTargetAdminDelay}).
 *
 * For each target contract, admins can configure the following without any delay:
 *
 * * The target's {AccessManaged-authority} via {updateAuthority}.
 * * Close or open a target via {setTargetClosed} keeping the permissions intact.
 * * The roles that are allowed (or disallowed) to call a given function (identified by its selector) through {setTargetFunctionRole}.
 *
 * By default every address is member of the `PUBLIC_ROLE` and every target function is restricted to the `ADMIN_ROLE` until configured otherwise.
 * Additionally, each role has the following configuration options restricted to this manager's admins:
 *
 * * A role's admin role via {setRoleAdmin} who can grant or revoke roles.
 * * A role's guardian role via {setRoleGuardian} who's allowed to cancel operations.
 * * A delay in which a role takes effect after being granted through {setGrantDelay}.
 * * A delay of any target's admin action via {setTargetAdminDelay}.
 * * A role label for discoverability purposes with {labelRole}.
 *
 * Any account can be added and removed into any number of these roles by using the {grantRole} and {revokeRole} functions
 * restricted to each role's admin (see {getRoleAdmin}).
 *
 * Since all the permissions of the managed system can be modified by the admins of this instance, it is expected that
 * they will be highly secured (e.g., a multisig or a well-configured DAO).
 *
 * NOTE: This contract implements a form of the {IAuthority} interface, but {canCall} has additional return data so it
 * doesn't inherit `IAuthority`. It is however compatible with the `IAuthority` interface since the first 32 bytes of
 * the return data are a boolean as expected by that interface.
 *
 * NOTE: Systems that implement other access control mechanisms (for example using {Ownable}) can be paired with an
 * {AccessManager} by transferring permissions (ownership in the case of {Ownable}) directly to the {AccessManager}.
 * Users will be able to interact with these contracts through the {execute} function, following the access rules
 * registered in the {AccessManager}. Keep in mind that in that context, the msg.sender seen by restricted functions
 * will be {AccessManager} itself.
 *
 * WARNING: When granting permissions over an {Ownable} or {AccessControl} contract to an {AccessManager}, be very
 * mindful of the danger associated with functions such as {{Ownable-renounceOwnership}} or
 * {{AccessControl-renounceRole}}.
 */
contract AccessManagerUpgradeable is Initializable, ContextUpgradeable, MulticallUpgradeable, IAccessManager {
    using Time for *;

    // Structure that stores the details for a target contract.
    struct TargetConfig {
        mapping(bytes4 selector => uint64 roleId) allowedRoles;
        Time.Delay adminDelay;
        bool closed;
    }

    // Structure that stores the details for a role/account pair. This structures fit into a single slot.
    struct Access {
        // Timepoint at which the user gets the permission.
        // If this is either 0 or in the future, then the role permission is not available.
        uint48 since;
        // Delay for execution. Only applies to restricted() / execute() calls.
        Time.Delay delay;
    }

    // Structure that stores the details of a role.
    struct Role {
        // Members of the role.
        mapping(address user => Access access) members;
        // Admin who can grant or revoke permissions.
        uint64 admin;
        // Guardian who can cancel operations targeting functions that need this role.
        uint64 guardian;
        // Delay in which the role takes effect after being granted.
        Time.Delay grantDelay;
    }

    // Structure that stores the details for a scheduled operation. This structure fits into a single slot.
    struct Schedule {
        // Moment at which the operation can be executed.
        uint48 timepoint;
        // Operation nonce to allow third-party contracts to identify the operation.
        uint32 nonce;
    }

    uint64 public constant ADMIN_ROLE = type(uint64).min; // 0
    uint64 public constant PUBLIC_ROLE = type(uint64).max; // 2**64-1

    /// @custom:storage-location erc7201:openzeppelin.storage.AccessManager
    struct AccessManagerStorage {
        mapping(address target => TargetConfig mode) _targets;
        mapping(uint64 roleId => Role) _roles;
        mapping(bytes32 operationId => Schedule) _schedules;

        // Used to identify operations that are currently being executed via {execute}.
        // This should be transient storage when supported by the EVM.
        bytes32 _executionId;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessManager")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant AccessManagerStorageLocation = 0x40c6c8c28789853c7efd823ab20824bbd71718a8a5915e855f6f288c9a26ad00;

    function _getAccessManagerStorage() private pure returns (AccessManagerStorage storage $) {
        assembly {
            $.slot := AccessManagerStorageLocation
        }
    }

    /**
     * @dev Check that the caller is authorized to perform the operation, following the restrictions encoded in
     * {_getAdminRestrictions}.
     */
    modifier onlyAuthorized() {
        _checkAuthorized();
        _;
    }

    function __AccessManager_init(address initialAdmin) internal onlyInitializing {
        __AccessManager_init_unchained(initialAdmin);
    }

    function __AccessManager_init_unchained(address initialAdmin) internal onlyInitializing {
        if (initialAdmin == address(0)) {
            revert AccessManagerInvalidInitialAdmin(address(0));
        }

        // admin is active immediately and without any execution delay.
        _grantRole(ADMIN_ROLE, initialAdmin, 0, 0);
    }

    // =================================================== GETTERS ====================================================
    /// @inheritdoc IAccessManager
    function canCall(
        address caller,
        address target,
        bytes4 selector
    ) public view virtual returns (bool immediate, uint32 delay) {
        if (isTargetClosed(target)) {
            return (false, 0);
        } else if (caller == address(this)) {
            // Caller is AccessManager, this means the call was sent through {execute} and it already checked
            // permissions. We verify that the call "identifier", which is set during {execute}, is correct.
            return (_isExecuting(target, selector), 0);
        } else {
            uint64 roleId = getTargetFunctionRole(target, selector);
            (bool isMember, uint32 currentDelay) = hasRole(roleId, caller);
            return isMember ? (currentDelay == 0, currentDelay) : (false, 0);
        }
    }

    /// @inheritdoc IAccessManager
    function expiration() public view virtual returns (uint32) {
        return 1 weeks;
    }

    /// @inheritdoc IAccessManager
    function minSetback() public view virtual returns (uint32) {
        return 5 days;
    }

    /// @inheritdoc IAccessManager
    function isTargetClosed(address target) public view virtual returns (bool) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        return $._targets[target].closed;
    }

    /// @inheritdoc IAccessManager
    function getTargetFunctionRole(address target, bytes4 selector) public view virtual returns (uint64) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        return $._targets[target].allowedRoles[selector];
    }

    /// @inheritdoc IAccessManager
    function getTargetAdminDelay(address target) public view virtual returns (uint32) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        return $._targets[target].adminDelay.get();
    }

    /// @inheritdoc IAccessManager
    function getRoleAdmin(uint64 roleId) public view virtual returns (uint64) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        return $._roles[roleId].admin;
    }

    /// @inheritdoc IAccessManager
    function getRoleGuardian(uint64 roleId) public view virtual returns (uint64) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        return $._roles[roleId].guardian;
    }

    /// @inheritdoc IAccessManager
    function getRoleGrantDelay(uint64 roleId) public view virtual returns (uint32) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        return $._roles[roleId].grantDelay.get();
    }

    /// @inheritdoc IAccessManager
    function getAccess(
        uint64 roleId,
        address account
    ) public view virtual returns (uint48 since, uint32 currentDelay, uint32 pendingDelay, uint48 effect) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        Access storage access = $._roles[roleId].members[account];

        since = access.since;
        (currentDelay, pendingDelay, effect) = access.delay.getFull();

        return (since, currentDelay, pendingDelay, effect);
    }

    /// @inheritdoc IAccessManager
    function hasRole(
        uint64 roleId,
        address account
    ) public view virtual returns (bool isMember, uint32 executionDelay) {
        if (roleId == PUBLIC_ROLE) {
            return (true, 0);
        } else {
            (uint48 hasRoleSince, uint32 currentDelay, , ) = getAccess(roleId, account);
            return (hasRoleSince != 0 && hasRoleSince <= Time.timestamp(), currentDelay);
        }
    }

    // =============================================== ROLE MANAGEMENT ===============================================
    /// @inheritdoc IAccessManager
    function labelRole(uint64 roleId, string calldata label) public virtual onlyAuthorized {
        if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }
        emit RoleLabel(roleId, label);
    }

    /// @inheritdoc IAccessManager
    function grantRole(uint64 roleId, address account, uint32 executionDelay) public virtual onlyAuthorized {
        _grantRole(roleId, account, getRoleGrantDelay(roleId), executionDelay);
    }

    /// @inheritdoc IAccessManager
    function revokeRole(uint64 roleId, address account) public virtual onlyAuthorized {
        _revokeRole(roleId, account);
    }

    /// @inheritdoc IAccessManager
    function renounceRole(uint64 roleId, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessManagerBadConfirmation();
        }
        _revokeRole(roleId, callerConfirmation);
    }

    /// @inheritdoc IAccessManager
    function setRoleAdmin(uint64 roleId, uint64 admin) public virtual onlyAuthorized {
        _setRoleAdmin(roleId, admin);
    }

    /// @inheritdoc IAccessManager
    function setRoleGuardian(uint64 roleId, uint64 guardian) public virtual onlyAuthorized {
        _setRoleGuardian(roleId, guardian);
    }

    /// @inheritdoc IAccessManager
    function setGrantDelay(uint64 roleId, uint32 newDelay) public virtual onlyAuthorized {
        _setGrantDelay(roleId, newDelay);
    }

    /**
     * @dev Internal version of {grantRole} without access control. Returns true if the role was newly granted.
     *
     * Emits a {RoleGranted} event.
     */
    function _grantRole(
        uint64 roleId,
        address account,
        uint32 grantDelay,
        uint32 executionDelay
    ) internal virtual returns (bool) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        if (roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }

        bool newMember = $._roles[roleId].members[account].since == 0;
        uint48 since;

        if (newMember) {
            since = Time.timestamp() + grantDelay;
            $._roles[roleId].members[account] = Access({since: since, delay: executionDelay.toDelay()});
        } else {
            // No setback here. Value can be reset by doing revoke + grant, effectively allowing the admin to perform
            // any change to the execution delay within the duration of the role admin delay.
            ($._roles[roleId].members[account].delay, since) = $._roles[roleId].members[account].delay.withUpdate(
                executionDelay,
                0
            );
        }

        emit RoleGranted(roleId, account, executionDelay, since, newMember);
        return newMember;
    }

    /**
     * @dev Internal version of {revokeRole} without access control. This logic is also used by {renounceRole}.
     * Returns true if the role was previously granted.
     *
     * Emits a {RoleRevoked} event if the account had the role.
     */
    function _revokeRole(uint64 roleId, address account) internal virtual returns (bool) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        if (roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }

        if ($._roles[roleId].members[account].since == 0) {
            return false;
        }

        delete $._roles[roleId].members[account];

        emit RoleRevoked(roleId, account);
        return true;
    }

    /**
     * @dev Internal version of {setRoleAdmin} without access control.
     *
     * Emits a {RoleAdminChanged} event.
     *
     * NOTE: Setting the admin role as the `PUBLIC_ROLE` is allowed, but it will effectively allow
     * anyone to set grant or revoke such role.
     */
    function _setRoleAdmin(uint64 roleId, uint64 admin) internal virtual {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }

        $._roles[roleId].admin = admin;

        emit RoleAdminChanged(roleId, admin);
    }

    /**
     * @dev Internal version of {setRoleGuardian} without access control.
     *
     * Emits a {RoleGuardianChanged} event.
     *
     * NOTE: Setting the guardian role as the `PUBLIC_ROLE` is allowed, but it will effectively allow
     * anyone to cancel any scheduled operation for such role.
     */
    function _setRoleGuardian(uint64 roleId, uint64 guardian) internal virtual {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        if (roleId == ADMIN_ROLE || roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }

        $._roles[roleId].guardian = guardian;

        emit RoleGuardianChanged(roleId, guardian);
    }

    /**
     * @dev Internal version of {setGrantDelay} without access control.
     *
     * Emits a {RoleGrantDelayChanged} event.
     */
    function _setGrantDelay(uint64 roleId, uint32 newDelay) internal virtual {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        if (roleId == PUBLIC_ROLE) {
            revert AccessManagerLockedRole(roleId);
        }

        uint48 effect;
        ($._roles[roleId].grantDelay, effect) = $._roles[roleId].grantDelay.withUpdate(newDelay, minSetback());

        emit RoleGrantDelayChanged(roleId, newDelay, effect);
    }

    // ============================================= FUNCTION MANAGEMENT ==============================================
    /// @inheritdoc IAccessManager
    function setTargetFunctionRole(
        address target,
        bytes4[] calldata selectors,
        uint64 roleId
    ) public virtual onlyAuthorized {
        for (uint256 i = 0; i < selectors.length; ++i) {
            _setTargetFunctionRole(target, selectors[i], roleId);
        }
    }

    /**
     * @dev Internal version of {setTargetFunctionRole} without access control.
     *
     * Emits a {TargetFunctionRoleUpdated} event.
     */
    function _setTargetFunctionRole(address target, bytes4 selector, uint64 roleId) internal virtual {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        $._targets[target].allowedRoles[selector] = roleId;
        emit TargetFunctionRoleUpdated(target, selector, roleId);
    }

    /// @inheritdoc IAccessManager
    function setTargetAdminDelay(address target, uint32 newDelay) public virtual onlyAuthorized {
        _setTargetAdminDelay(target, newDelay);
    }

    /**
     * @dev Internal version of {setTargetAdminDelay} without access control.
     *
     * Emits a {TargetAdminDelayUpdated} event.
     */
    function _setTargetAdminDelay(address target, uint32 newDelay) internal virtual {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        uint48 effect;
        ($._targets[target].adminDelay, effect) = $._targets[target].adminDelay.withUpdate(newDelay, minSetback());

        emit TargetAdminDelayUpdated(target, newDelay, effect);
    }

    // =============================================== MODE MANAGEMENT ================================================
    /// @inheritdoc IAccessManager
    function setTargetClosed(address target, bool closed) public virtual onlyAuthorized {
        _setTargetClosed(target, closed);
    }

    /**
     * @dev Set the closed flag for a contract. This is an internal setter with no access restrictions.
     *
     * Emits a {TargetClosed} event.
     */
    function _setTargetClosed(address target, bool closed) internal virtual {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        if (target == address(this)) {
            revert AccessManagerLockedAccount(target);
        }
        $._targets[target].closed = closed;
        emit TargetClosed(target, closed);
    }

    // ============================================== DELAYED OPERATIONS ==============================================
    /// @inheritdoc IAccessManager
    function getSchedule(bytes32 id) public view virtual returns (uint48) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        uint48 timepoint = $._schedules[id].timepoint;
        return _isExpired(timepoint) ? 0 : timepoint;
    }

    /// @inheritdoc IAccessManager
    function getNonce(bytes32 id) public view virtual returns (uint32) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        return $._schedules[id].nonce;
    }

    /// @inheritdoc IAccessManager
    function schedule(
        address target,
        bytes calldata data,
        uint48 when
    ) public virtual returns (bytes32 operationId, uint32 nonce) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        address caller = _msgSender();

        // Fetch restrictions that apply to the caller on the targeted function
        (, uint32 setback) = _canCallExtended(caller, target, data);

        uint48 minWhen = Time.timestamp() + setback;

        // if call with delay is not authorized, or if requested timing is too soon
        if (setback == 0 || (when > 0 && when < minWhen)) {
            revert AccessManagerUnauthorizedCall(caller, target, _checkSelector(data));
        }

        // Reuse variable due to stack too deep
        when = uint48(Math.max(when, minWhen)); // cast is safe: both inputs are uint48

        // If caller is authorised, schedule operation
        operationId = hashOperation(caller, target, data);

        _checkNotScheduled(operationId);

        unchecked {
            // It's not feasible to overflow the nonce in less than 1000 years
            nonce = $._schedules[operationId].nonce + 1;
        }
        $._schedules[operationId].timepoint = when;
        $._schedules[operationId].nonce = nonce;
        emit OperationScheduled(operationId, nonce, when, caller, target, data);

        // Using named return values because otherwise we get stack too deep
    }

    /**
     * @dev Reverts if the operation is currently scheduled and has not expired.
     * (Note: This function was introduced due to stack too deep errors in schedule.)
     */
    function _checkNotScheduled(bytes32 operationId) private view {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        uint48 prevTimepoint = $._schedules[operationId].timepoint;
        if (prevTimepoint != 0 && !_isExpired(prevTimepoint)) {
            revert AccessManagerAlreadyScheduled(operationId);
        }
    }

    /// @inheritdoc IAccessManager
    // Reentrancy is not an issue because permissions are checked on msg.sender. Additionally,
    // _consumeScheduledOp guarantees a scheduled operation is only executed once.
    // slither-disable-next-line reentrancy-no-eth
    function execute(address target, bytes calldata data) public payable virtual returns (uint32) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        address caller = _msgSender();

        // Fetch restrictions that apply to the caller on the targeted function
        (bool immediate, uint32 setback) = _canCallExtended(caller, target, data);

        // If caller is not authorised, revert
        if (!immediate && setback == 0) {
            revert AccessManagerUnauthorizedCall(caller, target, _checkSelector(data));
        }

        bytes32 operationId = hashOperation(caller, target, data);
        uint32 nonce;

        // If caller is authorised, check operation was scheduled early enough
        // Consume an available schedule even if there is no currently enforced delay
        if (setback != 0 || getSchedule(operationId) != 0) {
            nonce = _consumeScheduledOp(operationId);
        }

        // Mark the target and selector as authorised
        bytes32 executionIdBefore = $._executionId;
        $._executionId = _hashExecutionId(target, _checkSelector(data));

        // Perform call
        Address.functionCallWithValue(target, data, msg.value);

        // Reset execute identifier
        $._executionId = executionIdBefore;

        return nonce;
    }

    /// @inheritdoc IAccessManager
    function cancel(address caller, address target, bytes calldata data) public virtual returns (uint32) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        address msgsender = _msgSender();
        bytes4 selector = _checkSelector(data);

        bytes32 operationId = hashOperation(caller, target, data);
        if ($._schedules[operationId].timepoint == 0) {
            revert AccessManagerNotScheduled(operationId);
        } else if (caller != msgsender) {
            // calls can only be canceled by the account that scheduled them, a global admin, or by a guardian of the required role.
            (bool isAdmin, ) = hasRole(ADMIN_ROLE, msgsender);
            (bool isGuardian, ) = hasRole(getRoleGuardian(getTargetFunctionRole(target, selector)), msgsender);
            if (!isAdmin && !isGuardian) {
                revert AccessManagerUnauthorizedCancel(msgsender, caller, target, selector);
            }
        }

        delete $._schedules[operationId].timepoint; // reset the timepoint, keep the nonce
        uint32 nonce = $._schedules[operationId].nonce;
        emit OperationCanceled(operationId, nonce);

        return nonce;
    }

    /// @inheritdoc IAccessManager
    function consumeScheduledOp(address caller, bytes calldata data) public virtual {
        address target = _msgSender();
        if (IAccessManaged(target).isConsumingScheduledOp() != IAccessManaged.isConsumingScheduledOp.selector) {
            revert AccessManagerUnauthorizedConsume(target);
        }
        _consumeScheduledOp(hashOperation(caller, target, data));
    }

    /**
     * @dev Internal variant of {consumeScheduledOp} that operates on bytes32 operationId.
     *
     * Returns the nonce of the scheduled operation that is consumed.
     */
    function _consumeScheduledOp(bytes32 operationId) internal virtual returns (uint32) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        uint48 timepoint = $._schedules[operationId].timepoint;
        uint32 nonce = $._schedules[operationId].nonce;

        if (timepoint == 0) {
            revert AccessManagerNotScheduled(operationId);
        } else if (timepoint > Time.timestamp()) {
            revert AccessManagerNotReady(operationId);
        } else if (_isExpired(timepoint)) {
            revert AccessManagerExpired(operationId);
        }

        delete $._schedules[operationId].timepoint; // reset the timepoint, keep the nonce
        emit OperationExecuted(operationId, nonce);

        return nonce;
    }

    /// @inheritdoc IAccessManager
    function hashOperation(address caller, address target, bytes calldata data) public view virtual returns (bytes32) {
        return keccak256(abi.encode(caller, target, data));
    }

    // ==================================================== OTHERS ====================================================
    /// @inheritdoc IAccessManager
    function updateAuthority(address target, address newAuthority) public virtual onlyAuthorized {
        IAccessManaged(target).setAuthority(newAuthority);
    }

    // ================================================= ADMIN LOGIC ==================================================
    /**
     * @dev Check if the current call is authorized according to admin logic.
     */
    function _checkAuthorized() private {
        address caller = _msgSender();
        (bool immediate, uint32 delay) = _canCallSelf(caller, _msgData());
        if (!immediate) {
            if (delay == 0) {
                (, uint64 requiredRole, ) = _getAdminRestrictions(_msgData());
                revert AccessManagerUnauthorizedAccount(caller, requiredRole);
            } else {
                _consumeScheduledOp(hashOperation(caller, address(this), _msgData()));
            }
        }
    }

    /**
     * @dev Get the admin restrictions of a given function call based on the function and arguments involved.
     *
     * Returns:
     * - bool restricted: does this data match a restricted operation
     * - uint64: which role is this operation restricted to
     * - uint32: minimum delay to enforce for that operation (max between operation's delay and admin's execution delay)
     */
    function _getAdminRestrictions(
        bytes calldata data
    ) private view returns (bool restricted, uint64 roleAdminId, uint32 executionDelay) {
        if (data.length < 4) {
            return (false, 0, 0);
        }

        bytes4 selector = _checkSelector(data);

        // Restricted to ADMIN with no delay beside any execution delay the caller may have
        if (
            selector == this.labelRole.selector ||
            selector == this.setRoleAdmin.selector ||
            selector == this.setRoleGuardian.selector ||
            selector == this.setGrantDelay.selector ||
            selector == this.setTargetAdminDelay.selector
        ) {
            return (true, ADMIN_ROLE, 0);
        }

        // Restricted to ADMIN with the admin delay corresponding to the target
        if (
            selector == this.updateAuthority.selector ||
            selector == this.setTargetClosed.selector ||
            selector == this.setTargetFunctionRole.selector
        ) {
            // First argument is a target.
            address target = abi.decode(data[0x04:0x24], (address));
            uint32 delay = getTargetAdminDelay(target);
            return (true, ADMIN_ROLE, delay);
        }

        // Restricted to that role's admin with no delay beside any execution delay the caller may have.
        if (selector == this.grantRole.selector || selector == this.revokeRole.selector) {
            // First argument is a roleId.
            uint64 roleId = abi.decode(data[0x04:0x24], (uint64));
            return (true, getRoleAdmin(roleId), 0);
        }

        return (false, 0, 0);
    }

    // =================================================== HELPERS ====================================================
    /**
     * @dev An extended version of {canCall} for internal usage that checks {_canCallSelf}
     * when the target is this contract.
     *
     * Returns:
     * - bool immediate: whether the operation can be executed immediately (with no delay)
     * - uint32 delay: the execution delay
     */
    function _canCallExtended(
        address caller,
        address target,
        bytes calldata data
    ) private view returns (bool immediate, uint32 delay) {
        if (target == address(this)) {
            return _canCallSelf(caller, data);
        } else {
            return data.length < 4 ? (false, 0) : canCall(caller, target, _checkSelector(data));
        }
    }

    /**
     * @dev A version of {canCall} that checks for admin restrictions in this contract.
     */
    function _canCallSelf(address caller, bytes calldata data) private view returns (bool immediate, uint32 delay) {
        if (data.length < 4) {
            return (false, 0);
        }

        if (caller == address(this)) {
            // Caller is AccessManager, this means the call was sent through {execute} and it already checked
            // permissions. We verify that the call "identifier", which is set during {execute}, is correct.
            return (_isExecuting(address(this), _checkSelector(data)), 0);
        }

        (bool enabled, uint64 roleId, uint32 operationDelay) = _getAdminRestrictions(data);
        if (!enabled) {
            return (false, 0);
        }

        (bool inRole, uint32 executionDelay) = hasRole(roleId, caller);
        if (!inRole) {
            return (false, 0);
        }

        // downcast is safe because both options are uint32
        delay = uint32(Math.max(operationDelay, executionDelay));
        return (delay == 0, delay);
    }

    /**
     * @dev Returns true if a call with `target` and `selector` is being executed via {executed}.
     */
    function _isExecuting(address target, bytes4 selector) private view returns (bool) {
        AccessManagerStorage storage $ = _getAccessManagerStorage();
        return $._executionId == _hashExecutionId(target, selector);
    }

    /**
     * @dev Returns true if a schedule timepoint is past its expiration deadline.
     */
    function _isExpired(uint48 timepoint) private view returns (bool) {
        return timepoint + expiration() <= Time.timestamp();
    }

    /**
     * @dev Extracts the selector from calldata. Panics if data is not at least 4 bytes
     */
    function _checkSelector(bytes calldata data) private pure returns (bytes4) {
        return bytes4(data[0:4]);
    }

    /**
     * @dev Hashing function for execute protection
     */
    function _hashExecutionId(address target, bytes4 selector) private pure returns (bytes32) {
        return keccak256(abi.encode(target, selector));
    }
}

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

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}

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

pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract ContextUpgradeable is Initializable {
    function __Context_init() internal onlyInitializing {
    }

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}

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

pragma solidity ^0.8.20;

import {Address} from "@openzeppelin/contracts/utils/Address.sol";
import {ContextUpgradeable} from "./ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Provides a function to batch together multiple calls in a single external call.
 *
 * Consider any assumption about calldata validation performed by the sender may be violated if it's not especially
 * careful about sending transactions invoking {multicall}. For example, a relay address that filters function
 * selectors won't filter calls nested within a {multicall} operation.
 *
 * NOTE: Since 5.0.1 and 4.9.4, this contract identifies non-canonical contexts (i.e. `msg.sender` is not {_msgSender}).
 * If a non-canonical context is identified, the following self `delegatecall` appends the last bytes of `msg.data`
 * to the subcall. This makes it safe to use with {ERC2771Context}. Contexts that don't affect the resolution of
 * {_msgSender} are not propagated to subcalls.
 */
abstract contract MulticallUpgradeable is Initializable, ContextUpgradeable {
    function __Multicall_init() internal onlyInitializing {
    }

    function __Multicall_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Receives and executes a batch of function calls on this contract.
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function multicall(bytes[] calldata data) external virtual returns (bytes[] memory results) {
        bytes memory context = msg.sender == _msgSender()
            ? new bytes(0)
            : msg.data[msg.data.length - _contextSuffixLength():];

        results = new bytes[](data.length);
        for (uint256 i = 0; i < data.length; i++) {
            results[i] = Address.functionDelegateCall(address(this), bytes.concat(data[i], context));
        }
        return results;
    }
}

// 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 8 of 130 : 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) (access/Ownable.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

    /**
     * @dev Returns the address of the current owner.
     */
    function owner() public view virtual returns (address) {
        return _owner;
    }

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

File 14 of 130 : IERC5267.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)

pragma solidity ^0.8.20;

interface IERC5267 {
    /**
     * @dev MAY be emitted to signal that the domain could have changed.
     */
    event EIP712DomainChanged();

    /**
     * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
     * signature.
     */
    function eip712Domain()
        external
        view
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        );
}

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

pragma solidity ^0.8.20;

import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 */
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
    mapping(address account => uint256) private _balances;

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

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

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

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

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

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

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

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

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

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

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

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

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

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

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

        emit Transfer(from, to, value);
    }

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

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

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

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

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

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

pragma solidity ^0.8.20;

import {IERC20Permit} from "./IERC20Permit.sol";
import {ERC20} from "../ERC20.sol";
import {ECDSA} from "../../../utils/cryptography/ECDSA.sol";
import {EIP712} from "../../../utils/cryptography/EIP712.sol";
import {Nonces} from "../../../utils/Nonces.sol";

/**
 * @dev Implementation 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.
 */
abstract contract ERC20Permit is ERC20, IERC20Permit, EIP712, Nonces {
    bytes32 private constant PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

    /**
     * @dev Permit deadline has expired.
     */
    error ERC2612ExpiredSignature(uint256 deadline);

    /**
     * @dev Mismatched signature.
     */
    error ERC2612InvalidSigner(address signer, address owner);

    /**
     * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`.
     *
     * It's a good idea to use the same `name` that is defined as the ERC20 token name.
     */
    constructor(string memory name) EIP712(name, "1") {}

    /**
     * @inheritdoc IERC20Permit
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        if (block.timestamp > deadline) {
            revert ERC2612ExpiredSignature(deadline);
        }

        bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline));

        bytes32 hash = _hashTypedDataV4(structHash);

        address signer = ECDSA.recover(hash, v, r, s);
        if (signer != owner) {
            revert ERC2612InvalidSigner(signer, owner);
        }

        _approve(owner, spender, value);
    }

    /**
     * @inheritdoc IERC20Permit
     */
    function nonces(address owner) public view virtual override(IERC20Permit, Nonces) returns (uint256) {
        return super.nonces(owner);
    }

    /**
     * @inheritdoc IERC20Permit
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view virtual returns (bytes32) {
        return _domainSeparatorV4();
    }
}

// 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 21 of 130 : 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/cryptography/ECDSA.sol)

pragma solidity ^0.8.20;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS
    }

    /**
     * @dev The signature derives the `address(0)`.
     */
    error ECDSAInvalidSignature();

    /**
     * @dev The signature has an invalid length.
     */
    error ECDSAInvalidSignatureLength(uint256 length);

    /**
     * @dev The signature has an S value that is in the upper half order.
     */
    error ECDSAInvalidSignatureS(bytes32 s);

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
     * return address(0) without also returning an error description. Errors are documented using an enum (error type)
     * and a bytes32 providing additional information about the error.
     *
     * If no error is returned, then the address can be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
        unchecked {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError, bytes32) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS, s);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature, bytes32(0));
        }

        return (signer, RecoverError.NoError, bytes32(0));
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
     */
    function _throwError(RecoverError error, bytes32 errorArg) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert ECDSAInvalidSignature();
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert ECDSAInvalidSignatureLength(uint256(errorArg));
        } else if (error == RecoverError.InvalidSignatureS) {
            revert ECDSAInvalidSignatureS(errorArg);
        }
    }
}

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

pragma solidity ^0.8.20;

import {MessageHashUtils} from "./MessageHashUtils.sol";
import {ShortStrings, ShortString} from "../ShortStrings.sol";
import {IERC5267} from "../../interfaces/IERC5267.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
 * encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
 * does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
 * produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
 * separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the
 * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
 *
 * @custom:oz-upgrades-unsafe-allow state-variable-immutable
 */
abstract contract EIP712 is IERC5267 {
    using ShortStrings for *;

    bytes32 private constant TYPE_HASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _cachedDomainSeparator;
    uint256 private immutable _cachedChainId;
    address private immutable _cachedThis;

    bytes32 private immutable _hashedName;
    bytes32 private immutable _hashedVersion;

    ShortString private immutable _name;
    ShortString private immutable _version;
    string private _nameFallback;
    string private _versionFallback;

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        _name = name.toShortStringWithFallback(_nameFallback);
        _version = version.toShortStringWithFallback(_versionFallback);
        _hashedName = keccak256(bytes(name));
        _hashedVersion = keccak256(bytes(version));

        _cachedChainId = block.chainid;
        _cachedDomainSeparator = _buildDomainSeparator();
        _cachedThis = address(this);
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
            return _cachedDomainSeparator;
        } else {
            return _buildDomainSeparator();
        }
    }

    function _buildDomainSeparator() private view returns (bytes32) {
        return keccak256(abi.encode(TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
    }

    /**
     * @dev See {IERC-5267}.
     */
    function eip712Domain()
        public
        view
        virtual
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        return (
            hex"0f", // 01111
            _EIP712Name(),
            _EIP712Version(),
            block.chainid,
            address(this),
            bytes32(0),
            new uint256[](0)
        );
    }

    /**
     * @dev The name parameter for the EIP712 domain.
     *
     * NOTE: By default this function reads _name which is an immutable value.
     * It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
     */
    // solhint-disable-next-line func-name-mixedcase
    function _EIP712Name() internal view returns (string memory) {
        return _name.toStringWithFallback(_nameFallback);
    }

    /**
     * @dev The version parameter for the EIP712 domain.
     *
     * NOTE: By default this function reads _version which is an immutable value.
     * It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
     */
    // solhint-disable-next-line func-name-mixedcase
    function _EIP712Version() internal view returns (string memory) {
        return _version.toStringWithFallback(_versionFallback);
    }
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
 *
 * The library provides methods for generating a hash of a message that conforms to the
 * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
 * specifications.
 */
library MessageHashUtils {
    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing a bytes32 `messageHash` with
     * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
     * keccak256, although any bytes32 value can be safely used because the final digest will
     * be re-hashed.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
            mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
            digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
        }
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing an arbitrary `message` with
     * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
        return
            keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x00` (data with intended validator).
     *
     * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
     * `validator` address. Then hashing the result.
     *
     * See {ECDSA-recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(hex"19_00", validator, data));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
     *
     * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
     * `\x19\x01` and hashing the result. It corresponds to the hash signed by the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
     *
     * See {ECDSA-recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, hex"19_01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            digest := keccak256(ptr, 0x42)
        }
    }
}

// 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 30 of 130 : 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/math/SignedMath.sol)

pragma solidity ^0.8.20;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

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

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

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

/**
 * @dev Provides tracking nonces for addresses. Nonces will only increment.
 */
abstract contract Nonces {
    /**
     * @dev The nonce used for an `account` is not the expected current nonce.
     */
    error InvalidAccountNonce(address account, uint256 currentNonce);

    mapping(address account => uint256) private _nonces;

    /**
     * @dev Returns the next unused nonce for an address.
     */
    function nonces(address owner) public view virtual returns (uint256) {
        return _nonces[owner];
    }

    /**
     * @dev Consumes a nonce.
     *
     * Returns the current value and increments nonce.
     */
    function _useNonce(address owner) internal virtual returns (uint256) {
        // For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be
        // decremented or reset. This guarantees that the nonce never overflows.
        unchecked {
            // It is important to do x++ and not ++x here.
            return _nonces[owner]++;
        }
    }

    /**
     * @dev Same as {_useNonce} but checking that `nonce` is the next valid for `owner`.
     */
    function _useCheckedNonce(address owner, uint256 nonce) internal virtual {
        uint256 current = _useNonce(owner);
        if (nonce != current) {
            revert InvalidAccountNonce(owner, current);
        }
    }
}

// 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/Strings.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant HEX_DIGITS = "0123456789abcdef";
    uint8 private constant ADDRESS_LENGTH = 20;

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), HEX_DIGITS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toStringSigned(int256 value) internal pure returns (string memory) {
        return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value)));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        uint256 localValue = value;
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = HEX_DIGITS[localValue & 0xf];
            localValue >>= 4;
        }
        if (localValue != 0) {
            revert StringsInsufficientHexLength(value, length);
        }
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal
     * representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

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

            return true;
        } else {
            return false;
        }
    }

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

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

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

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

    // Bytes32Set

    struct Bytes32Set {
        Set _inner;
    }

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

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

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

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

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

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

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // AddressSet

    struct AddressSet {
        Set _inner;
    }

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

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

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

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

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

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

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }

    // UintSet

    struct UintSet {
        Set _inner;
    }

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

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

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

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

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

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

        /// @solidity memory-safe-assembly
        assembly {
            result := store
        }

        return result;
    }
}

// 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 38 of 130 : AccessAdmin.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";
import {EnumerableSet} from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol";

import {IAccess} from "./IAccess.sol";
import {IAccessAdmin} from "./IAccessAdmin.sol";
import {IAuthorization} from "./IAuthorization.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {IServiceAuthorization} from "./IServiceAuthorization.sol";

import {AccessAdminLib} from "./AccessAdminLib.sol";
import {AccessManagerCloneable} from "./AccessManagerCloneable.sol";
import {Blocknumber, BlocknumberLib} from "../type/Blocknumber.sol";
import {ContractLib} from "../shared/ContractLib.sol";
import {NftId, NftIdLib} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {RoleId, RoleIdLib, ADMIN_ROLE, PUBLIC_ROLE} from "../type/RoleId.sol";
import {Selector, SelectorSetLib} from "../type/Selector.sol";
import {Str, StrLib} from "../type/String.sol";
import {TimestampLib} from "../type/Timestamp.sol";
import {VersionPart} from "../type/Version.sol";


/**
 * @dev A generic access amin contract that implements role based access control based on OpenZeppelin's AccessManager contract.
 * The contract provides read functions to query all available roles, targets and access rights.
 * This contract works for both a constructor based deployment or a deployment based on cloning and initialization.
 */ 
contract AccessAdmin is
    AccessManagedUpgradeable,
    IAccessAdmin
{
    using EnumerableSet for EnumerableSet.AddressSet;

    /// @dev admin name used for logging only
    string internal _adminName;

    /// @dev the access manager driving the access admin contract
    /// hold link to registry and release version
    AccessManagerCloneable internal _authority;

    /// @dev the authorization contract used for initial access control
    IAuthorization internal _authorization;

    // /// @dev stores the deployer address and allows to create initializers
    // /// that are restricted to the deployer address.
    // address internal _deployer;

    /// @dev the linked NFT ID
    NftId internal _linkedNftId;

    /// @dev store role info per role id
    mapping(RoleId roleId => RoleInfo info) internal _roleInfo;

    /// @dev store role name info per role name
    mapping(Str roleName => RoleNameInfo nameInfo) internal _roleForName;

    /// @dev store array with all created roles
    RoleId [] internal _roleIds;

    // @dev target type specific role id counters
    mapping(TargetType => uint64) internal _nextRoleId;

    /// @dev store set of current role members for given role
    mapping(RoleId roleId => EnumerableSet.AddressSet roleMembers) internal _roleMembers;

    /// @dev store target info per target address
    mapping(address target => TargetInfo info) internal _targetInfo;

    /// @dev store role name info per role name
    mapping(Str targetName => address target) internal _targetForName;

    /// @dev store array with all created targets
    address [] internal _targets;

    /// @dev store all managed functions per target
    mapping(address target => SelectorSetLib.Set selectors) internal _targetFunctions;

    /// @dev function infos array
    mapping(address target => mapping(Selector selector => FunctionInfo)) internal _functionInfo;

    /// @dev temporary dynamic functions array
    bytes4[] private _functions;


    //-------------- initialization functions ------------------------------//

    /// @dev Initializes this admin with the provided accessManager (and authorization specification).
    /// Internally initializes access manager with this admin and creates basic role setup.
    function initialize(
        address authority,
        string memory adminName 
    )
        public
        initializer()
    {
        __AccessAdmin_init(authority, adminName);
    }


    /// @dev Initializes this admin with the provided accessManager and name.
    /// IMPORTANT
    /// - cloning of an access admin and initialization MUST be done in the same tx.
    /// - this function as well as any completeSetup functions MUST be called in the same tx.
    function __AccessAdmin_init(
        address authority, 
        string memory adminName 
    )
        internal
        onlyInitializing()
    {
        AccessAdminLib.checkInitParameters(authority, adminName);
        _authority = AccessManagerCloneable(authority);
        _authority.initialize(address(this));

        // delayed additional check for authority after its initialization
        if (!ContractLib.isAuthority(authority)) {
            revert ErrorAccessAdminAccessManagerNotAccessManager(authority);
        }

        // effects
        // set and initialize this access manager contract as
        // the admin (ADMIN_ROLE) of the provided authority
        __AccessManaged_init(authority);

        // set name for logging
        _adminName = adminName;

        // set initial linked NFT ID to zero
        _linkedNftId = NftIdLib.zero();

        // setup admin role
        _createRoleUnchecked(
            ADMIN_ROLE(), AccessAdminLib.adminRoleInfo());

        // add this contract as admin role member, as contract roles cannot be revoked
        // and max member count is 1 for admin role this access admin contract will
        // always be the only admin of the access manager.
        _roleMembers[
            RoleIdLib.toRoleId(_authority.ADMIN_ROLE())].add(address(this));

        // setup public role
        _createRoleUnchecked(
            PUBLIC_ROLE(), AccessAdminLib.publicRoleInfo());
    }

    //--- view functions for access admin ---------------------------------------//

    function getRelease() public view virtual returns (VersionPart release) {
        return _authority.getRelease();
    }


    function getRegistry() public view returns (IRegistry registry) {
        return _authority.getRegistry();
    }


    function getLinkedNftId() external view returns (NftId linkedNftId) {
        return _linkedNftId;
    }


    function getAuthorization() public view returns (IAuthorization authorization) {
        return _authorization;
    }


    function isLocked() public view returns (bool locked) {
        return _authority.isLocked();
    }

    //--- view functions for roles ------------------------------------------//

    function roles() external view returns (uint256 numberOfRoles) {
        return _roleIds.length;
    }

    function getRoleId(uint256 idx) external view returns (RoleId roleId) {
        return _roleIds[idx];
    }

    function getAdminRole() public view returns (RoleId roleId) {
        return RoleId.wrap(_authority.ADMIN_ROLE());
    }

    function getPublicRole() public view returns (RoleId roleId) {
        return RoleId.wrap(_authority.PUBLIC_ROLE());
    }

    function roleExists(RoleId roleId) public view returns (bool exists) {
        return _roleInfo[roleId].targetType != TargetType.Undefined;
    }

    function getRoleForName(string memory name) public view returns (RoleId roleId, bool exists) {
        roleId = _roleForName[StrLib.toStr(name)].roleId;
        exists = false;

        if (roleId.gtz() || AccessAdminLib.isAdminRoleName(name)) {
            exists = true;
        }
    }

    function getRoleInfo(RoleId roleId) public view returns (RoleInfo memory) {
        return _roleInfo[roleId];
    }

    function isRoleActive(RoleId roleId) external view returns (bool isActive) {
        return _roleInfo[roleId].pausedAt > TimestampLib.current();
    }

    function isRoleCustom(RoleId roleId) external view returns (bool isActive) {
        return _roleInfo[roleId].targetType == TargetType.Custom;
    }

    function roleMembers(RoleId roleId) external view returns (uint256 numberOfMembers) {
        return _roleMembers[roleId].length();
    }

    function getRoleMember(RoleId roleId, uint256 idx) external view returns (address account) {
        return _roleMembers[roleId].at(idx);
    }

    function isRoleMember(RoleId roleId, address account) public view returns (bool) {
        (bool isMember, ) = _authority.hasRole(
            RoleId.unwrap(roleId), 
            account);
        return isMember;
    }

    function isRoleAdmin(RoleId roleId, address account) public virtual view returns (bool) {
        return isRoleMember(_roleInfo[roleId].adminRoleId, account);
    }

    //--- view functions for targets ----------------------------------------//

    function targetExists(address target) public view returns (bool exists) {
        return _targetInfo[target].createdAt.gtz();
    }

    function targets() external view returns (uint256 numberOfTargets) {
        return _targets.length;
    }

    function getTargetAddress(uint256 idx) external view returns (address target) {
        return _targets[idx];
    }

    function getTargetInfo(address target) public view returns (TargetInfo memory targetInfo) {
        return _targetInfo[target];
    }

    function getTargetForName(Str name) public view returns (address target) {
        return _targetForName[name];
    }

    function isTargetLocked(address target) public view returns (bool locked) {
        return _authority.isLocked() || _authority.isTargetClosed(target);
    }

    //--- view functions for target functions -------------------------------//

    function authorizedFunctions(address target) external view returns (uint256 numberOfFunctions) {
        return SelectorSetLib.size(_targetFunctions[target]);
    }

    function getAuthorizedFunction(
        address target, 
        uint256 idx
    )
        external 
        view 
        returns (
            FunctionInfo memory func, 
            RoleId roleId
        )
    {
        Selector selector = SelectorSetLib.at(_targetFunctions[target], idx);
        func = _functionInfo[target][selector];
        roleId = AccessAdminLib.getFunctionRoleId(_authority, target, selector);
        // roleId = RoleIdLib.toRoleId(
        //     _authority.getTargetFunctionRole(
        //         target, 
        //         selector.toBytes4()));
    }


    function getFunctionInfo(address target, Selector selector)
        external
        view
        returns (FunctionInfo memory functionInfo)
    {
        return _functionInfo[target][selector];
    }

    //--- internal/private functions -------------------------------------------------//

    function _linkToNftOwnable(address registerable) internal {
        if (!getRegistry().isRegistered(registerable)) {
            revert ErrorAccessAdminNotRegistered(registerable);
        }

        _linkedNftId = getRegistry().getNftIdForAddress(registerable);
    }


    function _createRoles(
        IServiceAuthorization authorization
    )
        internal
    {
        RoleId[] memory roles = authorization.getRoles();

        for(uint256 i = 0; i < roles.length; i++) {
            RoleId authzRoleId = roles[i];
            IAccess.RoleInfo memory roleInfo = authorization.getRoleInfo(authzRoleId);
            (RoleId roleId, bool exists) = getRoleForName(roleInfo.name.toString());

            if (!exists) {
                if (!AccessAdminLib.isDynamicRoleId(authzRoleId) || roleInfo.targetType == TargetType.Custom) {
                    roleId = authzRoleId;
                }

                _createRole(
                    roleId,
                    roleInfo,
                    true);
            }
        }
    }


    /// @dev Creates a role based on the provided parameters.
    /// Checks that the provided role and role id and role name not already used.
    function _createRole(
        RoleId roleId, 
        RoleInfo memory info,
        bool revertOnExistingRole
    )
        internal
    {
        bool isAdminOrPublicRole = AccessAdminLib.checkRoleCreation(this, roleId, info, revertOnExistingRole);
        if (!isAdminOrPublicRole) {
            _createRoleUnchecked(roleId, info);
        }
    }


    function _createRoleUnchecked(
        RoleId roleId, 
        RoleInfo memory info
    )
        private
    {
        // create role info
        info.createdAt = TimestampLib.current();
        info.pausedAt = TimestampLib.max();
        _roleInfo[roleId] = info;

        // create role name info
        _roleForName[info.name] = RoleNameInfo({
            roleId: roleId,
            exists: true});

        // add role to list of roles
        _roleIds.push(roleId);

        emit LogAccessAdminRoleCreated(_adminName, roleId, info.targetType, info.adminRoleId, info.name.toString());
    }


    /// @dev Activates or deactivates role.
    /// The role activ property is indirectly controlled over the pausedAt timestamp.
    function _setRoleActive(RoleId roleId, bool active)
        internal
    {
        AccessAdminLib.checkRoleExists(this, roleId, false, false);

        if (active) {
            _roleInfo[roleId].pausedAt = TimestampLib.max();
        } else {
            _roleInfo[roleId].pausedAt = TimestampLib.current();
        }

        Blocknumber lastUpdateIn = _roleInfo[roleId].lastUpdateIn;
        _roleInfo[roleId].lastUpdateIn = BlocknumberLib.current();

        emit LogAccessAdminRoleActivatedSet(_adminName, roleId, active, lastUpdateIn);
    }


    /// @dev grant the specified role to the provided account
    function _grantRoleToAccount(RoleId roleId, address account)
        internal
    {
        AccessAdminLib.checkRoleExists(this, roleId, true, false);

        // check max role members will not be exceeded
        if (_roleMembers[roleId].length() >= _roleInfo[roleId].maxMemberCount) {
            revert ErrorAccessAdminRoleMembersLimitReached(roleId, _roleInfo[roleId].maxMemberCount);
        }

        // check account is contract for contract role
        if (
            _roleInfo[roleId].targetType != TargetType.Custom &&
            !ContractLib.isContract(account) // will fail in account's constructor
        ) {
            revert ErrorAccessAdminRoleMemberNotContract(roleId, account);
        }

        // effects
        _roleMembers[roleId].add(account);
        _authority.grantRole(
            RoleId.unwrap(roleId), 
            account, 
            0);
        
        emit LogAccessAdminRoleGranted(
            _adminName, 
            account, 
            AccessAdminLib.getRoleName(this, roleId));
    }


    /// @dev revoke the specified role from the provided account
    function _revokeRoleFromAccount(RoleId roleId, address account)
        internal
    {
        AccessAdminLib.checkRoleExists(this, roleId, false, false);

        // check for attempt to revoke contract role
        if (_roleInfo[roleId].targetType != TargetType.Custom) {
            revert ErrorAccessAdminRoleMemberRemovalDisabled(roleId, account);
        }

        // effects
        _roleMembers[roleId].remove(account);
        _authority.revokeRole(
            RoleId.unwrap(roleId), 
            account);

        emit LogAccessAdminRoleRevoked(_adminName, account, _roleInfo[roleId].name.toString());
    }


    function _getOrCreateTargetRoleIdAndName(
        address target,
        string memory targetName,
        TargetType targetType
    )
        internal
        returns (
            RoleId roleId,
            string memory roleName,
            bool exists
        )
    {
        roleName = AccessAdminLib.toRoleName(targetName);
        (roleId, exists) = getRoleForName(roleName);

        if (exists) {
            return (roleId, roleName, true);
        } 

        // get roleId
        if (targetType == TargetType.Service || targetType == TargetType.GenericService) {
            roleId = AccessAdminLib.getServiceRoleId(target, targetType); 
        } else {
            uint64 nextRoleId = _nextRoleId[targetType];
            roleId = AccessAdminLib.getTargetRoleId(target, targetType, nextRoleId);

            // increment target type specific role id counter
            _nextRoleId[targetType]++;
        }
    }


    function _createTarget(
        address target, 
        string memory targetName, 
        TargetType targetType,
        bool checkAuthority
    )
        internal
        returns (RoleId contractRoleId)
    {
        // checks
        AccessAdminLib.checkTargetCreation(this, target, targetName, checkAuthority);

        // effects
        contractRoleId = _createTargetUnchecked(
            target, 
            targetName, 
            targetType,
            checkAuthority);

        // deal with token handler, if applicable
        (
            address tokenHandler,
            string memory tokenHandlerName
        ) = AccessAdminLib.getTokenHandler(target, targetName, targetType);

        if (tokenHandler != address(0)) {
            _createTargetUnchecked(
                tokenHandler, 
                tokenHandlerName, 
                targetType,
                checkAuthority);
        }
    }


    /// @dev Creates a new target and a corresponding contract role.
    /// The function assigns the role to the target and logs the creation.
    function _createTargetUnchecked(
        address target, 
        string memory targetName, 
        TargetType targetType,
        bool managed
    )
        internal
        returns (RoleId targetRoleId)
    {
        // create target role (if not existing)
        string memory roleName;
        bool roleExists;
        (targetRoleId, roleName, roleExists) = _getOrCreateTargetRoleIdAndName(target, targetName, targetType);

        if (!roleExists) {
            _createRole(
                targetRoleId, 
                AccessAdminLib.roleInfo(
                    ADMIN_ROLE(), 
                    targetType, 
                    1, 
                    roleName),
                true); // revert on existing role
        }

        // create target info
        Str name = StrLib.toStr(targetName);
        _targetInfo[target] = TargetInfo({
            name: name,
            targetType: targetType,
            roleId: targetRoleId,
            createdAt: TimestampLib.current(),
            lastUpdateIn: BlocknumberLib.current()});

        // create name to target mapping
        _targetForName[name] = target;

        // add target to list of targets
        _targets.push(target);

        // grant contract role to target
        _grantRoleToAccount(targetRoleId, target);

        emit LogAccessAdminTargetCreated(_adminName, targetName, managed, target, targetRoleId);
    }


    function _setTargetLocked(address target, bool locked)
        internal
    {
        AccessAdminLib.checkTargetExists(this, target);
        _authority.setTargetClosed(target, locked);

        // logging
        Blocknumber lastUpdateIn = _targetInfo[target].lastUpdateIn;
        _targetInfo[target].lastUpdateIn = BlocknumberLib.current();

        emit LogAccessAdminTargetLockedSet(_adminName, target, locked, lastUpdateIn);
    }


    /// @dev Authorize the functions of the target for the specified role.
    function _authorizeFunctions(IAuthorization authorization, Str target, RoleId roleId)
        internal
    {
        _authorizeTargetFunctions(
            getTargetForName(target),
            AccessAdminLib.getAuthorizedRole(
                this, 
                authorization, 
                roleId),
            authorization.getAuthorizedFunctions(
                target, 
                roleId),
            false,
            true);
    }


    /// @dev Authorize the functions of the target for the specified role.
    /// Flag addFunctions determines if functions are added or removed.
    function _authorizeTargetFunctions(
        address target, 
        RoleId roleId, 
        FunctionInfo[] memory functions,
        bool onlyComponentOrContractTargets,
        bool addFunctions
    )
        internal
    {
        // checks
        AccessAdminLib.checkTargetAndRoleForFunctions(
            this, 
            target, 
            roleId,
            onlyComponentOrContractTargets);

        if (addFunctions && roleId == getAdminRole()) {
            revert ErrorAccessAdminAuthorizeForAdminRoleInvalid(target);
        }

        _authority.setTargetFunctionRole(
            target,
            AccessAdminLib.getSelectors(functions),
            RoleId.unwrap(roleId));

        // update function set and log function grantings
        for (uint256 i = 0; i < functions.length; i++) {
            _updateFunctionAccess(
                target, 
                roleId,
                functions[i], 
                addFunctions);
        }
    }


    function _updateFunctionAccess(
        address target, 
        RoleId roleId,
        FunctionInfo memory func, 
        bool addFunction
    )
        internal
    {
        // update functions info
        Selector selector = func.selector;
        Blocknumber lastUpdateIn = _functionInfo[target][selector].lastUpdateIn;

        // update function sets
        if (addFunction) { SelectorSetLib.add(_targetFunctions[target], selector); } 
        else { SelectorSetLib.remove(_targetFunctions[target], selector); }

        _functionInfo[target][selector] = func;
        _functionInfo[target][selector].lastUpdateIn = BlocknumberLib.current();

        // logging
        emit LogAccessAdminFunctionGranted(
            _adminName, 
            target, 
            AccessAdminLib.toFunctionGrantingString(this, func.name, roleId),
            lastUpdateIn);
    }
}

File 39 of 130 : AccessAdminLib.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";

import {IAccess} from "./IAccess.sol";
import {IAccessAdmin} from "./IAccessAdmin.sol";
import {IAuthorization} from "./IAuthorization.sol";
import {IComponent} from "../shared/IComponent.sol";
import {IAuthorizedComponent} from "../shared/IAuthorizedComponent.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {IService} from "../shared/IService.sol";
import {IServiceAuthorization} from "./IServiceAuthorization.sol";

import {AccessManagerCloneable} from "./AccessManagerCloneable.sol";
import {BlocknumberLib} from "../type/Blocknumber.sol";
import {ContractLib} from "../shared/ContractLib.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {RoleId, RoleIdLib, ADMIN_ROLE, PUBLIC_ROLE} from "../type/RoleId.sol";
import {Selector, SelectorLib} from "../type/Selector.sol";
import {Str, StrLib} from "../type/String.sol";
import {TimestampLib} from "../type/Timestamp.sol";
import {VersionPart, VersionPartLib} from "../type/Version.sol";


library AccessAdminLib { // ACCESS_ADMIN_LIB

    string public constant TOKEN_HANDLER_SUFFIX = "Th";
    string public constant ROLE_SUFFIX = "_Role";

    uint64 public constant SERVICE_DOMAIN_ROLE_FACTOR = 100;
    uint64 public constant COMPONENT_ROLE_FACTOR = 1000;
    uint64 public constant COMPONENT_ROLE_MAX = 19000;

    uint64 public constant CORE_ROLE_MIN        =     100;
    uint64 public constant SERVICE_ROLE_MIN     =    1000; // + service domain * SERVICE_ROLE_FACTOR + release
    uint64 public constant SERVICE_ROLE_FACTOR  =    1000; 
    uint64 public constant INSTANCE_ROLE_MIN    =  100000;

    // MUST match with Authorization.COMPONENT_ROLE_MIN
    uint64 public constant COMPONENT_ROLE_MIN   =  110000;

    uint64 public constant CUSTOM_ROLE_MIN      = 1000000;


    function ADMIN_ROLE_NAME() public pure returns (string memory) { 
        return "AdminRole"; 
    }


    function isAdminRoleName(string memory name) public pure returns (bool) { 
        return StrLib.eq(name, ADMIN_ROLE_NAME()); 
    }


    function PUBLIC_ROLE_NAME() public pure returns (string memory) { 
        return "PublicRole"; 
    }


    function getAdminRole() public pure returns (RoleId adminRoleId) { 
        // see oz AccessManagerUpgradeable
        return RoleId.wrap(type(uint64).min); 
    }


    function getPublicRole() public pure returns (RoleId publicRoleId) { 
        // see oz AccessManagerUpgradeable
        return RoleId.wrap(type(uint64).max); 
    }


    function isAdminOrPublicRole(string memory name) 
        public
        view
        returns (bool)
    {
        return StrLib.eq(name, ADMIN_ROLE_NAME())
            || StrLib.eq(name, PUBLIC_ROLE_NAME()); 
    }


    function isDynamicRoleId(RoleId roleId) 
        public 
        pure 
        returns (bool)
    {
        return roleId.toInt() >= COMPONENT_ROLE_MIN;
    }


    function adminRoleInfo()
        public 
        view 
        returns (IAccess.RoleInfo memory)
    {
        return roleInfo(
            getAdminRole(), 
            IAccess.TargetType.Core, 
            1, 
            ADMIN_ROLE_NAME());
    }


    function publicRoleInfo()
        public 
        view 
        returns (IAccess.RoleInfo memory)
    {
        return roleInfo(
            getAdminRole(), 
            IAccess.TargetType.Custom, 
            type(uint32).max, 
            PUBLIC_ROLE_NAME());
    }


    function coreRoleInfo(string memory name)
        public 
        view 
        returns (IAccess.RoleInfo memory)
    {
        return roleInfo(
            getAdminRole(), 
            IAccess.TargetType.Core, 
            1, 
            name);
    }


    function serviceRoleInfo(string memory serviceName)
        public 
        view 
        returns (IAccess.RoleInfo memory)
    {
        return roleInfo(
            getAdminRole(), 
            IAccess.TargetType.Service, 
            1, 
            serviceName);
    }


    /// @dev Creates a role info object from the provided parameters
    function roleInfo(
        RoleId adminRoleId, 
        IAccess.TargetType targetType, 
        uint32 maxMemberCount, 
        string memory roleName
    )
        public 
        view 
        returns (IAccess.RoleInfo memory info)
    {
        return IAccess.RoleInfo({
            name: StrLib.toStr(roleName),
            adminRoleId: adminRoleId,
            targetType: targetType,
            maxMemberCount: maxMemberCount,
            createdAt: TimestampLib.current(),
            pausedAt: TimestampLib.max(),
            lastUpdateIn: BlocknumberLib.current()});
    }


    function getSelectors(
        IAccess.FunctionInfo[] memory functions
    )
        public
        pure
        returns (
            bytes4[] memory selectors
        )
    {
        uint256 n = functions.length;
        selectors = new bytes4[](n);
        for (uint256 i = 0; i < n; i++) {
            selectors[i] = functions[i].selector.toBytes4();
        }
    }


    function checkInitParameters(
        address authority, 
        string memory adminName 
    )
        public
        view
    {
        // only contract check (authority might not yet be initialized at this time)
        if (!ContractLib.isContract(authority)) {
            revert IAccessAdmin.ErrorAccessAdminAuthorityNotContract(authority);
        }

        // check name not empty
        if (bytes(adminName).length == 0) {
            revert IAccessAdmin.ErrorAccessAdminAccessManagerEmptyName();
        }
    }


    function checkRoleCreation(
        IAccessAdmin accessAdmin,
        RoleId roleId, 
        IAccess.RoleInfo memory info,
        bool revertOnExistingRole
    )
        public 
        view
        returns (bool isAdminOrPublicRole)
    {
        // check 
        if (roleId == ADMIN_ROLE() || roleId == PUBLIC_ROLE()) {
            return true;
        }

        // check role does not yet exist 
        if(revertOnExistingRole && accessAdmin.roleExists(roleId)) {
            revert IAccessAdmin.ErrorAccessAdminRoleAlreadyCreated(
                roleId, 
                accessAdmin.getRoleInfo(roleId).name.toString());
        }

        // check admin role exists
        if(!accessAdmin.roleExists(info.adminRoleId)) {
            revert IAccessAdmin.ErrorAccessAdminRoleAdminNotExisting(info.adminRoleId);
        }

        // check role name is not empty
        if(info.name.length() == 0) {
            revert IAccessAdmin.ErrorAccessAdminRoleNameEmpty(roleId);
        }

        // check role name is not used for another role
        (RoleId roleIdForName, bool exists) = accessAdmin.getRoleForName(StrLib.toString(info.name));
        if(revertOnExistingRole && exists) {
            revert IAccessAdmin.ErrorAccessAdminRoleNameAlreadyExists(
                roleId, 
                info.name.toString(),
                roleIdForName);
        }

        return false;
    }


    function checkRoleExists(
        IAccessAdmin accessAdmin,
        RoleId roleId, 
        bool onlyActiveRole,
        bool allowAdminAndPublicRoles
    )
        internal
        view
    {
        // check role exists
        if (!accessAdmin.roleExists(roleId)) {
            revert IAccessAdmin.ErrorAccessAdminRoleUnknown(roleId);
        }

        // if onlyActiveRoles: check if role is disabled
        if (onlyActiveRole && accessAdmin.getRoleInfo(roleId).pausedAt <= TimestampLib.current()) {
            revert IAccessAdmin.ErrorAccessAdminRoleIsPaused(roleId);
        }

        // if not allowAdminAndPublicRoles, check if role is admin or public role
        if (!allowAdminAndPublicRoles) {
            checkNotAdminOrPublicRole(roleId);
        }
    }


    function checkNotAdminOrPublicRole(RoleId roleId) public pure {
        if (roleId == ADMIN_ROLE()) {
            revert IAccessAdmin.ErrorAccessAdminInvalidUseOfAdminRole();
        }

        if (roleId == PUBLIC_ROLE()) {
            revert IAccessAdmin.ErrorAccessAdminInvalidUseOfPublicRole();
        }
    }

    function checkTargetCreation(
        IAccessAdmin accessAdmin,
        address target, 
        string memory targetName, 
        bool checkAuthority
    )
        public 
        view
    {
        // check target does not yet exist
        if(accessAdmin.targetExists(target)) {
            revert IAccessAdmin.ErrorAccessAdminTargetAlreadyCreated(
                target, 
                accessAdmin.getTargetInfo(target).name.toString());
        }

        // check target name is not empty
        Str name = StrLib.toStr(targetName);
        if(name.length() == 0) {
            revert IAccessAdmin.ErrorAccessAdminTargetNameEmpty(target);
        }

        // check target name is not used for another target
        address targetForName = accessAdmin.getTargetForName(name);
        if(targetForName != address(0)) {
            revert IAccessAdmin.ErrorAccessAdminTargetNameAlreadyExists(
                target, 
                targetName,
                targetForName);
        }

        // check target is an access managed contract
        if (!ContractLib.isAccessManaged(target)) {
            revert IAccessAdmin.ErrorAccessAdminTargetNotAccessManaged(target);
        }

        // check target shares authority with this contract
        if (checkAuthority) {
            address targetAuthority = AccessManagedUpgradeable(target).authority();
            if (targetAuthority != accessAdmin.authority()) {
                revert IAccessAdmin.ErrorAccessAdminTargetAuthorityMismatch(accessAdmin.authority(), targetAuthority);
            }
        }
    }


    function checkComponentInitialization(
        IAccessAdmin accessAdmin,
        IAuthorization instanceAuthorization,
        address componentAddress,
        ObjectType expectedType
    )
        public
        view
        returns (IAuthorization componentAuthorization)
    {
        checkIsRegistered(address(accessAdmin.getRegistry()), componentAddress, expectedType);

        VersionPart expecteRelease = accessAdmin.getRelease();
        IAuthorizedComponent component = IAuthorizedComponent(componentAddress);
        componentAuthorization = component.getAuthorization();

        checkAuthorization(
            address(instanceAuthorization), 
            address(componentAuthorization), 
            expectedType, 
            expecteRelease,
            false, // expectServiceAuthorization,
            false); // checkAlreadyInitialized
    }


    function checkTargetAndRoleForFunctions(
        IAccessAdmin accessAdmin,
        address target,
        RoleId roleId,
        bool onlyComponentOrContractTargets
    ) 
        public
        view
    {
        // check target exists
        IAccess.TargetType targetType = accessAdmin.getTargetInfo(target).targetType;
        if (targetType == IAccess.TargetType.Undefined) {
            revert IAccessAdmin.ErrorAccessAdminTargetNotCreated(target);
        }

        // check target type
        if (onlyComponentOrContractTargets) {
            if (targetType != IAccess.TargetType.Component && targetType != IAccess.TargetType.Contract) {
                revert IAccessAdmin.ErrorAccessAdminNotComponentOrCustomTarget(target);
            }
        }

        // check role exist
        checkRoleExists(accessAdmin, roleId, true, true);
    } 


    function checkTargetExists(
        IAccessAdmin accessAdmin,
        address target
    )
        public
        view
    {
        // check not yet created
        if (!accessAdmin.targetExists(target)) {
            revert IAccessAdmin.ErrorAccessAdminTargetNotCreated(target);
        }
    }


    function checkAuthorization( 
        address authorizationOld,
        address authorization,
        ObjectType expectedDomain, 
        VersionPart expectedRelease,
        bool expectServiceAuthorization,
        bool checkAlreadyInitialized
    )
        public
        view
    {
        // checks
        // check not yet initialized
        if (checkAlreadyInitialized && authorizationOld != address(0)) {
            revert IAccessAdmin.ErrorAccessAdminAlreadyInitialized(authorizationOld);
        }

        // check contract type matches
        if (expectServiceAuthorization) {
            if (!ContractLib.supportsInterface(authorization, type(IServiceAuthorization).interfaceId)) {
                revert IAccessAdmin.ErrorAccessAdminNotServiceAuthorization(authorization);
            }
        } else {
            if (!ContractLib.supportsInterface(authorization, type(IAuthorization).interfaceId)) {  
                revert IAccessAdmin.ErrorAccessAdminNotAuthorization(authorization);
            }
        }

        // check domain matches
        ObjectType domain = IAuthorization(authorization).getDomain();
        if (domain != expectedDomain) {
            revert IAccessAdmin.ErrorAccessAdminDomainMismatch(authorization, expectedDomain, domain);
        }

        // check release matches
        VersionPart authorizationRelease = IAuthorization(authorization).getRelease();
        if (authorizationRelease != expectedRelease) {
            revert IAccessAdmin.ErrorAccessAdminReleaseMismatch(authorization, expectedRelease, authorizationRelease);
        }
    }


    function checkIsRegistered( 
        address registry,
        address target,
        ObjectType expectedType
    )
        public
        view 
    {
        checkRegistry(registry);

        ObjectType tagetType = IRegistry(registry).getObjectInfo(target).objectType;
        if (tagetType.eqz()) {
            revert IAccessAdmin.ErrorAccessAdminNotRegistered(target);
        }

        if (tagetType != expectedType) {
            revert IAccessAdmin.ErrorAccessAdminTargetTypeMismatch(target, expectedType, tagetType);
        }
    }


    function checkRegistry(
        address registry
    )
        public
        view
    {
        if (!ContractLib.isRegistry(registry)) {
            revert IAccessAdmin.ErrorAccessAdminNotRegistry(registry);
        }
    }


    function getAuthorizedRole(
        IAccessAdmin accessAdmin,
        IAuthorization authorization, 
        RoleId roleId
    )
        public
        view 
        returns (RoleId authorizedRoleId)
    {
        string memory roleName = authorization.getRoleInfo(roleId).name.toString();
        (authorizedRoleId, ) = accessAdmin.getRoleForName(roleName);
    }


    function getFunctionRoleId(
        AccessManagerCloneable authority,
        address target,
        Selector selector
    )
        public
        view
        returns (RoleId functionRoleId)
    {
        return RoleIdLib.toRoleId(
            authority.getTargetFunctionRole(
                target, 
                selector.toBytes4()));
    }


    function getServiceRoleId(
        address serviceAddress,
        IAccess.TargetType serviceTargetType
    )
        public
        view
        returns (RoleId serviceRoleId)
    {
        IService service = IService(serviceAddress);

        if (serviceTargetType == IAccess.TargetType.Service) {
            return RoleIdLib.toServiceRoleId(service.getDomain(), service.getRelease());
        } else if (serviceTargetType == IAccess.TargetType.GenericService) {
            return RoleIdLib.toGenericServiceRoleId(service.getDomain());
        }

        revert IAccessAdmin.ErrorAccessAdminInvalidServiceType(serviceAddress, serviceTargetType);
    }


    function getVersionedServiceRoleId(
        ObjectType serviceDomain,
        VersionPart release
    )
        public
        pure
        returns (RoleId serviceRoleId)
    {
        return RoleIdLib.toRoleId(
            SERVICE_ROLE_MIN + SERVICE_ROLE_FACTOR * serviceDomain.toInt() + release.toInt());
    }


    function getGenericServiceRoleId(
        ObjectType serviceDomain
    )
        public
        pure
        returns (RoleId serviceRoleId)
    {
        return RoleIdLib.toRoleId(
            SERVICE_ROLE_MIN + SERVICE_ROLE_FACTOR * serviceDomain.toInt() + VersionPartLib.releaseMax().toInt());
    }


    function getCustomRoleId(uint64 index)
        public 
        pure 
        returns (RoleId customRoleId)
    {
        return RoleIdLib.toRoleId(CUSTOM_ROLE_MIN + index);
    }


    function isCustomRole(RoleId roleId)
        public
        pure
        returns (bool)
    {
        return roleId.toInt() >= CUSTOM_ROLE_MIN;
    }


    function getTargetRoleId(
        address target,
        IAccess.TargetType targetType,
        uint64 index
    )
        public 
        view
        returns (RoleId targetRoleId)
    {
        if (targetType == IAccess.TargetType.Core) {
            return RoleIdLib.toRoleId(CORE_ROLE_MIN + index);
        }

        if (targetType == IAccess.TargetType.Service || targetType == IAccess.TargetType.GenericService ) { 
            return getServiceRoleId(target, targetType);
        }

        if (targetType == IAccess.TargetType.Instance) { 
            return RoleIdLib.toRoleId(INSTANCE_ROLE_MIN + index);
        }

        if (targetType == IAccess.TargetType.Component) { 
            return RoleIdLib.toRoleId(COMPONENT_ROLE_MIN + index);
        }

        if (targetType == IAccess.TargetType.Custom
            || targetType == IAccess.TargetType.Contract) 
        { 
            return RoleIdLib.toRoleId(CUSTOM_ROLE_MIN + index);
        }

        revert IAccessAdmin.ErrorAccessAdminInvalidTargetType(target, targetType);
    }


    function getTokenHandler(
        address target, 
        string memory targetName, 
        IAccess.TargetType targetType
    )
        public
        view
        returns (
            address tokenHandler,
            string memory tokenHandlerName
        )
    {
        // not component or core (we need to check core because of staking)
        if (targetType != IAccess.TargetType.Component && targetType != IAccess.TargetType.Core) {
            return (address(0), "");
        }

        // not contract
        if (!ContractLib.isContract(target)) {
            return (address(0), "");
        }

        // not component
        if (!ContractLib.supportsInterface(target, type(IComponent).interfaceId)) {
            return (address(0), "");
        }

        tokenHandler = address(IComponent(target).getTokenHandler());
        tokenHandlerName = string(abi.encodePacked(targetName, TOKEN_HANDLER_SUFFIX));
    }


    function toFunctionGrantingString(
        IAccessAdmin accessAdmin,
        Str functionName,
        RoleId roleId
    )
        public
        view
        returns (string memory)
    {
        return string(
            abi.encodePacked(
                functionName.toString(),
                "(): ",
                getRoleName(accessAdmin, roleId)));
    }


    function getRoleName(
        IAccessAdmin accessAdmin,
        RoleId roleId
    )
        public
        view 
        returns (string memory)
    {
        if (accessAdmin.roleExists(roleId)) {
            return accessAdmin.getRoleInfo(roleId).name.toString();
        }

        return "<unknown-role>";
    }


    function toRoleName(string memory name) public pure returns (string memory) {
        return string(
            abi.encodePacked(
                name,
                ROLE_SUFFIX));
    }


    function toFunction(
        bytes4 selector, 
        string memory name
    ) 
        public 
        view 
        returns (IAccess.FunctionInfo memory) 
    { 
        if(selector == bytes4(0)) {
            revert IAccessAdmin.ErrorAccessAdminSelectorZero();
        }

        if(bytes(name).length == 0) {
            revert IAccessAdmin.ErrorAccessAdminFunctionNameEmpty();
        }

        return IAccess.FunctionInfo({
            name: StrLib.toStr(name),
            selector: SelectorLib.toSelector(selector),
            createdAt: TimestampLib.current(),
            lastUpdateIn: BlocknumberLib.current()});
    }

}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {AccessManagerUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagerUpgradeable.sol";
import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol";

import {InitializableERC165} from "../shared/InitializableERC165.sol";
import {RegistryLinked} from "../shared/RegistryLinked.sol";
import {VersionPart} from "../type/Version.sol";


/// @dev An AccessManager based on OpenZeppelin that is cloneable and has a central lock property.
/// The lock property allows to lock all services of a release in a central place.
/// Cloned by upon release preparation and instance cloning.
contract AccessManagerCloneable is
    AccessManagerUpgradeable,
    InitializableERC165,
    RegistryLinked
{
    error ErrorAccessManagerCallerNotAdmin(address caller);
    error ErrorAccessManagerRegistryAlreadySet(address registry);
    error ErrorAccessManagerInvalidRelease(VersionPart release);

    error ErrorAccessManagerTargetAdminLocked(address target);
    error ErrorAccessManagerCallerAdminLocked(address caller);

    VersionPart private _release;
    bool private _isLocked;


    modifier onlyAdminRole() {
        (bool isMember, ) = hasRole(ADMIN_ROLE, msg.sender);
        if(!isMember) {
            revert ErrorAccessManagerCallerNotAdmin(msg.sender);
        }
        _;
    }


    function initialize(address admin)
        public
        initializer()
    {
        __ERC165_init();
        __AccessManager_init(admin);

        _registerInterface(type(IAccessManager).interfaceId);
    }


    /// @dev Completes the setup of the access manager.
    /// Links the access manager to the registry and sets the release version.
    function completeSetup(
        address registry, 
        VersionPart release
    )
        external
        onlyAdminRole
        reinitializer(uint64(release.toInt()))
    {
        _checkAndSetRegistry(registry);
        _checkAndSetRelease(release);
    }

    /// @dev Returns true if the caller is authorized to call the target with the given selector and the manager lock is not set to locked.
    /// Feturn values as in OpenZeppelin AccessManager.
    /// For a locked manager the function reverts with ErrorAccessManagerTargetAdminLocked.
    function canCall(
        address caller,
        address target,
        bytes4 selector
    )
        public 
        view 
        virtual override 
        returns (
            bool immediate, 
            uint32 delay
        ) 
    {
        // locking of all contracts under control of this access manager
        if (_isLocked) {
            revert ErrorAccessManagerTargetAdminLocked(target);
        }

        (immediate, delay) = super.canCall(caller, target, selector);
    }


    /// @dev Locks/unlocks all services of this access manager.
    /// Only the corresponding access admin can lock/unlock the services.
    function setLocked(bool locked)
        external
        onlyAdminRole() 
    {
        _isLocked = locked;
    }


    /// @dev Returns the release version of this access manager.
    /// For the registry admin release 3 is returned.
    /// For the release admin and the instance admin the actual release version is returned.
    function getRelease() external view returns (VersionPart release) {
        return _release;
    }


    /// @dev Returns true iff all contracts of this access manager are locked.
    function isLocked()
        public
        view
        returns (bool)
    {
        return _isLocked;
    }


    function _checkAndSetRelease(VersionPart release)
        internal
    {
        if (!release.isValidRelease()) {
            revert ErrorAccessManagerInvalidRelease(release);
        }

        _release = release;
    }


    function _checkAndSetRegistry(address registry)
        internal
    {
        // checks
        if(address(getRegistry()) != address(0)) {
            revert ErrorAccessManagerRegistryAlreadySet(address(getRegistry()) );
        }

        // effects
        __RegistryLinked_init(registry);
    }
}

File 41 of 130 : 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: UNLICENSED
pragma solidity ^0.8.20;

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

import {IAccess} from "./IAccess.sol";
import {IAuthorization} from "./IAuthorization.sol";
import {IRegistryLinked} from "../shared/IRegistryLinked.sol";
import {IRelease} from "../registry/IRelease.sol";

import {Blocknumber} from "../type/Blocknumber.sol";
import {NftId} from "../type/NftId.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";

/// @dev Base interface for registry admin, release admin, and instance admin 
interface IAccessAdmin is 
    IAccessManaged,
    IAccess,
    IRegistryLinked,
    IRelease
{

    // roles, targets and functions
    event LogAccessAdminRoleCreated(string admin, RoleId roleId, TargetType targetType, RoleId roleAdminId, string name);
    event LogAccessAdminTargetCreated(string admin, string name, bool managed, address target, RoleId roleId);

    event LogAccessAdminRoleActivatedSet(string admin, RoleId roleId, bool active, Blocknumber lastUpdateIn);
    event LogAccessAdminRoleGranted(string admin, address account, string roleName);
    event LogAccessAdminRoleRevoked(string admin, address account, string roleName);
    event LogAccessAdminTargetLockedSet(string admin, address target, bool locked, Blocknumber lastUpdateIn);
    event LogAccessAdminFunctionGranted(string admin, address target, string func, Blocknumber lastUpdateIn);

    // only deployer modifier
    error ErrorAccessAdminNotDeployer();

    // only role admin modifier
    error ErrorAccessAdminNotAdminOfRole(RoleId adminRoleId, address account);

    // only role owner modifier
    error ErrorAccessAdminNotRoleOwner(RoleId roleId, address account);

    // role management
    error ErrorAccessAdminInvalidUseOfAdminRole();
    error ErrorAccessAdminInvalidUseOfPublicRole();
    error ErrorAccessAdminRoleNotCustom(RoleId roleId);

    // initialization
    error ErrorAccessAdminNotRegistry(address registry);
    error ErrorAccessAdminAuthorityNotContract(address authority);
    error ErrorAccessAdminAccessManagerNotAccessManager(address authority);
    error ErrorAccessAdminAccessManagerEmptyName();

    // check target
    error ErrorAccessAdminInvalidTargetType(address target, TargetType targetType);
    error ErrorAccessAdminInvalidServiceType(address target, TargetType serviceTargetType);
    error ErrorAccessAdminTargetNotCreated(address target);
    error ErrorAccessAdminTargetNotRegistered(address target);
    error ErrorAccessAdminTargetTypeMismatch(address target, ObjectType expectedType, ObjectType actualType);

    // check authorization
    error ErrorAccessAdminAlreadyInitialized(address authorization);
    error ErrorAccessAdminNotAuthorization(address authorization);
    error ErrorAccessAdminNotServiceAuthorization(address serviceAuthorization);
    error ErrorAccessAdminDomainMismatch(address authorization, ObjectType expectedDomain, ObjectType actualDomain);
    error ErrorAccessAdminReleaseMismatch(address authorization, VersionPart expectedRelease, VersionPart actualRelease);

    // link to nft
    error ErrorAccessAdminNotRegistered(address registerable);

    // initialize authority
    error ErrorAccessAdminAdminRoleMissing();

    // create role
    error ErrorAccessAdminRoleAlreadyCreated(RoleId roleId, string name);
    error ErrorAccessAdminRoleAdminNotExisting(RoleId adminRoleId);
    error ErrorAccessAdminRoleNameEmpty(RoleId roleId);
    error ErrorAccessAdminRoleNameAlreadyExists(RoleId roleId, string name, RoleId existingRoleId);

    // grant/revoke/renounce role
    error ErrorAccessAdminRoleUnknown(RoleId roleId);
    error ErrorAccessAdminRoleIsLocked(RoleId roleId);
    error ErrorAccessAdminRoleIsPaused(RoleId roleId);
    error ErrorAccessAdminRoleMembersLimitReached(RoleId roleId, uint256 memberCountLimit);
    error ErrorAccessAdminRoleMemberNotContract(RoleId roleId, address notContract);
    error ErrorAccessAdminRoleMemberRemovalDisabled(RoleId roleId, address expectedMember);

    // create target
    error ErrorAccessAdminTargetAlreadyCreated(address target, string name);
    error ErrorAccessAdminTargetNameEmpty(address target);
    error ErrorAccessAdminTargetNameAlreadyExists(address target, string name, address existingTarget);
    error ErrorAccessAdminTargetNotAccessManaged(address target);
    error ErrorAccessAdminTargetAuthorityMismatch(address expectedAuthority, address actualAuthority);

    // lock target
    error ErrorAccessAdminTagetNotLockable();
    error ErrorAccessAdminTargetAlreadyLocked(address target, bool isLocked);

    // authorize target functions
    error ErrorAccessAdminNotComponentOrCustomTarget(address target);
    error ErrorAccessAdminAuthorizeForAdminRoleInvalid(address target);

    // toFunction
    error ErrorAccessAdminSelectorZero();
    error ErrorAccessAdminFunctionNameEmpty();

    // check target
    error ErrorAccessAdminTargetUnknown(address target);

    //--- view functions ----------------------------------------------------//

    function getAuthorization() external view returns (IAuthorization authorization);
    function getLinkedNftId() external view returns (NftId linkedNftId);

    function isLocked() external view returns (bool locked);

    function roles() external view returns (uint256 numberOfRoles);
    function getRoleId(uint256 idx) external view returns (RoleId roleId);

    function roleExists(RoleId roleId) external view returns (bool exists); 
    function getRoleForName(string memory name) external view returns (RoleId roleId, bool exists);
    function getRoleInfo(RoleId roleId) external view returns (RoleInfo memory roleInfo);
    function isRoleActive(RoleId roleId) external view returns (bool isActive);
    function isRoleCustom(RoleId roleId) external view returns (bool isCustom);

    function isRoleMember(RoleId roleId, address account) external view returns (bool);
    function isRoleAdmin(RoleId roleId, address account) external view returns (bool);
    function roleMembers(RoleId roleId) external view returns (uint256 numberOfMembers);
    function getRoleMember(RoleId roleId, uint256 idx) external view returns (address account);

    function targetExists(address target) external view returns (bool exists);
    function getTargetForName(Str name) external view returns (address target);
    function targets() external view returns (uint256 numberOfTargets);
    function getTargetAddress(uint256 idx) external view returns (address target);
    function getTargetInfo(address target) external view returns (TargetInfo memory targetInfo);
    function isTargetLocked(address target) external view returns (bool locked);

    function authorizedFunctions(address target) external view returns (uint256 numberOfFunctions);
    function getAuthorizedFunction(address target, uint256 idx) external view returns (FunctionInfo memory func, RoleId roleId);
}

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

import {IServiceAuthorization} from "./IServiceAuthorization.sol";
import {Str} from "../type/String.sol";


interface IAuthorization is 
     IServiceAuthorization
{

     /// @dev Returns the token hander name.
     /// Only components have a token handler.
     function getTokenHandlerName() external view returns(string memory name);

     /// @dev Returns the token hander target.
     /// Only components have a token handler.
     function getTokenHandlerTarget() external view returns(Str target);

     /// @dev Returns the complete list of targets.
     function getTargets() external view returns(Str[] memory targets);

     /// @dev Returns true iff the specified target exists.
     function targetExists(Str target) external view returns(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);
}

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

import {IPolicy} from "../instance/module/IPolicy.sol";
import {IService} from "../shared/IService.sol";

import {Amount} from "../type/Amount.sol";
import {DistributorType} from "../type/DistributorType.sol";
import {InstanceReader} from "../instance/InstanceReader.sol";
import {NftId} from "../type/NftId.sol";
import {ReferralId, ReferralStatus} from "../type/Referral.sol";
import {Seconds} from "../type/Seconds.sol";
import {Timestamp} from "../type/Timestamp.sol";
import {UFixed} from "../type/UFixed.sol";


interface IDistributionService is IService {
    error ErrorDistributionServiceCallerNotRegistered(address caller);
    error ErrorDistributionServiceParentNftIdNotInstance(NftId nftId, NftId parentNftId);
    error ErrorDistributionServiceCallerNotDistributor(address caller);
    error ErrorDistributionServiceInvalidReferralId(ReferralId referralId);
    error ErrorDistributionServiceMaxReferralsExceeded(uint256 limit, uint256 maxReferrals);
    error ErrorDistributionServiceDiscountTooLow(UFixed minDiscountPercentage, UFixed discountPercentage);
    error ErrorDistributionServiceDiscountTooHigh(UFixed maxDiscountPercentage, UFixed discountPercentage);
    error ErrorDistributionServiceExpiryTooLong(Seconds maxReferralLifetime, Timestamp expiryAt);
    error ErrorDistributionServiceInvalidReferral();
    error ErrorDistributionServiceExpirationInvalid(Timestamp expiryAt);
    error ErrorDistributionServiceCommissionTooHigh(uint256 commissionPercentage, uint256 maxCommissionPercentage);
    error ErrorDistributionServiceMinFeeTooHigh(uint256 minFee, uint256 limit);
    error ErrorDistributionServiceDistributorTypeDistributionMismatch(DistributorType distributorType, NftId distributorTypeDistributionNftId, NftId distributionNftId);
    error ErrorDistributionServiceDistributorDistributionMismatch(NftId distributorNftId, NftId distributorDistributionNftId, NftId distributionNftId);

    error ErrorDistributionServiceCommissionWithdrawAmountExceedsLimit(Amount amount, Amount limit);
    
    error ErrorDistributionServiceVariableFeesTooHight(uint256 maxDiscountPercentage, uint256 limit);
    error ErrorDistributionServiceMaxDiscountTooHigh(uint256 maxDiscountPercentage, uint256 limit);

    error ErrorDistributionServiceReferralInvalid(NftId distributionNftId, ReferralId referralId);
    error ErrorDistributionServiceInvalidFeeTransferred(Amount transferredDistributionFeeAmount, Amount expectedDistributionFeeAmount);
    error ErrorDistributionServiceReferralDistributionMismatch(ReferralId referralId, NftId referralDistributionNft, NftId distributionNftId);

    event LogDistributionServiceCommissionWithdrawn(NftId distributorNftId, address recipient, address tokenAddress, Amount amount);
    event LogDistributionServiceDistributorTypeCreated(NftId distributionNftId, string name);
    event LogDistributionServiceDistributorCreated(NftId distributionNftId, NftId distributorNftId, DistributorType distributorType, address distributor);
    event LogDistributionServiceDistributorTypeChanged(NftId distributorNftId, DistributorType oldDistributorType, DistributorType newDistributorType);
    event LogDistributionServiceReferralCreated(NftId distributionNftId, NftId distributorNftId, ReferralId referralId, string code);
    event LogDistributionServiceReferralProcessed(NftId distributionNftId, NftId distributorNftId, ReferralId referralId, uint32 usedReferrals);
    event LogDistributionServiceSaleProcessed(NftId distributionNftId, ReferralId referralId);

    function createDistributorType(
        string memory name,
        UFixed minDiscountPercentage,
        UFixed maxDiscountPercentage,
        UFixed commissionPercentage,
        uint32 maxReferralCount,
        Seconds maxReferralLifetime,
        bool allowSelfReferrals,
        bool allowRenewals,
        bytes memory data
    )
        external
        returns (DistributorType distributorType);

    function createDistributor(
        address distributor,
        DistributorType distributorType,
        bytes memory data
    ) external returns (NftId distributorNftId);

    function changeDistributorType(
        NftId distributorNftId,
        DistributorType newDistributorType,
        bytes memory data
    ) external;

    function createReferral(
        NftId distributorNftId,
        string memory code,
        UFixed discountPercentage,
        uint32 maxReferrals,
        Timestamp expiryAt,
        bytes memory data
    )
        external
        returns (ReferralId referralId);

    /// @dev callback from product service when a referral is used. 
    /// Calling this will increment the referral usage counter. 
    function processReferral(
        NftId distributionNftId, 
        ReferralId referralId
    ) external;

    /// @dev callback from product service when selling a policy for a specific referralId
    function processSale(
        NftId distributionNftId,
        ReferralId referralId,
        IPolicy.PremiumInfo memory premium
    ) external;

    function referralIsValid(
        NftId distributorNftId,
        ReferralId referralId
    ) external view returns (bool isValid);

    /// @dev Withdraw commission for the distributor
    /// @param distributorNftId the distributor Nft Id
    /// @param amount the amount to withdraw. If set to AMOUNT_MAX, the full commission available is withdrawn
    /// @return withdrawnAmount the effective withdrawn amount
    function withdrawCommission(NftId distributorNftId, Amount amount) external returns (Amount withdrawnAmount);

    /// @dev Returns the discount percentage for the provided referral code.
    /// The function retuns both the percentage and the status of the referral code.
    function getDiscountPercentage(InstanceReader instanceReader, ReferralId referralId) external view returns (UFixed discountPercentage, ReferralStatus status);
}

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

import {IPolicy} from "../../instance/module/IPolicy.sol";
import {IPolicyService} from "../../product/IPolicyService.sol";

import {Amount, AmountLib} from "../../type/Amount.sol";
import {FlightMessageVerifier} from "./FlightMessageVerifier.sol";
import {FlightProduct} from "./FlightProduct.sol";
import {InstanceReader} from "../../instance/InstanceReader.sol";
import {NftId} from "../../type/NftId.sol";
import {RequestId} from "../../type/RequestId.sol";
import {RiskId, RiskIdLib} from "../../type/RiskId.sol";
import {Seconds} from "../../type/Seconds.sol";
import {StateId} from "../../type/StateId.sol";
import {Str} from "../../type/String.sol";
import {Timestamp, TimestampLib} from "../../type/Timestamp.sol";


library FlightLib {

    event LogFlightProductErrorUnprocessableStatus(RequestId requestId, RiskId riskId, bytes1 status);
    event LogFlightProductErrorUnexpectedStatus(RequestId requestId, RiskId riskId, bytes1 status, int256 delayMinutes);

    error ErrorFlightProductRiskInvalid(RiskId riskId);
    error ErrorFlightProductPremiumAmountTooSmall(Amount premiumAmount, Amount minPremium);
    error ErrorFlightProductPremiumAmountTooLarge(Amount premiumAmount, Amount maxPremium);
    error ErrorFlightProductArrivalBeforeDepartureTime(Timestamp departureTime, Timestamp arrivalTime);
    error ErrorFlightProductArrivalAfterMaxFlightDuration(Timestamp arrivalTime, Timestamp maxArrivalTime, Seconds maxDuration);
    error ErrorFlightProductDepartureBeforeMinTimeBeforeDeparture(Timestamp departureTime, Timestamp now, Seconds minTimeBeforeDeparture);
    error ErrorFlightProductDepartureAfterMaxTimeBeforeDeparture(Timestamp departureTime, Timestamp now, Seconds maxTimeBeforeDeparture);
    error ErrorFlightProductNotEnoughObservations(uint256 observations, uint256 minObservations);
    error ErrorFlightProductClusterRisk(Amount totalSumInsured, Amount maxTotalPayout);

    function checkApplicationData(
        FlightProduct flightProduct,
        Str flightData, 
        Timestamp departureTime,
        Timestamp arrivalTime,
        Amount premiumAmount
    )
        public
        view
    {
        _checkApplicationData(flightProduct, premiumAmount, arrivalTime, departureTime);
    }


    function _checkApplicationData(
        FlightProduct flightProduct,
        Amount premiumAmount,
        Timestamp arrivalTime,
        Timestamp departureTime
    )
        internal
        view
    {
        bool testMode = flightProduct.isTestMode();

        // solhint-disable
        if (premiumAmount < flightProduct.MIN_PREMIUM()) {
            revert ErrorFlightProductPremiumAmountTooSmall(premiumAmount, flightProduct.MIN_PREMIUM());
        }
        if (premiumAmount > flightProduct.MAX_PREMIUM()) {
            revert ErrorFlightProductPremiumAmountTooLarge(premiumAmount, flightProduct.MAX_PREMIUM());
        }
        if (arrivalTime <= departureTime) {
            revert ErrorFlightProductArrivalBeforeDepartureTime(departureTime, arrivalTime);
        }
        if (arrivalTime > departureTime.addSeconds(flightProduct.MAX_FLIGHT_DURATION())) {
            revert ErrorFlightProductArrivalAfterMaxFlightDuration(arrivalTime, departureTime, flightProduct.MAX_FLIGHT_DURATION());
        }

        // test mode allows the creation for policies that are outside restricted policy creation times
        if (!testMode && departureTime < TimestampLib.current().addSeconds(flightProduct.MIN_TIME_BEFORE_DEPARTURE())) {
            revert ErrorFlightProductDepartureBeforeMinTimeBeforeDeparture(departureTime, TimestampLib.current(), flightProduct.MIN_TIME_BEFORE_DEPARTURE());
        }
        if (!testMode && departureTime > TimestampLib.current().addSeconds(flightProduct.MAX_TIME_BEFORE_DEPARTURE())) {
            revert ErrorFlightProductDepartureAfterMaxTimeBeforeDeparture(departureTime, TimestampLib.current(), flightProduct.MAX_TIME_BEFORE_DEPARTURE());
        }
        // solhint-enable
    }


    function checkClusterRisk(
        Amount sumOfSumInsuredAmounts,
        Amount sumInsuredAmount,
        Amount maxTotalPayout
    )
        public
        pure
    {
        if (sumOfSumInsuredAmounts + sumInsuredAmount > maxTotalPayout) {
            revert ErrorFlightProductClusterRisk(sumOfSumInsuredAmounts + sumInsuredAmount, maxTotalPayout);
        }
    }


    /// @dev calculates payout option based on flight status and delay minutes.
    /// Is not a view function as it emits log evens in case of unexpected status.
    function checkAndGetPayoutOption(
        RequestId requestId,
        RiskId riskId, 
        bytes1 status, 
        int256 delayMinutes
    )
        public
        returns (uint8 payoutOption)
    {
        // default: no payout
        payoutOption = type(uint8).max;

        // check status
        if (status != "L" && status != "A" && status != "C" && status != "D") {
            emit LogFlightProductErrorUnprocessableStatus(requestId, riskId, status);
            return payoutOption;
        }

        if (status == "A") {
            // todo: active, reschedule oracle call + 45 min
            emit LogFlightProductErrorUnexpectedStatus(requestId, riskId, status, delayMinutes);
            return payoutOption;
        }

        // trigger payout if applicable
        if (status == "C") { payoutOption = 3; } 
        else if (status == "D") { payoutOption = 4; } 
        else if (delayMinutes >= 15 && delayMinutes < 30) { payoutOption = 0; } 
        else if (delayMinutes >= 30 && delayMinutes < 45) { payoutOption = 1; } 
        else if (delayMinutes >= 45) { payoutOption = 2; }
    }


    function calculateWeight(
        FlightProduct flightProduct,
        uint256[6] memory statistics
    )
        public
        view
        returns (uint256 weight)
    {
        // check we have enough observations
        if (statistics[0] < flightProduct.MIN_OBSERVATIONS()) {
            revert ErrorFlightProductNotEnoughObservations(statistics[0], flightProduct.MIN_OBSERVATIONS());
        }

        weight = 0;
        for (uint256 i = 1; i < 6; i++) {
            weight += flightProduct.WEIGHT_PATTERN(i) * statistics[i] * 10000 / statistics[0];
        }

        // To avoid div0 in the payout section, we have to make a minimal assumption on weight
        if (weight == 0) {
            weight = 100000 / statistics[0];
        }

        // TODO comment on intended effect
        weight = (weight * (100 + flightProduct.MARGIN_PERCENT())) / 100;
    }


    // REMARK: each flight may get different payouts depending on the latest statics
    function calculatePayoutAmounts(
        FlightProduct flightProduct,
        Amount premium, 
        uint256[6] memory statistics
    )
        public
        view
        returns (
            uint256 weight, 
            Amount[5] memory payoutAmounts,
            Amount sumInsuredAmount // simply the max of payoutAmounts 
        )
    {
        if (premium < flightProduct.MIN_PREMIUM()) { 
            revert ErrorFlightProductPremiumAmountTooSmall(premium, flightProduct.MIN_PREMIUM()); 
        }
        if (premium > flightProduct.MAX_PREMIUM()) { 
            revert ErrorFlightProductPremiumAmountTooLarge(premium, flightProduct.MAX_PREMIUM()); 
        }

        sumInsuredAmount = AmountLib.zero();
        weight = calculateWeight(flightProduct, statistics);

        for (uint256 i = 0; i < 5; i++) {
            Amount payoutAmount = AmountLib.toAmount(
                premium.toInt() * flightProduct.WEIGHT_PATTERN(i + 1) * 10000 / weight);

            // cap payout and update sum insured if applicable
            if (payoutAmount > flightProduct.MAX_PAYOUT()) { payoutAmount = flightProduct.MAX_PAYOUT(); }
            if (payoutAmount > sumInsuredAmount) { sumInsuredAmount = payoutAmount; }

            payoutAmounts[i] = payoutAmount;
        }
    }


    function getPayoutOption(
        InstanceReader reader,
        NftId productNftId,
        RiskId riskId
    )
        public
        view
        returns (
            bool exists,
            bool statusAvailable,
            uint8 payoutOption
        )
    {
        FlightProduct.FlightRisk memory flightRisk;
        (exists, flightRisk) = getFlightRisk(
            reader, 
            productNftId, 
            riskId,
            false);
        
        statusAvailable = flightRisk.statusUpdatedAt.gtz();
        payoutOption = flightRisk.payoutOption;
    }


    function getPayoutAmount(
        bytes memory applicationData, 
        uint8 payoutOption
    )
        public
        pure
        returns (Amount payoutAmount)
    {
        if (payoutOption == type(uint8).max) {
            return AmountLib.zero();
        }

        // retrieve payout amounts from application data
        (, Amount[5] memory payoutAmounts) = abi.decode(
            applicationData, (Amount, Amount[5]));

        // get payout amount for selected option
        payoutAmount = payoutAmounts[payoutOption];
    }


    function getFlightRisk(
        InstanceReader reader,
        NftId productNftId, 
        Str flightData,
        Timestamp departureTime, 
        string memory departureTimeLocal,
        Timestamp arrivalTime,
        string memory arrivalTimeLocal
    )
        public
        view
        returns (
            RiskId riskId,
            bool exists,
            FlightProduct.FlightRisk memory flightRisk
        )
    {
        riskId = getRiskId(productNftId, flightData);
        (exists, flightRisk) = getFlightRisk(reader, productNftId, riskId, false);

        // create new risk if not existing
        if (!exists) {
            flightRisk = FlightProduct.FlightRisk({
                flightData: flightData,
                departureTime: departureTime,
                departureTimeLocal: departureTimeLocal,
                arrivalTime: arrivalTime,
                arrivalTimeLocal: arrivalTimeLocal,
                sumOfSumInsuredAmounts: AmountLib.toAmount(0),
                status: bytes1(0),
                delayMinutes: 0,
                payoutOption: uint8(0),
                statusUpdatedAt: TimestampLib.zero()});
        }
    }


    function getFlightRisk(
        InstanceReader reader,
        NftId productNftId,
        RiskId riskId,
        bool requireRiskExists
    )
        public
        view
        returns (
            bool exists,
            FlightProduct.FlightRisk memory flightRisk
        )
    {
        // check if risk exists
        exists = reader.isProductRisk(productNftId, riskId);

        if (!exists && requireRiskExists) {
            revert ErrorFlightProductRiskInvalid(riskId);
        }

        // get risk data if risk exists
        if (exists) {
            flightRisk = abi.decode(
                reader.getRiskInfo(riskId).data, (FlightProduct.FlightRisk));
        }
    }


    function getRiskId(
        NftId productNftId,
        Str flightData
    )
        public
        view 
        returns (RiskId riskId)
    {
        bytes32 riskKey = getRiskKey(flightData);
        riskId = getRiskId(productNftId, riskKey);
    }


    function getRiskKey(
        Str flightData
    )
        internal
        pure
        returns (bytes32 riskKey)
    {
        return keccak256(abi.encode(flightData));
    }


    function getRiskId(NftId productNftId, bytes32 riskKey) internal view returns (RiskId riskId) {
        return RiskIdLib.toRiskId(productNftId, riskKey);
    }
}

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

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol";
import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";


import {Amount} from "../../type/Amount.sol";
import {Str} from "../../type/String.sol";
import {Timestamp} from "../../type/Timestamp.sol";

contract FlightMessageVerifier is 
    Ownable
{

    error ErrorFlightMessageVerifierSignerZero();
    error ErrorFlightMessageVerifierContractSignerNotSupported();

    address private _expectedSigner;


    constructor() Ownable(msg.sender) { }


    function setExpectedSigner(address signer) external onlyOwner {
        if (signer == address(0)) { revert ErrorFlightMessageVerifierSignerZero(); }
        if (signer.code.length > 0) { revert ErrorFlightMessageVerifierContractSignerNotSupported(); }
        _expectedSigner = signer;
    }


    function getExpectedSigner() external view returns(address) {
        return _expectedSigner;
    }

    /// @dev creates digest hash based on application parameters
    /// proposal:
    /// use "LX 180 ZRH BKK 20241104" (23 chars, should be enough for all flights)
    /// carriers, airports: https://www.iata.org/en/publications/directories/code-search/
    /// flight numbers: https://en.wikipedia.org/wiki/Flight_number
    /// instead of separate strings, coding/decoding done anyway off-chain
    function getRatingsHash(
        Str flightData,
        Timestamp departureTime,
        Timestamp arrivalTime,
        Amount premiumAmount,
        uint256[6] memory statistics
    )
        public
        view
        returns(bytes32)
    {
        return MessageHashUtils.toEthSignedMessageHash(
            abi.encode(
                flightData,
                departureTime,
                arrivalTime,
                premiumAmount,
                statistics));
    }


    function verifyRatingsHash(
        Str flightData,
        Timestamp departureTime,
        Timestamp arrivalTime,
        Amount premiumAmount,
        uint256[6] memory statistics,
        // bytes memory signature,
        uint8 v, 
        bytes32 r, 
        bytes32 s
    )
        public
        view
        returns (
            address actualSigner,
            ECDSA.RecoverError errorStatus,
            bool success
        )
    {
        bytes32 messageHash = getRatingsHash(
            flightData,
            departureTime,
            arrivalTime,
            premiumAmount,
            statistics);

        (
            actualSigner, 
            errorStatus, 
        ) = ECDSA.tryRecover(messageHash, v, r, s);

        success = (
            errorStatus == ECDSA.RecoverError.NoError 
            && actualSigner == _expectedSigner);
    }


    // TODO re-enable or cleanup
    // function checkAndRegisterSignature (
    //     address policyHolder,
    //     address protectedWallet,
    //     uint256 protectedBalance,
    //     uint256 duration,
    //     uint256 bundleId,
    //     bytes32 signatureId,
    //     bytes calldata signature
    // )
    //     external 
    // {
    //     bytes32 signatureHash = keccak256(abi.encode(signature));
    //     require(!_signatureIsUsed[signatureHash], "ERROR:DMH-001:SIGNATURE_USED");

    //     address signer = getSignerFromDigestAndSignature(
    //         protectedWallet,
    //         protectedBalance,
    //         duration,
    //         bundleId,
    //         signatureId,
    //         signature);

    //     require(policyHolder == signer, "ERROR:DMH-002:SIGNATURE_INVALID");

    //     _signatureIsUsed[signatureHash] = true;
    // }
}

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

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

import {ACTIVE, FULFILLED, FAILED} from "../../type/StateId.sol";
import {NftId} from "../../type/NftId.sol";
import {BasicOracle} from "../../oracle/BasicOracle.sol";
import {RequestId} from "../../type/RequestId.sol";
import {LibRequestIdSet} from "../../type/RequestIdSet.sol";
import {RiskId} from "../../type/RiskId.sol";
import {StateId} from "../../type/StateId.sol";
import {Str} from "../../type/String.sol";
import {Timestamp, TimestampLib} from "../../type/Timestamp.sol";

contract FlightOracle is
    BasicOracle
{

    struct FlightStatusRequest {
        RiskId riskId;
        Str flightData; // "LX 180 ZRH BKK 20241104"
        Timestamp departureTime; // is this needed or is flight number and date unique aready?
    }

    struct FlightStatusResponse {
        RiskId riskId;
        bytes1 status;
        int256 delayMinutes;
    }

    event LogFlightOracleRequestReceived(RequestId requestId, NftId requesterId);
    event LogFlightOracleResponseSent(RequestId requestId, bytes1 status, int256 delay);
    event LogFlightOracleRequestCancelled(RequestId requestId);

    // TODO decide if this variable should be moved to instance store
    // if so it need to manage active requests by requestor nft id
    LibRequestIdSet.Set internal _activeRequests;


    constructor(
        address registry,
        NftId productNftId,
        string memory componentName,
        IAuthorization authorization
    ) 
    {
        address initialOwner = msg.sender;
        initialize(
            registry,
            productNftId,
            authorization,
            initialOwner,
            componentName
        );
    }


    function initialize(
        address registry,
        NftId productNftId,
        IAuthorization authorization,
        address initialOwner,
        string memory name
    )
        public
        virtual
        initializer()
    {
        _initializeBasicOracle(
            registry,
            productNftId,
            authorization,
            initialOwner,
            name);
    }


    function respondWithFlightStatus(
        RequestId requestId,
        bytes1 status,
        int256 delayMinutes
    )
        external
        restricted()
    {
        // obtain riskId for request
        bytes memory requestData = _getInstanceReader().getRequestInfo(requestId).requestData;
        (RiskId riskId,,) = abi.decode(requestData, (RiskId, Str, Timestamp));
        // assemble response data
        bytes memory responseData = abi.encode(
            FlightStatusResponse ({
                riskId: riskId,
                status: status,
                delayMinutes: delayMinutes}));

        // logging
        emit LogFlightOracleResponseSent(requestId, status, delayMinutes);

        // effects + interaction (via framework to receiving component)
        _respond(requestId, responseData);

        // TODO decide if the code below should be moved to GIF
        // check callback result
        bool requestFulfilled = _getInstanceReader().getRequestState(
            requestId) == FULFILLED();

        // remove from active requests when successful
        if (requestFulfilled) {
            LibRequestIdSet.remove(_activeRequests, requestId);
        }
    }


    //--- view functions ----------------------------------------------------//

    // TODO decide if the code below should be moved to GIF
    function activeRequests()
        external
        view
        returns(uint256 numberOfRequests)
    {
        return LibRequestIdSet.size(_activeRequests);
    }


    // TODO decide if the code below should be moved to GIF
    function getActiveRequest(uint256 idx)
        external
        view
        returns(RequestId requestId)
    {
        return LibRequestIdSet.getElementAt(_activeRequests, idx);
    }


    function isActiveRequest(RequestId requestId)
        external
        view
        returns(bool isActive)
    {
        return LibRequestIdSet.contains(_activeRequests, requestId);
    }


    function getRequestState(RequestId requestId)
        external
        view
        returns (
            RiskId riskId,
            string memory flightData,
            StateId requestState,
            bool readyForResponse,
            bool waitingForResend
        )
    {
        bytes memory requestData = _getInstanceReader().getRequestInfo(requestId).requestData;
        Str fltData;
        Timestamp departureTime;
        (riskId, fltData, departureTime) = abi.decode(requestData, (RiskId, Str, Timestamp));

        flightData = fltData.toString();
        requestState = _getInstanceReader().getRequestState(requestId);
        readyForResponse = requestState == ACTIVE() && TimestampLib.current() >= departureTime;
        waitingForResend = requestState == FAILED();
    }


    function decodeFlightStatusRequestData(bytes memory data) external pure returns (FlightStatusRequest memory) {
        return abi.decode(data, (FlightStatusRequest));
    }

    //--- internal functions ------------------------------------------------//

    /// @dev use case specific handling of oracle requests
    /// for now only log is emitted to verify that request has been received by oracle component 
    function _request(
        RequestId requestId,
        NftId requesterId,
        bytes calldata requestData,
        Timestamp expiryAt
    )
        internal
        virtual override
    {
        FlightStatusRequest memory request = abi.decode(requestData, (FlightStatusRequest));

        // TODO decide if the line below should be moved to GIF
        LibRequestIdSet.add(_activeRequests, requestId);
        emit LogFlightOracleRequestReceived(requestId, requesterId);
    }


    /// @dev use case specific handling of oracle requests
    /// for now only log is emitted to verify that cancelling has been received by oracle component 
    function _cancel(
        RequestId requestId
    )
        internal
        virtual override
    {
        // TODO decide if the line below should be moved to GIF
        LibRequestIdSet.remove(_activeRequests, requestId);
        emit LogFlightOracleRequestCancelled(requestId);
    }
}

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

import {Blocknumber, BlocknumberLib} from "../../type/Blocknumber.sol";
import {NftId} from "../../type/NftId.sol";
import {Amount} from "../../type/Amount.sol";

contract BalanceStore {

    error ErrorBalanceStoreTargetAlreadyRegistered(NftId targetNftId);
    error ErrorBalanceStoreTargetNotRegistered(NftId targetNftId);

    event LogBalanceStoreTargetRegistered(NftId targetNftId);

    event LogBalanceStoreFeesIncreased(NftId targetNftId, Amount addedAmount, Amount newBalance, Blocknumber lastUpdatedIn);
    event LogBalanceStoreFeesDecreased(NftId targetNftId, Amount addedAmount, Amount newBalance, Blocknumber lastUpdatedIn);

    event LogBalanceStoreLockedIncreased(NftId targetNftId, Amount addedAmount, Amount newBalance, Blocknumber lastUpdatedIn);
    event LogBalanceStoreLockedDecreased(NftId targetNftId, Amount addedAmount, Amount newBalance, Blocknumber lastUpdatedIn);

    event LogBalanceStoreBalanceIncreased(NftId targetNftId, Amount addedAmount, Amount newBalance, Blocknumber lastUpdatedIn);
    event LogBalanceStoreBalanceDecreased(NftId targetNftId, Amount addedAmount, Amount newBalance, Blocknumber lastUpdatedIn);

    mapping(NftId nftId => Amount balance) private _balanceAmount;
    mapping(NftId nftId => Amount locked) private _lockedAmount;
    mapping(NftId nftId => Amount fees) private _feeAmount;

    // used to indicate if the target has been registered as well as when it was last updated (not used externally atm)    
    mapping(NftId nftId => Blocknumber lastUpdatedIn) private _lastUpdatedIn;

    modifier onlyRegisteredTarget(NftId targetNftId) {
        if (!_lastUpdatedIn[targetNftId].gtz()) {
            revert ErrorBalanceStoreTargetNotRegistered(targetNftId);
        }
        _;
    }

    function getBalanceAmount(NftId targetNftId) external view returns (Amount balanceAmount) { return _balanceAmount[targetNftId]; }
    function getLockedAmount(NftId targetNftId) external view returns (Amount lockedAmount) { return _lockedAmount[targetNftId]; }
    function getFeeAmount(NftId targetNftId) external view returns (Amount feeAmount) { return _feeAmount[targetNftId]; }

    function getAmounts(NftId targetNftId)
        external
        view
        returns (
            Amount balanceAmount,
            Amount lockedAmount,
            Amount feeAmount
        )
    {
        balanceAmount = _balanceAmount[targetNftId];
        lockedAmount = _lockedAmount[targetNftId];
        feeAmount = _feeAmount[targetNftId];
    }

    function _registerBalanceTarget(NftId targetNftId) internal {
        if (_lastUpdatedIn[targetNftId].gtz()) {
            revert ErrorBalanceStoreTargetAlreadyRegistered(targetNftId);
        }

        _setLastUpdatedIn(targetNftId);

        emit LogBalanceStoreTargetRegistered(targetNftId);
    }

    //--- fee management ----------------------------------------------------//
    function _increaseFees(NftId targetNftId, Amount amount) internal onlyRegisteredTarget(targetNftId) returns (Amount newBalance) {
        newBalance = _feeAmount[targetNftId] + amount;
        _feeAmount[targetNftId] = newBalance;

        emit LogBalanceStoreFeesIncreased(targetNftId, amount, newBalance, _lastUpdatedIn[targetNftId]);
        _setLastUpdatedIn(targetNftId);
    }

    function _decreaseFees(NftId targetNftId, Amount amount) internal onlyRegisteredTarget(targetNftId) returns (Amount newBalance) {
        newBalance = _feeAmount[targetNftId] - amount;
        _feeAmount[targetNftId] = newBalance;

        emit LogBalanceStoreFeesDecreased(targetNftId, amount, newBalance, _lastUpdatedIn[targetNftId]);
        _setLastUpdatedIn(targetNftId);
    }

    //--- locked management -------------------------------------------------//
    function _increaseLocked(NftId targetNftId, Amount amount) internal onlyRegisteredTarget(targetNftId) returns (Amount newBalance) {
        newBalance = _lockedAmount[targetNftId] + amount;
        _lockedAmount[targetNftId] = newBalance;

        emit LogBalanceStoreLockedIncreased(targetNftId, amount, newBalance, _lastUpdatedIn[targetNftId]);
        _setLastUpdatedIn(targetNftId);
    }

    function _decreaseLocked(NftId targetNftId, Amount amount) internal onlyRegisteredTarget(targetNftId) returns (Amount newBalance) {
        newBalance = _lockedAmount[targetNftId] - amount;
        _lockedAmount[targetNftId] = newBalance;

        emit LogBalanceStoreLockedDecreased(targetNftId, amount, newBalance, _lastUpdatedIn[targetNftId]);
        _setLastUpdatedIn(targetNftId);
    }

    //--- balance management ------------------------------------------------//
    function _increaseBalance(NftId targetNftId, Amount amount) internal onlyRegisteredTarget(targetNftId) returns (Amount newBalance) {
        newBalance = _balanceAmount[targetNftId] + amount;
        _balanceAmount[targetNftId] = newBalance;

        emit LogBalanceStoreBalanceIncreased(targetNftId, amount, newBalance, _lastUpdatedIn[targetNftId]);
        _setLastUpdatedIn(targetNftId);
    }

    function _decreaseBalance(NftId targetNftId, Amount amount) internal onlyRegisteredTarget(targetNftId) returns (Amount newBalance) {
        newBalance = _balanceAmount[targetNftId] - amount;
        _balanceAmount[targetNftId] = newBalance;

        emit LogBalanceStoreBalanceDecreased(targetNftId, amount, newBalance, _lastUpdatedIn[targetNftId]);
        _setLastUpdatedIn(targetNftId);
    }

    //--- internal/private functions ----------------------------------------//
    function _setLastUpdatedIn(NftId targetNftId) internal {
        _lastUpdatedIn[targetNftId] = BlocknumberLib.current();
    }
}

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

import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";

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

abstract contract Cloneable is 
    AccessManagedUpgradeable
{
    IRegistry internal _registry;

    /// @dev call to initialize MUST be made in the same transaction as cloning of the contract
    function __Cloneable_init(
        address authority,
        address registry
    )
        internal 
        onlyInitializing
    {
        __AccessManaged_init(authority);
        _registry = IRegistry(registry);
    }

    function getRegistry() external view returns (IRegistry) {
        return _registry;
    }
}

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

import {Blocknumber, BlocknumberLib} from "../../type/Blocknumber.sol";
import {NftId} from "../../type/NftId.sol";
import {Amount} from "../../type/Amount.sol";
import {RequestId, RequestIdLib} from "../../type/RequestId.sol";

contract ObjectCounter {

    // TODO refactor risk id
    // mapping(NftId productNftId => uint64 risks) private _riskCounter;

    uint256 private _requestCounter = 0;

    function _createNextRequestId() internal returns (RequestId requestId) {
        _requestCounter++;
        requestId = RequestIdLib.toRequestId(_requestCounter);
    }
}

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

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 

import {COMPONENT, BUNDLE, POLICY, REQUEST, RISK, CLAIM, PAYOUT, POOL, PREMIUM, PRODUCT, DISTRIBUTION, DISTRIBUTOR, DISTRIBUTOR_TYPE, REFERRAL, FEE} from "../../type/ObjectType.sol";
import {ACTIVE, PAUSED, ARCHIVED, CLOSED, APPLIED, COLLATERALIZED, REVOKED, SUBMITTED, CONFIRMED, DECLINED, EXPECTED, PAID, FULFILLED, FAILED, CANCELLED} from "../../type/StateId.sol";
import {Lifecycle} from "../../shared/Lifecycle.sol";

contract ObjectLifecycle is
    Lifecycle,
    Initializable
{
    function _initializeLifecycle() internal onlyInitializing
    {
        _setupLifecycle();
    }

    function _setupLifecycle()
        internal
        override
    {
        _setupBundleLifecycle();
        _setupComponentLifecycle();
        _setupPolicyLifecycle();
        _setupPremiumLifecycle();
        _setupClaimLifecycle();
        _setupPayoutLifecycle();
        _setupRiskLifecycle();
        _setupRequestLifecycle();

        // setup dummy lifecycles to manage with key value store 
        _setUpPoolLifecycle();
        _setUpProductLifecycle();
        _setUpDistributionLifecycle();
    }

    function _setupComponentLifecycle() private {
        setInitialState(COMPONENT(), ACTIVE());
        setStateTransition(COMPONENT(), ACTIVE(), PAUSED());
        setStateTransition(COMPONENT(), PAUSED(), ACTIVE());
        setStateTransition(COMPONENT(), PAUSED(), ARCHIVED());
    }

    function _setupBundleLifecycle() private {
        setInitialState(BUNDLE(), ACTIVE());
        setStateTransition(BUNDLE(), ACTIVE(), CLOSED());
    }

    function _setupPolicyLifecycle() private {
        setInitialState(POLICY(), APPLIED());
        setStateTransition(POLICY(), APPLIED(), REVOKED());
        setStateTransition(POLICY(), APPLIED(), DECLINED());
        setStateTransition(POLICY(), APPLIED(), COLLATERALIZED());
        setStateTransition(POLICY(), COLLATERALIZED(), CLOSED());
    }

    function _setupPremiumLifecycle() private {
        setInitialState(PREMIUM(), EXPECTED());
        setStateTransition(PREMIUM(), EXPECTED(), PAID());
    }

    function _setupClaimLifecycle() private {
        setInitialState(CLAIM(), SUBMITTED());
        setStateTransition(CLAIM(), SUBMITTED(), REVOKED());
        setStateTransition(CLAIM(), SUBMITTED(), CONFIRMED());
        setStateTransition(CLAIM(), SUBMITTED(), DECLINED());
        setStateTransition(CLAIM(), CONFIRMED(), CLOSED());
        setStateTransition(CLAIM(), CONFIRMED(), CANCELLED());
    }

    function _setupPayoutLifecycle() private {
        setInitialState(PAYOUT(), EXPECTED());
        setStateTransition(PAYOUT(), EXPECTED(), PAID());
        setStateTransition(PAYOUT(), EXPECTED(), CANCELLED());
    }

    function _setupRiskLifecycle() private {
        setInitialState(RISK(), ACTIVE());
        setStateTransition(RISK(), ACTIVE(), CLOSED());
    }

    function _setupRequestLifecycle() private {
        setInitialState(REQUEST(), ACTIVE());
        setStateTransition(REQUEST(), ACTIVE(), FULFILLED());
        setStateTransition(REQUEST(), ACTIVE(), FAILED());
        setStateTransition(REQUEST(), FAILED(), FULFILLED());
        setStateTransition(REQUEST(), ACTIVE(), CANCELLED());
    }

    // dummy lifecycle only
    function _setUpPoolLifecycle() private {
        setInitialState(POOL(), ACTIVE());
    }

    // dummy lifecycle only
    function _setUpProductLifecycle() private {
        setInitialState(PRODUCT(), ACTIVE());
        setInitialState(FEE(), ACTIVE());
    }

    // dummy lifecycles only
    function _setUpDistributionLifecycle() private {
        setInitialState(DISTRIBUTION(), ACTIVE());
        setInitialState(DISTRIBUTOR(), ACTIVE());
        setInitialState(DISTRIBUTOR_TYPE(), ACTIVE());
        setInitialState(REFERRAL(), ACTIVE());
    }
}

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

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

import {LibKey32Set} from "../../type/Key32Set.sol";
import {NftId} from "../../type/NftId.sol";
import {Key32} from "../../type/Key32.sol";

contract ObjectSet is
    Cloneable
{
    using LibKey32Set for LibKey32Set.Set;

    event LogObjectSetInitialized(address instance);

    error ErrorObjectSetNftIdInvalid(NftId instanceNftId);

    mapping(NftId compnentNftId => LibKey32Set.Set objects) internal _activeObjects;
    mapping(NftId compnentNftId => LibKey32Set.Set objects) internal _allObjects;
    address internal _instanceAddress;

    /// @dev This initializer needs to be called from the instance itself.
    function initialize(address authority, address registry) 
        external
        initializer()
    {
        _instanceAddress = msg.sender;
        __Cloneable_init(authority, registry);
        
        emit LogObjectSetInitialized(address(_instanceAddress));
    }

    function getInstanceAddress() external view returns (address) {
        return _instanceAddress;
    }

    function _add(NftId componentNftId, Key32 key) internal {
        LibKey32Set.Set storage allSet = _allObjects[componentNftId];
        LibKey32Set.Set storage activeSet = _activeObjects[componentNftId];

        allSet.add(key);
        activeSet.add(key);
    }

    function _activate(NftId componentNftId, Key32 key) internal {
        _activeObjects[componentNftId].add(key);
    }

    function _deactivate(NftId componentNftId, Key32 key) internal {
        _activeObjects[componentNftId].remove(key);
    }

    function _objects(NftId componentNftId) internal view returns (uint256) {
        return _allObjects[componentNftId].size();
    }

    function _contains(NftId componentNftId, Key32 key) internal view returns (bool) {
        return _allObjects[componentNftId].contains(key);
    }

    function _getObject(NftId componentNftId, uint256 idx) internal view returns (Key32) {
        return _allObjects[componentNftId].getElementAt(idx);
    }

    function _activeObjs(NftId componentNftId) internal view returns (uint256)  {
        return _activeObjects[componentNftId].size();
    }

    function _isActive(NftId componentNftId, Key32 key) internal view returns (bool) {
        return _activeObjects[componentNftId].contains(key);
    }

    function _getActiveObject(NftId componentNftId, uint256 idx) internal view returns (Key32) {
        return _activeObjects[componentNftId].getElementAt(idx);
    }
}

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

import {IBundle} from "../module/IBundle.sol";
import {IInstance} from "../IInstance.sol";
import {IRisk} from "../module/IRisk.sol";
import {NftId} from "../../type/NftId.sol";
import {RiskId} from "../../type/RiskId.sol";


library ObjectSetHelperLib {

    function getRiskInfo(address instanceAddress, RiskId riskId) public view returns (IRisk.RiskInfo memory) {

        return IInstance(instanceAddress).getInstanceReader().getRiskInfo(riskId);
    }

    function getProductNftId(address instanceAddress, RiskId riskId) public view returns (NftId) {
        return getRiskInfo(instanceAddress, riskId).productNftId;
    }

    function getBundleInfo(address instanceAddress, NftId bundleNftId) public view returns (IBundle.BundleInfo memory) {
        return IInstance(instanceAddress).getInstanceReader().getBundleInfo(bundleNftId);
    }

    function getPoolNftId(address instanceAddress, NftId bundleNftId) public view returns (NftId) {
        return getBundleInfo(instanceAddress, bundleNftId).poolNftId;
    }

}

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

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

import {Blocknumber, BlocknumberLib} from "../type/Blocknumber.sol";
import {Key32, KeyId, Key32Lib} from "../type/Key32.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {StateId, KEEP_STATE} from "../type/StateId.sol";
import {Lifecycle} from "../shared/Lifecycle.sol";

abstract contract BaseStore is
    Lifecycle, 
    IBaseStore
{

    mapping(Key32 key32 => IBaseStore.Metadata metadata) private _metadata;

    function _createMetadata(
        Key32 key32
    )
        internal
    {
        ObjectType objectType = key32.toObjectType();
        if (objectType.eqz()) {
            revert ErrorBaseStoreTypeUndefined(objectType);
        }

        Metadata storage metadata = _metadata[key32];
        if (metadata.updatedIn.gtz()) {
            revert ErrorBaseStoreAlreadyCreated(key32, objectType);
        }

        if(!hasLifecycle(objectType)) {
            revert ErrorBaseStoreNoLifecycle(objectType);
        }

        Blocknumber blocknumber = BlocknumberLib.current();
        StateId initialState = getInitialState(objectType);

        // set metadata
        metadata.objectType = objectType;
        metadata.state = initialState;
        metadata.updatedIn = blocknumber;
    }

    function _updateState(
        Key32 key32, 
        StateId state
    )
        internal
        returns (Blocknumber lastUpdatedIn, StateId oldState)
    {
        if (state.eqz()) {
            revert ErrorBaseStoreStateZero(key32);
        }

        Metadata storage metadata = _metadata[key32];
        oldState = metadata.state;
        lastUpdatedIn = metadata.updatedIn;

        if (oldState.eqz()) {
            revert ErrorBaseStoreNotExisting(key32);
        }

        // update state 
        if(state != KEEP_STATE()) {
            checkTransition(oldState, metadata.objectType, oldState, state);
            metadata.state = state;

            // solhint-disable-next-line avoid-tx-origin
        }

        // update metadata
        metadata.updatedIn = BlocknumberLib.current();
    }

    function exists(Key32 key32) public view returns (bool) {
        return _metadata[key32].state.gtz();
    }

    function getMetadata(Key32 key32) public view returns (Metadata memory metadata) {
        return _metadata[key32];
    }

    function getState(Key32 key32) public view returns (StateId state) {
        return _metadata[key32].state;
    }

    function toKey32(ObjectType objectType, KeyId id) external pure override returns(Key32) {
        return Key32Lib.toKey32(objectType, id);
    }
}

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

import {LibNftIdSet} from "../type/NftIdSet.sol";
import {NftId, NftIdLib} from "../type/NftId.sol";
import {Key32} from "../type/Key32.sol";
import {BUNDLE} from "../type/ObjectType.sol";

import {ObjectSet} from "./base/ObjectSet.sol";
import {ObjectSetHelperLib} from "./base/ObjectSetHelperLib.sol";

contract BundleSet is 
    ObjectSet
{
    using LibNftIdSet for LibNftIdSet.Set;

    event LogBundleSetPolicyLinked(NftId bundleNftId, NftId policyNftId);
    event LogBundleSetPolicyUnlinked(NftId bundleNftId, NftId policyNftId);

    event LogBundleSetBundleAdded(NftId poolNftId, NftId bundleNftId);
    event LogBundleSetBundleUnlocked(NftId poolNftId, NftId bundleNftId);
    event LogBundleSetBundleLocked(NftId poolNftId, NftId bundleNftId);
    event LogBundleSetBundleClosed(NftId poolNftId, NftId bundleNftId);

    error ErrorBundleSetPolicyAlreadyActivated(NftId policyNftId);
    error ErrorBundleSetBundleLocked(NftId bundleNftId, NftId policyNftId);
    error ErrorBundleSetPolicyWithOpenClaims(NftId policyNftId, uint256 openClaimsCount);
    error ErrorBundleSetPolicyNotCloseable(NftId policyNftId);
    error ErrorBundleSetBundleUnknown(NftId bundleNftId);
    error ErrorBundleSetBundleNotRegistered(NftId bundleNftId);

    mapping(NftId bundleNftId => LibNftIdSet.Set policies) internal _activePolicies;

    /// @dev links a policy to its bundle
    function linkPolicy(NftId poolNftId, NftId bundleNftId, NftId policyNftId) external restricted() {

        // ensure bundle is unlocked (in active set) and registered with this instance
        if (!_isActive(poolNftId, _toBundleKey32(bundleNftId))) {
            revert ErrorBundleSetBundleLocked(bundleNftId, policyNftId);
        }

        _activePolicies[bundleNftId].add(policyNftId);
        emit LogBundleSetPolicyLinked(bundleNftId, policyNftId);
    }


    /// @dev unlinks a policy from its bundle
    function unlinkPolicy(NftId poolNftId, NftId bundleNftId, NftId policyNftId) external restricted() {

        // ensure bundle is registered with this instance
        if (!_contains(poolNftId, _toBundleKey32(bundleNftId))) {
            revert ErrorBundleSetBundleUnknown(bundleNftId);
        }

        _activePolicies[bundleNftId].remove(policyNftId);
        emit LogBundleSetPolicyUnlinked(bundleNftId, policyNftId);
    }


    /// @dev add a new bundle to a pool registerd with this instance
    // the corresponding pool is fetched via instance reader
    function add(NftId bundleNftId) external restricted() {
        NftId poolNftId = ObjectSetHelperLib.getPoolNftId(_instanceAddress, bundleNftId);

        // ensure pool is registered with instance
        if(poolNftId.eqz()) {
            revert ErrorBundleSetBundleNotRegistered(bundleNftId);
        }

        _add(poolNftId, _toBundleKey32(bundleNftId));
        emit LogBundleSetBundleAdded(poolNftId, bundleNftId);
    }


    /// @dev unlocked (active) bundles are available to collateralize new policies
    function unlock(NftId bundleNftId) external restricted() {
        NftId poolNftId = ObjectSetHelperLib.getPoolNftId(_instanceAddress, bundleNftId);
        _activate(poolNftId, _toBundleKey32(bundleNftId));
        emit LogBundleSetBundleUnlocked(poolNftId, bundleNftId);
    }

    /// @dev locked (deactivated) bundles may not collateralize any new policies
    function lock(NftId bundleNftId) external restricted() {
        NftId poolNftId = ObjectSetHelperLib.getPoolNftId(_instanceAddress, bundleNftId);
        _deactivate(poolNftId, _toBundleKey32(bundleNftId));
        emit LogBundleSetBundleLocked(poolNftId, bundleNftId);
    }


    function checkBundle(NftId productNftId, NftId bundleId)
        public
        view 
        returns (bool exists, bool active)
    {
        Key32 bundleKey32 = bundleId.toKey32(BUNDLE());
        exists = _contains(productNftId, bundleKey32);

        if (exists) {
            active = _isActive(productNftId, bundleKey32);
        }
    }

    function bundles(NftId poolNftId) external view returns(uint256) {
        return _objects(poolNftId);
    }

    function getBundleNftId(NftId poolNftId, uint256 idx) external view returns(NftId) {
        return NftIdLib.toNftId(_getObject(poolNftId, idx).toKeyId());
    }

    function activeBundles(NftId poolNftId) external view returns(uint256) {
        return _activeObjs(poolNftId);
    }

    function getActiveBundleNftId(NftId poolNftId, uint256 idx) external view returns(NftId) {
        return NftIdLib.toNftId(_getActiveObject(poolNftId, idx).toKeyId());
    }

    function activePolicies(NftId bundleNftId) external view returns(uint256) {
        return _activePolicies[bundleNftId].size();
    }

    function getActivePolicy(NftId bundleNftId, uint256 idx) external view returns(NftId policyNftId) {
        return _activePolicies[bundleNftId].getElementAt(idx);
    }

    function _toBundleKey32(NftId nftId) private pure returns (Key32) {
        return nftId.toKey32(BUNDLE());
    }
}

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

import {ILifecycle} from "../shared/ILifecycle.sol";

import {Blocknumber} from "../type/Blocknumber.sol";
import {Key32, KeyId} from "../type/Key32.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {StateId} from "../type/StateId.sol";


interface IBaseStore is ILifecycle {

    error ErrorBaseStoreTypeUndefined(ObjectType objectType);
    error ErrorBaseStoreAlreadyCreated(Key32 key, ObjectType objectType);
    error ErrorBaseStoreNoLifecycle(ObjectType objectType);
    error ErrorBaseStoreStateZero(Key32 key);
    error ErrorBaseStoreNotExisting(Key32 key);


    struct Metadata {
        // slot 0
        ObjectType objectType;
        StateId state;
        Blocknumber updatedIn;
    }

    /// @dev check if a metadata entry with the key exists
    function exists(Key32 key) external view returns (bool);
    /// @dev retrieve the metadata for a given key
    function getMetadata(Key32 key) external view returns (Metadata memory metadata);
    /// @dev retrieve the state for a given key
    function getState(Key32 key) external view returns (StateId state);

    /// @dev convert an object type and an id to a key32
    function toKey32(ObjectType objectType, KeyId id) external pure returns(Key32);
}

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

import {IAccess} from "../authorization/IAccess.sol";
import {IRegisterable} from "../shared/IRegisterable.sol";

import {Amount} from "../type/Amount.sol";
import {BundleSet} from "./BundleSet.sol";
import {RiskSet} from "./RiskSet.sol";
import {InstanceAdmin} from "./InstanceAdmin.sol";
import {InstanceReader} from "./InstanceReader.sol";
import {InstanceStore} from "./InstanceStore.sol";
import {NftId} from "../type/NftId.sol";
import {ProductStore} from "./ProductStore.sol";
import {RoleId} from "../type/RoleId.sol";
import {Seconds} from "../type/Seconds.sol";
import {UFixed} from "../type/UFixed.sol";


interface IInstance is 
    IRegisterable
{
    // role handling
    event LogInstanceCustomRoleCreated(RoleId roleId, string roleName, RoleId adminRoleId, uint32 maxMemberCount);
    event LogInstanceCustomRoleActiveSet(RoleId roleId, bool active, address caller);
    event LogInstanceCustomRoleGranted(RoleId roleId, address account, address caller);
    event LogInstanceCustomRoleRevoked(RoleId roleId, address account, address caller);

    // target handling
    event LogInstanceCustomTargetCreated(address target, RoleId targetRoleId, string name);
    event LogInstanceTargetLocked(address target, bool locked);
    event LogInstanceCustomTargetFunctionRoleSet(address target, bytes4[] selectors, RoleId roleId);

    // modifier is onlyRoleAdmin
    error ErrorInstanceNotCustomRole(RoleId roleId);
    error ErrorInstanceNotRoleAdmin(RoleId roleId, address account);

    error ErrorInstanceInstanceAdminZero();
    error ErrorInstanceInstanceAdminAlreadySet(address instanceAdmin);
    error ErrorInstanceInstanceAdminAuthorityMismatch(address instanceAuthority);

    error ErrorInstanceBundleSetAlreadySet(address instanceBundleSet);
    error ErrorInstanceBundleSetInstanceMismatch(address instance);
    error ErrorInstanceBundleSetAuthorityMismatch(address instanceAuthority);

    error ErrorInstanceRiskSetAlreadySet(address instanceRiskSet);
    error ErrorInstanceRiskSetInstanceMismatch(address instance);
    error ErrorInstanceRiskSetAuthorityMismatch(address instanceAuthority);

    error ErrorInstanceInstanceReaderInstanceMismatch(address instanceAuthority);

    error ErrorInstanceInstanceStoreAlreadySet(address instanceStore);
    error ErrorInstanceInstanceStoreAuthorityMismatch(address instanceAuthority);

    struct InstanceContracts {
        InstanceAdmin instanceAdmin;
        InstanceStore instanceStore;
        ProductStore productStore;
        BundleSet bundleSet;
        RiskSet riskSet;
        InstanceReader instanceReader;
    }

    struct InstanceInfo {
        uint64 requestsCount;
    }

    ///--- instance ---------------------------------------------------------//

    /// @dev Locks/unlocks the complete instance, including all its components.
    function setInstanceLocked(bool locked) external;

    /// @dev Upgrades the instance reader to the specified target.
    function upgradeInstanceReader() external;

    /// @dev Sets the instance reader for the instance.
    /// Permissioned: only the instance service may call this function.
    function setInstanceReader(InstanceReader instanceReader) external;

    ///--- staking ----------------------------------------------------------//

    /// @dev Sets the duration for locking new stakes on this instance..
    function setStakingLockingPeriod(Seconds stakeLockingPeriod) external;

    /// @dev Sets the staking reward rate [apr] for this instance.
    function setStakingRewardRate(UFixed rewardRate) external;

    /// @dev Sets the maximum staked amount for this instance.
    function setStakingMaxAmount(Amount maxStakedAmount) external;

    /// @dev Refills the staking reward reserves for the specified target.
    function refillStakingRewardReserves(Amount dipAmount) external returns (Amount newBalance);

    /// @dev Defunds the staking reward reserves for the specified target.
    function withdrawStakingRewardReserves(Amount dipAmount) external returns (Amount newBalance);

    ///--- product/component ------------------------------------------------//

    /// @dev Locks/unlocks the specified target.
    function setTargetLocked(address target, bool locked) external;

    /// @dev Register a product with the instance.
    function registerProduct(address product, address token) external returns (NftId productNftId);

    ///--- authz ------------------------------------------------------------//

    /// @dev Creates a new custom role for the calling instance.
    /// Custom roles are intended to be used for access control of custom components and its helper contracts.
    /// Custom roles are not intended to be used as target roles for custom contracts.
    function createRole(string memory roleName, RoleId adminRoleId, uint32 maxMemberCount) external returns (RoleId roleId);

    /// @dev Activates/deactivates the specified role.
    /// Only instance owner or account with role admin role can call this function.
    function setRoleActive(RoleId roleId, bool active) external;

    /// @dev Grants the specified role to the account.
    /// Only active roles can be granted.
    /// Only instance owner or account with role admin role can call this function.
    function grantRole(RoleId roleId, address account) external;

    /// @dev Revokes the specified role from the account.
    /// Only instance owner or account with role admin role can call this function.
    function revokeRole(RoleId roleId, address account) external;

    /// @dev Creates a new custom target.
    /// Custom targets are intended to be used for access control helper contracts of components.
    /// Custom targets are not intended to be used for components.
    function createTarget(address target, string memory name) external returns (RoleId contractRoleId);

    /// @dev Authorizes the specified functions for the target and provided role.
    function authorizeFunctions(address target, RoleId roleId, IAccess.FunctionInfo[] memory functions) external;

    /// @dev Removes any role authorization for the specified functions.
    function unauthorizeFunctions(address target, IAccess.FunctionInfo[] memory functions) external;

    //--- getters -----------------------------------------------------------//

    /// @dev returns the overall locking state of the instance (including all components)
    function isInstanceLocked() external view returns (bool isLocked);

    /// @dev returns the locking state of the specified target
    function isTargetLocked(address target) external view returns (bool isLocked);

    // get products
    function products() external view returns (uint256 productCount);
    function getProduct(uint256 idx) external view returns (NftId productNftId);

    // get supporting contracts
    function getInstanceReader() external view returns (InstanceReader);
    function getBundleSet() external view returns (BundleSet);
    function getRiskSet() external view returns (RiskSet);
    function getInstanceAdmin() external view returns (InstanceAdmin);
    function getInstanceStore() external view returns (InstanceStore);
    function getProductStore() external view returns (ProductStore);
    function isTokenRegistryDisabled() external view returns (bool);
}

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

import {IAccess} from "../authorization/IAccess.sol";
import {IInstance} from "./IInstance.sol";
import {IService} from "../shared/IService.sol";

import {Amount} from "../type/Amount.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {RoleId} from "../type/RoleId.sol";
import {Seconds} from "../type/Seconds.sol";
import {UFixed} from "../type/UFixed.sol";
import {VersionPart} from "../type/Version.sol";

interface IInstanceService is IService {

    // onlyInstance
    error ErrorInstanceServiceNotRegistered(address instance);
    error ErrorInstanceServiceNotInstance(address instance, ObjectType objectType);
    error ErrorInstanceServiceInstanceVersionMismatch(NftId instanceNftId, VersionPart expectedRelease, VersionPart instanceRelease);

    error ErrorInstanceServiceComponentNotInstanceLinked(address component);

    error ErrorInstanceServiceMasterInstanceAlreadySet();
    error ErrorInstanceServiceMasterInstanceAdminAlreadySet();
    error ErrorInstanceServiceMasterBundleSetAlreadySet();
    error ErrorInstanceServiceMasterRiskSetAlreadySet();
    error ErrorInstanceServiceInstanceAddressZero();

    error ErrorInstanceServiceMasterInstanceReaderNotSet();
    error ErrorInstanceServiceInstanceReaderAddressZero();
    error ErrorInstanceServiceInstanceReaderSameAsMasterInstanceReader();
    error ErrorInstanceServiceInstanceReaderInstanceMismatch();

    error ErrorInstanceServiceAccessManagerZero();
    error ErrorInstanceServiceInstanceAdminZero();
    error ErrorInstanceServiceInstanceReaderZero();
    error ErrorInstanceServiceBundleSetZero();
    error ErrorInstanceServiceRiskSetZero();
    error ErrorInstanceServiceInstanceStoreZero();
    error ErrorInstanceServiceProductStoreZero();

    error ErrorInstanceServiceInstanceAuthorityMismatch();
    error ErrorInstanceServiceBundleSetAuthorityMismatch();
    error ErrorInstanceServiceRiskSetAuthorityMismatch();
    error ErrorInstanceServiceInstanceReaderInstanceMismatch2();
    error ErrorInstanceServiceBundleSetInstanceMismatch();
    error ErrorInstanceServiceRiskSetInstanceMismatch();
    error ErrorInstanceServiceInstanceStoreAuthorityMismatch();
    error ErrorInstanceServiceProductStoreAuthorityMismatch();

    error ErrorInstanceServiceRequestUnauhorized(address caller);
    error ErrorInstanceServiceNotInstanceNftId(NftId nftId);
    error ErrorInstanceServiceComponentNotRegistered(address componentAddress);
    error ErrorInstanceServiceInstanceComponentMismatch(NftId instanceNftId, NftId componentNftId);
    error ErrorInstanceServiceInvalidComponentType(address componentAddress, ObjectType expectedType, ObjectType componentType);
    
    event LogInstanceServiceInstanceLocked(NftId instanceNftId, bool locked);
    event LogInstanceServiceInstanceCreated(NftId instanceNftId, address instance);
    event LogInstanceServiceMasterInstanceReaderUpgraded(NftId instanceNfId, address newInstanceReader);
    event LogInstanceServiceInstanceReaderUpgraded(NftId instanceNfId, address newInstanceReader);

    /// @dev Creates a new custom role for the calling instance.
    function createRole(string memory roleName, RoleId adminRoleId, uint32 maxMemberCount) external returns (RoleId roleId);

    /// @dev Sets the specified custom role as active or inactive for the calling instance.
    function setRoleActive(RoleId roleId, bool active) external;

    /// @dev Grants the specified custom role to the specified account for the calling instance.
    function grantRole(RoleId roleId, address account) external; 

    /// @dev Revokes the specified custom role from the specified account for the calling instance.
    function revokeRole(RoleId roleId, address account) external; 

    /// @dev Creates a new custom target for the calling instance.
    /// All custom trargets are created with a corresponding contract role.
    function createTarget(address target, string memory name) external returns (RoleId contractRoleId);

    /// @dev Authorizes the specified functions for the specified target.
    function authorizeFunctions(address target, RoleId roleId, IAccess.FunctionInfo[] memory functions) external; 

    /// @dev Removes any role authorization for the specified functions.
    function unauthorizeFunctions(address target, IAccess.FunctionInfo[] memory functions) external;

    /// @dev Locks/unlocks the specified target constrolled by the corresponding instance admin.
    function setTargetLocked(address target, bool locked) external;

    /// @dev Locks the complete instance, including all its components.
    function setInstanceLocked(bool locked) external;

    /// @dev Creates a new instance.
    /// The caller becomes the owner of the new instance.
    /// Creation of a new instance is achieved by this service through the creation and registration 
    /// of a new clone of the master instance and then setting up the initial wiring and authorization 
    /// of the necessary components.
    /// @param allowAnyToken specifies whether the new instance is allowed to use any token or 
    /// only tokens that are registered in the `TokenRegistry`. Use of tokens **not** registered in the
    /// `TokenRegistry` may result in problems with token handline and is therefor not recommended. The 
    /// use of such instances **is not supported** by Etherisc. 
    function createInstance(bool allowAnyToken)
        external 
        returns (
            IInstance instance,
            NftId instanceNftId
        );

    function upgradeInstanceReader() external;
    function upgradeMasterInstanceReader(address instanceReaderAddress) external;

    function setStakingLockingPeriod(Seconds stakeLockingPeriod) external;
    function setStakingRewardRate(UFixed rewardRate) external;
    function setStakingMaxAmount(Amount maxStakedAmount) external;

    function refillInstanceRewardReserves(address rewardProvider, Amount dipAmount) external returns (Amount newBalance);

    /// @dev Defunds the staking reward reserves for the specified target.
    function withdrawInstanceRewardReserves(Amount dipAmount) external returns (Amount newBalance);
}

// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.20;

import {IAccess} from "../authorization/IAccess.sol";
import {IAuthorization} from "../authorization/IAuthorization.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {IInstance} from "./IInstance.sol";

import {AccessAdmin} from "../authorization/AccessAdmin.sol";
import {AccessAdminLib} from "../authorization/AccessAdminLib.sol";
import {AccessManagerCloneable} from "../authorization/AccessManagerCloneable.sol";
import {ObjectType, INSTANCE} from "../type/ObjectType.sol";
import {RoleId, ADMIN_ROLE} from "../type/RoleId.sol";
import {Str} from "../type/String.sol";
import {VersionPart} from "../type/Version.sol";
import {INSTANCE_TARGET_NAME, INSTANCE_ADMIN_TARGET_NAME, INSTANCE_STORE_TARGET_NAME, PRODUCT_STORE_TARGET_NAME, BUNDLE_SET_TARGET_NAME, RISK_SET_TARGET_NAME} from "./TargetNames.sol";


contract InstanceAdmin is
    AccessAdmin
{
    // onlyInstanceService
    error ErrorInstanceAdminNotInstanceService(address caller);

    // authorizeFunctions
    error ErrorInstanceAdminNotComponentOrCustomTarget(address target);

    IInstance internal _instance;
    IRegistry internal _registry;

    uint64 internal _customRoleIdNext;


    modifier onlyInstanceService() {
        if (msg.sender != _registry.getServiceAddress(INSTANCE(), getRelease())) {
            revert ErrorInstanceAdminNotInstanceService(msg.sender);
        }
        _;
    }


    /// @dev Only used for master instance admin.
    constructor(address accessManager) {
        initialize(
            accessManager,
            "MasterInstanceAdmin");
    }


    /// @dev Completes the initialization of this instance admin using the provided instance, registry and version.
    /// Important: Initialization of instance admin is only complete after calling this function. 
    /// Important: The instance MUST be registered and all instance supporting contracts must be wired to this instance.
    function completeSetup(
        address registry,
        address authorization,
        VersionPart release,
        address instance
    )
        external
        reinitializer(uint64(release.toInt()))
    {
        // checks
        AccessAdminLib.checkIsRegistered(registry, instance, INSTANCE());

        // effects
        AccessManagerCloneable(
            authority()).completeSetup(
                registry, 
                release); 

        AccessAdminLib.checkAuthorization(
            address(_authorization), 
            authorization, 
            INSTANCE(), // expectedDomain
            release, // expectedRelease
            false, // expectServiceAuthorization
            true); // checkAlreadyInitialized

        _registry = IRegistry(registry);
        _instance = IInstance(instance);
        _authorization = IAuthorization(authorization);
        _customRoleIdNext = 0;

        // link nft ownability to instance
        _linkToNftOwnable(instance);

        // setup roles and services
        _createRoles(_authorization);
        _setupServices(_authorization);

        // setup instance targets
        _createInstanceTargets(_authorization.getMainTargetName());

        // authorize functions of instance contracts
        _createTargetAuthorizations(_authorization);
    }

    /// @dev grants the service roles to the service addresses based on the authorization specification.
    /// Service addresses used for the granting are determined by the registry and the release of this instance.
    function _setupServices(IAuthorization authorization)
        internal
    {
        ObjectType[] memory serviceDomains = authorization.getServiceDomains();

        for(uint256 i = 0; i < serviceDomains.length; i++) {
            ObjectType serviceDomain = serviceDomains[i];
            RoleId serviceRoleId = authorization.getServiceRole(serviceDomain);
            address service = _registry.getServiceAddress(serviceDomain, getRelease());

            _grantRoleToAccount(
                serviceRoleId,
                service);
        }
    }


    function _createInstanceTargets(string memory instanceTargetName)
        internal
    {
        _createInstanceTarget(address(_instance), instanceTargetName); 
        _createInstanceTarget(address(this), INSTANCE_ADMIN_TARGET_NAME); 
        _createInstanceTarget(address(_instance.getInstanceStore()), INSTANCE_STORE_TARGET_NAME); 
        _createInstanceTarget(address(_instance.getProductStore()), PRODUCT_STORE_TARGET_NAME); 
        _createInstanceTarget(address(_instance.getBundleSet()), BUNDLE_SET_TARGET_NAME); 
        _createInstanceTarget(address(_instance.getRiskSet()), RISK_SET_TARGET_NAME); 
    }


    function _createInstanceTarget(address target, string memory name) internal {
        _createTarget(target, name, TargetType.Instance, true); 
    }

    /// @dev Initializes the authorization for the specified component.
    /// Important: The component MUST be registered.
    function initializeComponentAuthorization(
        address componentAddress,
        ObjectType expectedType
    )
        external
        restricted()
    {
        IAuthorization authorization = AccessAdminLib.checkComponentInitialization(
            this, _authorization, componentAddress, expectedType);

        // effects
        _createRoles(authorization);
        _createTarget(componentAddress, authorization.getMainTargetName(), TargetType.Component, true);
        _createTargetAuthorizations(authorization);
    }


    /// @dev Creates a custom role.
    function createRole(
        string memory name,
        RoleId adminRoleId,
        uint32 maxMemberCount
    )
        external
        restricted()
        returns (RoleId roleId)
    {
        // create roleId
        roleId = AccessAdminLib.getCustomRoleId(_customRoleIdNext++);

        // create role
        _createRole(
            roleId, 
            AccessAdminLib.roleInfo(
                adminRoleId, 
                IAccess.TargetType.Custom, 
                maxMemberCount, 
                name),
            true); // revert on existing role
    }


    /// @dev Activtes/pauses the specified role.
    function setRoleActive(RoleId roleId, bool active)
        external
        restricted()
    {
        _setRoleActive(roleId, active);
    }


    /// @dev Grants the provided role to the specified account
    function grantRole(
        RoleId roleId, 
        address account)
        external
        restricted()
    {
        _grantRoleToAccount(roleId, account);
    }


    /// @dev Revokes the provided role from the specified account
    function revokeRole(
        RoleId roleId, 
        address account)
        external
        restricted()
    {
        _revokeRoleFromAccount(roleId, account);
    }


    /// @dev Create a new contract target.
    /// The target needs to be an access managed contract.
    function createTarget(
        address target, 
        string memory name
    )
        external
        restricted()
        returns (RoleId contractRoleId)
    {
        return _createTarget(
            target, 
            name, 
            TargetType.Contract,
            true); // check authority matches
    }


    /// @dev Add function authorizations for the specified component or custom target.
    function authorizeFunctions(
        address target,
        RoleId roleId,
        IAccess.FunctionInfo[] memory functions
    )
        external
        restricted()
    {
        _authorizeTargetFunctions(target, roleId, functions, true, true);
    }


    /// @dev Removes function authorizations for the specified component or custom target.
    function unauthorizeFunctions(
        address target,
        IAccess.FunctionInfo[] memory functions
    )
        external
        restricted()
    {
        _authorizeTargetFunctions(target, ADMIN_ROLE(), functions, true, false);
    }


    /// @dev locks the instance and all its releated targets including component and custom targets.
    function setInstanceLocked(bool locked)
        external
        // not restricted(): need to operate on locked instances to unlock instance
        onlyInstanceService()
    {
        AccessManagerCloneable accessManager = AccessManagerCloneable(authority());
        accessManager.setLocked(locked);
    }


    function setTargetLocked(address target, bool locked) 
        external 
        // not restricted(): need to operate on locked instances to unlock instance
        onlyInstanceService()
    {
        _setTargetLocked(target, locked);
    }


    function setContractLocked(address target, bool locked) 
        external 
        restricted() // component service
    {
        _setTargetLocked(target, locked);
    }


    /// @dev Returns the instance authorization specification used to set up this instance admin.
    function getInstanceAuthorization()
        external
        view
        returns (IAuthorization instanceAuthorizaion)
    {
        return _authorization;
    }

    // ------------------- Internal functions ------------------- //


    function _createTargetAuthorizations(IAuthorization authorization)
        internal
    {
        Str[] memory targets = authorization.getTargets();
        Str target;

        for(uint256 i = 0; i < targets.length; i++) {
            target = targets[i];
            RoleId[] memory authorizedRoles = authorization.getAuthorizedRoles(target);

            for(uint256 j = 0; j < authorizedRoles.length; j++) {
                _authorizeFunctions(authorization, target, authorizedRoles[j]);
            }
        }
    }
}

File 61 of 130 : InstanceReader.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

import {IAccess} from "../authorization/IAccess.sol";
import {IBaseStore} from "./IBaseStore.sol";
import {IBundle} from "../instance/module/IBundle.sol";
import {IComponents} from "../instance/module/IComponents.sol";
import {IDistribution} from "../instance/module/IDistribution.sol";
import {IDistributionService} from "../distribution/IDistributionService.sol";
import {IInstance} from "./IInstance.sol";
import {IOracle} from "../oracle/IOracle.sol";
import {IPolicy} from "../instance/module/IPolicy.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {IRisk} from "../instance/module/IRisk.sol";

import {AccessAdminLib} from "../authorization/AccessAdminLib.sol";
import {Amount} from "../type/Amount.sol";
import {BundleSet} from "./BundleSet.sol";
import {BUNDLE, COMPONENT, DISTRIBUTION, PREMIUM, POLICY} from "../type/ObjectType.sol";
import {ClaimId, ClaimIdLib} from "../type/ClaimId.sol";
import {DistributorType} from "../type/DistributorType.sol";
import {InstanceAdmin} from "./InstanceAdmin.sol";
import {InstanceStore} from "./InstanceStore.sol";
import {Key32} from "../type/Key32.sol";
import {NftId} from "../type/NftId.sol";
import {PayoutId, PayoutIdLib} from "../type/PayoutId.sol";
import {PolicyServiceLib} from "../product/PolicyServiceLib.sol";
import {ProductStore} from "./ProductStore.sol";
import {ReferralId, ReferralStatus, ReferralLib} from "../type/Referral.sol";
import {RequestId} from "../type/RequestId.sol";
import {RiskId} from "../type/RiskId.sol";
import {RiskSet} from "./RiskSet.sol";
import {RoleId, INSTANCE_OWNER_ROLE} from "../type/RoleId.sol";
import {StateId} from "../type/StateId.sol";
import {Str, StrLib} from "../type/String.sol";
import {TokenHandler} from "../shared/TokenHandler.sol";
import {UFixed, UFixedLib} from "../type/UFixed.sol";


/// @dev Central reader contract for a specific instance.
/// Provides reading functions for all instance data and related component data.
contract InstanceReader {

    error ErrorInstanceReaderAlreadyInitialized();
    error ErrorInstanceReaderInstanceAddressZero();

    bool private _initialized = false;

    IRegistry internal _registry;
    IInstance internal _instance;
    InstanceAdmin internal _instanceAdmin;

    InstanceStore internal _store;
    ProductStore internal _productStore;
    BundleSet internal _bundleSet;
    RiskSet internal _riskSet;
    IDistributionService internal _distributionService;

    /// @dev This initializer needs to be called from the instance itself.
    function initialize() public {
        if(_initialized) {
            revert ErrorInstanceReaderAlreadyInitialized();
        }

        initializeWithInstance(msg.sender);
    }


    /// @dev Initializer to upgrade instance reader via instance service
    function initializeWithInstance(address instanceAddress)
        public
    {
        if(_initialized) {
            revert ErrorInstanceReaderAlreadyInitialized();
        }

        _initialized = true;
        _instance = IInstance(instanceAddress);
        _instanceAdmin = _instance.getInstanceAdmin();
        _registry = _instance.getRegistry();

        _store = _instance.getInstanceStore();
        _productStore = _instance.getProductStore();
        _bundleSet = _instance.getBundleSet();
        _riskSet = _instance.getRiskSet();
        _distributionService = IDistributionService(_registry.getServiceAddress(DISTRIBUTION(), _instance.getRelease()));
    }


    //--- instance functions ---------------------------------------------------------//

    /// @dev Returns the registry this instance is registered in.
    function getRegistry() public view returns (IRegistry registry) {
        return _registry;
    }


    /// @dev Returns the instance NFT ID.
    function getInstanceNftId() public view returns (NftId instanceNftid) {
        return _registry.getNftIdForAddress(address(_instance));
    }


    /// @dev Returns the instance contract.
    function getInstance() public view returns (IInstance instance) {
        return _instance;
    }


    //--- component functions ------------------------------------------------------//


    /// @dev Returns the component info for the given component NFT ID.
    function getComponentInfo(NftId componentNftId) public view returns (IComponents.ComponentInfo memory info) {
        return _store.getComponentInfo(componentNftId);
    }


    /// @dev Returns the registered token for the given component NFT ID.
    function getToken(NftId componentNftId) public view returns (IERC20Metadata token) {
        TokenHandler tokenHandler = getTokenHandler(componentNftId);
        if (address(tokenHandler) != address(0)) { return tokenHandler.TOKEN(); }
    }


    /// @dev Returns the current wallet address for the given component NFT ID.
    /// The wallet address is either the component's own address or any other wallet address specified by the component owner.
    /// The wallet holds the component's funds. Tokens collected by the component are transferred to the wallet and 
    /// Tokens distributed from the component are transferred from this wallet.
    function getWallet(NftId componentNftId) public view returns (address wallet) {
        IComponents.ComponentInfo memory info = getComponentInfo(componentNftId);
        if (address(info.tokenHandler) != address(0)) { 
            return info.tokenHandler.getWallet();
        }
    }


    /// @dev Returns the token handler for the given component NFT ID.
    /// The token handler manages all transfers from/to the component's wallet.
    /// To allow a component to collect funds from an account, it has to create a corresponding allowance from the
    /// account to the address of the component's token handler.
    function getTokenHandler(NftId componentNftId) public view returns (TokenHandler tokenHandler) {
        IComponents.ComponentInfo memory info = _store.getComponentInfo(componentNftId);
        if(address(info.tokenHandler) != address(0)) {
            return info.tokenHandler;
        }
    }


    /// @dev Returns the current token balance amount for the given component NFT ID.
    /// The balance amount includes the fee amount.
    function getBalanceAmount(NftId targetNftId) external view returns (Amount) { 
        return _store.getBalanceAmount(targetNftId);
    }


    /// @dev Returns the current fee amount for the given NFT ID.
    /// The target NFT ID may reference a component, a distributor or a bundle.
    function getFeeAmount(NftId targetNftId) external view returns (Amount) { 
        return _store.getFeeAmount(targetNftId);
    }


    /// @dev Returns the currently locked amount for the given NFT ID.
    /// The target NFT ID may reference a pool or a bundle.
    function getLockedAmount(NftId targetNftId) external view returns (Amount) { 
        return _store.getLockedAmount(targetNftId);
    }

    //--- product functions ------------------------------------------------------//

    /// @dev Returns the number of registered products.
    function products() public view returns (uint256 productCount) {
        return _instance.products();
    }


    /// @dev Returns th product NFT ID for the given index.
    function getProduct(uint256 idx) public view returns (NftId productNftId) {
        return _instance.getProduct(idx);
    }


    /// @dev Returns the product info for the given product NFT ID.
    function getProductInfo(NftId productNftId) public view returns (IComponents.ProductInfo memory info) {
        return _productStore.getProductInfo(productNftId);
    }


    /// @dev Returns the current fee settings for the given product NFT ID.
    function getFeeInfo(NftId productNftId) public view returns (IComponents.FeeInfo memory feeInfo) {
        return _productStore.getFeeInfo(productNftId);
    }

    //--- risk functions ---------------------------------------------------------//

    /// @dev Returns the total number of registered risks for the specified product.
    function risks(NftId productNftId) public view returns (uint256 riskCount) {
        return _riskSet.risks(productNftId);
    }


    /// @dev Returns the number of active risks for the specified product.
    function activeRisks(NftId productNftId) public view returns (uint256 activeRiskCount) {
        return _riskSet.activeRisks(productNftId);
    }


    /// @dev Returns the risk ID for the given product NFT ID and (registered) risk index.
    function getRiskId(NftId productNftId, uint256 idx) public view returns (RiskId riskId) {
        return _riskSet.getRiskId(productNftId, idx);
    }


    /// @dev Returns the active risk ID for the given product NFT ID and (active) risk index.
    function getActiveRiskId(NftId productNftId, uint256 idx) public view returns (RiskId riskId) {
        return _riskSet.getActiveRiskId(productNftId, idx);
    }


    /// @dev Returns true if the specified risk exists for the given product NFT ID.
    function isProductRisk(NftId productNftId, RiskId riskId) public view returns (bool exists) {
        return _riskSet.hasRisk(productNftId, riskId);
    }


    /// @dev Returns the risk info for the given risk ID.
    function getRiskInfo(RiskId riskId) public view returns (IRisk.RiskInfo memory info) {
        return _productStore.getRiskInfo(riskId);
    }


    /// @dev Returns the risk state for the given risk ID.
    function getRiskState(RiskId riskId) public view returns (StateId stateId) {
        return _productStore.getState(riskId.toKey32());
    }


    //--- policy functions -------------------------------------------------------//

    /// @dev Returns the number of linked policies for the given risk ID.
    function policiesForRisk(RiskId riskId) public view returns (uint256 linkedPolicies) {
        return _riskSet.linkedPolicies(riskId);
    }


    /// @dev Returns the linked policy NFT ID for the given risk ID and index.
    function getPolicyForRisk(RiskId riskId, uint256 idx) public view returns (NftId linkedPolicyNftId) {
        return _riskSet.getLinkedPolicyNftId(riskId, idx);
    }

    /// @dev Returns the number of linked policies for the given bundle NFT ID.
    function policiesForBundle(NftId bundleNftId) public view returns (uint256 linkedPolicies) {
        return _bundleSet.activePolicies(bundleNftId);
    }


    /// @dev Returns the linked policy NFT ID for the given risk ID and index.
    function getPolicyForBundle(NftId bundleNftId, uint256 idx) public view returns (NftId linkedPolicyNftId) {
        return _bundleSet.getActivePolicy(bundleNftId, idx);
    }


    /// @dev Returns the info for the given policy NFT ID.
    function getPolicyInfo(NftId policyNftId) public view returns (IPolicy.PolicyInfo memory info) {
        return _productStore.getPolicyInfo(policyNftId);
    }


    /// @dev Returns the state for the given policy NFT ID.
    function getPolicyState(NftId policyNftId) public view returns (StateId state) {
        return _productStore.getState(_toPolicyKey(policyNftId));
    }


    /// @dev Returns true iff policy is active.
    function policyIsActive(NftId policyNftId) public view returns (bool isCloseable) {
        return PolicyServiceLib.policyIsActive(this, policyNftId);
    }

    //--- claim functions -------------------------------------------------------//

    /// @dev Returns the number of claims for the given policy NFT ID.
    function claims(NftId policyNftId) public view returns (uint16 claimCount) {
        return getPolicyInfo(policyNftId).claimsCount;
    }


    /// @dev Returns the claim ID for the given policy NFT ID and index.
    function getClaimId(uint256 idx) public pure returns (ClaimId claimId) {
        return ClaimIdLib.toClaimId(idx + 1);
    }


    /// @dev Returns the claim info for the given policy NFT ID and claim ID.
    function getClaimInfo(NftId policyNftId, ClaimId claimId) public view returns (IPolicy.ClaimInfo memory info) {
        return _productStore.getClaimInfo(policyNftId, claimId);
    }


    /// @dev Returns the current claim state for the given policy NFT ID and claim ID.
    function getClaimState(NftId policyNftId, ClaimId claimId) public view returns (StateId state) {
        return _productStore.getState(claimId.toKey32(policyNftId));
    }


    /// @dev Returns the remaining claimable amount for the given policy NFT ID.
    /// The remaining claimable amount is the difference between the sum insured amount and total approved claim amounts so far.
    function getRemainingClaimableAmount(NftId policyNftId)
        public view returns (Amount remainingClaimableAmount) {
        IPolicy.PolicyInfo memory info = getPolicyInfo(policyNftId);
        return info.sumInsuredAmount - info.claimAmount;
    }

    //--- payout functions -------------------------------------------------------//

    /// @dev Returns the number of payouts for the given policy NFT ID and claim ID.
    function payouts(NftId policyNftId, ClaimId claimId) public view returns (uint24 payoutCount) {
        return getClaimInfo(policyNftId, claimId).payoutsCount;
    }


    /// @dev Returns the payout ID for the given claim ID and index.
    function getPayoutId(ClaimId claimId, uint24 idx) public pure returns (PayoutId payoutId) {
        return PayoutIdLib.toPayoutId(claimId, idx + 1);
    }


    /// @dev Returns the payout info for the given policy NFT ID and payout ID.
    function getPayoutInfo(NftId policyNftId, PayoutId payoutId) public view returns (IPolicy.PayoutInfo memory info) {
        return _productStore.getPayoutInfo(policyNftId, payoutId);
    }


    /// @dev Returns the payout state for the given policy NFT ID and payout ID.
    function getPayoutState(NftId policyNftId, PayoutId payoutId) public view returns (StateId state) {
        return _productStore.getState(payoutId.toKey32(policyNftId));
    }

    //--- premium functions -------------------------------------------------------//

    /// @dev Returns the premium info for the given policy NFT ID.
    function getPremiumInfo(NftId policyNftId) public view returns (IPolicy.PremiumInfo memory info) {
        return _productStore.getPremiumInfo(policyNftId);
    }


    /// @dev Returns the premium state for the given policy NFT ID.
    function getPremiumState(NftId policyNftId) public view returns (StateId state) {
        return _productStore.getState(_toPremiumKey(policyNftId));
    }

    //--- oracle functions ---------------------------------------------------------//

    /// @dev Returns the request info for the given oracle request ID.
    function getRequestInfo(RequestId requestId) public view returns (IOracle.RequestInfo memory requestInfo) {
        return _store.getRequestInfo(requestId);
    }

    /// @dev Returns the request info for the given oracle request ID.
    function getRequestState(RequestId requestId) public view returns (StateId state) {
        return getState(requestId.toKey32());
    }

    //--- pool functions -----------------------------------------------------------//

    /// @dev Returns the pool info for the given pool NFT ID.
    function getPoolInfo(NftId poolNftId) public view returns (IComponents.PoolInfo memory info) {
        return _store.getPoolInfo(poolNftId);
    }

    //--- bundle functions -------------------------------------------------------//

    /// @dev Returns the total number of registered bundles for the given pool.
    function bundles(NftId poolNftId) public view returns (uint256 bundleCount) {
        return _bundleSet.bundles(poolNftId);
    }


    /// @dev Returns the number of active bundles for the given pool.
    function activeBundles(NftId poolNftId) public view returns (uint256 bundleCount) {
        return _bundleSet.activeBundles(poolNftId);
    }


    /// @dev Returns the bunde NFT ID for the given pool and index.
    function getBundleNftId(NftId poolNftId, uint256 idx) public view returns (NftId bundleNftId) {
        return _bundleSet.getBundleNftId(poolNftId, idx);
    }


    /// @dev Returns the active bunde NFT ID for the given pool and index.
    function getActiveBundleNftId(NftId poolNftId, uint256 idx) public view returns (NftId bundleNftId) {
        return _bundleSet.getActiveBundleNftId(poolNftId, idx);
    }


    /// @dev Returns the bundle info for the given bundle NFT ID.
    function getBundleInfo(NftId bundleNftId) public view  returns (IBundle.BundleInfo memory info) {
        return _store.getBundleInfo(bundleNftId);
    }


    /// @dev Returns the bundle state for the given bundle NFT ID.
    function getBundleState(NftId bundleNftId) public view returns (StateId state) {
        return getState(_toBundleKey(bundleNftId));
    }

    //--- distribution functions -------------------------------------------------------//

    function getDistributorTypeInfo(DistributorType distributorType) public view returns (IDistribution.DistributorTypeInfo memory info) {
        return _store.getDistributorTypeInfo(distributorType);
    }


    function getDistributorInfo(NftId distributorNftId) public view returns (IDistribution.DistributorInfo memory info) {
        return _store.getDistributorInfo(distributorNftId);
    }


    //--- referral functions -------------------------------------------------------//

    function toReferralId(NftId distributionNftId, string memory referralCode) public pure returns (ReferralId referralId) {
        return ReferralLib.toReferralId(distributionNftId, referralCode);      
    }


    function isReferralValid(NftId distributionNftId, ReferralId referralId) external view returns (bool isValid) {
        return _distributionService.referralIsValid(distributionNftId, referralId);
    }


    function getReferralInfo(ReferralId referralId) public view returns (IDistribution.ReferralInfo memory info) {
        return _store.getReferralInfo(referralId);
    }


    function getDiscountPercentage(ReferralId referralId)
        public
        view
        returns (
            UFixed discountPercentage, 
            ReferralStatus status
        )
    {
        return IDistributionService(
            _registry.getServiceAddress(
                DISTRIBUTION(),
                _instance.getRelease())).getDiscountPercentage(
                    this, // instance reader
                    referralId);
    }

    //--- authorization functions -------------------------------------------------------//

    /// @dev Returns the number of defined roles.
    function roles() public view returns (uint256) {
        return _instanceAdmin.roles();
    }


    /// @dev Returns the role ID for the given index.
    function getRoleId(uint256 idx) public view returns (RoleId roleId) {
        return _instanceAdmin.getRoleId(uint64(idx));
    }


    /// @dev Returns the role ID for the given index.
    function getRoleForName(string memory name) public view returns (RoleId roleId, bool exists) {
        return _instanceAdmin.getRoleForName(name);
    }


    /// @dev Returns the role ID for the instance owner role.
    /// This role may be used as a "root" admin role for other custom roles defined for this instance.
    function getInstanceOwnerRole() public pure returns (RoleId roleId) {
        return INSTANCE_OWNER_ROLE();
    }


    /// @dev Returns the role info for the given role ID.
    function getRoleInfo(RoleId roleId) public view returns (IAccess.RoleInfo memory roleInfo) { 
        return _instanceAdmin.getRoleInfo(roleId);
    }


    /// @dev Returns true iff the provided role ID is defined for this instance.
    function roleExists(RoleId roleId) public view returns (bool exists) {
        return _instanceAdmin.roleExists(roleId);
    }


    /// @dev Returns true iff the provided role ID represents a custom role ID.
    function isRoleCustom(RoleId roleId) public view returns (bool isCustom) {
        return _instanceAdmin.isRoleCustom(roleId);
    }


    /// @dev Returns true iff the provided role ID is active.
    function isRoleActive(RoleId roleId) public view returns (bool isActive) {
        return _instanceAdmin.isRoleActive(roleId);
    }


    /// @dev Returns the number of members (accounts) for the given role ID.
    function roleMembers(RoleId roleId) public view returns (uint256 numberOfMembers) {
        return _instanceAdmin.roleMembers(roleId);
    }


    /// @dev Returns the member (account address) for the given role ID and index.
    function getRoleMember(RoleId roleId, uint256 idx) public view returns (address account) {
        return _instanceAdmin.getRoleMember(roleId, idx);
    }


    /// @dev Returns true iff the given account is a member of the specified role ID.
    function isRoleMember(RoleId roleId, address account) public view returns (bool isMember) {
        return _instanceAdmin.isRoleMember(roleId, account);
    }


    /// @dev Returns true iff the given account is an admin of the specified role ID.
    /// Role admins may grant and revoke the role to other accounts.
    function isRoleAdmin(RoleId roleId, address account) public view returns (bool isMember) {
        return _instanceAdmin.isRoleAdmin(roleId, account);
    }


    /// @dev Returns the number of targets (contracts) defined for this instance.
    function targets() public view returns (uint256 targetCount) {
        return _instanceAdmin.targets();
    }


    /// @dev Returns the target address for the given index.
    function getTargetAddress(uint256 idx) public view returns (address target) {
        return _instanceAdmin.getTargetAddress(idx);
    }


    /// @dev Returns the target info for the given target address.
    function getTargetInfo(address target) public view returns (IAccess.TargetInfo memory targetInfo) {
        return _instanceAdmin.getTargetInfo(target);
    }


    /// @dev Returns true iff the given target is defined for this instance.
    function targetExists(address target) public view returns (bool exists) {
        return _instanceAdmin.targetExists(target);
    }


    /// @dev Returns true iff the given target is locked.
    function isLocked(address target) public view returns (bool) {
        return _instanceAdmin.isTargetLocked(target);
    }


    /// @dev Returns the number of authorized functions for the given target.
    function authorizedFunctions(address target) external view returns (uint256 numberOfFunctions) {
        return _instanceAdmin.authorizedFunctions(target);
    }


    /// @dev Returns the authorized function info for the given target and index.
    function getAuthorizedFunction(address target, uint256 idx) external view returns (IAccess.FunctionInfo memory func, RoleId roleId) {
        return _instanceAdmin.getAuthorizedFunction(target, idx);
    }


    /// @dev Returns a function info for the given function signature and function name.
    /// The function signature must not be zero and the function name must not be empty.
    function toFunction(bytes4 signature, string memory name) public view returns (IAccess.FunctionInfo memory) {
        return AccessAdminLib.toFunction(signature, name);
    }

    //--- low level function ----------------------------------------------------//

    function getInstanceAdmin() external view returns (InstanceAdmin instanceAdmin) {
        return _instanceAdmin;
    }

    function getBundleSet() external view returns (BundleSet bundleSet) {
        return _bundleSet;
    }

    function getRiskSet() external view returns (RiskSet riskSet) {
        return _riskSet;
    }


    function getMetadata(Key32 key) public view returns (IBaseStore.Metadata memory metadata) {
        return _store.getMetadata(key);
    }


    function getState(Key32 key) public view returns (StateId state) {
        return _store.getState(key);
    }


    function toInt(UFixed value) public pure returns (uint256) {
        return UFixedLib.toInt(value);
    }


    function toString(Str str) public pure returns (string memory) {
        return StrLib.toString(str);
    }


    function toUFixed(uint256 value, int8 exp) public pure returns (UFixed) {
        return UFixedLib.toUFixed(value, exp);
    }

    //--- internal functions ----------------------------------------------------//


    function _toPolicyKey(NftId policyNftId) internal pure returns (Key32) { 
        return policyNftId.toKey32(POLICY());
    }


    function _toPremiumKey(NftId policyNftId) internal pure returns (Key32) { 
        return policyNftId.toKey32(PREMIUM());
    }


    function _toBundleKey(NftId poolNftId) internal pure returns (Key32) { 
        return poolNftId.toKey32(BUNDLE());
    }


    function _toComponentKey(NftId componentNftId) internal pure returns (Key32) { 
        return componentNftId.toKey32(COMPONENT());
    }
}

File 62 of 130 : InstanceStore.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";

import {IBundle} from "./module/IBundle.sol";
import {IComponents} from "./module/IComponents.sol";
import {IDistribution} from "./module/IDistribution.sol";
import {IInstance} from "./IInstance.sol";
import {IOracle} from "../oracle/IOracle.sol";

import {Amount} from "../type/Amount.sol";
import {BaseStore} from "./BaseStore.sol";
import {Blocknumber} from "../type/Blocknumber.sol";
import {Key32} from "../type/Key32.sol";
import {NftId} from "../type/NftId.sol";
import {ClaimId} from "../type/ClaimId.sol";
import {ObjectType, BUNDLE, POOL, COMPONENT, DISTRIBUTOR} from "../type/ObjectType.sol";
import {RequestId} from "../type/RequestId.sol";
import {StateId} from "../type/StateId.sol";
import {ReferralId} from "../type/Referral.sol";
import {DistributorType} from "../type/DistributorType.sol";
import {PayoutId} from "../type/PayoutId.sol";
import {BalanceStore} from "./base/BalanceStore.sol";
import {ObjectCounter} from "./base/ObjectCounter.sol";
import {ObjectLifecycle} from "./base/ObjectLifecycle.sol";


contract InstanceStore is
    AccessManagedUpgradeable, 
    BalanceStore,
    ObjectCounter,
    ObjectLifecycle,
    BaseStore
{

    event LogProductStoreComponentInfoCreated(NftId componentNftId, StateId state, address createdby, address txOrigin);
    event LogProductStoreComponentInfoUpdated(NftId componentNftId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
    event LogProductStorePoolInfoCreated(NftId poolNftId, StateId state, address createdBy, address txOrigin);
    event LogProductStorePoolInfoUpdated(NftId poolNftId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
    event LogProductStoreDistributorTypeInfoCreated(DistributorType distributorType, StateId state, address createdBy, address txOrigin);
    event LogProductStoreDistributorTypeInfoUpdated(DistributorType distributorType, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
    event LogProductStoreDistributorInfoCreated(NftId distributorNftId, StateId state, address createdBy, address txOrigin);
    event LogProductStoreDistributorInfoUpdated(NftId distributorNftId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
    event LogProductStoreReferralInfoCreated(ReferralId referralId, StateId state, address createdBy, address txOrigin);
    event LogProductStoreReferralInfoUpdated(ReferralId referralId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
    event LogProductStoreBundleInfoCreated(NftId bundleNftId, StateId state, address createdBy, address txOrigin);
    event LogProductStoreBundleInfoUpdated(NftId bundleNftId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
    event LogProductStoreRequestInfoCreated(RequestId requestId, StateId state, address createdBy, address txOrigin);
    event LogProductStoreRequestInfoUpdated(RequestId requestId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);


    mapping(Key32 => IComponents.ComponentInfo) private _components;
    mapping(Key32 => IComponents.PoolInfo) private _pools;
    mapping(Key32 => IDistribution.DistributorTypeInfo) private _distributorTypes;
    mapping(Key32 => IDistribution.DistributorInfo) private _distributors;
    mapping(Key32 => IDistribution.ReferralInfo) private _referrals;
    mapping(Key32 => IBundle.BundleInfo) private _bundles;
    mapping(Key32 => IOracle.RequestInfo) private _requests;


    /// @dev This initializer needs to be called from the instance itself.
    function initialize()
        public 
        initializer()
    {
        address instance = msg.sender;
        address authority = IInstance(instance).authority();

        __AccessManaged_init(authority);
        // double initialization, safe
        _initializeLifecycle();
    }

    //--- Component ---------------------------------------------------------//

    function createComponent(
        NftId componentNftId, 
        IComponents.ComponentInfo memory componentInfo
    )
        external 
        restricted()
    {
        _registerBalanceTarget(componentNftId);
        // _create(_toNftKey32(componentNftId, COMPONENT()), abi.encode(componentInfo));
        Key32 key = _toNftKey32(componentNftId, COMPONENT());
        _createMetadata(key);
        _components[key] = componentInfo;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreComponentInfoCreated(componentNftId, getState(key), msg.sender, tx.origin);
    }

    function updateComponent(
        NftId componentNftId, 
        IComponents.ComponentInfo memory componentInfo,
        StateId newState
    )
        external 
        restricted()
    {
        // _update(_toNftKey32(componentNftId, COMPONENT()), abi.encode(componentInfo), newState);
        Key32 key = _toNftKey32(componentNftId, COMPONENT());
        (Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
        _components[key] = componentInfo;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreComponentInfoUpdated(componentNftId, oldState, newState, msg.sender, tx.origin, updatedIn);
    }

    function getComponentInfo(NftId componentNftId) external view returns (IComponents.ComponentInfo memory componentInfo) {
        return _components[_toNftKey32(componentNftId, COMPONENT())];
    }

    //--- Pool --------------------------------------------------------------//

    function createPool(
        NftId poolNftId, 
        IComponents.PoolInfo memory info
    )
        external 
        restricted()
    {
        Key32 key = _toNftKey32(poolNftId, POOL());
        _createMetadata(key);
        _pools[key] = info;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStorePoolInfoCreated(poolNftId, getState(key), msg.sender, tx.origin);
    }

    function updatePool(NftId poolNftId, IComponents.PoolInfo memory info, StateId newState) external restricted() {
        Key32 key = _toNftKey32(poolNftId, POOL());
        (Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
        _pools[key] = info;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStorePoolInfoUpdated(poolNftId, oldState, newState, msg.sender, tx.origin, updatedIn);
    }

    function getPoolInfo(NftId poolNftId) external view returns (IComponents.PoolInfo memory info) {
        return _pools[_toNftKey32(poolNftId, POOL())];
    }

    //--- DistributorType ---------------------------------------------------//
    function createDistributorType(DistributorType distributorType, IDistribution.DistributorTypeInfo memory info) external restricted() {
        Key32 key = distributorType.toKey32();
        _createMetadata(key);
        _distributorTypes[key] = info;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreDistributorTypeInfoCreated(distributorType, getState(key), msg.sender, tx.origin);
    }

    function updateDistributorType(DistributorType distributorType, IDistribution.DistributorTypeInfo memory info, StateId newState) external restricted() {
        Key32 key = distributorType.toKey32();
        (Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
        _distributorTypes[key] = info;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreDistributorTypeInfoUpdated(distributorType, oldState, newState, msg.sender, tx.origin, updatedIn);
    }

    function updateDistributorTypeState(DistributorType distributorType, StateId newState) external restricted() {
        Key32 key = distributorType.toKey32();
        (Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreDistributorTypeInfoUpdated(distributorType, oldState, newState, msg.sender, tx.origin, updatedIn);
    }

    function getDistributorTypeInfo(DistributorType distributorType) external view returns (IDistribution.DistributorTypeInfo memory info) {
        return _distributorTypes[distributorType.toKey32()];
    }

    //--- Distributor -------------------------------------------------------//
    function createDistributor(NftId distributorNftId, IDistribution.DistributorInfo memory info) external restricted() {
        _registerBalanceTarget(distributorNftId);
        Key32 key = _toNftKey32(distributorNftId, DISTRIBUTOR());
        _createMetadata(key);
        _distributors[key] = info;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreDistributorInfoCreated(distributorNftId, getState(key), msg.sender, tx.origin);
    }

    function updateDistributor(NftId distributorNftId, IDistribution.DistributorInfo memory info, StateId newState) external restricted() {
        Key32 key = _toNftKey32(distributorNftId, DISTRIBUTOR());
        (Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
        _distributors[key] = info;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreDistributorInfoUpdated(distributorNftId, oldState, newState, msg.sender, tx.origin, updatedIn);
    }

    function updateDistributorState(NftId distributorNftId, StateId newState) external restricted() {
        Key32 key = _toNftKey32(distributorNftId, DISTRIBUTOR());
        (Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreDistributorInfoUpdated(distributorNftId, oldState, newState, msg.sender, tx.origin, updatedIn);
    }

    function getDistributorInfo(NftId distributorNftId) external view returns (IDistribution.DistributorInfo memory info) {
        return _distributors[_toNftKey32(distributorNftId, DISTRIBUTOR())];
    }

    //--- Referral ----------------------------------------------------------//
    function createReferral(ReferralId referralId, IDistribution.ReferralInfo memory referralInfo) external restricted() {
        Key32 key = referralId.toKey32();
        _createMetadata(key);
        _referrals[key] = referralInfo;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreReferralInfoCreated(referralId, getState(key), msg.sender, tx.origin);
    }

    function updateReferral(ReferralId referralId, IDistribution.ReferralInfo memory referralInfo, StateId newState) external restricted() {
        Key32 key = referralId.toKey32();
        (Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
        _referrals[key] = referralInfo;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreReferralInfoUpdated(referralId, oldState, newState, msg.sender, tx.origin, updatedIn);
    }

    function updateReferralState(ReferralId referralId, StateId newState) external restricted() {
        Key32 key = referralId.toKey32();
        (Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreReferralInfoUpdated(referralId, oldState, newState, msg.sender, tx.origin, updatedIn);
    }

    function getReferralInfo(ReferralId referralId) external view returns (IDistribution.ReferralInfo memory referralInfo) {
        return _referrals[referralId.toKey32()];
    }

    //--- Bundle ------------------------------------------------------------//
    function createBundle(NftId bundleNftId, IBundle.BundleInfo memory bundle) external restricted() {
        _registerBalanceTarget(bundleNftId);
        Key32 key = _toNftKey32(bundleNftId, BUNDLE());
        _createMetadata(key);
        _bundles[key] = bundle;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreBundleInfoCreated(bundleNftId, getState(key), msg.sender, tx.origin);
    }

    function updateBundle(NftId bundleNftId, IBundle.BundleInfo memory bundle, StateId newState) external restricted() {
        Key32 key = _toNftKey32(bundleNftId, BUNDLE());
        (Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
        _bundles[key] = bundle;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreBundleInfoUpdated(bundleNftId, oldState, newState, msg.sender, tx.origin, updatedIn);
    }

    function updateBundleState(NftId bundleNftId, StateId newState) external restricted() {
        Key32 key = _toNftKey32(bundleNftId, BUNDLE());
        (Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreBundleInfoUpdated(bundleNftId, oldState, newState, msg.sender, tx.origin, updatedIn);
    }

    function getBundleInfo(NftId bundleNftId) external view returns (IBundle.BundleInfo memory bundle) {
        return _bundles[_toNftKey32(bundleNftId, BUNDLE())];
    }
    
    //--- Request -----------------------------------------------------------//

    function createRequest(IOracle.RequestInfo memory request) external restricted() returns (RequestId requestId) {
        requestId = _createNextRequestId();
        Key32 key = requestId.toKey32();
        _createMetadata(key);
        _requests[key] = request;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreRequestInfoCreated(requestId, getState(key), msg.sender, tx.origin);
    }

    function updateRequest(RequestId requestId, IOracle.RequestInfo memory request, StateId newState) external restricted() {
        Key32 key = requestId.toKey32();
        (Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
        _requests[key] = request;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreRequestInfoUpdated(requestId, oldState, newState, msg.sender, tx.origin, updatedIn);
    }

    function updateRequestState(RequestId requestId, StateId newState) external restricted() {
        Key32 key = requestId.toKey32();
        (Blocknumber updatedIn, StateId oldState) = _updateState(key, newState);
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreRequestInfoUpdated(requestId, oldState, newState, msg.sender, tx.origin, updatedIn);
    }

    function getRequestInfo(RequestId requestId) external view returns (IOracle.RequestInfo memory request) {
        return _requests[requestId.toKey32()];
    }

    //--- balance and fee management functions ------------------------------//

    function increaseBalance(NftId targetNftId, Amount amount) external restricted() returns (Amount newBalance) {
        return _increaseBalance(targetNftId, amount);
    }

    function decreaseBalance(NftId targetNftId, Amount amount) external restricted() returns (Amount newBalance) {
        return _decreaseBalance(targetNftId, amount);
    }

    function increaseFees(NftId targetNftId, Amount amount) external restricted() returns (Amount newFeeBalance) {
        return _increaseFees(targetNftId, amount);
    }

    function decreaseFees(NftId targetNftId, Amount amount) external restricted() returns (Amount newFeeBalance) {
        return _decreaseFees(targetNftId, amount);
    }

    function increaseLocked(NftId targetNftId, Amount amount) external restricted() returns (Amount newBalance) {
        return _increaseLocked(targetNftId, amount);
    }

    function decreaseLocked(NftId targetNftId, Amount amount) external restricted() returns (Amount newBalance) {
        return _decreaseLocked(targetNftId, amount);
    }

    //--- internal view/pure functions --------------------------------------//
    function _toNftKey32(NftId nftId, ObjectType objectType) private pure returns (Key32) {
        return nftId.toKey32(objectType);
    }

    function _toClaimKey32(NftId policyNftId, ClaimId claimId) private pure returns (Key32) {
        return claimId.toKey32(policyNftId);
    }

    function _toPayoutKey32(NftId policyNftId, PayoutId payoutId) private pure returns (Key32) {
        return payoutId.toKey32(policyNftId);
    }
}

File 63 of 130 : IBundle.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {NftId} from "../../type/NftId.sol";
import {Fee} from "../../type/Fee.sol";
import {Timestamp} from "../../type/Timestamp.sol";

interface IBundle {

    struct BundleInfo {
        // slot 0
        Timestamp activatedAt; 
        Timestamp expiredAt; // no new policies starting with this timestamp
        Timestamp closedAt; // no open policies, locked amount = 0
        NftId poolNftId;
        // slot 1
        Fee fee; // bundle fee on net premium amounts
        // slot 2
        bytes filter; // required conditions for applications to be considered for collateralization by this bundle
    }
}

File 64 of 130 : IComponents.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {Amount} from "../../type/Amount.sol";
import {Fee} from "../../type/Fee.sol";
import {NftId} from "../../type/NftId.sol";
import {TokenHandler} from "../../shared/TokenHandler.sol";
import {UFixed} from "../../type/UFixed.sol";

interface IComponents {

    struct ComponentInfo {
        // slot 0
        string name; // component name (needs to be unique per instance)
        // slot 1
        TokenHandler tokenHandler;
    }

    struct ProductInfo {
        // slot 0
        bool isProcessingFundedClaims; // custom logic to react to pool events for funded claims
        bool isInterceptingPolicyTransfers; // custom logic for policy nft transfers
        bool hasDistribution; // flag to indicate if distribution is enabled
        uint8 expectedNumberOfOracles; // expected number of oracles
        uint8 numberOfOracles; // actual number of oracles
        NftId poolNftId; // mandatory
        NftId distributionNftId; // 0..1 (optional)
        // slot 1
        NftId [] oracleNftId; // 0..n (optional)
    }

    struct FeeInfo {
        // slot 0
        Fee productFee; // product fee on net premium
        // slot 1
        Fee processingFee; // product fee on payout amounts        
        // slot 2
        Fee distributionFee; // distribution fee for sales that do not include commissions
        // slot 3
        Fee minDistributionOwnerFee; // min fee required by distribution owner (not including commissions for distributors)
        // slot 4
        Fee poolFee; // pool fee on net premium
        // slot 5
        Fee stakingFee; // pool fee on staked capital from investor
        // slot 6
        Fee performanceFee; // pool fee on profits from capital investors
    }

    struct PoolInfo {
        // slot 0
        Amount maxBalanceAmount; // max balance amount allowed for pool
        UFixed collateralizationLevel; // factor to calculate collateral for sum insurance (default 100%)
        // slot 1
        UFixed retentionLevel; // amount of collateral held in pool (default 100%)
        bool isInterceptingBundleTransfers; // custom logic for bundle nft transfers
        bool isProcessingConfirmedClaims; // custom logic for claims confirmation
        bool isExternallyManaged; // funding bundles is restricted to book keeping, actual funds may be provided as needed to support payouts
        bool isVerifyingApplications; // underwriting requires the pool component checks/confirms the applications 
    }
}

File 65 of 130 : IDistribution.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {DistributorType} from "../../type/DistributorType.sol";
import {NftId} from "../../type/NftId.sol";
import {Seconds} from "../../type/Seconds.sol";
import {Timestamp} from "../../type/Timestamp.sol";
import {UFixed} from "../../type/UFixed.sol";

interface IDistribution {

    struct DistributorTypeInfo {
        // slot 0
        UFixed minDiscountPercentage;
        NftId distributionNftId;
        // slot 1
        UFixed maxDiscountPercentage;
        uint32 maxReferralCount;
        Seconds maxReferralLifetime;
        bool allowSelfReferrals;
        bool allowRenewals;
        // slot 2
        UFixed commissionPercentage;
        // slot 3
        string name;                
        // slot 4
        bytes data;
    }

    struct DistributorInfo {
        // slot 0
        DistributorType distributorType;
        bool active;
        uint32 numPoliciesSold;
        // slot 1
        bytes data;
    }

    struct ReferralInfo {
        // slot 0
        NftId distributionNftId;
        NftId distributorNftId;
        uint32 maxReferrals;
        uint32 usedReferrals;
        // slot 1
        UFixed discountPercentage;
        Timestamp expiryAt;
        // slot 2
        string referralCode;
        // slot 3
        bytes data;
    }

}

File 66 of 130 : IPolicy.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {Amount} from "../../type/Amount.sol";
import {NftId} from "../../type/NftId.sol";
import {ClaimId} from "../../type/ClaimId.sol";
import {ReferralId} from "../../type/Referral.sol";
import {RiskId} from "../../type/RiskId.sol";
import {Seconds} from "../../type/Seconds.sol";
import {Timestamp} from "../../type/Timestamp.sol";

interface IPolicy {

    struct PremiumInfo {
        // slot 0
        // premium splitting per target wallet
        Amount productFeeAmount;
        Amount distributionFeeAndCommissionAmount;
        // slot 1
        Amount poolPremiumAndFeeAmount;

        // detailed positions
        // this is the net premium calculated by the product 
        Amount netPremiumAmount;
        // slot 2
        // fullPremium = netPremium + all fixed amounts + all variable amounts (excl commission and minDistribtuionOwnerFee variable part)
        Amount fullPremiumAmount;
        // effective premium = fullPremium - discount 
        Amount premiumAmount;
        // slot 3
        Amount productFeeFixAmount;
        Amount poolFeeFixAmount;
        // slot 4
        Amount bundleFeeFixAmount;
        Amount distributionFeeFixAmount;
        // slot 5
        Amount productFeeVarAmount;
        Amount poolFeeVarAmount;
        // slot 6
        Amount bundleFeeVarAmount;
        Amount distributionFeeVarAmount;
        // slot 7
        Amount distributionOwnerFeeFixAmount;
        // this is the remaining amount when the commission and discount are subtracted from the distribution fee variable part (must be at least the minDistributionOwnerFee)
        Amount distributionOwnerFeeVarAmount;
        // slot 8
        // this value is based on distributor type referenced in the referral 
        Amount commissionAmount;
        // this is based on referral used
        Amount discountAmount;
    }

    /// @dev policy data for the full policy lifecycle
    struct PolicyInfo {
        // slot 0
        NftId productNftId;
        NftId bundleNftId;
        RiskId riskId;
        // slot 1
        Amount sumInsuredAmount;
        Amount premiumAmount; // expected premium at application time
        ReferralId referralId;
        // slot 2
        uint16 claimsCount;
        uint16 openClaimsCount;
        Amount claimAmount; // sum of confirmed claim amounts (max = sum insured amount)
        Amount payoutAmount; // sum of payouts (max = sum confirmed claim amountst)
        // slot 3
        Timestamp activatedAt; // time of underwriting
        Seconds lifetime;
        Timestamp expiredAt; // no new claims (activatedAt + lifetime)
        Timestamp closedAt; // no locked capital (or declinedAt)
        // slot 4
        bytes applicationData;
        // slot 5
        bytes processData;
    }

    // claimId neeeds to be encoded policyNftId:claimId combination
    struct ClaimInfo {
        // slot 0
        Amount claimAmount;
        Amount paidAmount;
        Timestamp closedAt; // payment of confirmed claim amount (or declinedAt)
        uint24 payoutsCount;
        // slot 1
        uint24 openPayoutsCount;
        // slot 2
        bytes submissionData; // use case specific claim submission data, no changes after submitting the claim
        // slot 3
        bytes processData; // use case specific data that may include information supporting confirm or decline
    }

    // claimId neeeds to be encoded policyNftId:claimId combination
    struct PayoutInfo {
        // slot 0
        ClaimId claimId;
        Amount amount;
        Timestamp paidAt; // timestamp for actual payout
        // slot 1
        address beneficiary; // for address(0) beneficiary is policy nft owner
        // slot 2
        bytes data; // use case specific supporting data
    }
}

File 67 of 130 : IRisk.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

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

interface IRisk {

    struct RiskInfo {
        // slot 0
        NftId productNftId;
        Timestamp createdAt;
        // slot 1
        bytes data;
    }
}

File 68 of 130 : ProductStore.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {AccessManagedUpgradeable} from "@openzeppelin/contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";

import {IComponents} from "./module/IComponents.sol";
import {IInstance} from "./IInstance.sol";
import {IPolicy} from "./module/IPolicy.sol";
import {IRisk} from "./module/IRisk.sol";

import {BalanceStore} from "./base/BalanceStore.sol";
import {BaseStore} from "./BaseStore.sol";
import {Blocknumber} from "../type/Blocknumber.sol";
import {ClaimId} from "../type/ClaimId.sol";
import {Key32} from "../type/Key32.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectCounter} from "./base/ObjectCounter.sol";
import {ObjectLifecycle} from "./base/ObjectLifecycle.sol";
import {ObjectType, FEE, POLICY, PREMIUM, PRODUCT} from "../type/ObjectType.sol";
import {PayoutId} from "../type/PayoutId.sol";
import {RiskId} from "../type/RiskId.sol";
import {StateId, KEEP_STATE} from "../type/StateId.sol";


contract ProductStore is
    AccessManagedUpgradeable, 
    BalanceStore,
    BaseStore,
    ObjectCounter,
    ObjectLifecycle
{
    event LogProductStoreProductInfoCreated(NftId productNftId, StateId state, address createdBy, address txOrigin);
    event LogProductStoreProductInfoUpdated(NftId productNftId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
    event LogProductStoreFeeInfoCreated(NftId productNftId, StateId state, address createdBy, address txOrigin);
    event LogProductStoreFeeInfoUpdated(NftId productNftId, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
    event LogProductStoreRiskInfoCreated(RiskId riskId, StateId state, address createdBy, address txOrigin);
    event LogProductStoreRiskInfoUpdated(RiskId riskId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
    event LogProductStorePolicyInfoCreated(NftId policyNftId, StateId state, address createdBy, address txOrigin);
    event LogProductStorePolicyInfoUpdated(NftId policyNftId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
    event LogProductStorePremiumInfoCreated(NftId policyNftId, StateId state, address createdBy, address txOrigin);
    event LogProductStorePremiumInfoUpdated(NftId policyNftId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
    event LogProductStoreClaimInfoCreated(NftId policyNftId, ClaimId claimId, StateId state, address createdBy, address txOrigin);
    event LogProductStoreClaimInfoUpdated(NftId policyNftId, ClaimId claimId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);
    event LogProductStorePayoutInfoCreated(NftId policyNftId, PayoutId payoutId, StateId state, address createdBy, address txOrigin);
    event LogProductStorePayoutInfoUpdated(NftId policyNftId, PayoutId payoutId, StateId oldState, StateId newState, address updatedBy, address txOrigin, Blocknumber lastUpdatedIn);

    mapping(Key32 key32 => IComponents.ProductInfo) private _products;
    mapping(Key32 key32 => IComponents.FeeInfo) private _fees;
    mapping(Key32 key32 => IRisk.RiskInfo) private _risks;
    mapping(Key32 key32 => IPolicy.PolicyInfo) private _policies;
    mapping(Key32 key32 => IPolicy.PremiumInfo) private _premiums;
    mapping(Key32 key32 => IPolicy.ClaimInfo) private _claims;
    mapping(Key32 key32 => IPolicy.PayoutInfo) private _payouts;


    /// @dev This initializer needs to be called from the instance itself.
    function initialize()
        public 
        initializer()
    {
        address instance = msg.sender;
        address authority = IInstance(instance).authority();

        __AccessManaged_init(authority);
        // double initialization, safe
        _initializeLifecycle();
    }


    //--- Product -----------------------------------------------------------//

    function createProduct(NftId productNftId, IComponents.ProductInfo memory info) external restricted() {
        Key32 key = _toNftKey32(productNftId, PRODUCT());
        _createMetadata(key);
        _products[key] = info;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreProductInfoCreated(productNftId, getState(key), msg.sender, tx.origin);
    }

    function updateProduct(NftId productNftId, IComponents.ProductInfo memory info, StateId newState) external restricted() {
        Key32 key = _toNftKey32(productNftId, PRODUCT());
        (Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
        _products[key] = info;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreProductInfoUpdated(productNftId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
    }

    function getProductInfo(NftId productNftId) external view returns (IComponents.ProductInfo memory info) {
        return _products[_toNftKey32(productNftId, PRODUCT())];
    }

    //--- Fee -----------------------------------------------------------//

    function createFee(NftId productNftId, IComponents.FeeInfo memory info) external restricted() {
        Key32 key = _toNftKey32(productNftId, FEE());
        _createMetadata(key);
        _fees[key] = info;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreFeeInfoCreated(productNftId, getState(key), msg.sender, tx.origin);
    }

    // Fee only has one state, so no change change possible
    function updateFee(NftId productNftId, IComponents.FeeInfo memory info) external restricted() {
        Key32 key = _toNftKey32(productNftId, FEE());
        (Blocknumber lastUpdatedIn,) = _updateState(key, KEEP_STATE());
        _fees[key] = info;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreFeeInfoUpdated(productNftId, msg.sender, tx.origin, lastUpdatedIn);
    }

    function getFeeInfo(NftId productNftId) external view returns (IComponents.FeeInfo memory info) {
        return _fees[_toNftKey32(productNftId, FEE())];
    }

    //--- Risk --------------------------------------------------------------//
    function createRisk(RiskId riskId, IRisk.RiskInfo memory info) external restricted() {
        Key32 key = riskId.toKey32();
        _createMetadata(key);
        _risks[key] = info;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreRiskInfoCreated(riskId, getState(key), msg.sender, tx.origin);
    }

    function updateRisk(RiskId riskId, IRisk.RiskInfo memory info, StateId newState) external restricted() {
        Key32 key = riskId.toKey32();
        (Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
        _risks[key] = info;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreRiskInfoUpdated(riskId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
    }

    function updateRiskState(RiskId riskId, StateId newState) external restricted() {
        // _updateState(riskId.toKey32(), newState);
        Key32 key = riskId.toKey32();
        (Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreRiskInfoUpdated(riskId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
    }

    function getRiskInfo(RiskId riskId) external view returns (IRisk.RiskInfo memory info) {
        return _risks[riskId.toKey32()];
    }

    //--- Application (Policy) ----------------------------------------------//

    function createApplication(NftId applicationNftId, IPolicy.PolicyInfo memory policy) external restricted() {
        _registerBalanceTarget(applicationNftId);
        Key32 key = _toNftKey32(applicationNftId, POLICY());
        _createMetadata(key);
        _policies[key] = policy;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStorePolicyInfoCreated(applicationNftId, getState(key), msg.sender, tx.origin);
    }

    function updateApplication(NftId applicationNftId, IPolicy.PolicyInfo memory policy, StateId newState) external restricted() {
        Key32 key = _toNftKey32(applicationNftId, POLICY());
        (Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
        _policies[key] = policy;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStorePolicyInfoUpdated(applicationNftId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
    }

    function updateApplicationState(NftId applicationNftId, StateId newState) external restricted() {
        Key32 key = _toNftKey32(applicationNftId, POLICY());
        (Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStorePolicyInfoUpdated(applicationNftId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
    }

    //--- Policy ------------------------------------------------------------//

    function updatePolicy(NftId policyNftId, IPolicy.PolicyInfo memory policy, StateId newState) external restricted() {
        Key32 key = _toNftKey32(policyNftId, POLICY());
        (Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
        _policies[key] = policy;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStorePolicyInfoUpdated(policyNftId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
    }

    function updatePolicyClaims(NftId policyNftId, IPolicy.PolicyInfo memory policy, StateId newState) external restricted() {
        Key32 key = _toNftKey32(policyNftId, POLICY());
        (Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
        _policies[key] = policy;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStorePolicyInfoUpdated(policyNftId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
    }

    function updatePolicyState(NftId policyNftId, StateId newState) external restricted() {
        Key32 key = _toNftKey32(policyNftId, POLICY());
        (Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStorePolicyInfoUpdated(policyNftId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
    }

    function getPolicyInfo(NftId policyNftId) external view returns (IPolicy.PolicyInfo memory policy) {
        return _policies[_toNftKey32(policyNftId, POLICY())];
    }

    //--- Premium (Policy) ----------------------------------------------//

    function createPremium(NftId policyNftId, IPolicy.PremiumInfo memory premium) external restricted() {
        Key32 key = _toNftKey32(policyNftId, PREMIUM());
        _createMetadata(key);
        _premiums[key] = premium;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStorePremiumInfoCreated(policyNftId, getState(key), msg.sender, tx.origin);
    }

    function updatePremiumState(NftId policyNftId, StateId newState) external restricted() {
        Key32 key = _toNftKey32(policyNftId, PREMIUM());
        (Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStorePremiumInfoUpdated(policyNftId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
    }

    function getPremiumInfo(NftId policyNftId) external view returns (IPolicy.PremiumInfo memory premium) {
        return _premiums[_toNftKey32(policyNftId, PREMIUM())];
    }

    //--- Claim -------------------------------------------------------------//

    function createClaim(NftId policyNftId, ClaimId claimId, IPolicy.ClaimInfo memory claim) external restricted() {
        Key32 key = _toClaimKey32(policyNftId, claimId);
        _createMetadata(key);
        _claims[key] = claim;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreClaimInfoCreated(policyNftId, claimId, getState(key), msg.sender, tx.origin);
    }

    function updateClaim(NftId policyNftId, ClaimId claimId, IPolicy.ClaimInfo memory claim, StateId newState) external restricted() {
        Key32 key = _toClaimKey32(policyNftId, claimId);
        (Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
        _claims[key] = claim;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreClaimInfoUpdated(policyNftId, claimId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
    }

    function updateClaimState(NftId policyNftId, ClaimId claimId, StateId newState) external restricted() {
        Key32 key = _toClaimKey32(policyNftId, claimId);
        (Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStoreClaimInfoUpdated(policyNftId, claimId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
    }

    function getClaimInfo(NftId policyNftId, ClaimId claimId) external view returns (IPolicy.ClaimInfo memory claim) {
        return _claims[_toClaimKey32(policyNftId, claimId)];
    }

    //--- Payout ------------------------------------------------------------//

    function createPayout(NftId policyNftId, PayoutId payoutId, IPolicy.PayoutInfo memory payout) external restricted() {
        Key32 key = _toPayoutKey32(policyNftId, payoutId);
        _createMetadata(key);
        _payouts[key] = payout;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStorePayoutInfoCreated(policyNftId, payoutId, getState(key), msg.sender, tx.origin);
    }

    function updatePayout(NftId policyNftId, PayoutId payoutId, IPolicy.PayoutInfo memory payout, StateId newState) external restricted() {
        Key32 key = _toPayoutKey32(policyNftId, payoutId);
        (Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
        _payouts[key] = payout;
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStorePayoutInfoUpdated(policyNftId, payoutId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
    }

    function updatePayoutState(NftId policyNftId, PayoutId payoutId, StateId newState) external restricted() {
        Key32 key = _toPayoutKey32(policyNftId, payoutId);
        (Blocknumber lastUpdatedIn, StateId oldState) = _updateState(key, newState);
        // solhint-disable-next-line avoid-tx-origin
        emit LogProductStorePayoutInfoUpdated(policyNftId, payoutId, oldState, newState, msg.sender, tx.origin, lastUpdatedIn);
    }

    function getPayoutInfo(NftId policyNftId, PayoutId payoutId) external view returns (IPolicy.PayoutInfo memory payout) {
        return _payouts[_toPayoutKey32(policyNftId, payoutId)];
    }

    //--- internal view/pure functions --------------------------------------//
    function _toNftKey32(NftId nftId, ObjectType objectType) private pure returns (Key32) {
        return nftId.toKey32(objectType);
    }

    function _toClaimKey32(NftId policyNftId, ClaimId claimId) private pure returns (Key32) {
        return claimId.toKey32(policyNftId);
    }

    function _toPayoutKey32(NftId policyNftId, PayoutId payoutId) private pure returns (Key32) {
        return payoutId.toKey32(policyNftId);
    }
}

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

import {Key32} from "../type/Key32.sol";
import {LibNftIdSet} from "../type/NftIdSet.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectSet} from "./base/ObjectSet.sol";
import {ObjectSetHelperLib} from "./base/ObjectSetHelperLib.sol";
import {RiskIdLib, RiskId} from "../type/RiskId.sol";

/// @dev RiskSet manages the risks and its active policies per product.
contract RiskSet is
    ObjectSet
{

    event LogRiskSetPolicyLinked(RiskId riskId, NftId policyNftId);
    event LogRiskSetPolicyUnlinked(RiskId riskId, NftId policyNftId);

    event LogRiskSetRiskAdded(NftId productNftId, RiskId riskId);
    event LogRiskSetRiskActive(NftId poolNftId,  RiskId riskId);
    event LogRiskSetRiskPaused(NftId poolNftId,  RiskId riskId);

    error ErrorRiskSetRiskLocked(RiskId riskId, NftId policyNftId); 
    error ErrorRiskSetRiskUnknown(RiskId riskId);
    error ErrorRiskSetRiskNotRegistered(RiskId riskId);

    mapping(RiskId riskId => LibNftIdSet.Set policies) internal _activePolicies;

    /// @dev links a policy to its bundle
    function linkPolicy(NftId productNftId, RiskId riskId, NftId policyNftId) external restricted() {

        // ensure risk is active (in active set) and registered with this instance
        if (!_isActive(productNftId, riskId.toKey32())) {
            revert ErrorRiskSetRiskLocked(riskId, policyNftId);
        }

        LibNftIdSet.add(_activePolicies[riskId], policyNftId);
        emit LogRiskSetPolicyLinked(riskId, policyNftId);
    }

    /// @dev Unlinks a policy from its risk.
    function unlinkPolicy(NftId productNftId, RiskId riskId, NftId policyNftId) external restricted() {

        // ensure risk is registered with this instance
        if (!_contains(productNftId, riskId.toKey32())) {
            revert ErrorRiskSetRiskUnknown(riskId);
        }

        LibNftIdSet.remove(_activePolicies[riskId], policyNftId);
        emit LogRiskSetPolicyUnlinked(riskId, policyNftId);
    }

    /// @dev add a new risk to a product registered with this instance
    // the corresponding product is fetched via instance reader
    function add(RiskId riskId) external restricted() {
        NftId productNftId = ObjectSetHelperLib.getProductNftId(_instanceAddress, riskId);

        // ensure product is registered with instance
        if(productNftId.eqz()) {
            revert ErrorRiskSetRiskNotRegistered(riskId);
        }

        _add(productNftId, riskId.toKey32());
        emit LogRiskSetRiskAdded(productNftId, riskId);
    }

    /// @dev Applications linked to active risks may be underwritten
    function activate(RiskId riskId) external restricted() {
        NftId productNftId = ObjectSetHelperLib.getProductNftId(_instanceAddress, riskId);
        _activate(productNftId, riskId.toKey32());
        emit LogRiskSetRiskActive(productNftId, riskId);
    }

    /// @dev Applications linked to paused/archived risks may not be underwritten
    function deactivate(RiskId riskId) external restricted() {
        NftId productNftId = ObjectSetHelperLib.getProductNftId(_instanceAddress, riskId);
        _deactivate(productNftId, riskId.toKey32());
        emit LogRiskSetRiskPaused(productNftId, riskId);
    }

    function checkRisk(NftId productNftId, RiskId riskId)
        public
        view 
        returns (bool exists, bool active)
    {
        Key32 riskKey32 = riskId.toKey32();
        exists = _contains(productNftId, riskKey32);

        if (exists) {
            active = _isActive(productNftId, riskKey32);
        }
    }

    function hasRisk(NftId productNftId, RiskId riskId)
        public
        view 
        returns (bool)
    {
        Key32 riskKey32 = riskId.toKey32();
        return _contains(productNftId, riskKey32);
    }

    function risks(NftId productNftId) external view returns(uint256) {
        return _objects(productNftId);
    }

    function getRiskId(NftId productNftId, uint256 idx) external view returns(RiskId riskId) {
        return RiskIdLib.toRiskId(_getObject(productNftId, idx).toKeyId());
    }
    
    function activeRisks(NftId productNftId) external view returns(uint256) {
        return _activeObjs(productNftId);
    }

    function getActiveRiskId(NftId productNftId, uint256 idx) external view returns(RiskId riskId) {
        return RiskIdLib.toRiskId(_getActiveObject(productNftId, idx).toKeyId());
    }

    function linkedPolicies(RiskId riskId) external view returns(uint256) {
        return LibNftIdSet.size(_activePolicies[riskId]);
    }

    function getLinkedPolicyNftId(RiskId riskId, uint256 idx) external view returns(NftId policyNftId) {
        return LibNftIdSet.getElementAt(_activePolicies[riskId], idx);
    }
}

File 70 of 130 : TargetNames.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

string constant INSTANCE_TARGET_NAME = "Instance";
string constant INSTANCE_ADMIN_TARGET_NAME = "InstanceAdmin";
string constant INSTANCE_STORE_TARGET_NAME = "InstanceStore";
string constant PRODUCT_STORE_TARGET_NAME = "ProductStore";
string constant BUNDLE_SET_TARGET_NAME = "BundleSet";
string constant RISK_SET_TARGET_NAME = "RiskSet";

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

import {IAuthorization} from "../../contracts/authorization/IAuthorization.sol";
import {NftId} from "../../contracts/type/NftId.sol";
import {Oracle} from "../../contracts/oracle/Oracle.sol";
import {RequestId} from "../../contracts/type/RequestId.sol";

contract BasicOracle is
    Oracle
{

    /// Function to provide reponse data releated to request id.
    function respond(
        RequestId requestId,
        bytes memory responseData
    )
        external
        virtual
        restricted()
    {
        _respond(requestId, responseData);
    }


    function _initializeBasicOracle(
        address registry,
        NftId instanceNftId,
        IAuthorization authorization,
        address initialOwner,
        string memory name
    )
        internal
        virtual
        onlyInitializing()
    {

        __Oracle_init(
            registry,
            instanceNftId,
            authorization,
            initialOwner,
            name);
    }
}

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

import {NftId} from "../type/NftId.sol";
import {RequestId} from "../type/RequestId.sol";
import {Timestamp} from "../type/Timestamp.sol";


interface IOracle {

    struct RequestInfo {
        // slot 0
        NftId requesterNftId; // originator of the request
        NftId oracleNftId; // responsible oracle component
        bool isCancelled;
        Timestamp respondedAt; // response timestamp
        // slot 1
        Timestamp expiredAt; // expiry timestamp
        // slot 2
        string callbackMethodName; // callback function of the requestor to call to provide response data
        // slot 3
        bytes requestData; 
        // slot 4
        bytes responseData; 
    }


    /// @dev Callback function for oracle service to notify this oracle component to retreive some oracle data.
    function request(
        RequestId requestId,
        NftId requesterId,
        bytes calldata requestData,
        Timestamp expiryAt
    ) external;


    /// @dev Callback function for oracle service to notify this oracle component that the specified oracle request has ben cancelled by the requestor.
    function cancel(
        RequestId requestId
    ) external;
}

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

import {Fee} from "../type/Fee.sol";
import {IInstanceLinkedComponent} from "../shared/IInstanceLinkedComponent.sol";
import {ReferralId, ReferralStatus} from "../type/Referral.sol";
import {NftId} from "../type/NftId.sol";
import {DistributorType} from "../type/DistributorType.sol";
import {RequestId} from "../type/RequestId.sol";
import {UFixed} from "../type/UFixed.sol";
import {Timestamp} from "../type/Timestamp.sol";

interface IOracleComponent is IInstanceLinkedComponent {
    error ErrorOracleNotImplemented(string methodName);
    
    /// @dev callback method for requesting some data from the oracle
    function request(
        RequestId requestId,
        NftId requesterNftId,
        bytes calldata requestData,
        Timestamp expiryAt
    ) external;


    /// @dev callback method for cancelling the specified oracle request
    function cancel(
        RequestId requestId
    ) external;


    /// @dev returns true iff the component needs to be called when selling/renewing policis
    function isVerifying() external view returns (bool verifying);
}

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

import {IService} from "../shared/IService.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {RequestId} from "../type/RequestId.sol";
import {StateId} from "../type/StateId.sol";
import {Timestamp} from "../type/Timestamp.sol";


interface IOracleService is IService {

    event LogOracleServiceRequestCreated(RequestId requestId, NftId requesterNftId, NftId oracleNftId, Timestamp expiryAt);
    event LogOracleServiceResponseProcessed(RequestId requestId, NftId oracleNftId);
    event LogOracleServiceDeliveryFailed(RequestId requestId, address requesterAddress, string functionSignature);
    event LogOracleServiceResponseResent(RequestId requestId, NftId requesterNftId);
    event LogOracleServiceRequestCancelled(RequestId requestId, NftId requesterNftId);

    // create request
    error ErrorOracleServiceProductMismatch(ObjectType callerObjectType, NftId productNft, NftId oracleParentNftId);
    error ErrorOracleServiceExpiryInThePast(Timestamp blockTimestamp, Timestamp expiryAt);
    error ErrorOracleServiceCallbackMethodNameEmpty();

    // respond
    error ErrorOracleServiceNotResponsibleOracle(RequestId requestId, NftId expectedOracleNftId, NftId oracleNftId);

    // get request info
    error ErrorOracleServiceRequestStateNotActive(RequestId requestId, StateId state);
    error ErrorOracleServiceCallerNotResponsibleOracle(RequestId requestId, NftId oracleNftId, NftId callerNftId);
    error ErrorOracleServiceCallerNotRequester(RequestId requestId, NftId requesterNftId, NftId callerNftId);
    error ErrorOracleServiceRequestExpired(RequestId requestId, Timestamp expiredAt);
    
    /// @dev send an oracle request to the specified oracle component.
    /// the function returns the id of the newly created request.
    /// permissioned: only registered components may send requests to oracles.
    function request(
        NftId oracleNftId,
        bytes calldata requestData,
        Timestamp expiryAt,
        string calldata callbackMethodName
    ) external returns (RequestId requestId);

    /// @dev Respond to oracle request by oracle compnent.
    /// The response data is amended in the request info stored with the instance.
    /// The request state changes to FULFILLED (when calling the callback method of the requester is successful)
    /// or to FAILED when calling the requester is not succesful.
    /// The function returns true iff the state changes to FULFILLED.
    /// Permissioned: only the receiving oracle component may call this method
    function respond(
        RequestId requestId,
        bytes calldata responseData
    ) external returns (bool success);

    /// @dev Resend a failed response to the requester.
    /// Only requests in state FAILED may be resent.
    /// The request state changes to FULFILLED when calling the callback method of the requester is successful.
    /// Permissioned: only the receiving oracle may resend a request
    function resend(RequestId requestId) external;

    /// @dev Notify the oracle component that the specified request has become invalid.
    /// Only requests in state ACTIVE may be cancelled.
    /// Permissioned: only the requester may cancel a request
    function cancel(RequestId requestId) external;

}

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

import {Amount} from "../type/Amount.sol";
import {COMPONENT, PRODUCT, ORACLE} from "../type/ObjectType.sol";
import {IAuthorization} from "../authorization/IAuthorization.sol";
import {IComponentService} from "../shared/IComponentService.sol";
import {IInstanceLinkedComponent} from "../shared/IInstanceLinkedComponent.sol";
import {IOracleComponent} from "./IOracleComponent.sol";
import {IOracleService} from "./IOracleService.sol";
import {NftId} from "../type/NftId.sol";
import {InstanceLinkedComponent} from "../shared/InstanceLinkedComponent.sol";
import {RequestId} from "../type/RequestId.sol";
import {Timestamp} from "../type/Timestamp.sol";


abstract contract Oracle is
    InstanceLinkedComponent,
    IOracleComponent
{
    // keccak256(abi.encode(uint256(keccak256("etherisc.storage.Oracle")) - 1)) & ~bytes32(uint256(0xff));
    bytes32 public constant ORACLE_STORAGE_LOCATION_V1 = 0xaab7c0ea03d290e56d6c060e0733d3ebcbe647f7694616a2ec52738a64b2f900;

    struct OracleStorage {
        IComponentService _componentService;
        IOracleService _oracleService;
    }


    function request(
        RequestId requestId,
        NftId requesterId,
        bytes calldata requestData,
        Timestamp expiryAt
    )
        external
        virtual
        restricted()
    {
        _request(requestId, requesterId, requestData, expiryAt);
    }


    function cancel(
        RequestId requestId
    )
        external
        virtual
        restricted()
    {
        _cancel(requestId);
    }


    /// @dev Not relevant for oracle components, always returns false.
    function isVerifying()
        external 
        virtual 
        view 
        returns (bool verifying)
    {
        return false;
    }

    /// @dev Not relevant for oracle components
    function withdrawFees(Amount amount)
        external
        virtual
        override(IInstanceLinkedComponent, InstanceLinkedComponent)
        onlyOwner()
        restricted()
        returns (Amount)
    {
        revert ErrorOracleNotImplemented("withdrawFees");
    }


    function __Oracle_init(
        address registry,
        NftId productNftId,
        IAuthorization authorization,
        address initialOwner,
        string memory name
    )
        internal
        virtual
        onlyInitializing()
    {
        __InstanceLinkedComponent_init(
            registry, 
            productNftId, 
            name, 
            ORACLE(), 
            authorization,
            true, 
            initialOwner);

        OracleStorage storage $ = _getOracleStorage();
        $._componentService = IComponentService(_getServiceAddress(COMPONENT())); 
        $._oracleService = IOracleService(_getServiceAddress(ORACLE())); 

        _registerInterface(type(IOracleComponent).interfaceId);
    }


    /// @dev Internal function for handling requests.
    /// Empty implementation.
    /// Overwrite this function to implement use case specific handling for oracle calls.
    function _request(
        RequestId requestId,
        NftId requesterId,
        bytes calldata requestData,
        Timestamp expiryAt
    )
        internal
        virtual
    {
    }


    /// @dev Internal function for cancelling requests.
    /// Empty implementation.
    /// Overwrite this function to implement use case specific cancelling.
    function _cancel(
        RequestId requestId
    )
        internal
        virtual
    {
    }


    /// @dev Internal function for handling oracle responses.
    /// Default implementation sends response back to oracle service.
    /// Use this function in use case specific external/public functions to handle use case specific response handling.
    function _respond(
        RequestId requestId,
        bytes memory responseData
    )
        internal
        virtual
    {
        _getOracleStorage()._oracleService.respond(requestId, responseData);
    }


    function _getOracleStorage() private pure returns (OracleStorage storage $) {
        assembly {
            $.slot := ORACLE_STORAGE_LOCATION_V1
        }
    }
}

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

import {IPolicy} from "../instance/module/IPolicy.sol";
import {IService} from "../shared/IService.sol";

import {Amount} from "../type/Amount.sol";
import {NftId} from "../type/NftId.sol";
import {ReferralId} from "../type/Referral.sol";
import {RiskId} from "../type/RiskId.sol";
import {Seconds} from "../type/Seconds.sol";

/// @dev gif service responsible for creating applications
/// only product components may call transaction functions
interface IApplicationService is IService {
    
    event LogApplicationServiceApplicationCreated(
        NftId applicationNftId,
        NftId productNftId,
        NftId bundleNftId, 
        RiskId riskId,
        ReferralId referralId,
        address applicationOwner,
        Amount sumInsuredAmount,
        Amount premiumAmount,
        Seconds lifetime);
    event LogApplicationServiceApplicationRenewed(NftId policyNftId, NftId bundleNftId);
    event LogApplicationServiceApplicationAdjusted(
        NftId applicationNftId, 
        NftId bundleNftId, 
        RiskId riskId, 
        ReferralId referralId, 
        Amount sumInsuredAmount, 
        Seconds lifetime);
    event LogApplicationServiceApplicationRevoked(NftId applicationNftId);

    // _checkLinkedApplicationParameters
    error ErrorApplicationServiceRiskProductMismatch(RiskId riskId, NftId riskProductNftId, NftId productNftId);
    error ErrorApplicationServiceRiskUnknown(RiskId riskId, NftId productNftId);
    error ErrorApplicationServiceRiskLocked(RiskId riskId, NftId productNftId);
    error ErrorApplicationServiceBundleUnknown(NftId bundleNftId, NftId poolNftId);
    error ErrorApplicationServiceBundleLocked(NftId bundleNftId, NftId poolNftId);
    error ErrorApplicationServiceReferralInvalid(NftId productNftId, NftId distributionNftId, ReferralId referralId);
    

    /// @dev creates a new application based on the specified attributes
    /// may only be called by a product component
    function create(
        address applicationOwner,
        RiskId riskId,
        Amount sumInsuredAmount,
        Amount premiumAmount,
        Seconds lifetime,
        NftId bundleNftId,
        ReferralId referralId,
        bytes memory applicationData
    ) external returns (NftId applicationNftId);

    /// @dev updates application attributes
    /// may only be called while the application is in applied state
    /// may only be called by the referenced product related to applicationNftId
    function adjust(
        NftId applicationNftId,
        RiskId riskId,
        NftId bundleNftId,
        ReferralId referralId,
        Amount sumInsuredAmount,
        Seconds lifetime,
        bytes memory applicationData
    ) external;

    /// @dev creates a new application that extends the provided policy
    /// lifetime will seamlessly extend referenced policy, for closed policies
    /// lifetime will start at underwriting time
    /// product will need to limit the time window for renewal as underwriting
    /// will lock the collateral at underwriting time which might be earlier than activation time
    /// policyNftId needs to refer to an underwritten (or active or closed) policy
    /// may only be called by the referenced product related to policyNftId
    function renew(
        NftId policyNftId, // policy to be renewd (renewal inherits policy attributes)
        NftId bundleNftId // will likely need a newer bundle for underwriting
    ) external returns (NftId applicationNftId);

    /// @dev revokes the application represented by {policyNftId}
    /// an application can only be revoked in applied state
    /// only the application holder may revoke an application
    function revoke(NftId policyNftId) external;
}

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

import {IInstance} from "../instance/IInstance.sol";
import {InstanceReader} from "../instance/InstanceReader.sol";
import {IService} from "../shared/IService.sol";

import {Amount} from "../type/Amount.sol";
import {ClaimId} from "../type/ClaimId.sol";
import {PayoutId} from "../type/PayoutId.sol";
import {NftId} from "../type/NftId.sol";
import {StateId} from "../type/StateId.sol";
import {Timestamp} from "../type/Timestamp.sol";
import {UFixed} from "../type/UFixed.sol";
import {Fee} from "../type/Fee.sol";

/// @dev gif service responsible for creating claims and payouts
/// only product components may call transaction functions
interface IClaimService is
    IService
{
    event LogClaimServiceClaimSubmitted(NftId policyNftId, ClaimId claimId, Amount claimAmount);
    event LogClaimServiceClaimConfirmed(NftId policyNftId, ClaimId claimId, Amount confirmedAmount);
    event LogClaimServiceClaimDeclined(NftId policyNftId, ClaimId claimId);
    event LogClaimServiceClaimRevoked(NftId policyNftId, ClaimId claimId);
    event LogClaimServiceClaimCancelled(NftId policyNftId, ClaimId claimId);

    event LogClaimServicePayoutCreated(NftId policyNftId, PayoutId payoutId, Amount amount, address beneficiary);
    event LogClaimServicePayoutProcessed(NftId policyNftId, PayoutId payoutId, Amount amount);
    event LogClaimServicePayoutCancelled(NftId policyNftId, PayoutId payoutId);

    error ErrorClaimServiceBeneficiarySet(NftId policyNftId, PayoutId payoutId, address beneficiary);

    error ErrorClaimServicePolicyProductMismatch(NftId policyNftId, NftId expectedProduct, NftId actualProduct);
    error ErrorClaimServicePolicyNotOpen(NftId policyNftId);
    error ErrorClaimServiceClaimAmountIsZero(NftId policyNftId);
    error ErrorClaimServiceClaimExceedsSumInsured(NftId policyNftId, Amount sumInsured, Amount payoutsIncludingClaimAmount);
    error ErrorClaimServiceBeneficiaryIsZero(NftId policyNftId, ClaimId claimId);
    error ErrorClaimsServicePayoutAmountIsZero(NftId policyNftId, PayoutId payoutId);

    error ErrorClaimServiceClaimWithOpenPayouts(NftId policyNftId, ClaimId claimId, uint24 openPayouts);
    error ErrorClaimServiceClaimWithMissingPayouts(NftId policyNftId, ClaimId claimId, Amount claimAmount, Amount paidAmount);
    error ErrorClaimServiceClaimNotInExpectedState(NftId policyNftId, ClaimId claimId, StateId expectedState, StateId actualState);

    error ErrorClaimServiceClaimNotConfirmed(NftId policyNftId, ClaimId claimId, StateId actualState);
    error ErrorClaimServicePayoutExceedsClaimAmount(NftId policyNftId, ClaimId claimId, Amount claimAmount, Amount totalPayoutAmount);
    error ErrorClaimServicePayoutNotExpected(NftId policyNftId, PayoutId payoutId, StateId actualState);

    /// @dev create a new claim for the specified policy
    /// returns the id of the newly created claim
    /// function can only be called by product, policy needs to match with calling product
    function submit(
        NftId policyNftId, 
        Amount claimAmount,
        bytes memory claimData
    ) external returns (ClaimId claimId);


    /// @dev declines the specified claim
    /// function can only be called by product, policy needs to match with calling product
    function decline(
        NftId policyNftId, 
        ClaimId claimId,
        bytes memory data // claim processing data
    ) external;


    /// @dev revokes the specified claim
    /// function can only be called by product, policy needs to match with calling product
    function revoke(
        NftId policyNftId, 
        ClaimId claimId
    ) external;


    /// @dev confirms the specified claim and specifies the payout amount
    /// function can only be called by product, policy needs to match with calling product
    function confirm(
        NftId policyNftId, 
        ClaimId claimId,
        Amount confirmedAmount,
        bytes memory data // claim processing data
    ) external;


    /// @dev cancels a confirmed claim before it has been fully paid out. 
    /// Can only be called when there are not pending payouts
    /// function can only be called by product, policy needs to match with calling product
    function cancelConfirmedClaim(
        NftId policyNftId, 
        ClaimId claimId
    ) external;


    /// @dev Creates a new payout for the specified claim.
    /// The beneficiary is the holder of the policy NFT
    /// returns the id of the newly created payout, this id is unique for the specified policy
    /// function can only be called by product, policy needs to match with calling product
    function createPayout(
        NftId policyNftId, 
        ClaimId claimId,
        Amount amount,
        bytes memory data
    )
        external
        returns (PayoutId payoutId);


    /// @dev Creates a new payout for the specified claim and beneficiary.
    /// returns the id of the newly created payout, this id is unique for the specified policy
    /// function can only be called by product, policy needs to match with calling product
    function createPayoutForBeneficiary(
        NftId policyNftId, 
        ClaimId claimId,
        Amount amount,
        address beneficiary,
        bytes memory data
    )
        external
        returns (PayoutId payoutId);


    /// @dev processes the specified payout
    /// this includes moving the payout token to the beneficiary (default: policy holder)
    /// function can only be called by product, policy needs to match with calling product
    function processPayout(
        NftId policyNftId, 
        PayoutId payoutId
    ) external returns (Amount netPayoutAmount, Amount processingFeeAmount);

    /// @dev cancels the specified payout. no tokens are moved, payout is set to cancelled. 
    function cancelPayout(
        NftId policyNftId, 
        PayoutId payoutId
    ) external;
}

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

import {IService} from "../shared/IService.sol";

import {Amount} from "../type/Amount.sol";
import {IInstance} from "../instance/IInstance.sol";
import {NftId} from "../type/NftId.sol";
import {StateId} from "../type/StateId.sol";
import {Timestamp} from "../type/Timestamp.sol";

interface IPolicyService is IService {

    event LogPolicyServicePolicyCreated(NftId policyNftId, Amount premiumAmount, Timestamp activatedAt);
    event LogPolicyServicePolicyDeclined(NftId policyNftId);
    event LogPolicyServicePolicyPremiumCollected(NftId policyNftId, Amount premiumAmount);
    event LogPolicyServicePolicyActivated(NftId policyNftId, Timestamp activatedAt);
    event LogPolicyServicePolicyActivatedUpdated(NftId policyNftId, Timestamp activatedAt);
    event LogPolicyServicePolicyExpirationUpdated(NftId policyNftId, Timestamp expiredAt);
    event LogPolicyServicePolicyClosed(NftId policyNftId);

    error LogPolicyServiceMaxPremiumAmountExceeded(NftId policyNftId, Amount maxPremiumAmount, Amount premiumAmount);
    error ErrorPolicyServicePolicyProductMismatch(NftId applicationNftId, NftId expectedProductNftId, NftId actualProductNftId);
    error ErrorPolicyServicePolicyStateNotApplied(NftId applicationNftId);
    error ErrorPolicyServicePolicyStateNotCollateralized(NftId applicationNftId);
    error ErrorPolicyServicePolicyAlreadyActivated(NftId policyNftId);

    error ErrorPolicyServicePolicyNotActivated(NftId policyNftId);
    error ErrorPolicyServicePolicyActivationTooEarly(NftId policyNftId, Timestamp lowerLimit, Timestamp activatedAt);
    error ErrorPolicyServicePolicyActivationTooLate(NftId policyNftId, Timestamp upperLimit, Timestamp activatedAt);
    
    error ErrorPolicyServiceInsufficientAllowance(address customer, address tokenHandlerAddress, uint256 amount);
    error ErrorPolicyServicePremiumAlreadyPaid(NftId policyNftId);

    error ErrorPolicyServicePolicyNotCloseable(NftId policyNftId);

    error ErrorPolicyServicePolicyNotActive(NftId policyNftId, StateId state);
    error ErrorPolicyServicePremiumNotPaid(NftId policyNftId, Amount premiumAmount);
    error ErrorPolicyServiceOpenClaims(NftId policyNftId, uint16 openClaimsCount);
    error ErrorPolicyServicePolicyHasNotExpired(NftId policyNftId, Timestamp expiredAt);
    error ErrorPolicyServicePolicyExpirationTooLate(NftId policyNftId, Timestamp upperLimit, Timestamp expiredAt);
    error ErrorPolicyServicePolicyExpirationTooEarly(NftId policyNftId, Timestamp lowerLimit, Timestamp expiredAt);

    error ErrorPolicyServicePremiumMismatch(NftId policyNftId, Amount expectedPremiumAmount, Amount recalculatedPremiumAmount);
    error ErrorPolicyServiceTransferredPremiumMismatch(NftId policyNftId, Amount expectedPremiumAmount, Amount transferredPremiumAmount);

    /// @dev creates the policy from {applicationNftId}. 
    /// @param applicationNftId the application NftId
    /// @param activateAt the timestamp when the policy should be activated
    /// @param maxPremiumAmount the maximum premium amount that the policy holder is willing to pay
    /// During policy creation, the effective premium amount is calculated based on the provided parameters. If this
    /// amount is higher than the maxPremiumAmount, the function will revert.
    /// After successful completion of the function the policy can be referenced using the application NftId.
    /// Locks the sum insured amount in the pool, but does not transfer tokens. Call collectPremium to transfer tokens. 
    /// Sets the policy state to collateralized.
    /// Optionally activates the policy if activateAt is a non-zero timestamp.
    /// only the related product may create a policy from an application
    /// @return premiumAmount the effective premium amount
    function createPolicy(
        NftId applicationNftId,
        Timestamp activateAt,
        Amount maxPremiumAmount
    )
        external
        returns (Amount premiumAmount);

    /// @dev declines an application represented by {policyNftId}
    /// an application can only be declined in applied state
    /// only the related product may decline an application
    function decline(NftId policyNftId) external;

    /// @dev collects the premium token for the specified policy (must be in COLLATERALIZED state)
    function collectPremium(NftId policyNftId, Timestamp activateAt) external;

    /// @dev activates the specified policy and sets the activation date in the policy metadata
    /// to activate a policy it needs to be in underwritten state
    function activate(NftId policyNftId, Timestamp activateAt) external;

    /// @dev adjusts the activation date of the specified policy and sets the new activation date in the policy metadata
    /// to adjust the activation date of a policy it needs to have an activation date set. 
    /// the new activation date must not be before the current block timestamp or after the expiry date
    function adjustActivation(NftId policyNftId, Timestamp newActivateAt) external;

    /// @dev Expires the specified policy and sets the expiry date in the policy metadata. 
    /// Function consumers are products.
    /// If expiry date is set to 0, then the earliest possible expiry date (current blocktime) is set
    /// to expire a policy it must be in active state, policies may be expired even when the predefined expiry date is still in the future
    /// a policy can only be closed when it has been expired. in addition, it must not have any open claims
    /// this function can only be called by a product. the policy needs to match with the calling product
    /// @return expiredAt the effective expiry date
    function expire(NftId policyNftId, Timestamp expireAt) external returns (Timestamp expiredAt);

    /// @dev Closes the specified policy and sets the closed data in the policy metadata
    /// a policy can only be closed when it has been expired. in addition, it must not have any open claims
    /// this function can only be called by a product. the policy needs to match with the calling product
    function close(NftId policyNftId) external;

    /// @dev Expires the specified policy and sets the expiry date in the policy metadata. 
    /// Function consumers is claim service.
    function expirePolicy(IInstance instance, NftId policyNftId, Timestamp expireAt) external returns (Timestamp expiredAt);

}

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

import {Seconds} from "../type/Seconds.sol";
import {NftId} from "../type/NftId.sol";
import {ReferralId} from "../type/Referral.sol";
import {RiskId} from "../type/RiskId.sol";
import {Amount} from "../type/Amount.sol";

import {IService} from "./IApplicationService.sol";
import {IPolicy} from "../instance/module/IPolicy.sol";

interface IPricingService is IService
{

    error ErrorPricingServiceTargetWalletAmountsMismatch();
    error ErrorPricingServiceRiskProductMismatch(RiskId riskId, NftId riskProductNftId, NftId productNftId);
    error ErrorPricingServiceBundlePoolMismatch(NftId bundleNftId, NftId bundlePoolNftId, NftId poolNftId);
    error ErrorPricingServiceFeeCalculationMismatch(
        Amount distributionFeeFixAmount,
        Amount distributionFeeVarAmount,
        Amount distributionOwnerFeeFixAmount,
        Amount distributionOwnerFeeVarAmount,
        Amount commissionAmount,
        Amount discountAmount
    );

    function calculatePremium(
        NftId productNftId,
        RiskId riskId,
        Amount sumInsuredAmount,
        Seconds lifetime,
        bytes memory applicationData,
        NftId bundleNftId,
        ReferralId referralId
    )
        external
        view
        returns (IPolicy.PremiumInfo memory premium);
}

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

import {Amount} from "../type/Amount.sol";
import {ClaimId} from "../type/ClaimId.sol";
import {IInstanceLinkedComponent} from "../shared/IInstanceLinkedComponent.sol";
import {IComponents} from "../instance/module/IComponents.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {ReferralId} from "../type/Referral.sol";
import {RiskId} from "../type/RiskId.sol";
import {Seconds} from "../type/Seconds.sol";

interface IProductComponent is
    IInstanceLinkedComponent
{

    // @dev register a new component for this product cluster.
    function registerComponent(address component)
        external
        returns (NftId componentNftId);

    /// @dev Callback function to inform product compnent about arrival of funding for a claim.
    /// The callback is called by the pool service after the corresponding pool triggers this function.
    /// The callback is only called when the product's property isProcessingFundedClaims is set.
    function processFundedClaim(
        NftId policyNftId, 
        ClaimId claimId, 
        Amount availableAmount
    ) external;


    /// @dev Calculates the premium amount for the provided application data.
    /// The returned premium amounts takes into account potential discounts and fees.
    function calculatePremium(
        Amount sumInsuredAmount,
        RiskId riskId,
        Seconds lifetime,
        bytes memory applicationData,
        NftId bundleNftId,
        ReferralId referralId
    ) external view returns (Amount premiumAmount);


    /// @dev Calculates the net premium amount for the provided application data.
    /// The returned net premium amounts only covers the cost of collateralizing the application.
    /// This amount purely depends on the use case specific risk and does not include any fees/commission.
    function calculateNetPremium(
        Amount sumInsuredAmount,
        RiskId riskId,
        Seconds lifetime,
        bytes memory applicationData
    ) external view returns (Amount netPremiumAmount);    


    /// @dev returns initial product specific infos 
    function getInitialProductInfo() external view returns (IComponents.ProductInfo memory info);

    /// @dev returns initial fee infos
    function getInitialFeeInfo() external view returns (IComponents.FeeInfo memory info);
    
}

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

import {IService} from "../shared/IService.sol";

import {NftId} from "../type/NftId.sol";
import {RiskId} from "../type/RiskId.sol";
import {StateId} from "../type/StateId.sol";

interface IRiskService is IService {

    event LogRiskServiceRiskCreated(NftId productNftId, RiskId riskId);
    event LogRiskServiceRiskUpdated(NftId productNftId, RiskId riskId);
    event LogRiskServiceRiskLocked(NftId productNftId, RiskId riskId);
    event LogRiskServiceRiskUnlocked(NftId productNftId, RiskId riskId);
    event LogRiskServiceRiskClosed(NftId productNftId, RiskId riskId);
    
    error ErrorRiskServiceRiskProductMismatch(RiskId riskId, NftId riskProductNftId, NftId productNftId);
    error ErrorRiskServiceRiskNotActive(NftId productNftId, RiskId riskId);
    error ErrorRiskServiceUnknownRisk(NftId productNftId, RiskId riskId);
    error ErrorRiskServiceRiskNotLocked(NftId productNftId, RiskId riskId);

    /// @dev Create a new risk with the given id and provided data. 
    /// The key of the risk derived from the risk id in comination with the product NftId. 
    /// Risk data is stored in the instance store. 
    function createRisk(
        bytes32 id,
        bytes memory data
    ) external returns (RiskId riskId);


    function updateRisk(
        RiskId riskId,
        bytes memory data
    ) external;

    /// @dev Locks/unlocks the risk with the given id.
    /// No new policies can be underwritten for a locked risk.
    function setRiskLocked(
        RiskId riskId,
        bool locked
    ) external;

    /// @dev Close the risk with the given id.
    function closeRisk(
        RiskId riskId
    ) external;
}

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

import {InstanceReader} from "../instance/InstanceReader.sol";
import {IPolicy} from "../instance/module/IPolicy.sol";
import {IPolicyService} from "../product/IPolicyService.sol";
import {NftId} from "../type/NftId.sol";
import {StateId, CLOSED, COLLATERALIZED} from "../type/StateId.sol";
import {Timestamp, TimestampLib} from "../type/Timestamp.sol";

library PolicyServiceLib {

    function policyIsActive(InstanceReader instanceReader, NftId policyNftId)
        external 
        view
        returns (bool isActive)
    {
        // policy not collateralized
        if (instanceReader.getPolicyState(policyNftId) != COLLATERALIZED()) {
            return false;
        }

        IPolicy.PolicyInfo memory info = instanceReader.getPolicyInfo(policyNftId);

        if (info.productNftId.eqz()) { return false; } // not closeable: policy does not exist (or does not belong to this instance)
        if (info.activatedAt.eqz()) { return false; } // not closeable: not yet activated
        if (info.activatedAt > TimestampLib.current()) { return false; } // not yet active
        if (info.expiredAt <= TimestampLib.current()) { return false; } // already expired

        return true;
    }

    function activate(
        NftId policyNftId, 
        IPolicy.PolicyInfo memory policyInfo,
        Timestamp activateAt
    )
        external
        pure 
        returns (IPolicy.PolicyInfo memory)
    {
        // fail if policy has already been activated and activateAt is different
        if(! policyInfo.activatedAt.eqz() && activateAt != policyInfo.activatedAt) {
            revert IPolicyService.ErrorPolicyServicePolicyAlreadyActivated(policyNftId);
        }

        // ignore if policy has already been activated and activateAt is the same
        if (policyInfo.activatedAt == activateAt) {
            return policyInfo;
        }

        policyInfo.activatedAt = activateAt;
        policyInfo.expiredAt = activateAt.addSeconds(policyInfo.lifetime);

        return policyInfo;
    }

    function expire(
        InstanceReader instanceReader,
        NftId policyNftId,
        IPolicy.PolicyInfo memory policyInfo,
        Timestamp expireAt
    )
        external
        view
        returns (IPolicy.PolicyInfo memory)
    {
        StateId policyState = instanceReader.getPolicyState(policyNftId);

        checkExpiration(
            expireAt,
            policyNftId,
            policyState,
            policyInfo);

        // effects
        // update policyInfo with new expiredAt timestamp
        if (expireAt.gtz()) {
            policyInfo.expiredAt = expireAt;
        } else {
            policyInfo.expiredAt = TimestampLib.current();
        }

        return policyInfo;
    }

    function checkExpiration(
        Timestamp newExpiredAt,
        NftId policyNftId,
        StateId policyState,
        IPolicy.PolicyInfo memory policyInfo
    )
        public 
        view
    {
        if (policyState != COLLATERALIZED()) { 
            revert IPolicyService.ErrorPolicyServicePolicyNotActive(policyNftId, policyState);
        }

        if (policyInfo.activatedAt.eqz() || TimestampLib.current() < policyInfo.activatedAt) { 
            revert IPolicyService.ErrorPolicyServicePolicyNotActive(policyNftId, policyState);
        } 

        // check expiredAt represents a valid expiry time
        if (newExpiredAt > policyInfo.expiredAt) {
            revert IPolicyService.ErrorPolicyServicePolicyExpirationTooLate(policyNftId, policyInfo.expiredAt, newExpiredAt);
        }

        if (newExpiredAt.gtz() && newExpiredAt < TimestampLib.current()) {
            revert IPolicyService.ErrorPolicyServicePolicyExpirationTooEarly(policyNftId, TimestampLib.current(), newExpiredAt);
        }
    }


    function policyIsCloseable(InstanceReader instanceReader, NftId policyNftId)
        external 
        view
        returns (bool isCloseable)
    {
        // policy already closed
        if (instanceReader.getPolicyState(policyNftId) == CLOSED()) {
            return false;
        }

        IPolicy.PolicyInfo memory info = instanceReader.getPolicyInfo(policyNftId);
        
        if (info.productNftId.eqz()) { return false; } // not closeable: policy does not exist (or does not belong to this instance)
        if (info.activatedAt.eqz()) { return false; } // not closeable: not yet activated
        if (info.openClaimsCount > 0) { return false; } // not closeable: has open claims

        // closeable: if sum of claims matches sum insured a policy may be closed prior to the expiry date
        if (info.claimAmount == info.sumInsuredAmount) { return true; }

        // not closeable: not yet expired
        if (TimestampLib.current() < info.expiredAt) { return false; }

        // all conditions to close the policy are met
        return true; 
    }

}

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

import {Amount, AmountLib} from "../type/Amount.sol";
import {ClaimId} from "../type/ClaimId.sol";
import {Fee} from "../type/Fee.sol";
import {IApplicationService} from "./IApplicationService.sol";
import {IAuthorization} from "../authorization/IAuthorization.sol";
import {IComponentService} from "../shared/IComponentService.sol";
import {InstanceLinkedComponent} from "../shared/InstanceLinkedComponent.sol";
import {IPolicyService} from "./IPolicyService.sol";
import {IRiskService} from "./IRiskService.sol";
import {IClaimService} from "./IClaimService.sol";
import {IPricingService} from "./IPricingService.sol";
import {IProductComponent} from "./IProductComponent.sol";
import {NftId} from "../type/NftId.sol";
import {COMPONENT, PRODUCT, BUNDLE, APPLICATION, POLICY, CLAIM, PRICE } from "../type/ObjectType.sol";
import {PayoutId} from "../type/PayoutId.sol";
import {COMPONENT, PRODUCT, APPLICATION, POLICY, CLAIM, PRICE, BUNDLE, RISK } from "../type/ObjectType.sol";
import {ReferralId} from "../type/Referral.sol";
import {RiskId} from "../type/RiskId.sol";
import {Seconds} from "../type/Seconds.sol";
import {StateId} from "../type/StateId.sol";
import {Timestamp} from "../type/Timestamp.sol";

import {IPolicy} from "../instance/module/IPolicy.sol";
import {IComponents} from "../instance/module/IComponents.sol";

abstract contract Product is
    InstanceLinkedComponent, 
    IProductComponent
{
    // keccak256(abi.encode(uint256(keccak256("etherisc.storage.Product")) - 1)) & ~bytes32(uint256(0xff));
    bytes32 public constant PRODUCT_STORAGE_LOCATION_V1 = 0x0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d6800;

    struct ProductStorage {
        IComponents.ProductInfo _productInfo;
        IComponents.FeeInfo _feeInfo;
        IComponentService _componentService;
        IRiskService _riskService;
        IApplicationService _applicationService;
        IPolicyService _policyService;
        IClaimService _claimService;
        IPricingService _pricingService;
    }


    function registerComponent(address component)
        external
        virtual
        restricted()
        onlyOwner()
        returns (NftId componentNftId)
    {
        return _getProductStorage()._componentService.registerComponent(component);
    }


    function processFundedClaim(
        NftId policyNftId, 
        ClaimId claimId, 
        Amount availableAmount
    )
        external
        virtual
        restricted() // pool service role
        onlyNftOfType(policyNftId, POLICY())
    {
        // default implementation does nothing
    }


    function calculatePremium(
        Amount sumInsuredAmount,
        RiskId riskId,
        Seconds lifetime,
        bytes memory applicationData,
        NftId bundleNftId,
        ReferralId referralId
    )
        public 
        view 
        virtual
        override 
        onlyNftOfType(bundleNftId, BUNDLE())
        returns (Amount premiumAmount)
    {
        IPolicy.PremiumInfo memory premium = _getProductStorage()._pricingService.calculatePremium(
            getNftId(),
            riskId,
            sumInsuredAmount,
            lifetime,
            applicationData,
            bundleNftId,
            referralId
        );

        return premium.premiumAmount;
    }

    function calculateNetPremium(
        Amount sumInsuredAmount,
        RiskId,
        Seconds,
        bytes memory
    )
        external
        view
        virtual override
        returns (Amount netPremiumAmount)
    {
        // default 10% of sum insured
        return AmountLib.toAmount(sumInsuredAmount.toInt() / 10);
    }


    function getInitialProductInfo()
        public 
        virtual 
        view 
        returns (IComponents.ProductInfo memory poolInfo)
    {
        return _getProductStorage()._productInfo;
    }

    function getInitialFeeInfo()
        public 
        virtual 
        view 
        returns (IComponents.FeeInfo memory feeInfo)
    {
        return _getProductStorage()._feeInfo;
    }


    function __Product_init(
        address registry,
        NftId instanceNftId,
        string memory name,
        IComponents.ProductInfo memory productInfo,
        IComponents.FeeInfo memory feeInfo,
        IAuthorization authorization,
        address initialOwner
    )
        internal
        virtual
        onlyInitializing()
    {
        __InstanceLinkedComponent_init(
            registry, 
            instanceNftId, 
            name, 
            PRODUCT(), 
            authorization, 
            productInfo.isInterceptingPolicyTransfers, 
            initialOwner);

        ProductStorage storage $ = _getProductStorage();
        $._productInfo = productInfo;
        $._feeInfo = feeInfo;
        $._riskService = IRiskService(_getServiceAddress(RISK())); 
        $._applicationService = IApplicationService(_getServiceAddress(APPLICATION())); 
        $._policyService = IPolicyService(_getServiceAddress(POLICY())); 
        $._claimService = IClaimService(_getServiceAddress(CLAIM())); 
        $._pricingService = IPricingService(_getServiceAddress(PRICE()));
        $._componentService = IComponentService(_getServiceAddress(COMPONENT()));

        _registerInterface(type(IProductComponent).interfaceId);  
    }


    function _setFees(
        Fee memory productFee,
        Fee memory processingFee
    )
        internal
        virtual
    {
        _getProductStorage()._componentService.setProductFees(productFee, processingFee);
    }


    function _createRisk(
        bytes32 id,
        bytes memory data
    )
        internal
        virtual
        returns (RiskId riskId)
    {
        return _getProductStorage()._riskService.createRisk(
            id,
            data
        );
    }

    function _updateRisk(
        RiskId id,
        bytes memory data
    )
        internal
        virtual
    {
        _getProductStorage()._riskService.updateRisk(
            id,
            data
        );
    }

    function _setRiskLocked(
        RiskId id,
        bool locked
    )
        internal
        virtual
    {
        _getProductStorage()._riskService.setRiskLocked(
            id,
            locked
        );
    }

    function _closeRisk(
        RiskId id
    )
        internal
        virtual
    {
        _getProductStorage()._riskService.closeRisk(
            id
        );
    }


    function _createApplication(
        address applicationOwner,
        RiskId riskId,
        Amount sumInsuredAmount,
        Amount premiumAmount,
        Seconds lifetime,
        NftId bundleNftId,
        ReferralId referralId,
        bytes memory applicationData
    )
        internal
        virtual
        returns (NftId applicationNftId) 
    {
        return _getProductStorage()._applicationService.create(
            applicationOwner,
            riskId,
            sumInsuredAmount,
            premiumAmount,
            lifetime,
            bundleNftId,
            referralId,
            applicationData
        );
    }

    function _revoke(
        NftId applicationNftId
    )
        internal
        virtual
    {
        _getProductStorage()._applicationService.revoke(
            applicationNftId);
    }

    function _createPolicy(
        NftId applicationNftId,
        Timestamp activateAt,
        Amount maxPremiumAmount
    )
        internal
        virtual
        returns (Amount premiumAmount)
    {
        premiumAmount = _getProductStorage()._policyService.createPolicy(
            applicationNftId, 
            activateAt,
            maxPremiumAmount);
    }

    function _decline(
        NftId policyNftId
    )
        internal
        virtual
    {
        _getProductStorage()._policyService.decline(
            policyNftId);
    }

    function _expire(
        NftId policyNftId,
        Timestamp expireAt
    )
        internal
        virtual
        returns (Timestamp expiredAt)
    {
        expiredAt = _getProductStorage()._policyService.expire(policyNftId, expireAt);
    }

    /// @dev adjust the activation date of the policy. 
    /// The policy must already have an activation date set.
    /// Allowed values are from the current blocktime to the expiration date of the policy.
    function _adjustActivation(
        NftId policyNftId,
        Timestamp activateAt
    )
        internal
        virtual
    {
        _getProductStorage()._policyService.adjustActivation(
            policyNftId, 
            activateAt);
    }

    function _collectPremium(
        NftId policyNftId,
        Timestamp activateAt
    )
        internal
        virtual
    {
        _getProductStorage()._policyService.collectPremium(
            policyNftId, 
            activateAt);
    }

    function _activate(
        NftId policyNftId,
        Timestamp activateAt
    )
        internal
        virtual
    {
        _getProductStorage()._policyService.activate(
            policyNftId, 
            activateAt);
    }

    function _close(
        NftId policyNftId
    )
        internal
        virtual
    {
        _getProductStorage()._policyService.close(
            policyNftId);
    }

    function _submitClaim(
        NftId policyNftId,
        Amount claimAmount,
        bytes memory claimData
    )
        internal
        virtual
        returns(ClaimId)
    {
        return _getProductStorage()._claimService.submit(
            policyNftId,
            claimAmount,
            claimData);
    }

    function _revokeClaim(
        NftId policyNftId,
        ClaimId claimId
    )
        internal
        virtual
    {
        _getProductStorage()._claimService.revoke(
            policyNftId,
            claimId);
    }

    function _confirmClaim(
        NftId policyNftId,
        ClaimId claimId,
        Amount confirmedAmount,
        bytes memory data
    )
        internal
        virtual
    {
        _getProductStorage()._claimService.confirm(
            policyNftId,
            claimId,
            confirmedAmount,
            data);
    }

    function _declineClaim(
        NftId policyNftId,
        ClaimId claimId,
        bytes memory data
    )
        internal
        virtual
    {
        _getProductStorage()._claimService.decline(
            policyNftId,
            claimId,
            data);
    }

    function _cancelConfirmedClaim(
        NftId policyNftId,
        ClaimId claimId
    )
        internal
        virtual
    {
        _getProductStorage()._claimService.cancelConfirmedClaim(
            policyNftId,
            claimId);
    }

    function _createPayout(
        NftId policyNftId,
        ClaimId claimId,
        Amount amount,
        bytes memory data
    )
        internal
        virtual
        returns (PayoutId)
    {
        return _getProductStorage()._claimService.createPayout(
            policyNftId, 
            claimId, 
            amount, 
            data);
    }

    function _createPayoutForBeneficiary(
        NftId policyNftId,
        ClaimId claimId,
        Amount amount,
        address beneficiary,
        bytes memory data
    )
        internal
        virtual
        returns (PayoutId)
    {
        return _getProductStorage()._claimService.createPayoutForBeneficiary(
            policyNftId, 
            claimId, 
            amount, 
            beneficiary,
            data);
    }

    function _processPayout(
        NftId policyNftId,
        PayoutId payoutId
    )
        internal
        virtual
        returns (Amount netPayoutAmount, Amount processingFeeAmount)
    {
        (netPayoutAmount, processingFeeAmount) = _getProductStorage()._claimService.processPayout(
            policyNftId,
            payoutId);
    }

    function _cancelPayout(
        NftId policyNftId,
        PayoutId payoutId
    )
        internal
        virtual
    {
        _getProductStorage()._claimService.cancelPayout(
            policyNftId,
            payoutId);
    }

    function _getProductStorage() internal virtual pure returns (ProductStorage storage $) {
        assembly {
            $.slot := PRODUCT_STORAGE_LOCATION_V1
        }
    }
}

File 84 of 130 : 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 85 of 130 : 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;

interface ITransferInterceptor {
    function nftTransferFrom(address from, address to, uint256 tokenId, address operator) external;
}

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

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

import {Amount, AmountLib} from "../type/Amount.sol";
import {IComponent} from "./IComponent.sol";
import {IComponents} from "../instance/module/IComponents.sol";
import {IComponentService} from "./IComponentService.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType, COMPONENT} from "../type/ObjectType.sol";
import {Registerable} from "../shared/Registerable.sol";
import {TokenHandler} from "../shared/TokenHandler.sol";
import {Version, VersionLib} from "../type/Version.sol";


abstract contract Component is
    Registerable,
    IComponent
{
    // keccak256(abi.encode(uint256(keccak256("gif-next.contracts.component.Component.sol")) - 1)) & ~bytes32(uint256(0xff));
    bytes32 public constant COMPONENT_LOCATION_V1 = 0xffe8d4462baed26a47154f4b8f6db497d2f772496965791d25bd456e342b7f00;

    struct ComponentStorage {
        string _name; // unique (per instance) component name
        bool _isInterceptor;
        IComponentService _componentService;
    }


    modifier onlyChainNft() {
        if(msg.sender != getRegistry().getChainNftAddress()) {
            revert ErrorComponentNotChainNft(msg.sender);
        }
        _;
    }


    function _getComponentStorage() private pure returns (ComponentStorage storage $) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            $.slot := COMPONENT_LOCATION_V1
        }
    }


    function __Component_init(
        address authority,
        address registry,
        NftId parentNftId,
        string memory name,
        ObjectType componentType,
        bool isInterceptor,
        address initialOwner,
        bytes memory registryData // writeonly data that will saved in the object info record of the registry
    )
        internal
        virtual
        onlyInitializing()
    {
        if (bytes(name).length == 0) {
            revert ErrorComponentNameLengthZero();
        }

        __Registerable_init(
            authority,
            registry, 
            parentNftId, 
            componentType, 
            isInterceptor, 
            initialOwner, 
            registryData);

        // set component state
        ComponentStorage storage $ = _getComponentStorage();
        $._name = name;
        $._isInterceptor = isInterceptor;
        $._componentService = IComponentService(_getServiceAddress(COMPONENT()));

        _registerInterface(type(IAccessManaged).interfaceId);
        _registerInterface(type(IComponent).interfaceId);
    }


    /// @dev callback function for nft transfers
    /// may only be called by chain nft contract.
    /// override internal function _nftTransferFrom to implement custom behaviour
    function nftTransferFrom(address from, address to, uint256 tokenId, address operator)
        external
        onlyChainNft
    {
        _nftTransferFrom(from, to, tokenId, operator);
    }


    function getWallet() public view virtual returns (address walletAddress) {
        return getTokenHandler().getWallet();
    }

    function getTokenHandler() public virtual view returns (TokenHandler tokenHandler) {
        return getComponentInfo().tokenHandler;
    }

    function getToken() public view virtual returns (IERC20Metadata token) {
        return getTokenHandler().TOKEN();
    }

    function getName() public view override returns(string memory name) {
        return getComponentInfo().name;
    }

    function getVersion() public view virtual returns (Version version) {
        return VersionLib.toVersion(1, 0, 0);
    }

    function getComponentInfo() public virtual view returns (IComponents.ComponentInfo memory info) {
        if (isRegistered()) {
            return _getComponentInfo();
        } else {
            return getInitialComponentInfo();
        }
    }

    /// @dev defines initial component specification
    /// overwrite this function according to your use case
    function getInitialComponentInfo() public virtual view returns (IComponents.ComponentInfo memory info) {
        return _getComponentInfo();
    }


    function isNftInterceptor() public virtual view returns(bool isInterceptor) {
        if (isRegistered()) {
            return getRegistry().getObjectInfo(address(this)).isInterceptor;
        } else {
            return _getComponentStorage()._isInterceptor;
        }
    }


    function isRegistered() public virtual view returns (bool) {
        return getRegistry().getNftIdForAddress(address(this)).gtz();
    }


    /// @dev Approves token hanlder to spend up to the specified amount of tokens.
    /// Reverts if component wallet is not token handler itself.
    /// Only component owner (nft holder) is authorizes to call this function.
    function _approveTokenHandler(IERC20Metadata token, Amount amount)
        internal
        virtual
        returns (Amount oldAllowanceAmount)
    {
        oldAllowanceAmount = AmountLib.toAmount(
            token.allowance(address(getTokenHandler()), address(this)));

        _getComponentStorage()._componentService.approveTokenHandler(
            token, 
            amount);
    }


    /// @dev internal function for nft transfers.
    /// handling logic that deals with nft transfers need to overwrite this function
    function _nftTransferFrom(address from, address to, uint256 tokenId, address operator)
        internal
        virtual
    // solhint-disable-next-line no-empty-blocks
    { 
        // empty default implementation
    }


    /// @dev Sets the components wallet to the specified address.
    /// Depending on the source of the component information this function needs to be overwritten. 
    /// eg for instance linked components that externally store this information with the instance store contract
    function _setWallet(
        address newWallet
    )
        internal
        virtual
    {
        _getComponentStorage()._componentService.setWallet(newWallet);
    }

    function _setLocked(bool locked)
        internal
        virtual
    {
        _getComponentStorage()._componentService.setLocked(locked);
    }


    /// @dev depending on the source of the component information this function needs to be overwritten. 
    /// eg for instance linked components that externally store this information with the instance store contract
    function _getComponentInfo() internal virtual view returns (IComponents.ComponentInfo memory info) {
        ComponentStorage storage $ = _getComponentStorage();
        
        return IComponents.ComponentInfo({
            name: $._name,
            tokenHandler: TokenHandler(address(0))
        });
    }

    /// @dev returns the service address for the specified domain
    /// gets address via lookup from registry using the major version form the linked instance
    function _getServiceAddress(ObjectType domain) internal view returns (address) {
        return getRegistry().getServiceAddress(domain, getRelease());
    }
}

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

File 89 of 130 : IAuthorizedComponent.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.20;

import {IComponent} from "../shared/IComponent.sol";
import {IAuthorization} from "../authorization/IAuthorization.sol";

/// @dev component base class
/// component examples are product, distribution, pool and oracle
interface IAuthorizedComponent is 
    IComponent
{
    /// @dev returns the initial component authorization specification.
    function getAuthorization() external view returns (IAuthorization authorization);

}

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

import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

import {Amount} from "../type/Amount.sol";
import {IComponents} from "../instance/module/IComponents.sol";
import {IRegisterable} from "../shared/IRegisterable.sol";
import {ITransferInterceptor} from "../registry/ITransferInterceptor.sol";
import {TokenHandler} from "../shared/TokenHandler.sol";

/// @dev component base class
/// component examples are staking, product, distribution, pool and oracle
interface IComponent is 
    IRegisterable,
    ITransferInterceptor
{
    error ErrorComponentProductNftIdZero();
    error ErrorComponentProductNftIdNonzero();
    error ErrorComponentNameLengthZero();

    error ErrorComponentNotChainNft(address caller);

    error ErrorComponentWalletAddressZero();
    error ErrorComponentWalletAddressIsSameAsCurrent();
    error ErrorComponentWalletNotComponent();

    event LogComponentWalletAddressChanged(address oldWallet, address newWallet);
    event LogComponentWalletTokensTransferred(address from, address to, uint256 amount);
    event LogComponentTokenHandlerApproved(address tokenHandler, address token, Amount limit, bool isMaxAmount);

    /// @dev returns the name of this component
    /// to successfully register the component with an instance the name MUST be unique in the linked instance
    function getName() external view returns (string memory name);

    /// @dev defines which ERC20 token is used by this component
    function getToken() external view returns (IERC20Metadata token);

    /// @dev returns token handler for this component
    function getTokenHandler() external view returns (TokenHandler tokenHandler);

    /// @dev defines the wallet address used to hold the ERC20 tokens related to this component
    /// the default address is the token handler address
    function getWallet() external view returns (address walletAddress);

    /// @dev returns true iff this compoent intercepts nft minting and transfers for objects registered by this component
    function isNftInterceptor() external view returns(bool isInterceptor);

    /// @dev returns true iff this component is registered with the registry
    function isRegistered() external view returns (bool);

    /// @dev returns the component infos for this component
    /// for a non registered component the function returns getInitialComponentInfo()
    function getComponentInfo() external view returns (IComponents.ComponentInfo memory info);

    /// @dev returns the iniital component infos for this component
    function getInitialComponentInfo() external view returns (IComponents.ComponentInfo memory info);
}

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

import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";

import {Amount} from "../type/Amount.sol";
import {Fee} from "../type/Fee.sol";
import {IService} from "../shared/IService.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {UFixed} from "../type/UFixed.sol";
import {VersionPart} from "../type/Version.sol";

/// @dev component base class
/// component examples are staking, product, distribution, pool and oracle
interface IComponentService is 
    IService
{
    // registerProduct
    error ErrorComponentServiceCallerNotInstance(address caller);
    error ErrorComponentServiceNotProduct(address product);
    error ErrorComponentServiceTokenInvalid(address token);

    // registerComponent
    error ErrorComponentServiceCallerNotProduct(address caller);
    error ErrorComponentServiceNotComponent(address component);

    error ErrorComponentServiceNotInstanceLinkedComponent(address component);
    error ErrorComponentServiceComponentTypeNotSupported(address component, ObjectType invalid);
    error ErrorComponentServiceComponentParentInvalid(address component, NftId required, NftId actual);
    error ErrorComponentServiceComponentReleaseMismatch(address component, VersionPart serviceRelease, VersionPart componentRelease);
    error ErrorComponentServiceComponentAlreadyRegistered(address component);
    
    error ErrorProductServiceNoDistributionExpected(NftId productNftId);
    error ErrorProductServiceDistributionAlreadyRegistered(NftId productNftId, NftId distributionNftId);
    error ErrorProductServiceNoOraclesExpected(NftId productNftId);
    error ErrorProductServiceOraclesAlreadyRegistered(NftId productNftId, uint8 expectedOracles);
    error ErrorProductServicePoolAlreadyRegistered(NftId productNftId, NftId poolNftId);

    error ErrorComponentServiceNewWalletAddressZero();
    error ErrorComponentServiceWalletAddressZero();
    error ErrorComponentServiceWalletAddressIsSameAsCurrent();

    error ErrorComponentServiceWithdrawAmountIsZero();
    error ErrorComponentServiceWithdrawAmountExceedsLimit(Amount withdrawnAmount, Amount withdrawLimit);
    error ErrorComponentServiceWalletAllowanceTooSmall(address wallet, address spender, uint256 allowance, uint256 amount);

    event LogComponentServiceComponentLocked(address component, bool locked);
    event LogComponentServiceRegistered(NftId instanceNftId, NftId componentNftId, ObjectType componentType, address component, address token, address initialOwner); 
    event LogComponentServiceWalletAddressChanged(NftId componentNftId, address currentWallet, address newWallet);
    event LogComponentServiceWalletTokensTransferred(NftId componentNftId, address currentWallet, address newWallet, uint256 currentBalance);
    event LogComponentServiceComponentFeesWithdrawn(NftId componentNftId, address recipient, address token, Amount withdrawnAmount);
    event LogComponentServiceProductFeesUpdated(NftId productNftId);
    event LogComponentServiceDistributionFeesUpdated(NftId distributionNftId);
    event LogComponentServicePoolFeesUpdated(NftId poolNftId);
    event LogComponentServiceUpdateFee(
        NftId nftId, 
        string feeName, 
        UFixed previousFractionalFee, 
        Amount previousFixedFee,
        UFixed newFractionalFee, 
        Amount newFixedFee
    );
    
    //-------- component ----------------------------------------------------//

    /// @dev Approves the callers token handler to spend up to the specified amount of tokens.
    /// Reverts if the component's token handler wallet is not the token handler itself.
    function approveTokenHandler(IERC20Metadata token, Amount amount) external;

    /// @dev Sets the components associated wallet address.
    /// To set the wallet to the token handler contract, use address(0) as the new wallet adress.
    function setWallet(address newWallet) external;

    /// @dev Locks/Unlocks the calling component.
    function setLocked(bool locked) external;

    /// @dev Withdraw fees from the distribution component. Only component owner is allowed to withdraw fees.
    /// @param withdrawAmount the amount to withdraw
    /// @return withdrawnAmount the amount that was actually withdrawn
    function withdrawFees(Amount withdrawAmount) external returns (Amount withdrawnAmount);

    /// @dev Registers the provided component with the product (sender)
    function registerComponent(address component) external returns (NftId componentNftId);

    //-------- product ------------------------------------------------------//

    /// @dev Registers the specified product component for the instance (sender)
    function registerProduct(address product, address token) external returns (NftId productNftId);

    function setProductFees(
        Fee memory productFee, // product fee on net premium
        Fee memory processingFee // product fee on payout amounts        
    ) external;

    //-------- distribution -------------------------------------------------//

    function setDistributionFees(
        Fee memory distributionFee, // distribution fee for sales that do not include commissions
        Fee memory minDistributionOwnerFee // min fee required by distribution owner (not including commissions for distributors)
    ) external;

    //-------- pool ---------------------------------------------------------//

    function setPoolFees(
        Fee memory poolFee, // pool fee on net premium
        Fee memory stakingFee, // pool fee on staked capital from investor
        Fee memory performanceFee // pool fee on profits from capital investors
    ) external;

}

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

import {Amount} from "../type/Amount.sol";
import {IAuthorizedComponent} from "../shared/IAuthorizedComponent.sol";
import {IAuthorization} from "../authorization/IAuthorization.sol";
import {IInstance} from "../instance/IInstance.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";

/// @dev component base class
/// component examples are product, distribution, pool and oracle
interface IInstanceLinkedComponent is 
    IAuthorizedComponent
{
    error ErrorInstanceLinkedComponentTypeMismatch(ObjectType requiredType, ObjectType objectType);
    error ErrorInstanceLinkedComponentNotProduct(NftId nftId, ObjectType objectType);

    /// @dev Withdraw fees from the distribution component. Only component owner is allowed to withdraw fees.
    /// @param amount the amount to withdraw
    /// @return withdrawnAmount the amount that was actually withdrawn
    function withdrawFees(Amount amount) external returns (Amount withdrawnAmount);

    /// @dev defines the instance to which this component is linked to
    function getInstance() external view returns (IInstance instance);

}

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

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

interface ILifecycle {

    error ErrorNoLifecycle(address contractAddress, ObjectType objectType);
    error ErrorFromStateMissmatch(address contractAddress, ObjectType objectType, StateId actual, StateId required);
    error ErrorInvalidStateTransition(
        address contractAddress,
        ObjectType objectType,
        StateId fromStateId,
        StateId toStateId
    );

    function hasLifecycle(
        ObjectType objectType
    ) external view returns (bool);

    function getInitialState(
        ObjectType objectType
    ) external view returns (StateId);

    function isValidTransition(
        ObjectType objectType,
        StateId fromId,
        StateId toId
    ) external view returns (bool);
}

// 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 {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 

contract InitializableERC165 is
    Initializable,
    IERC165
{
    mapping(bytes4 => bool) private _isSupported;

    // @dev initializes with support for ERC165
    function __ERC165_init() internal onlyInitializing() {
        _initializeERC165();
    }

    function _initializeERC165() internal {
        _isSupported[type(IERC165).interfaceId] = true;
    }

    // @dev register support for provided interfaceId
    // includes initialization for ERC165_ID if not yet done
    function _registerInterface(bytes4 interfaceId) internal onlyInitializing() {
        _registerInterfaceNotInitializing(interfaceId);
    }

    function _registerInterfaceNotInitializing(bytes4 interfaceId) internal{
        _isSupported[interfaceId] = true;
    }

    function supportsInterface(bytes4 interfaceId) external view override returns (bool) {
        return _isSupported[interfaceId];
    }
}

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

import {IAuthorization} from "../authorization/IAuthorization.sol";
import {IAuthorizedComponent} from "../shared/IAuthorizedComponent.sol";
import {IComponents} from "../instance/module/IComponents.sol";
import {IComponentService} from "./IComponentService.sol";
import {IInstance} from "../instance/IInstance.sol";
import {IInstanceLinkedComponent} from "./IInstanceLinkedComponent.sol";
import {IInstanceService} from "../instance/IInstanceService.sol";
import {IOracleService} from "../oracle/IOracleService.sol";

import {Amount} from "../type/Amount.sol";
import {Component} from "./Component.sol";
import {InstanceReader} from "../instance/InstanceReader.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType, COMPONENT, INSTANCE, ORACLE, PRODUCT} from "../type/ObjectType.sol";
import {RequestId} from "../type/RequestId.sol";
import {RoleId, RoleIdLib} from "../type/RoleId.sol";
import {TokenHandler} from "../shared/TokenHandler.sol";
import {Timestamp} from "../type/Timestamp.sol";
import {VersionPart} from "../type/Version.sol";

// then add (Distribution|Pool|Product)Upradeable that also intherit from Versionable
// same pattern as for Service which is also upgradeable
abstract contract InstanceLinkedComponent is
    Component,
    IInstanceLinkedComponent
{
    // keccak256(abi.encode(uint256(keccak256("gif-next.contracts.component.Component.sol")) - 1)) & ~bytes32(uint256(0xff));
    bytes32 public constant INSTANCE_LINKED_COMPONENT_LOCATION_V1 = 0xffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f00;

    struct InstanceLinkedComponentStorage {
        IInstance _instance; // instance for this component
        InstanceReader _instanceReader; // instance reader for this component
        IAuthorization _initialAuthorization;
        IComponentService _componentService;
        IOracleService _oracleService;
    }

    //--- view functions ----------------------------------------------------//

    /// @inheritdoc IInstanceLinkedComponent
    function getInstance() public view virtual override returns (IInstance instance) {
        return _getInstanceLinkedComponentStorage()._instance;
    }


    /// @inheritdoc IAuthorizedComponent
    function getAuthorization() external view virtual returns (IAuthorization authorization) {
        return _getInstanceLinkedComponentStorage()._initialAuthorization;
    }


    /// @inheritdoc IInstanceLinkedComponent
    function withdrawFees(Amount amount)
        external
        virtual
        restricted()
        onlyOwner()
        returns (Amount withdrawnAmount)
    {
        return _withdrawFees(amount);
    }

    //--- internal functions ------------------------------------------------//

    function _sendRequest(
        NftId oracleNftId,
        bytes memory requestData,
        Timestamp expiryAt,
        string memory callbackMethod
    )
        internal
        virtual
        returns (RequestId requestId)
    {
        return _getInstanceLinkedComponentStorage()._oracleService.request(
            oracleNftId,
            requestData,
            expiryAt,
            callbackMethod);
    }


    function _cancelRequest(RequestId requestId)
        internal
        virtual
    {
        _getInstanceLinkedComponentStorage()._oracleService.cancel(requestId);
    }


    function _resendRequest(RequestId requestId)
        internal
        virtual
    {
        _getInstanceLinkedComponentStorage()._oracleService.resend(requestId);
    }


    function _getInstanceLinkedComponentStorage() private pure returns (InstanceLinkedComponentStorage storage $) {
        assembly {
            $.slot := INSTANCE_LINKED_COMPONENT_LOCATION_V1
        }
    }


    function __InstanceLinkedComponent_init(
        address registry,
        NftId parentNftId,
        string memory name,
        ObjectType componentType,
        IAuthorization authorization,
        bool isInterceptor,
        address initialOwner
    )
        internal
        virtual
        onlyInitializing()
    {
        // validate registry, nft ids and get parent nft id
        NftId instanceNftId = _checkAndGetInstanceNftId(
            registry, 
            parentNftId, 
            componentType);

        // set and check linked instance
        InstanceLinkedComponentStorage storage $ = _getInstanceLinkedComponentStorage();
        $._instance = IInstance(
            IRegistry(registry).getObjectAddress(instanceNftId));

        // set component specific parameters
        __Component_init(
            $._instance.authority(), // instance linked components need to point to instance admin
            registry, 
            parentNftId, 
            name, 
            componentType, 
            isInterceptor, 
            initialOwner, 
            ""); // registry data

        // set instance linked specific parameters
        $._instanceReader = $._instance.getInstanceReader();
        $._initialAuthorization = authorization;
        $._componentService = IComponentService(_getServiceAddress(COMPONENT())); 
        $._oracleService = IOracleService(_getServiceAddress(ORACLE()));

        // register interfaces
        _registerInterface(type(IInstanceLinkedComponent).interfaceId);
    }


    function _checkAndGetInstanceNftId(
        address registryAddress,
        NftId parentNftId,
        ObjectType componentType
    )
        internal
        view
        returns (NftId instanceNftId)
    {
        // if product, then parent is already instance
        if (componentType == PRODUCT()) {
            _checkAndGetRegistry(registryAddress, parentNftId, INSTANCE());
            return parentNftId;
        }

        // if not product parent is product, and parent of product is instance
        IRegistry registry = _checkAndGetRegistry(registryAddress, parentNftId, PRODUCT());
        return registry.getParentNftId(parentNftId);
    }

    /// @dev checks the and gets registry.
    /// validates registry using a provided nft id and expected object type.
    function _checkAndGetRegistry(
        address registryAddress,
        NftId objectNftId,
        ObjectType requiredType
    )
        internal
        view
        returns (IRegistry registry)
    {
        registry = IRegistry(registryAddress);
        IRegistry.ObjectInfo memory info = registry.getObjectInfo(objectNftId);

        if (info.objectType != requiredType) {
            revert ErrorInstanceLinkedComponentTypeMismatch(requiredType, info.objectType);
        }
    }


    /// @dev for instance linked components the wallet address stored in the instance store.
    /// updating needs to go throug component service
    function _setWallet(address newWallet) internal virtual override onlyOwner {
        IComponentService(_getServiceAddress(COMPONENT())).setWallet(newWallet);
    }


    function _getComponentInfo() internal virtual override view returns (IComponents.ComponentInfo memory info) {
        NftId componentNftId = getRegistry().getNftIdForAddress(address(this));

        // if registered, attempt to return component info via instance reader
        if (componentNftId.gtz()) {
            // component registerd with registry
            info = _getInstanceReader().getComponentInfo(getNftId());

            // check if also registered with instance
            if (address(info.tokenHandler) != address(0)) {
                return info;
            }
        }

        // return data from component contract if not yet registered
        return super._getComponentInfo();
    }


    /// @dev returns reader for linked instance
    function _getInstanceReader() internal view returns (InstanceReader reader) {
        return _getInstanceLinkedComponentStorage()._instanceReader;
    }

    function _withdrawFees(Amount amount)
        internal
        returns (Amount withdrawnAmount)
    {
        return _getInstanceLinkedComponentStorage()._componentService.withdrawFees(amount);
    }
}

// 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 99 of 130 : 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 {ObjectType} from "../type/ObjectType.sol";
import {StateId, zeroStateId} from "../type/StateId.sol";
import {ILifecycle} from "./ILifecycle.sol";

abstract contract Lifecycle is
    ILifecycle
{
    mapping(ObjectType objectType => StateId initialState)
        private _initialState;

    mapping(ObjectType objectType => mapping(StateId stateFrom => mapping(StateId stateTo => bool isValid)))
        private _isValidTransition;

    /// @dev child class must implement and CALL setup func at deployment/initializaton time
    function _setupLifecycle() internal virtual;

    function setInitialState(ObjectType ttype, StateId state) internal virtual {
        assert(_initialState[ttype] == zeroStateId());
        _initialState[ttype] = state;
    }

    function setStateTransition(ObjectType ttype, StateId oldState, StateId newState) internal virtual {
        assert(_isValidTransition[ttype][oldState][newState] == false);
        _isValidTransition[ttype][oldState][newState] = true;
    }

    function hasLifecycle(
        ObjectType objectType
    )
        public
        view
        override
        returns (bool)
    {
        return _initialState[objectType].gtz();
    }

    function getInitialState(
        ObjectType objectType
    )
        public
        view
        returns (StateId)
    {
        return _initialState[objectType];
    }

    function checkTransition(
        StateId stateId,
        ObjectType objectType,
        StateId expectedFromId,
        StateId toId
    )
        public
        view
    {
        // revert if no life cycle support
        if (_initialState[objectType].eqz()) {
            revert ErrorNoLifecycle(address(this), objectType);
        }

        // revert if current state is not expected `from` state
        if(stateId != expectedFromId) {
            revert ErrorFromStateMissmatch(address(this), objectType, stateId, expectedFromId);
        }

        // enforce valid state transition
        if (!_isValidTransition[objectType][stateId][toId]) {
            revert ErrorInvalidStateTransition(
                address(this),
                objectType, 
                stateId, 
                toId
            );
        }
    }

    function isValidTransition(
        ObjectType objectType,
        StateId fromId,
        StateId toId
    ) public view returns (bool) {
        return _isValidTransition[objectType][fromId][toId];
    }
}

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

import {InitializableERC165} from "./InitializableERC165.sol";
import {INftOwnable} from "./INftOwnable.sol";
import {NftId} from "../type/NftId.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {RegistryLinked} from "./RegistryLinked.sol";

contract NftOwnable is
    InitializableERC165,
    RegistryLinked,
    INftOwnable
{
    // keccak256(abi.encode(uint256(keccak256("etherisc.storage.NftOwnable")) - 1)) & ~bytes32(uint256(0xff));
    bytes32 public constant NFT_OWNABLE_STORAGE_LOCATION_V1 = 0x07ebcf49758b6ed3af50fa146bec0abe157c0218fe65dc0874c286e9d5da4f00;

    struct NftOwnableStorage {
        NftId _nftId;
        address _initialOwner; 
    }

    /// @dev enforces msg.sender is owner of nft (or initial owner of nft ownable)
    modifier onlyOwner() {
        if (msg.sender != getOwner()) {
            revert ErrorNftOwnableNotOwner(msg.sender);
        }
        _;
    }

    modifier onlyNftOwner(NftId nftId) {
        if(!getRegistry().isOwnerOf(nftId, msg.sender)) {
            revert ErrorNftOwnableNotOwner(msg.sender);
        }
        _;
    }

    modifier onlyNftOfType(NftId nftId, ObjectType expectedObjectType) {
        _checkNftType(nftId, expectedObjectType);
        _;
    }

    function _checkNftType(NftId nftId, ObjectType expectedObjectType) internal view {
        if(expectedObjectType.eqz() || !getRegistry().isObjectType(nftId, expectedObjectType)) {
            revert ErrorNftOwnableInvalidType(nftId, expectedObjectType);
        }
    }


    /// @dev Initialization for upgradable contracts.
    // used in __Registerable_init, ProxyManager._preDeployChecksAndSetup
    function __NftOwnable_init(
        address registry,
        address initialOwner
    )
        internal
        virtual
        onlyInitializing()
    {
        __ERC165_init();
        __RegistryLinked_init(registry);

        if(initialOwner == address(0)) {
            revert ErrorNftOwnableInitialOwnerZero();
        }

        _getNftOwnableStorage()._initialOwner = initialOwner;
    }


    /// @dev links this contract to nft after registration
    // needs to be done once per registered contract and 
    // reduces registry calls to check ownership
    // does not need any protection as function can only do the "right thing"
    function linkToRegisteredNftId()
        public
        virtual
        returns (NftId nftId)
    {
        return _linkToNftOwnable(address(this));
    }

    function getNftId() public view virtual override returns (NftId) {
        return _getNftOwnableStorage()._nftId;
    }

    function getOwner() public view virtual override returns (address) {
        NftOwnableStorage storage $ = _getNftOwnableStorage();

        if ($._nftId.gtz()) {
            return getRegistry().ownerOf($._nftId);
        }

        return $._initialOwner;
    }

    /// @dev used in constructor of registry service manager
    // links ownership of registry service manager ot nft owner of registry service
    function _linkToNftOwnable(
        address nftOwnableAddress
    )
        internal
        returns (NftId)
    {
        NftOwnableStorage storage $ = _getNftOwnableStorage();

        if ($._nftId.gtz()) {
            revert ErrorNftOwnableAlreadyLinked($._nftId);
        }

        if (!getRegistry().isRegistered(nftOwnableAddress)) {
            revert ErrorNftOwnableContractNotRegistered(nftOwnableAddress);
        }

        $._nftId = getRegistry().getNftIdForAddress(nftOwnableAddress);

        emit LogNftOwnableNftLinkedToAddress($._nftId, getOwner());

        return $._nftId;
    }


    function _getNftOwnableStorage() private pure returns (NftOwnableStorage storage $) {
        // solhint-disable-next-line no-inline-assembly
        assembly {
            $.slot := NFT_OWNABLE_STORAGE_LOCATION_V1
        }
    }
}

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

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

import {AccessManagerCloneable} from "../authorization/AccessManagerCloneable.sol";
import {ContractLib} from "../shared/ContractLib.sol";
import {NftId, NftIdLib} from "../type/NftId.sol";
import {NftOwnable} from "../shared/NftOwnable.sol";
import {ObjectType} from "../type/ObjectType.sol";
import {VersionPart, VersionPartLib} from "../type/Version.sol";

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

abstract contract Registerable is
    AccessManagedUpgradeable,
    NftOwnable,
    IRegisterable
{
    // keccak256(abi.encode(uint256(keccak256("gif-next.contracts.shared.Registerable.sol")) - 1)) & ~bytes32(uint256(0xff));
    bytes32 public constant REGISTERABLE_LOCATION_V1 = 0x6548007c3f4340f82f348c576c0ff69f4f529cadd5ad41f96aae61abceeaa300;

    struct RegisterableStorage {
        NftId _parentNftId;
        ObjectType _objectType;
        bool _isInterceptor;
        bytes _data;
    }

    modifier onlyActive() {
        if (!isActive()) {
            revert ErrorRegisterableNotActive();
        }
        _;
    }

    function __Registerable_init(
        address authority,
        address registry,
        NftId parentNftId,
        ObjectType objectType,
        bool isInterceptor,
        address initialOwner,
        bytes memory data // writeonly data that will saved in the object info record of the registry
    )
        internal
        virtual
        onlyInitializing()
    {
        if (!ContractLib.isAuthority(authority)) {
            revert ErrorAuthorityInvalid(authority);
        }

        __AccessManaged_init(authority);
        __NftOwnable_init(registry, initialOwner);

        RegisterableStorage storage $ = _getRegisterableStorage();
        $._parentNftId = parentNftId;
        $._objectType = objectType;
        $._isInterceptor = isInterceptor;
        $._data = data;

        _registerInterface(type(IAccessManaged).interfaceId);
    }


    /// @inheritdoc IRegisterable
    function isActive() public virtual view returns (bool active) {
        return !AccessManagerCloneable(authority()).isTargetClosed(address(this));
    }


    /// @inheritdoc IRelease
    function getRelease() public virtual view returns (VersionPart release) {
        return AccessManagerCloneable(authority()).getRelease();
    }


    /// @inheritdoc IRegisterable
    function getInitialInfo() 
        public 
        view 
        virtual 
        returns (IRegistry.ObjectInfo memory info) 
    {
        RegisterableStorage storage $ = _getRegisterableStorage();
        return IRegistry.ObjectInfo(
            NftIdLib.zero(),
            $._parentNftId,
            $._objectType,
            $._isInterceptor,
            address(this), 
            getOwner(),
            $._data);
    }


    function _getRegisterableStorage() private pure returns (RegisterableStorage storage $) {
        assembly {
            $.slot := REGISTERABLE_LOCATION_V1
        }
    }
}

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

import {Initializable} from "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol"; 

import {ContractLib} from "../shared/ContractLib.sol";
import {IRegistry} from "../registry/IRegistry.sol";
import {IRegistryLinked} from "./IRegistryLinked.sol";

contract RegistryLinked is
    Initializable,
    IRegistryLinked
{

    // priorize simplicity and size over using standard upgradeability structs
    IRegistry private _registry;

    /// @dev initialization for upgradable contracts
    // used in _initializeRegisterable
    function __RegistryLinked_init(
        address registry
    )
        internal
        virtual
        onlyInitializing()
    {
        if (!ContractLib.isRegistry(registry)) {
            revert ErrorNotRegistry(registry);
        }

        _registry = IRegistry(registry);
    }


    function getRegistry() public view returns (IRegistry) {
        return _registry;
    }
}

// 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: 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 {Key32, KeyId, Key32Lib} from "./Key32.sol";
import {NftId} from "./NftId.sol";
import {DISTRIBUTOR_TYPE} from "./ObjectType.sol";

type DistributorType is bytes8;

// type bindings
using {
    eqDistributorType as ==,
    neDistributorType as !=,
    DistributorTypeLib.toKey32
} for DistributorType global;

// general pure free functions

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

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

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

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

    // @dev Converts a referral string into an id.
    function toDistributorType(NftId distributionNftId, string memory name) public pure returns (DistributorType) {
        return DistributorType.wrap(bytes8(keccak256(abi.encode(distributionNftId, name))));
    }

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

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

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

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

struct Fee {
    // slot 0
    UFixed fractionalFee;
    Amount fixedFee;
}

library FeeLib {

    /// @dev Return a zero fee struct (0, 0)
    function zero() public pure returns (Fee memory fee) {
        return Fee(UFixed.wrap(0), AmountLib.zero());
    }

    /// @dev Converts the uint256 to a fee struct.
    function toFee(
        UFixed fractionalFee,
        uint256 fixedFee
    ) public pure returns (Fee memory fee) {
        return Fee(fractionalFee, AmountLib.toAmount(fixedFee));
    }

    /// @dev Calculates fee and net amounts for the provided parameters
    function calculateFee(
        Fee memory fee,
        Amount amount
    )
        public
        pure
        returns (
            Amount feeAmount, 
            Amount netAmount
        )
    {
        netAmount = amount;
        if(gtz(fee)) {
            UFixed fractionalAmount = 
                amount.toUFixed() * fee.fractionalFee;
            feeAmount = AmountLib.toAmount(fractionalAmount.toInt()) + fee.fixedFee;
            netAmount = netAmount - feeAmount;
        }
    }

    /// @dev Return the percent fee struct (x%, 0)
    function percentageFee(uint8 percent) public pure returns (Fee memory fee) {
        return Fee(UFixedLib.toUFixed(percent, -2), AmountLib.zero());
    }

    // pure free functions for operators
    function eq(Fee memory a, Fee memory b) public pure returns (bool isSame) {
        return a.fixedFee == b.fixedFee && a.fractionalFee == b.fractionalFee;
    }

    function gtz(Fee memory fee) public pure returns (bool) {
        return UFixed.unwrap(fee.fractionalFee) > 0 || fee.fixedFee.gtz();
    }

    function eqz(Fee memory fee) public pure returns (bool) {
        return fee.fixedFee.eqz() && UFixed.unwrap(fee.fractionalFee) == 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} from "../type/Key32.sol";

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

    struct Set {
        Key32[] keys;
        mapping(Key32 key => uint256 index) at;
    }

    error ErrorKey32SetAlreadyAdded(Key32 key);
    error ErrorKey32SetNotInSet(Key32 key);


    function add(Set storage set, Key32 key) external {
        if (set.at[key] > 0) {
            revert ErrorKey32SetAlreadyAdded(key);
        }

        set.keys.push(key);
        set.at[key] = set.keys.length;
    }

    function remove(Set storage set, Key32 key) external {
        uint256 nftIdIndex = set.at[key];

        if (nftIdIndex == 0) {
            revert ErrorKey32SetNotInSet(key);
        }

        uint256 toDeleteIndex = nftIdIndex - 1;
        uint256 lastIndex = set.keys.length - 1;

        if (lastIndex != toDeleteIndex) {
            Key32 lastId = set.keys[lastIndex];
            set.keys[toDeleteIndex] = lastId;
            set.at[lastId] = nftIdIndex; // Replace lastValue's index to valueIndex
        }

        set.keys.pop();
        delete set.at[key];
    }

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

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

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

    function getElementAt(Set storage set, uint256 index) external view returns(Key32 key) {
        return set.keys[index];
    }
}

// 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 {NftId} from "../type/NftId.sol";

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

    struct Set {
        NftId[] ids;
        mapping(NftId nftid => uint256 index) at;
    }

    error ErrorNftIdSetAlreadyAdded(NftId nftId);
    error ErrorNftIdSetNotInSet(NftId nftId);


    function add(Set storage set, NftId nftId) external {
        if (set.at[nftId] > 0) {
            revert ErrorNftIdSetAlreadyAdded(nftId);
        }

        set.ids.push(nftId);
        set.at[nftId] = set.ids.length;
    }

    function remove(Set storage set, NftId nftId) external {
        uint256 nftIdIndex = set.at[nftId];

        if (nftIdIndex == 0) {
            revert ErrorNftIdSetNotInSet(nftId);
        }

        uint256 toDeleteIndex = nftIdIndex - 1;
        uint256 lastIndex = set.ids.length - 1;

        if (lastIndex != toDeleteIndex) {
            NftId lastId = set.ids[lastIndex];
            set.ids[toDeleteIndex] = lastId;
            set.at[lastId] = nftIdIndex; // Replace lastValue's index to valueIndex
        }

        set.ids.pop();
        delete set.at[nftId];
    }

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

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

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

    function getElementAt(Set storage set, uint256 index) external view returns(NftId nftId) {
        return set.ids[index];
    }
}

// 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 {NftId} from "./NftId.sol";
import {REFERRAL} from "./ObjectType.sol";

type ReferralId is bytes8;
type ReferralStatus is uint8;

// type bindings
using {
    eqReferralId as ==, 
    neReferralId as !=,
    ReferralLib.eqz,
    ReferralLib.toInt,
    ReferralLib.toKey32
} for ReferralId global;

using {
    eqReferralStatus as ==, 
    neReferralStatus as !=
} for ReferralStatus global;

// general pure free functions

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

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

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

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

function REFERRAL_OK() pure returns (ReferralStatus) {
    return ReferralStatus.wrap(10);
}

function REFERRAL_ERROR_UNKNOWN() pure returns (ReferralStatus) {
    return ReferralStatus.wrap(100);
}

function REFERRAL_ERROR_EXPIRED() pure returns (ReferralStatus) {
    return ReferralStatus.wrap(110);
}

function REFERRAL_ERROR_EXHAUSTED() pure returns (ReferralStatus) {
    return ReferralStatus.wrap(120);
}

library ReferralLib {

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

    // @dev Converts a referral string into an id.
    function toReferralId(NftId distributionNftId, string memory referral) public pure returns (ReferralId) {
        return ReferralId.wrap(bytes8(keccak256(abi.encode(distributionNftId, referral))));
    }

    function toReferralStatus(uint8 status) public pure returns (ReferralStatus) {
        return ReferralStatus.wrap(status);
    }

    /// @dev Converts a referral id into a uint256.
    function toInt(ReferralId referralId) public pure returns (uint256) {
        return uint64(ReferralId.unwrap(referralId));
    }

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

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

    function eqz(ReferralId id) public pure returns (bool) {
        return eqReferralId(id, zero());
    }
}

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

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

type RequestId is uint64;

// type bindings
using {
    eqRequestId as ==, 
    neRequestId as !=,
    RequestIdLib.eqz,
    RequestIdLib.gtz,
    RequestIdLib.toInt,
    RequestIdLib.toKey32
} for RequestId global;

// general pure free functions

function eqRequestId(RequestId a, RequestId b) pure returns (bool isSame) {
    return RequestIdLib.eq(a, b);
}

function neRequestId(RequestId a, RequestId b) pure returns (bool isSame) {
    return RequestIdLib.ne(a, b);
}

library RequestIdLib {

    // @dev zero element to refer to a non existing/initialized request
    function zero() public pure returns (RequestId) {
        return RequestId.wrap(0);
    }

    // @dev Converts an int id into a request id.
    function toRequestId(uint256 id) public pure returns (RequestId) {
        return RequestId.wrap(uint64(id));
    }

    // @dev Converts a request id back to an int value.
    function toInt(RequestId requestId) public pure returns (uint256) {
        return RequestId.unwrap(requestId);
    }

    // @dev Returns true iff request id a == 0
    function eqz(RequestId a) public pure returns (bool) {
        return RequestId.unwrap(a) == 0;
    }

    // @dev Returns true iff request id a > 0
    function gtz(RequestId a) public pure returns (bool) {
        return RequestId.unwrap(a) > 0;
    }

    // @dev Returns true iff risk ids a and b are identical
    function eq(RequestId a, RequestId b) public pure returns (bool isSame) {
        return RequestId.unwrap(a) == RequestId.unwrap(b);
    }

    // @dev Returns true iff risk ids a and b are different
    function ne(RequestId a, RequestId b) public pure returns (bool isSame) {
        return RequestId.unwrap(a) != RequestId.unwrap(b);
    }

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

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

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

import {RequestId} from "../type/RequestId.sol";

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

    struct Set {
        RequestId[] ids;
        mapping(RequestId requestId => uint256 index) at;
    }

    error ErrorRequestIdSetAlreadyAdded(RequestId requestId);
    error ErrorRequestIdSetNotInSet(RequestId requestId);


    function add(Set storage set, RequestId requestId) external {
        if (set.at[requestId] > 0) {
            revert ErrorRequestIdSetAlreadyAdded(requestId);
        }

        set.ids.push(requestId);
        set.at[requestId] = set.ids.length;
    }

    function remove(Set storage set, RequestId requestId) external {
        uint256 requestIdIndex = set.at[requestId];

        if (requestIdIndex == 0) {
            revert ErrorRequestIdSetNotInSet(requestId);
        }

        uint256 toDeleteIndex = requestIdIndex - 1;
        uint256 lastIndex = set.ids.length - 1;

        if (lastIndex != toDeleteIndex) {
            RequestId lastId = set.ids[lastIndex];
            set.ids[toDeleteIndex] = lastId;
            set.at[lastId] = requestIdIndex; // Replace lastValue's index to valueIndex
        }

        set.ids.pop();
        delete set.at[requestId];
    }

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

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

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

    function getElementAt(Set storage set, uint256 index) external view returns(RequestId requestId) {
        return set.ids[index];
    }
}

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

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

type RiskId is bytes8;

// type bindings
using {
    eqRiskId as ==, 
    neRiskId as !=,
    RiskIdLib.eq,
    RiskIdLib.eqz,
    RiskIdLib.gtz,
    RiskIdLib.toInt,
    RiskIdLib.toKeyId,
    RiskIdLib.toKey32
} for RiskId global;

// general pure free functions

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

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

library RiskIdLib {
    function zero() public pure returns (RiskId) {
        return RiskId.wrap(bytes8(0));
    }

    // @dev Converts a risk id into a uint256.
    function toInt(RiskId riskId) public pure returns (uint256) {
        return uint64(RiskId.unwrap(riskId));
    }

    // @dev Converts a risk id string with a product NftId into a risk id.
    function toRiskId(NftId productNftId, bytes32 risk) public pure returns (RiskId) {
        return RiskId.wrap(bytes8(keccak256(abi.encode(productNftId, risk))));
    }

    /// @dev Returns the key32 value for the specified risk id.
    function toKey32(RiskId riskId) public pure returns (Key32 key) {
        return Key32Lib.toKey32(RISK(), toKeyId(riskId));
    }

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

    function toRiskId(KeyId keyId) public pure returns (RiskId riskId) {
        riskId = RiskId.wrap(bytes8(KeyId.unwrap(keyId)));
        assert(toInt(riskId) < 2**64);
    }

    function eq(RiskId a, RiskId b) public pure returns (bool isSame) {
        return eqRiskId(a, b);
    }

    function eqz(RiskId a) public pure returns (bool isZero) {
        return eqRiskId(a, zero());
    }

    function gtz(RiskId a) public pure returns (bool isZero) {
        return uint64(RiskId.unwrap(a)) > 0;
    }
}

// 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 128 of 130 : 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/Fee.sol": {
      "FeeLib": "0x7344Ca80C9187E40D161c55aa59B68b680ab0b9A"
    },
    "contracts/examples/flight/FlightLib.sol": {
      "FlightLib": "0x08bd2a26fcf141f9244e9f71c3c99249451c89d3"
    },
    "contracts/type/Amount.sol": {
      "AmountLib": "0x82860d6202c009092d0d7507380ba8ef8f4fd567"
    },
    "contracts/type/NftId.sol": {
      "NftIdLib": "0xb6fe977f3e63d01a734dd238ba5fc05bf45bf32c"
    },
    "contracts/type/ObjectType.sol": {
      "ObjectTypeLib": "0xa6b4725b0a1cf143baf5099fdd58abc8d862d34f"
    },
    "contracts/type/Referral.sol": {
      "ReferralLib": "0xf41933c60fdea61085b0ff18b906cddcd547634c"
    },
    "contracts/type/RequestId.sol": {
      "RequestIdLib": "0x6583a0cc1ea5b6841adae78a8ce62f319a028053"
    },
    "contracts/type/Seconds.sol": {
      "SecondsLib": "0x3fb33d6b382d000f1ef0928f8942c56ef3af6c41"
    },
    "contracts/type/String.sol": {
      "StrLib": "0xb7336e875465659bad9659b25d078c6cace41706"
    },
    "contracts/type/Timestamp.sol": {
      "TimestampLib": "0xfdbcee5ffb06ba0f107922a3294110ce43c3112e"
    },
    "contracts/type/Version.sol": {
      "VersionLib": "0x1c7f4780c639cb898b64769694d308a5a363a83e"
    }
  }
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"registry","type":"address"},{"internalType":"NftId","name":"instanceNftId","type":"uint96"},{"internalType":"string","name":"componentName","type":"string"},{"internalType":"contract IAuthorization","name":"authorization","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":"authority","type":"address"}],"name":"ErrorAuthorityInvalid","type":"error"},{"inputs":[],"name":"ErrorComponentNameLengthZero","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"ErrorComponentNotChainNft","type":"error"},{"inputs":[],"name":"ErrorComponentProductNftIdNonzero","type":"error"},{"inputs":[],"name":"ErrorComponentProductNftIdZero","type":"error"},{"inputs":[],"name":"ErrorComponentWalletAddressIsSameAsCurrent","type":"error"},{"inputs":[],"name":"ErrorComponentWalletAddressZero","type":"error"},{"inputs":[],"name":"ErrorComponentWalletNotComponent","type":"error"},{"inputs":[{"internalType":"NftId","name":"nftId","type":"uint96"},{"internalType":"ObjectType","name":"objectType","type":"uint8"}],"name":"ErrorInstanceLinkedComponentNotProduct","type":"error"},{"inputs":[{"internalType":"ObjectType","name":"requiredType","type":"uint8"},{"internalType":"ObjectType","name":"objectType","type":"uint8"}],"name":"ErrorInstanceLinkedComponentTypeMismatch","type":"error"},{"inputs":[{"internalType":"NftId","name":"nftId","type":"uint96"}],"name":"ErrorNftOwnableAlreadyLinked","type":"error"},{"inputs":[{"internalType":"address","name":"contractAddress","type":"address"}],"name":"ErrorNftOwnableContractNotRegistered","type":"error"},{"inputs":[],"name":"ErrorNftOwnableInitialOwnerZero","type":"error"},{"inputs":[{"internalType":"NftId","name":"nftId","type":"uint96"},{"internalType":"ObjectType","name":"expectedObjectType","type":"uint8"}],"name":"ErrorNftOwnableInvalidType","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"ErrorNftOwnableNotOwner","type":"error"},{"inputs":[{"internalType":"address","name":"registryAddress","type":"address"}],"name":"ErrorNotRegistry","type":"error"},{"inputs":[],"name":"ErrorRegisterableNotActive","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"authority","type":"address"}],"name":"AuthorityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"tokenHandler","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"Amount","name":"limit","type":"uint96"},{"indexed":false,"internalType":"bool","name":"isMaxAmount","type":"bool"}],"name":"LogComponentTokenHandlerApproved","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldWallet","type":"address"},{"indexed":false,"internalType":"address","name":"newWallet","type":"address"}],"name":"LogComponentWalletAddressChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"from","type":"address"},{"indexed":false,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"LogComponentWalletTokensTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"RiskId","name":"riskId","type":"bytes8"},{"indexed":false,"internalType":"uint8","name":"payoutOption","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"policiesProcessed","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"policiesRemaining","type":"uint256"}],"name":"LogFlightPoliciesProcessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"NftId","name":"policyNftId","type":"uint96"},{"indexed":false,"internalType":"Amount","name":"payoutAmount","type":"uint96"}],"name":"LogFlightPolicyClosed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"NftId","name":"policyNftId","type":"uint96"},{"indexed":false,"internalType":"string","name":"flightData","type":"string"},{"indexed":false,"internalType":"Amount","name":"premiumAmount","type":"uint96"}],"name":"LogFlightPolicyPurchased","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"RequestId","name":"requestId","type":"uint64"},{"indexed":false,"internalType":"RiskId","name":"riskId","type":"bytes8"},{"indexed":false,"internalType":"bytes1","name":"status","type":"bytes1"},{"indexed":false,"internalType":"int256","name":"delayMinutes","type":"int256"},{"indexed":false,"internalType":"uint8","name":"payoutOption","type":"uint8"}],"name":"LogFlightStatusProcessed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"NftId","name":"nftId","type":"uint96"},{"indexed":false,"internalType":"address","name":"owner","type":"address"}],"name":"LogNftOwnableNftLinkedToAddress","type":"event"},{"inputs":[],"name":"COMPONENT_LOCATION_V1","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"INSTANCE_LINKED_COMPONENT_LOCATION_V1","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"LIFETIME","outputs":[{"internalType":"Seconds","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MARGIN_PERCENT","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_FLIGHT_DURATION","outputs":[{"internalType":"Seconds","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PAYOUT","outputs":[{"internalType":"Amount","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_POLICIES_TO_PROCESS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_PREMIUM","outputs":[{"internalType":"Amount","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TIME_BEFORE_DEPARTURE","outputs":[{"internalType":"Seconds","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MAX_TOTAL_PAYOUT","outputs":[{"internalType":"Amount","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_OBSERVATIONS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_PREMIUM","outputs":[{"internalType":"Amount","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIN_TIME_BEFORE_DEPARTURE","outputs":[{"internalType":"Seconds","name":"","type":"uint40"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"NFT_OWNABLE_STORAGE_LOCATION_V1","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRODUCT_STORAGE_LOCATION_V1","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"REGISTERABLE_LOCATION_V1","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"WEIGHT_PATTERN","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract IERC20Metadata","name":"token","type":"address"},{"internalType":"Amount","name":"amount","type":"uint96"}],"name":"approveTokenHandler","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"authority","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"Amount","name":"","type":"uint96"},{"internalType":"RiskId","name":"","type":"bytes8"},{"internalType":"Seconds","name":"","type":"uint40"},{"internalType":"bytes","name":"applicationData","type":"bytes"}],"name":"calculateNetPremium","outputs":[{"internalType":"Amount","name":"netPremiumAmount","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"contract FlightProduct","name":"flightProduct","type":"address"},{"internalType":"Amount","name":"premium","type":"uint96"},{"internalType":"uint256[6]","name":"statistics","type":"uint256[6]"}],"name":"calculatePayoutAmounts","outputs":[{"internalType":"uint256","name":"weight","type":"uint256"},{"internalType":"Amount[5]","name":"payoutAmounts","type":"uint96[5]"},{"internalType":"Amount","name":"sumInsuredAmount","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"Amount","name":"sumInsuredAmount","type":"uint96"},{"internalType":"RiskId","name":"riskId","type":"bytes8"},{"internalType":"Seconds","name":"lifetime","type":"uint40"},{"internalType":"bytes","name":"applicationData","type":"bytes"},{"internalType":"NftId","name":"bundleNftId","type":"uint96"},{"internalType":"ReferralId","name":"referralId","type":"bytes8"}],"name":"calculatePremium","outputs":[{"internalType":"Amount","name":"premiumAmount","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct FlightProduct.PermitData","name":"permit","type":"tuple"},{"components":[{"internalType":"string","name":"flightData","type":"string"},{"internalType":"Timestamp","name":"departureTime","type":"uint40"},{"internalType":"string","name":"departureTimeLocal","type":"string"},{"internalType":"Timestamp","name":"arrivalTime","type":"uint40"},{"internalType":"string","name":"arrivalTimeLocal","type":"string"},{"internalType":"Amount","name":"premiumAmount","type":"uint96"},{"internalType":"uint256[6]","name":"statistics","type":"uint256[6]"}],"internalType":"struct FlightProduct.ApplicationData","name":"application","type":"tuple"}],"name":"createPolicyWithPermit","outputs":[{"internalType":"RiskId","name":"riskId","type":"bytes8"},{"internalType":"NftId","name":"policyNftId","type":"uint96"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"data","type":"bytes"}],"name":"decodeFlightRiskData","outputs":[{"components":[{"internalType":"Str","name":"flightData","type":"bytes32"},{"internalType":"Timestamp","name":"departureTime","type":"uint40"},{"internalType":"string","name":"departureTimeLocal","type":"string"},{"internalType":"Timestamp","name":"arrivalTime","type":"uint40"},{"internalType":"string","name":"arrivalTimeLocal","type":"string"},{"internalType":"Amount","name":"sumOfSumInsuredAmounts","type":"uint96"},{"internalType":"bytes1","name":"status","type":"bytes1"},{"internalType":"int256","name":"delayMinutes","type":"int256"},{"internalType":"uint8","name":"payoutOption","type":"uint8"},{"internalType":"Timestamp","name":"statusUpdatedAt","type":"uint40"}],"internalType":"struct FlightProduct.FlightRisk","name":"","type":"tuple"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"RequestId","name":"requestId","type":"uint64"},{"internalType":"bytes","name":"responseData","type":"bytes"}],"name":"flightStatusCallback","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAuthorization","outputs":[{"internalType":"contract IAuthorization","name":"authorization","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getComponentInfo","outputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"contract TokenHandler","name":"tokenHandler","type":"address"}],"internalType":"struct IComponents.ComponentInfo","name":"info","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"RiskId","name":"riskId","type":"bytes8"},{"internalType":"bool","name":"requireRiskExists","type":"bool"}],"name":"getFlightRisk","outputs":[{"internalType":"bool","name":"exists","type":"bool"},{"components":[{"internalType":"Str","name":"flightData","type":"bytes32"},{"internalType":"Timestamp","name":"departureTime","type":"uint40"},{"internalType":"string","name":"departureTimeLocal","type":"string"},{"internalType":"Timestamp","name":"arrivalTime","type":"uint40"},{"internalType":"string","name":"arrivalTimeLocal","type":"string"},{"internalType":"Amount","name":"sumOfSumInsuredAmounts","type":"uint96"},{"internalType":"bytes1","name":"status","type":"bytes1"},{"internalType":"int256","name":"delayMinutes","type":"int256"},{"internalType":"uint8","name":"payoutOption","type":"uint8"},{"internalType":"Timestamp","name":"statusUpdatedAt","type":"uint40"}],"internalType":"struct FlightProduct.FlightRisk","name":"flightRisk","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getInitialComponentInfo","outputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"contract TokenHandler","name":"tokenHandler","type":"address"}],"internalType":"struct IComponents.ComponentInfo","name":"info","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getInitialFeeInfo","outputs":[{"components":[{"components":[{"internalType":"UFixed","name":"fractionalFee","type":"uint160"},{"internalType":"Amount","name":"fixedFee","type":"uint96"}],"internalType":"struct Fee","name":"productFee","type":"tuple"},{"components":[{"internalType":"UFixed","name":"fractionalFee","type":"uint160"},{"internalType":"Amount","name":"fixedFee","type":"uint96"}],"internalType":"struct Fee","name":"processingFee","type":"tuple"},{"components":[{"internalType":"UFixed","name":"fractionalFee","type":"uint160"},{"internalType":"Amount","name":"fixedFee","type":"uint96"}],"internalType":"struct Fee","name":"distributionFee","type":"tuple"},{"components":[{"internalType":"UFixed","name":"fractionalFee","type":"uint160"},{"internalType":"Amount","name":"fixedFee","type":"uint96"}],"internalType":"struct Fee","name":"minDistributionOwnerFee","type":"tuple"},{"components":[{"internalType":"UFixed","name":"fractionalFee","type":"uint160"},{"internalType":"Amount","name":"fixedFee","type":"uint96"}],"internalType":"struct Fee","name":"poolFee","type":"tuple"},{"components":[{"internalType":"UFixed","name":"fractionalFee","type":"uint160"},{"internalType":"Amount","name":"fixedFee","type":"uint96"}],"internalType":"struct Fee","name":"stakingFee","type":"tuple"},{"components":[{"internalType":"UFixed","name":"fractionalFee","type":"uint160"},{"internalType":"Amount","name":"fixedFee","type":"uint96"}],"internalType":"struct Fee","name":"performanceFee","type":"tuple"}],"internalType":"struct IComponents.FeeInfo","name":"feeInfo","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getInitialInfo","outputs":[{"components":[{"internalType":"NftId","name":"nftId","type":"uint96"},{"internalType":"NftId","name":"parentNftId","type":"uint96"},{"internalType":"ObjectType","name":"objectType","type":"uint8"},{"internalType":"bool","name":"isInterceptor","type":"bool"},{"internalType":"address","name":"objectAddress","type":"address"},{"internalType":"address","name":"initialOwner","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"internalType":"struct IRegistry.ObjectInfo","name":"info","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getInitialProductInfo","outputs":[{"components":[{"internalType":"bool","name":"isProcessingFundedClaims","type":"bool"},{"internalType":"bool","name":"isInterceptingPolicyTransfers","type":"bool"},{"internalType":"bool","name":"hasDistribution","type":"bool"},{"internalType":"uint8","name":"expectedNumberOfOracles","type":"uint8"},{"internalType":"uint8","name":"numberOfOracles","type":"uint8"},{"internalType":"NftId","name":"poolNftId","type":"uint96"},{"internalType":"NftId","name":"distributionNftId","type":"uint96"},{"internalType":"NftId[]","name":"oracleNftId","type":"uint96[]"}],"internalType":"struct IComponents.ProductInfo","name":"poolInfo","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getInstance","outputs":[{"internalType":"contract IInstance","name":"instance","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getName","outputs":[{"internalType":"string","name":"name","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getNftId","outputs":[{"internalType":"NftId","name":"","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOracleNftId","outputs":[{"internalType":"NftId","name":"oracleNftId","type":"uint96"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRegistry","outputs":[{"internalType":"contract IRegistry","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRelease","outputs":[{"internalType":"VersionPart","name":"release","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"RiskId","name":"riskId","type":"bytes8"}],"name":"getRequestForRisk","outputs":[{"internalType":"RequestId","name":"requestId","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getToken","outputs":[{"internalType":"contract IERC20Metadata","name":"token","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokenHandler","outputs":[{"internalType":"contract TokenHandler","name":"tokenHandler","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVersion","outputs":[{"internalType":"Version","name":"version","type":"uint24"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWallet","outputs":[{"internalType":"address","name":"walletAddress","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isActive","outputs":[{"internalType":"bool","name":"active","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isConsumingScheduledOp","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isNftInterceptor","outputs":[{"internalType":"bool","name":"isInterceptor","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isRegistered","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isTestMode","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"linkToRegisteredNftId","outputs":[{"internalType":"NftId","name":"nftId","type":"uint96"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"tokenId","type":"uint256"},{"internalType":"address","name":"operator","type":"address"}],"name":"nftTransferFrom","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"NftId","name":"policyNftId","type":"uint96"},{"internalType":"ClaimId","name":"claimId","type":"uint16"},{"internalType":"Amount","name":"availableAmount","type":"uint96"}],"name":"processFundedClaim","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"RiskId","name":"riskId","type":"bytes8"},{"internalType":"uint8","name":"maxPoliciesToProcess","type":"uint8"}],"name":"processPayoutsAndClosePolicies","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"component","type":"address"}],"name":"registerComponent","outputs":[{"internalType":"NftId","name":"componentNftId","type":"uint96"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"RequestId","name":"requestId","type":"uint64"}],"name":"resendRequest","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newAuthority","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"Amount","name":"minPremium","type":"uint96"},{"internalType":"Amount","name":"maxPremium","type":"uint96"},{"internalType":"Amount","name":"maxPayout","type":"uint96"},{"internalType":"Amount","name":"maxTotalPayout","type":"uint96"},{"internalType":"Seconds","name":"minTimeBeforeDeparture","type":"uint40"},{"internalType":"Seconds","name":"maxTimeBeforeDeparture","type":"uint40"},{"internalType":"uint8","name":"maxPoliciesToProcess","type":"uint8"}],"name":"setConstants","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"NftId","name":"bundleNftId","type":"uint96"}],"name":"setDefaultBundle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"locked","type":"bool"}],"name":"setLocked","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setOracleNftId","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"testMode","type":"bool"}],"name":"setTestMode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"newWallet","type":"address"}],"name":"setWallet","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"Amount","name":"amount","type":"uint96"}],"name":"withdrawFees","outputs":[{"internalType":"Amount","name":"withdrawnAmount","type":"uint96"}],"stateMutability":"nonpayable","type":"function"}]

608060405234801561000f575f80fd5b5060405161799338038061799383398101604081905261002e91611657565b3361003c8585858585610046565b5050505050611a1c565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000810460ff1615906001600160401b03165f8115801561008f5750825b90505f826001600160401b031660011480156100aa5750303b155b9050811580156100b8575080155b156100d65760405163f92ee8a960e01b815260040160405180910390fd5b84546001600160401b0319166001178555831561010457845460ff60401b1916680100000000000000001785555b6105798a8a8a6040518061010001604052805f151581526020015f151581526020015f15158152602001600160ff1681526020015f60ff16815260200173b6fe977f3e63d01a734dd238ba5fc05bf45bf32c63bc1b392d6040518163ffffffff1660e01b8152600401602060405180830381865af4158015610188573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101ac91906116dc565b6001600160601b0316815260200173b6fe977f3e63d01a734dd238ba5fc05bf45bf32c63bc1b392d6040518163ffffffff1660e01b8152600401602060405180830381865af4158015610201573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061022591906116dc565b6001600160601b031681526020015f60405190808252806020026020018201604052801561025d578160200160208202803683370190505b508152506040518060e00160405280737344ca80c9187e40d161c55aa59b68b680ab0b9a63bc1b392d6040518163ffffffff1660e01b81526004016040805180830381865af41580156102b2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102d691906116f7565b8152602001737344ca80c9187e40d161c55aa59b68b680ab0b9a63bc1b392d6040518163ffffffff1660e01b81526004016040805180830381865af4158015610321573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061034591906116f7565b8152602001737344ca80c9187e40d161c55aa59b68b680ab0b9a63bc1b392d6040518163ffffffff1660e01b81526004016040805180830381865af4158015610390573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103b491906116f7565b8152602001737344ca80c9187e40d161c55aa59b68b680ab0b9a63bc1b392d6040518163ffffffff1660e01b81526004016040805180830381865af41580156103ff573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061042391906116f7565b8152602001737344ca80c9187e40d161c55aa59b68b680ab0b9a63bc1b392d6040518163ffffffff1660e01b81526004016040805180830381865af415801561046e573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061049291906116f7565b8152602001737344ca80c9187e40d161c55aa59b68b680ab0b9a63bc1b392d6040518163ffffffff1660e01b81526004016040805180830381865af41580156104dd573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061050191906116f7565b8152602001737344ca80c9187e40d161c55aa59b68b680ab0b9a63bc1b392d6040518163ffffffff1660e01b81526004016040805180830381865af415801561054c573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061057091906116f7565b90528c8c610741565b6040516209629960e41b815260026004820152733fb33d6b382d000f1ef0928f8942c56ef3af6c41906296299090602401602060405180830381865af41580156105c5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105e99190611755565b6003805464ffffffffff92909216600160b01b0264ffffffffff60b01b199092169190911790556040516209629960e41b8152601e6004820152733fb33d6b382d000f1ef0928f8942c56ef3af6c41906296299090602401602060405180830381865af415801561065c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106809190611755565b6003805464ffffffffff92909216600160d81b026001600160d81b039092169190911790556040805160c0810182525f8082526020820181905291810191909152601e606082015260326080820181905260a08201526106e490600490600661141c565b50600a600555601e600655831561073557845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50505050505050505050565b610749610a4e565b61075e878787600c6020890151879087610a9e565b5f7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d68008551815460208089015160408a015160608b015160808c015160a08d015160c08e015161ffff1990971697151561ff00191697909717610100941515949094029390931763ffff00001916620100009215159290920263ff000000191691909117630100000060ff9283160217600160201b600160881b0319166401000000009190921602600160281b600160881b03191617650100000000006001600160601b039485160217600160881b600160e81b031916600160881b939092169290920217825560e08701518051929350879284926108639260018501929101906114ac565b5050845180516020918201516001600160601b03908116600160a01b9081026001600160a01b03938416176002870155838901518051908501518316820290841617600387015560408901518051908501518316820290841617600487015560608901518051908501518316820290841617600587015560808901518051908501518316820290841617600687015560a08901518051908501518316820290841617600787015560c089015180519401519091160291161760088301555061093161092c602c90565b610cf9565b600a820180546001600160a01b0319166001600160a01b039290921691909117905561095e61092c601490565b600b820180546001600160a01b0319166001600160a01b039290921691909117905561098b61092c601590565b600c820180546001600160a01b0319166001600160a01b03929092169190911790556109b861092c602d90565b600d820180546001600160a01b0319166001600160a01b03929092169190911790556109e561092c602a90565b600e820180546001600160a01b0319166001600160a01b0392909216919091179055610a1261092c600b90565b6009820180546001600160a01b0319166001600160a01b0392909216919091179055610a44630cdf80bd60e31b610d90565b5050505050505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005468010000000000000000900460ff16610a9c57604051631afcd79f60e31b815260040160405180910390fd5b565b610aa6610a4e565b5f610ab2888887610dc3565b90505f7fffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f0060405163bf8e179760e01b81526001600160601b03841660048201529091506001600160a01b038a169063bf8e179790602401602060405180830381865afa158015610b24573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b489190611779565b81546001600160a01b0319166001600160a01b039190911690811782556040805163bf7e214f60e01b81529051610be6929163bf7e214f9160048083019260209291908290030181865afa158015610ba2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610bc69190611779565b8a8a8a8a898960405180602001604052805f815250610e6e60201b60201c565b8054604080516302cd307160e01b815290516001600160a01b03909216916302cd3071916004808201926020929091908290030181865afa158015610c2d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c519190611779565b6001820180546001600160a01b03199081166001600160a01b0393841617909155600283018054909116918716919091179055610c8f61092c600b90565b6003820180546001600160a01b0319166001600160a01b0392909216919091179055610cbc61092c600d90565b6004820180546001600160a01b0319166001600160a01b0392909216919091179055610cee6330841f2160e21b610d90565b505050505050505050565b5f610d0c6001546001600160a01b031690565b6001600160a01b031663d39e604383610d23610f37565b6040516001600160e01b031960e085901b16815260ff928316600482015291166024820152604401602060405180830381865afa158015610d66573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d8a9190611779565b92915050565b610d98610a4e565b610dc0816001600160e01b0319165f908152602081905260409020805460ff19166001179055565b50565b5f60ff8216600c03610de457610ddb8484600a610fcd565b50829050610e67565b5f610df18585600c610fcd565b60405163677d3feb60e11b81526001600160601b03861660048201529091506001600160a01b0382169063cefa7fd690602401602060405180830381865afa158015610e3f573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e6391906116dc565b9150505b9392505050565b610e76610a4e565b84515f03610e975760405163591eebf360e11b815260040160405180910390fd5b610ea688888887878787611097565b7fffe8d4462baed26a47154f4b8f6db497d2f772496965791d25bd456e342b7f0080610ed28782611818565b5060018101805460ff1916851515179055610eee61092c600b90565b6001820180546001600160a01b039290921661010002610100600160a81b0319909216919091179055610f27634a531f3360e01b610d90565b610cee6306e9e5ff60e31b610d90565b5f610f697ff3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00546001600160a01b031690565b6001600160a01b03166376b707b76040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fa4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fc891906118eb565b905090565b6040516305247a1760e51b81526001600160601b038316600482015283905f906001600160a01b0383169063a48f42e0906024015f60405180830381865afa15801561101b573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526110429190810190611933565b905061105981604001518460ff9081169116141590565b1561108f57604080820151905163b0c1f6eb60e01b815260ff808616600483015290911660248201526044015b60405180910390fd5b509392505050565b61109f610a4e565b604051632330f24760e01b81526001600160a01b0388166004820152730dca08c2260a16010538655b2894c4d7fed280fa90632330f24790602401602060405180830381865af41580156110f5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111199190611a03565b6111415760405163cf6935e560e01b81526001600160a01b0388166004820152602401611086565b61114a876111e1565b61115486836111f2565b5f7f6548007c3f4340f82f348c576c0ff69f4f529cadd5ad41f96aae61abceeaa30080546001600160601b0388166001600160681b0319909116176c0100000000000000000000000060ff8816021760ff60681b19166d0100000000000000000000000000861515021781559050600181016111d08382611818565b50610a44634a531f3360e01b610d90565b6111e9610a4e565b610dc081611284565b6111fa610a4e565b611202611295565b61120b826112dd565b6001600160a01b0381166112325760405163f17ef42d60e01b815260040160405180910390fd5b7f07ebcf49758b6ed3af50fa146bec0abe157c0218fe65dc0874c286e9d5da4f0080546001600160601b03166c010000000000000000000000006001600160a01b039093169290920291909117905550565b61128c610a4e565b610dc0816113a9565b61129d610a4e565b610a9c6301ffc9a760e01b5f9081526020527f67be87c3ff9960ca1e9cfac5cab2ff4747269cf9ed20c9b7306235ac35a491c5805460ff19166001179055565b6112e5610a4e565b604051633334d16b60e01b81526001600160a01b0382166004820152730dca08c2260a16010538655b2894c4d7fed280fa90633334d16b90602401602060405180830381865af415801561133b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061135f9190611a03565b6113875760405163fdeac91f60e01b81526001600160a01b0382166004820152602401611086565b600180546001600160a01b0319166001600160a01b0392909216919091179055565b7ff3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a0080546001600160a01b0383166001600160a01b03199091168117825560408051918252517f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad9181900360200190a15050565b60018301918390821561149c579160200282015f5b8382111561146e57835183826101000a81548160ff021916908360ff16021790555092602001926001016020815f01049283019260010302611431565b801561149a5782816101000a81549060ff02191690556001016020815f0104928301926001030261146e565b505b506114a892915061154f565b5090565b828054828255905f5260205f209060010160029004810192821561149c579160200282015f5b8382111561151c57835183826101000a8154816001600160601b0302191690836001600160601b031602179055509260200192600c01602081600b010492830192600103026114d2565b801561149a5782816101000a8154906001600160601b030219169055600c01602081600b0104928301926001030261151c565b5b808211156114a8575f8155600101611550565b6001600160a01b0381168114610dc0575f80fd5b805161158281611563565b919050565b6001600160601b0381168114610dc0575f80fd5b805161158281611587565b634e487b7160e01b5f52604160045260245ffd5b60405160e081016001600160401b03811182821017156115dc576115dc6115a6565b60405290565b5f806001600160401b038411156115fb576115fb6115a6565b50604051601f19601f85018116603f011681018181106001600160401b0382111715611629576116296115a6565b604052838152905080828401851015611640575f80fd5b8383602083015e5f60208583010152509392505050565b5f805f806080858703121561166a575f80fd5b845161167581611563565b602086015190945061168681611587565b60408601519093506001600160401b038111156116a1575f80fd5b8501601f810187136116b1575f80fd5b6116c0878251602084016115e2565b92505060608501516116d181611563565b939692955090935050565b5f602082840312156116ec575f80fd5b8151610e6781611587565b5f6040828403128015611708575f80fd5b50604080519081016001600160401b038111828210171561172b5761172b6115a6565b604052825161173981611563565b8152602083015161174981611587565b60208201529392505050565b5f60208284031215611765575f80fd5b815164ffffffffff81168114610e67575f80fd5b5f60208284031215611789575f80fd5b8151610e6781611563565b600181811c908216806117a857607f821691505b6020821081036117c657634e487b7160e01b5f52602260045260245ffd5b50919050565b601f82111561181357805f5260205f20601f840160051c810160208510156117f15750805b601f840160051c820191505b81811015611810575f81556001016117fd565b50505b505050565b81516001600160401b03811115611831576118316115a6565b6118458161183f8454611794565b846117cc565b6020601f821160018114611877575f83156118605750848201515b5f19600385901b1c1916600184901b178455611810565b5f84815260208120601f198516915b828110156118a65787850151825560209485019460019092019101611886565b50848210156118c357868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b60ff81168114610dc0575f80fd5b8051611582816118d2565b5f602082840312156118fb575f80fd5b8151610e67816118d2565b80518015158114611582575f80fd5b5f82601f830112611924575f80fd5b610e67838351602085016115e2565b5f60208284031215611943575f80fd5b81516001600160401b03811115611958575f80fd5b820160e08185031215611969575f80fd5b6119716115ba565b61197a8261159b565b81526119886020830161159b565b6020820152611999604083016118e0565b60408201526119aa60608301611906565b60608201526119bb60808301611577565b60808201526119cc60a08301611577565b60a082015260c08201516001600160401b038111156119e9575f80fd5b6119f586828501611915565b60c083015250949350505050565b5f60208284031215611a13575f80fd5b610e6782611906565b615f6a80611a295f395ff3fe608060405234801561000f575f80fd5b50600436106103bf575f3560e01c806360efe48c116101f5578063b277d78011610114578063de7b5d14116100a9578063ea15869f11610079578063ea15869f1461094f578063f0107c1514610963578063f179d0641461097b578063f3e349ae1461098e575f80fd5b8063de7b5d14146108e5578063deaa59df14610915578063e1f216fa14610928578063e9b2460c1461093b575f80fd5b8063bf7e214f116100e4578063bf7e214f14610866578063c108bd4c1461086e578063d11efdfa14610883578063d3c576d8146108cd575f80fd5b8063b277d78014610819578063b423086c1461082c578063b7b357ac14610834578063be5c8f441461084c575f80fd5b8063893d20e81161018a578063972e22eb1161015a578063972e22eb146107ba578063a1794029146107d2578063ad12ad60146107f2578063ada9652e14610805575f80fd5b8063893d20e8146107745780638b0af5d81461077c5780638fb360371461078457806390edbd35146107a5575f80fd5b806376b707b7116101c557806376b707b7146107225780637a9e5e4b1461072a578063844e0f201461073d578063846c9fce14610752575f80fd5b806360efe48c146106c7578063644c45e0146106d75780636bd1c0eb146106df578063768e87771461070f575f80fd5b80632c47e75a116102e15780634cdabb16116102765780635ab1bd53116102465780635ab1bd531461066257806360426af3146106735780636078a3b214610686578063608a5f8d14610699575f80fd5b80634cdabb16146106215780635349d2a81461063457806354d33735146106475780635741e5e91461065a575f80fd5b8063419197fe116102b1578063419197fe146105df57806343d752d3146105f25780634b396c4f146105fa5780634b9ddafe14610607575f80fd5b80632c47e75a146105855780632eb3e6f4146105a65780632f15ce0d146105cd57806339cee407146105d6575f80fd5b80631d0ed70211610357578063211e28b611610327578063211e28b61461055a57806321df0da71461056d578063223668441461057557806322f3e2d41461057d575f80fd5b80631d0ed702146104b25780631e6239c6146104ed5780631eff4b22146105125780631ff1292f14610547575f80fd5b80631329960411610392578063132996041461044a578063138461e01461046a57806317d7de7c1461048a5780631c6b21901461049f575f80fd5b806301ffc9a7146103c35780630b045f60146104045780630d8e6e2c146104195780630fec111c14610435575b5f80fd5b6103ef6103d1366004614322565b6001600160e01b0319165f9081526020819052604090205460ff1690565b60405190151581526020015b60405180910390f35b61041761041236600461436c565b6109a1565b005b6104216109fe565b60405162ffffff90911681526020016103fb565b61043d610a85565b6040516103fb91906143d1565b610452610c3f565b6040516001600160a01b0390911681526020016103fb565b610472610ca7565b6040516001600160601b0390911681526020016103fb565b610492610cb1565b6040516103fb919061445d565b6104726104ad36600461448e565b610cc1565b6104c56104c0366004614779565b610d17565b604080516001600160c01b031990931683526001600160601b039091166020830152016103fb565b6105006104fb36600461482d565b610dd9565b60405160ff90911681526020016103fb565b6105397f6548007c3f4340f82f348c576c0ff69f4f529cadd5ad41f96aae61abceeaa30081565b6040519081526020016103fb565b610417610555366004614851565b610e02565b610417610568366004614851565b610e60565b610452610ea7565b6103ef610eeb565b6103ef610fe1565b61059861059336600461486c565b611058565b6040516103fb929190614980565b6105397fffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f0081565b61053960065481565b61053960055481565b6104176105ed36600461499a565b611124565b610452611177565b6007546105009060ff1681565b60025461047290600160601b90046001600160601b031681565b600254610472906001600160601b031681565b6104726106423660046149c6565b611189565b610417610655366004614a6f565b61124b565b6103ef61125d565b6001546001600160a01b0316610452565b610417610681366004614a8a565b611313565b610472610694366004614acc565b61134a565b6003546106b190600160881b900464ffffffffff1681565b60405164ffffffffff90911681526020016103fb565b600754610100900460ff166103ef565b61047261141c565b7fffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f02546001600160a01b0316610452565b61041761071d366004614ae7565b611437565b610500611521565b610417610738366004614acc565b611589565b61074561160f565b6040516103fb9190614b99565b610765610760366004614c27565b6117e8565b6040516103fb93929190614c98565b61045261187c565b6104176119ad565b61078c611a78565b6040516001600160e01b031990911681526020016103fb565b6107ad611aad565b6040516103fb9190614cc3565b6003546106b190600160b01b900464ffffffffff1681565b6107e56107e0366004614cff565b611ade565b6040516103fb9190614d30565b610417610800366004614d42565b611afa565b6105395f80516020615eb583398151915281565b61041761082736600461448e565b611b9d565b6107ad611c08565b6003546106b190600160601b900464ffffffffff1681565b60015461047290600160a01b90046001600160601b031681565b610452611c24565b610876611c3f565b6040516103fb9190614dd5565b6108b5610891366004614e65565b6001600160c01b0319165f908152600860205260409020546001600160401b031690565b6040516001600160401b0390911681526020016103fb565b6003546106b190600160d81b900464ffffffffff1681565b7fffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f00546001600160a01b0316610452565b610417610923366004614acc565b611d93565b610417610936366004614e8f565b611de0565b6105395f80516020615f1583398151915281565b6105395f80516020615ed583398151915281565b600954600160601b90046001600160601b0316610472565b610472610989366004614ed7565b611df6565b600354610472906001600160601b031681565b6109ad335b5f36611e15565b6109b561187c565b6001600160a01b0316336001600160a01b0316146109ed5760405163086391f760e31b81523360048201526024015b60405180910390fd5b6109f78282611f13565b5050505050565b604051632efe011360e01b8152600160048201525f602482018190526044820181905290731c7f4780c639cb898b64769694d308a5a363a83e90632efe011390606401602060405180830381865af4158015610a5c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a809190614f47565b905090565b6040805160e0810182525f8082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820152907f6548007c3f4340f82f348c576c0ff69f4f529cadd5ad41f96aae61abceeaa30090506040518060e0016040528073b6fe977f3e63d01a734dd238ba5fc05bf45bf32c63bc1b392d6040518163ffffffff1660e01b8152600401602060405180830381865af4158015610b31573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b559190614f74565b6001600160601b0390811682528354908116602083015260ff600160601b820481166040840152600160681b909104161515606082015230608082015260a001610b9d61187c565b6001600160a01b03168152602001826001018054610bba90614f8f565b80601f0160208091040260200160405190810160405280929190818152602001828054610be690614f8f565b8015610c315780601f10610c0857610100808354040283529160200191610c31565b820191905f5260205f20905b815481529060010190602001808311610c1457829003601f168201915b505050505081525091505090565b5f610c48611177565b6001600160a01b031663132996046040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c83573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a809190614fd2565b5f610a80306123cc565b6060610cbb611aad565b51919050565b5f610ccb336109a6565b610cd361187c565b6001600160a01b0316336001600160a01b031614610d065760405163086391f760e31b81523360048201526024016109e4565b610d0f82612622565b90505b919050565b5f80610d22336109a6565b610d2b8461267a565b8351835160405163cdc23e6960e01b8152610dcd91839173b7336e875465659bad9659b25d078c6cace417069163cdc23e6991610d6b919060040161445d565b602060405180830381865af4158015610d86573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610daa9190614fed565b86602001518760400151886060015189608001518a60a001518b60c0015161272c565b90969095509350505050565b60048160068110610de8575f80fd5b60209182820401919006915054906101000a900460ff1681565b610e0b336109a6565b610e1361187c565b6001600160a01b0316336001600160a01b031614610e465760405163086391f760e31b81523360048201526024016109e4565b600780549115156101000261ff0019909216919091179055565b610e6861187c565b6001600160a01b0316336001600160a01b031614610e9b5760405163086391f760e31b81523360048201526024016109e4565b610ea481612b86565b50565b5f610eb0611177565b6001600160a01b03166382bfefc86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c83573d5f803e3d5ffd5b5f610efe6001546001600160a01b031690565b604051632f2a35f760e11b81523060048201526001600160a01b039190911690635e546bee90602401602060405180830381865afa158015610f42573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f669190614f74565b6040516330b8415f60e01b81526001600160601b03909116600482015273b6fe977f3e63d01a734dd238ba5fc05bf45bf32c906330b8415f90602401602060405180830381865af4158015610fbd573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a80919061500f565b5f610fea611c24565b60405163a166aa8960e01b81523060048201526001600160a01b03919091169063a166aa8990602401602060405180830381865afa15801561102e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611052919061500f565b15905090565b5f6110616141d7565b7308bd2a26fcf141f9244e9f71c3c99249451c89d3637bc17425611083612bf0565b61108b61141c565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526001600160601b031660248201526001600160c01b03198716604482015285151560648201526084015f60405180830381865af41580156110f2573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526111199190810190615193565b909590945092505050565b61112d336109a6565b61113561187c565b6001600160a01b0316336001600160a01b0316146111685760405163086391f760e31b81523360048201526024016109e4565b6111728282612c1e565b505050565b5f611180611aad565b60200151905090565b5f8260166111978282612d9f565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680e545f906001600160a01b031663f4bfe53a6111d161141c565b8b8d8c8c8c8c6040518863ffffffff1660e01b81526004016111f997969594939291906151d6565b61024060405180830381865afa158015611215573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112399190615253565b60a001519a9950505050505050505050565b611254336109a6565b610ea481612ed3565b5f611266610eeb565b156112ea576001546001600160a01b03166040516308b09a5f60e41b81523060048201526001600160a01b039190911690638b09a5f0906024015f60405180830381865afa1580156112ba573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526112e191908101906153b5565b60600151905090565b507fffe8d4462baed26a47154f4b8f6db497d2f772496965791d25bd456e342b7f015460ff1690565b61131c336109a6565b5f818060200190518101906113319190615490565b905061117283825f015183602001518460400151612f2b565b5f611354336109a6565b61135c61187c565b6001600160a01b0316336001600160a01b03161461138f5760405163086391f760e31b81523360048201526024016109e4565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d68095460405163303c51d960e11b81526001600160a01b03848116600483015290911690636078a3b2906024015b6020604051808303815f875af11580156113f8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d0f9190614f74565b5f80516020615eb5833981519152546001600160601b031690565b611440336109a6565b61144861187c565b6001600160a01b0316336001600160a01b03161461147b5760405163086391f760e31b81523360048201526024016109e4565b600180546001600160a01b0316600160a01b6001600160601b03998a1602179055600280549688166001600160c01b031990971696909617600160601b958816860217909555600380549390961670ffffffffffffffffffffffffffffffffff199093169290921764ffffffffff9182169093029290921764ffffffffff60881b1916600160881b9190921602179091556007805460ff191660ff909216919091179055565b5f61152a611c24565b6001600160a01b03166376b707b76040518163ffffffff1660e01b8152600401602060405180830381865afa158015611565573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a8091906154f8565b33611592611c24565b6001600160a01b0316816001600160a01b0316146115cd5760405162d1953b60e31b81526001600160a01b03821660048201526024016109e4565b816001600160a01b03163b5f03611602576040516361798f2f60e11b81526001600160a01b03831660048201526024016109e4565b61160b826131a7565b5050565b61161761422c565b5060408051610120810182527f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d6802546001600160a01b0380821660e084019081526001600160601b03600160a01b938490048116610100860152908452845180860186527f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d6803548084168252849004821660208083019190915280860191909152855180870187527f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680454808516825285900483168183015285870152855180870187527f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d68055480851682528590048316818301526060860152855180870187527f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d68065480851682528590048316818301526080860152855180870187527f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680754808516825285900483168183015260a086015285518087019096527f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680854928316865292909104169083015260c081019190915290565b5f6117f1614304565b6040516355d166e160e01b81525f907308bd2a26fcf141f9244e9f71c3c99249451c89d3906355d166e19061182e90899089908990600401615513565b60e060405180830381865af4158015611849573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061186d91906155b1565b92509250925093509350939050565b5f805f80516020615eb583398151915280546040516330b8415f60e01b81526001600160601b03909116600482015290915073b6fe977f3e63d01a734dd238ba5fc05bf45bf32c906330b8415f90602401602060405180830381865af41580156118e8573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061190c919061500f565b15611997576001546001600160a01b03168154604051631c5da14d60e11b81526001600160601b0390911660048201526001600160a01b0391909116906338bb429a90602401602060405180830381865afa15801561196d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119919190614fd2565b91505090565b54600160601b90046001600160a01b0316919050565b6119b5612bf0565b6001600160a01b0316630b5d931c6119cb61141c565b6040516001600160e01b031960e084901b1681526001600160601b0390911660048201526024015f60405180830381865afa158015611a0c573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611a33919081019061566c565b60e001515f81518110611a4857611a4861574e565b60200260200101516009600c6101000a8154816001600160601b0302191690836001600160601b03160217905550565b5f80516020615ef583398151915280545f9190600160a01b900460ff16611a9f575f611991565b638fb3603760e01b91505090565b60408051808201909152606081525f6020820152611ac9610eeb565b15611ad657610a80613207565b610a80611c08565b611ae66141d7565b81806020019051810190610d0f9190615762565b6001546001600160a01b03166001600160a01b031663120726c36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b41573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b659190614fd2565b6001600160a01b0316336001600160a01b031614611b975760405162f0630960e01b81523360048201526024016109e4565b50505050565b611ba6336109a6565b611bae61187c565b6001600160a01b0316336001600160a01b031614611be15760405163086391f760e31b81523360048201526024016109e4565b600980546bffffffffffffffffffffffff19166001600160601b0392909216919091179055565b60408051808201909152606081525f6020820152610a80613207565b5f80516020615ef5833981519152546001600160a01b031690565b60408051610100810182525f8082526020820181905291810182905260608082018390526080820183905260a0820183905260c082019290925260e08101919091525f80516020615f15833981519152604080516101008082018352835460ff808216151584529181048216151560208085019190915262010000820483161515848601526301000000820483166060850152640100000000820490921660808401526001600160601b03650100000000008204811660a0850152600160881b9091041660c083015260018401805484518184028101840190955280855292949360e08601939092830182828015611d8557602002820191905f5260205f20905f905b82829054906101000a90046001600160601b03166001600160601b0316815260200190600c0190602082600b01049283019260010382029150808411611d425790505b505050505081525050905090565b611d9c336109a6565b611da461187c565b6001600160a01b0316336001600160a01b031614611dd75760405163086391f760e31b81523360048201526024016109e4565b610ea4816133c0565b611de9336109a6565b8260156109f78282612d9f565b5f81806020019051810190611e0b9190615793565b5095945050505050565b5f80516020615ef58339815191525f80611e4d611e30611c24565b8730611e3f60045f8a8c6157c7565b611e48916157ee565b613435565b9150915081611f0b5763ffffffff811615611ee857825460ff60a01b1916600160a01b178355611e7b611c24565b6001600160a01b03166394c7d7ee8787876040518463ffffffff1660e01b8152600401611eaa93929190615824565b5f604051808303815f87803b158015611ec1575f80fd5b505af1158015611ed3573d5f803e3d5ffd5b5050845460ff60a01b1916855550611f0b9050565b60405162d1953b60e31b81526001600160a01b03871660048201526024016109e4565b505050505050565b5f805f80611f1f612bf0565b90507308bd2a26fcf141f9244e9f71c3c99249451c89d363bcfba16982611f4461141c565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526001600160601b031660248201526001600160c01b031989166044820152606401606060405180830381865af4158015611fa4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fc89190615863565b91955093509150831580611fda575082155b15611fe557506123c5565b604051631954e65360e21b81526001600160c01b0319871660048201525f906001600160a01b03831690636553994c90602401602060405180830381865afa158015612033573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120579190614fed565b90505f8660ff16821061206d578660ff1661206f565b815b90505f816001600160401b0381111561208a5761208a6144a9565b6040519080825280602002602001820160405280156120b3578160200160208202803683370190505b5090505f5b82811015612169576040516325fb9d0d60e11b81526001600160c01b03198b166004820152602481018290526001600160a01b03861690634bf73a1a90604401602060405180830381865afa158015612113573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121379190614f74565b8282815181106121495761214961574e565b6001600160601b03909216602092830291909101909101526001016120b8565b505f5b82811015612361575f8282815181106121875761218761574e565b602002602001015190505f7308bd2a26fcf141f9244e9f71c3c99249451c89d3638a37ab63886001600160a01b031663bd7d9d85856040518263ffffffff1660e01b81526004016121e791906001600160601b0391909116815260200190565b5f60405180830381865afa158015612201573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261222891908101906158ad565b6101c001518a6040518363ffffffff1660e01b815260040161224b929190615a40565b602060405180830381865af4158015612266573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061228a9190614f74565b9050612296828261353d565b61230a8273fdbcee5ffb06ba0f107922a3294110ce43c3112e639fa6a6e36040518163ffffffff1660e01b8152600401602060405180830381865af41580156122e1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123059190615a64565b61361d565b50612314826136b5565b604080516001600160601b038085168252831660208201527f1c424341cadf6615bb5fa2a96492cd7ed267c05f7d1dcef2f2229ae26d4d1a7a910160405180910390a1505060010161216c565b507f497e6cfdb1ef3149777822f9e0716eebbeef1264fbc8effc6004e0111ec600818986846123908188615a7f565b604080516001600160c01b0319909516855260ff909316602085015291830152606082015260800160405180910390a1505050505b9250925092565b5f805f80516020615eb583398151915280546040516330b8415f60e01b81526001600160601b03909116600482015290915073b6fe977f3e63d01a734dd238ba5fc05bf45bf32c906330b8415f90602401602060405180830381865af4158015612438573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061245c919061500f565b156124885780546040516301ab8b6760e21b81526001600160601b0390911660048201526024016109e4565b6001546001600160a01b031660405163c3c5a54760e01b81526001600160a01b038581166004830152919091169063c3c5a54790602401602060405180830381865afa1580156124da573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124fe919061500f565b6125265760405163b9304b0d60e01b81526001600160a01b03841660048201526024016109e4565b6001546001600160a01b0316604051632f2a35f760e11b81526001600160a01b0385811660048301529190911690635e546bee90602401602060405180830381865afa158015612578573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061259c9190614f74565b81546bffffffffffffffffffffffff19166001600160601b039190911690811782557f88857b75cbef7f257c9d5a473e28aaf6a91cc28f717f60f2cda3bcf5623d7b54906125e861187c565b604080516001600160601b0390931683526001600160a01b0390911660208301520160405180910390a1546001600160601b031692915050565b7fffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f03546040516301c6b21960e41b81526001600160601b03831660048201525f916001600160a01b031690631c6b2190906024016113dc565b612683336109a6565b5f61268c610ea7565b825160208401516040808601516060870151608088015160a089015160c08a0151945163d505accf60e01b81526001600160a01b03978816600482015295871660248701526044860193909352606485019190915260ff16608484015260a483015260c482015291925082169063d505accf9060e4015b5f604051808303815f87803b15801561271a575f80fd5b505af1158015611f0b573d5f803e3d5ffd5b604051634617dc7960e11b81523060048201526024810188905264ffffffffff8088166044830152851660648201526001600160601b03831660848201525f9081907308bd2a26fcf141f9244e9f71c3c99249451c89d390638c2fb8f29060a4015f6040518083038186803b1580156127a3575f80fd5b505af41580156127b5573d5f803e3d5ffd5b505050506127c98a8a8a8a8a8a8a8a61370d565b80925081935050506128468173fdbcee5ffb06ba0f107922a3294110ce43c3112e63bc1b392d6040518163ffffffff1660e01b8152600401602060405180830381865af415801561281c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128409190615a64565b86613806565b5061285181896138a7565b6001600160c01b031982165f908152600860205260409081902054905163015c60ad60e21b81526001600160401b039091166004820152736583a0cc1ea5b6841adae78a8ce62f319a0280539063057182b490602401602060405180830381865af41580156128c2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128e6919061500f565b15612ac95760095460408051606080820183526001600160c01b0319861680835260208084018f815264ffffffffff8f81169587019586528651928301939093525194810194909452915190911690820152612a9191600160601b90046001600160601b03169060800160408051601f19818403018152908290526209629960e41b8252601e60048301529073fdbcee5ffb06ba0f107922a3294110ce43c3112e9063a5798b4e9064ffffffffff8c1690733fb33d6b382d000f1ef0928f8942c56ef3af6c41906296299090602401602060405180830381865af41580156129d0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129f49190615a64565b6040516001600160e01b031960e085901b16815264ffffffffff928316600482015291166024820152604401602060405180830381865af4158015612a3b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a5f9190615a64565b60405180604001604052806014815260200173666c6967687453746174757343616c6c6261636b60601b81525061390c565b6001600160c01b031983165f908152600860205260409020805467ffffffffffffffff19166001600160401b03929092169190911790555b604051631623433d60e31b8152600481018a90527fb51fd6991e0581f19175f5ef6b8bc59d44dbff4511eb57379f0ce2862228794490829073b7336e875465659bad9659b25d078c6cace417069063b11a19e8906024015f60405180830381865af4158015612b3a573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612b619190810190615a9e565b86604051612b7193929190615acf565b60405180910390a19850989650505050505050565b5f80516020615ed58339815191526001015460405163108f145b60e11b815282151560048201526101009091046001600160a01b03169063211e28b6906024015b5f604051808303815f87803b158015612bde575f80fd5b505af11580156109f7573d5f803e3d5ffd5b7fffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f01546001600160a01b031690565b5f7382860d6202c009092d0d7507380ba8ef8f4fd567634c41dd96846001600160a01b031663dd62ed3e612c50611177565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152306024820152604401602060405180830381865afa158015612c98573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612cbc9190614fed565b6040518263ffffffff1660e01b8152600401612cda91815260200190565b602060405180830381865af4158015612cf5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d199190614f74565b90505f80516020615ed5833981519152600101546040516320c8cbff60e11b81526001600160a01b0385811660048301526001600160601b03851660248301526101009092049091169063419197fe906044015f604051808303815f87803b158015612d83575f80fd5b505af1158015612d95573d5f803e3d5ffd5b5050505092915050565b60405163037c8cb160e51b815260ff8216600482015273a6b4725b0a1cf143baf5099fdd58abc8d862d34f90636f91962090602401602060405180830381865af4158015612def573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e13919061500f565b80612ea157506001546001600160a01b0316604051635b12715d60e11b81526001600160601b038416600482015260ff831660248201526001600160a01b03919091169063b624e2ba90604401602060405180830381865afa158015612e7b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e9f919061500f565b155b1561160b5760405163d711af7560e01b81526001600160601b038316600482015260ff821660248201526044016109e4565b7fffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f0454604051630b331f6960e21b81526001600160401b03831660048201526001600160a01b0390911690632ccc7da490602401612bc7565b5f612f37846001611058565b61012081015160405163790a38ad60e01b815264ffffffffff909116600482015290925073fdbcee5ffb06ba0f107922a3294110ce43c3112e915063790a38ad90602401602060405180830381865af4158015612f96573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612fba919061500f565b1561311c5773fdbcee5ffb06ba0f107922a3294110ce43c3112e639fa6a6e36040518163ffffffff1660e01b8152600401602060405180830381865af4158015613006573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061302a9190615a64565b64ffffffffff166101208201526001600160f81b0319831660c0820181905260e08201839052604051621600c960ea1b81526001600160401b03871660048201526001600160c01b0319861660248201526044810191909152606481018390527308bd2a26fcf141f9244e9f71c3c99249451c89d390635803240090608401602060405180830381865af41580156130c4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130e891906154f8565b60ff1661010082015260405161311c908590613108908490602001614d30565b6040516020818303038152906040526139aa565b6007545f9061312f90869060ff16611f13565b604080516001600160401b038b1681526001600160c01b03198a1660208201526001600160f81b03198916818301526060810188905260ff8316608082015290519194507f6088f5fc3484ec0fdf466ecfcea487e5566da0db0bda62b4dbeb3ed86fd3ef0b935081900360a0019150a1505050505050565b5f80516020615ef583398151915280546001600160a01b0383166001600160a01b03199091168117825560408051918252517f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad9181900360200190a15050565b60408051808201909152606081525f60208201525f61322e6001546001600160a01b031690565b604051632f2a35f760e11b81523060048201526001600160a01b039190911690635e546bee90602401602060405180830381865afa158015613272573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132969190614f74565b6040516330b8415f60e01b81526001600160601b038216600482015290915073b6fe977f3e63d01a734dd238ba5fc05bf45bf32c906330b8415f90602401602060405180830381865af41580156132ef573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613313919061500f565b156133b857613320612bf0565b6001600160a01b031663f0ea17c361333661141c565b6040516001600160e01b031960e084901b1681526001600160601b0390911660048201526024015f60405180830381865afa158015613377573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261339e9190810190615b09565b60208101519092506001600160a01b0316156133b8575090565b6119916139fb565b6133c861187c565b6001600160a01b0316336001600160a01b0316146133fb5760405163086391f760e31b81523360048201526024016109e4565b613405600b613ac5565b60405163deaa59df60e01b81526001600160a01b038381166004830152919091169063deaa59df90602401612bc7565b6040516001600160a01b03848116602483015283811660448301526001600160e01b0319831660648301525f9182918291829189169060840160408051601f198184030181529181526020820180516001600160e01b031663b700961360e01b179052516134a39190615ba7565b5f60405180830381855afa9150503d805f81146134db576040519150601f19603f3d011682016040523d82523d5f602084013e6134e0565b606091505b5091509150811561353257604081511061351257808060200190518101906135089190615bbd565b9094509250613532565b6020815110613532578080602001905181019061352f919061500f565b93505b505094509492505050565b604051630a4d29dd60e31b81526001600160601b03821660048201527382860d6202c009092d0d7507380ba8ef8f4fd567906352694ee890602401602060405180830381865af4158015613593573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906135b7919061500f565b156135c0575050565b5f6135da838360405180602001604052805f815250613b56565b90506135f683828460405180602001604052805f815250613be9565b5f61361184838560405180602001604052805f815250613c71565b9050611f0b8482613d06565b5f5f80516020615f15833981519152600c0154604051637baa9a0b60e01b81526001600160601b038516600482015264ffffffffff841660248201526001600160a01b0390911690637baa9a0b906044016020604051808303815f875af115801561368a573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136ae9190615a64565b9392505050565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680c54604051630155751d60e31b81526001600160601b03831660048201526001600160a01b0390911690630aaba8e890602401612bc7565b5f80613717614304565b5f6137278b8b8b8b8b8b8b613d97565b8093508194508296505050506137f58c8583896003601b9054906101000a900464ffffffffff1660095f9054906101000a90046001600160601b031673f41933c60fdea61085b0ff18b906cddcd547634c63bc1b392d6040518163ffffffff1660e01b8152600401602060405180830381865af41580156137aa573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906137ce9190615bf2565b8d8a6040516020016137e1929190615c0d565b604051602081830303815290604052613e42565b925050509850989650505050505050565b5f5f80516020615f15833981519152600c015460405163d22413e560e01b81526001600160601b03808716600483015264ffffffffff86166024830152841660448201526001600160a01b039091169063d22413e5906064016020604051808303815f875af115801561387b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061389f9190614f74565b949350505050565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680c54604051635dfddfdd60e11b81526001600160601b038416600482015264ffffffffff831660248201526001600160a01b039091169063bbfbbfba90604401612703565b7fffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f045460405163f684566360e01b81525f916001600160a01b03169063f684566390613961908890889088908890600401615c2a565b6020604051808303815f875af115801561397d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906139a19190615c75565b95945050505050565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680a54604051638f997f6d60e01b81526001600160a01b0390911690638f997f6d906127039085908590600401615c90565b60408051808201909152606081525f60208201525f5f80516020615ed583398151915290506040518060400160405280825f018054613a3990614f8f565b80601f0160208091040260200160405190810160405280929190818152602001828054613a6590614f8f565b8015613ab05780601f10613a8757610100808354040283529160200191613ab0565b820191905f5260205f20905b815481529060010190602001808311613a9357829003601f168201915b50505091835250505f60209091015292915050565b5f613ad86001546001600160a01b031690565b6001600160a01b031663d39e604383613aef611521565b6040516001600160e01b031960e085901b16815260ff928316600482015291166024820152604401602060405180830381865afa158015613b32573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d0f9190614fd2565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680d546040516374b775eb60e01b81525f916001600160a01b0316906374b775eb90613ba990879087908790600401615cb4565b6020604051808303815f875af1158015613bc5573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061389f9190615ce4565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680d54604051636e2cdce760e01b81526001600160a01b0390911690636e2cdce790613c3e908790879087908790600401615cff565b5f604051808303815f87803b158015613c55575f80fd5b505af1158015613c67573d5f803e3d5ffd5b5050505050505050565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680d54604051631e2992c760e11b81525f916001600160a01b031690633c53258e90613cc6908890889088908890600401615cff565b6020604051808303815f875af1158015613ce2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906139a19190615a64565b5f805f80516020615f15833981519152600d015460405163137a92a760e01b81526001600160601b038616600482015264ffffffffff851660248201526001600160a01b039091169063137a92a79060440160408051808303815f875af1158015613d73573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111199190615d39565b5f613da0614304565b5f807308bd2a26fcf141f9244e9f71c3c99249451c89d36355d166e13088886040518463ffffffff1660e01b8152600401613ddd93929190615513565b60e060405180830381865af4158015613df8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613e1c91906155b1565b90945092509050613e328b8b8b8b8b8787613eec565b9350509750975097945050505050565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680b54604051639a83274160e01b81525f916001600160a01b031690639a83274190613e9f908c908c908c908c908c908c908c908c90600401615d66565b6020604051808303815f875af1158015613ebb573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613edf9190614f74565b9998505050505050505050565b5f80613ef66141d7565b7308bd2a26fcf141f9244e9f71c3c99249451c89d3634f339f15613f18612bf0565b613f2061141c565b8d8d8d8d8d6040518863ffffffff1660e01b8152600401613f479796959493929190615ddc565b5f60405180830381865af4158015613f61573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613f889190810190615e3d565b9194509250905081613fcc575f613f9e8b61408b565b9050613fc98183604051602001613fb59190614d30565b6040516020818303038152906040526140bc565b50505b60a081015160035460405163586a46dd60e11b81526001600160601b0392831660048201528288166024820152911660448201527308bd2a26fcf141f9244e9f71c3c99249451c89d39063b0d48dba906064015f6040518083038186803b158015614035575f80fd5b505af4158015614047573d5f803e3d5ffd5b505050506140598160a0015186614153565b6001600160601b031660a082015260405161407e908490613108908490602001614d30565b5050979650505050505050565b5f8160405160200161409f91815260200190565b604051602081830303815290604052805190602001209050919050565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680a5460405163c3fa12bb60e01b81525f916001600160a01b03169063c3fa12bb9061410d9086908690600401615e9c565b6020604051808303815f875af1158015614129573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136ae9190615bf2565b92915050565b60405163274acb3560e01b81526001600160601b038084166004830152821660248201525f907382860d6202c009092d0d7507380ba8ef8f4fd5679063274acb3590604401602060405180830381865af41580156141b3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136ae9190614f74565b60408051610140810182525f808252602082018190526060928201839052828201819052608082019290925260a0810182905260c0810182905260e08101829052610100810182905261012081019190915290565b6040805161012081019091525f60e0820181815261010083019190915281908152602001614269604080518082019091525f808252602082015290565b8152602001614287604080518082019091525f808252602082015290565b81526020016142a5604080518082019091525f808252602082015290565b81526020016142c3604080518082019091525f808252602082015290565b81526020016142e1604080518082019091525f808252602082015290565b81526020016142ff604080518082019091525f808252602082015290565b905290565b6040518060a001604052806005906020820280368337509192915050565b5f60208284031215614332575f80fd5b81356001600160e01b0319811681146136ae575f80fd5b6001600160c01b031981168114610ea4575f80fd5b60ff81168114610ea4575f80fd5b5f806040838503121561437d575f80fd5b823561438881614349565b915060208301356143988161435e565b809150509250929050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081526001600160601b0382511660208201526001600160601b03602083015116604082015260ff60408301511660608201525f6060830151614419608084018215159052565b5060808301516001600160a01b03811660a08401525060a08301516001600160a01b03811660c08401525060c083015160e08084015261389f6101008401826143a3565b602081525f6136ae60208301846143a3565b6001600160601b0381168114610ea4575f80fd5b8035610d128161446f565b5f6020828403121561449e575f80fd5b81356136ae8161446f565b634e487b7160e01b5f52604160045260245ffd5b60405160e081016001600160401b03811182821017156144df576144df6144a9565b60405290565b60405161014081016001600160401b03811182821017156144df576144df6144a9565b60405161024081016001600160401b03811182821017156144df576144df6144a9565b60405161010081016001600160401b03811182821017156144df576144df6144a9565b60405161020081016001600160401b03811182821017156144df576144df6144a9565b604051601f8201601f191681016001600160401b0381118282101715614599576145996144a9565b604052919050565b6001600160a01b0381168114610ea4575f80fd5b5f6001600160401b038211156145cd576145cd6144a9565b50601f01601f191660200190565b5f82601f8301126145ea575f80fd5b8135602083015f6146026145fd846145b5565b614571565b9050828152858383011115614615575f80fd5b828260208301375f92810160200192909252509392505050565b64ffffffffff81168114610ea4575f80fd5b8035610d128161462f565b5f82601f83011261465b575f80fd5b61466560c0614571565b8060c0840185811115614676575f80fd5b845b81811015614690578035845260209384019301614678565b509095945050505050565b5f61018082840312156146ac575f80fd5b6146b46144bd565b905081356001600160401b038111156146cb575f80fd5b6146d7848285016145db565b8252506146e660208301614641565b602082015260408201356001600160401b03811115614703575f80fd5b61470f848285016145db565b60408301525061472160608301614641565b606082015260808201356001600160401b0381111561473e575f80fd5b61474a848285016145db565b60808301525061475c60a08301614483565b60a082015261476e8360c0840161464c565b60c082015292915050565b5f8082840361010081121561478c575f80fd5b60e0811215614799575f80fd5b506147a26144bd565b83356147ad816145a1565b815260208401356147bd816145a1565b6020820152604084810135908201526060808501359082015260808401356147e48161435e565b608082015260a0848101359082015260c08085013590820152915060e08301356001600160401b03811115614817575f80fd5b6148238582860161469b565b9150509250929050565b5f6020828403121561483d575f80fd5b5035919050565b8015158114610ea4575f80fd5b5f60208284031215614861575f80fd5b81356136ae81614844565b5f806040838503121561487d575f80fd5b823561488881614349565b9150602083013561439881614844565b805182525f60208201516148b5602085018264ffffffffff169052565b50604082015161014060408501526148d16101408501826143a3565b905060608301516148eb606086018264ffffffffff169052565b506080830151848203608086015261490382826143a3565b91505060a083015161492060a08601826001600160601b03169052565b5060c083015161493c60c08601826001600160f81b0319169052565b5060e083015160e085015261010083015161495d61010086018260ff169052565b5061012083015161497861012086018264ffffffffff169052565b509392505050565b8215158152604060208201525f61389f6040830184614898565b5f80604083850312156149ab575f80fd5b82356149b6816145a1565b915060208301356143988161446f565b5f805f805f8060c087890312156149db575f80fd5b86356149e68161446f565b955060208701356149f681614349565b94506040870135614a068161462f565b935060608701356001600160401b03811115614a20575f80fd5b614a2c89828a016145db565b9350506080870135614a3d8161446f565b915060a0870135614a4d81614349565b809150509295509295509295565b6001600160401b0381168114610ea4575f80fd5b5f60208284031215614a7f575f80fd5b81356136ae81614a5b565b5f8060408385031215614a9b575f80fd5b8235614aa681614a5b565b915060208301356001600160401b03811115614ac0575f80fd5b614823858286016145db565b5f60208284031215614adc575f80fd5b81356136ae816145a1565b5f805f805f805f60e0888a031215614afd575f80fd5b8735614b088161446f565b96506020880135614b188161446f565b95506040880135614b288161446f565b94506060880135614b388161446f565b93506080880135614b488161462f565b925060a0880135614b588161462f565b915060c0880135614b688161435e565b8091505092959891949750929550565b80516001600160a01b031682526020908101516001600160601b0316910152565b5f6101c082019050614bac828451614b78565b6020830151614bbe6040840182614b78565b506040830151614bd16080840182614b78565b506060830151614be460c0840182614b78565b506080830151614bf8610100840182614b78565b5060a0830151614c0c610140840182614b78565b5060c0830151614c20610180840182614b78565b5092915050565b5f805f6101008486031215614c3a575f80fd5b8335614c45816145a1565b92506020840135614c558161446f565b9150614c64856040860161464c565b90509250925092565b805f5b6005811015611b975781516001600160601b0316845260209384019390910190600101614c70565b83815260e08101614cac6020830185614c6d565b6001600160601b03831660c0830152949350505050565b602081525f825160406020840152614cde60608401826143a3565b602094909401516001600160a01b0316604093909301929092525090919050565b5f60208284031215614d0f575f80fd5b81356001600160401b03811115614d24575f80fd5b61389f848285016145db565b602081525f6136ae6020830184614898565b5f805f8060808587031215614d55575f80fd5b8435614d60816145a1565b93506020850135614d70816145a1565b9250604085013591506060850135614d87816145a1565b939692955090935050565b5f8151808452602084019350602083015f5b82811015614dcb5781516001600160601b0316865260209586019590910190600101614da4565b5093949350505050565b602081528151151560208201526020820151151560408201525f6040830151614e02606084018215159052565b50606083015160ff8116608084015250608083015160ff811660a08401525060a08301516001600160601b03811660c08401525060c08301516001600160601b03811660e08401525060e08301516101008084015261389f610120840182614d92565b5f60208284031215614e75575f80fd5b81356136ae81614349565b61ffff81168114610ea4575f80fd5b5f805f60608486031215614ea1575f80fd5b8335614eac8161446f565b92506020840135614ebc81614e80565b91506040840135614ecc8161446f565b809150509250925092565b5f805f8060808587031215614eea575f80fd5b8435614ef58161446f565b93506020850135614f0581614349565b92506040850135614f158161462f565b915060608501356001600160401b03811115614f2f575f80fd5b614f3b878288016145db565b91505092959194509250565b5f60208284031215614f57575f80fd5b815162ffffff811681146136ae575f80fd5b8051610d128161446f565b5f60208284031215614f84575f80fd5b81516136ae8161446f565b600181811c90821680614fa357607f821691505b602082108103614fc157634e487b7160e01b5f52602260045260245ffd5b50919050565b8051610d12816145a1565b5f60208284031215614fe2575f80fd5b81516136ae816145a1565b5f60208284031215614ffd575f80fd5b5051919050565b8051610d1281614844565b5f6020828403121561501f575f80fd5b81516136ae81614844565b8051610d128161462f565b5f82601f830112615044575f80fd5b8151602083015f6150576145fd846145b5565b905082815285838301111561506a575f80fd5b8282602083015e5f92810160200192909252509392505050565b80516001600160f81b031981168114610d12575f80fd5b8051610d128161435e565b5f61014082840312156150b7575f80fd5b6150bf6144e5565b8251815290506150d16020830161502a565b602082015260408201516001600160401b038111156150ee575f80fd5b6150fa84828501615035565b60408301525061510c6060830161502a565b606082015260808201516001600160401b03811115615129575f80fd5b61513584828501615035565b60808301525061514760a08301614f69565b60a082015261515860c08301615084565b60c082015260e08281015190820152615174610100830161509b565b610100820152615187610120830161502a565b61012082015292915050565b5f80604083850312156151a4575f80fd5b82516151af81614844565b60208401519092506001600160401b038111156151ca575f80fd5b614823858286016150a6565b6001600160601b03881681526001600160401b0360c01b871660208201526001600160601b038616604082015264ffffffffff8516606082015260e060808201525f61522560e08301866143a3565b6001600160601b039490941660a0830152506001600160c01b03199190911660c09091015295945050505050565b5f610240828403128015615265575f80fd5b5061526e614508565b61527783614f69565b815261528560208401614f69565b602082015261529660408401614f69565b60408201526152a760608401614f69565b60608201526152b860808401614f69565b60808201526152c960a08401614f69565b60a08201526152da60c08401614f69565b60c08201526152eb60e08401614f69565b60e08201526152fd6101008401614f69565b6101008201526153106101208401614f69565b6101208201526153236101408401614f69565b6101408201526153366101608401614f69565b6101608201526153496101808401614f69565b61018082015261535c6101a08401614f69565b6101a082015261536f6101c08401614f69565b6101c08201526153826101e08401614f69565b6101e08201526153956102008401614f69565b6102008201526153a86102208401614f69565b6102208201529392505050565b5f602082840312156153c5575f80fd5b81516001600160401b038111156153da575f80fd5b820160e081850312156153eb575f80fd5b6153f36144bd565b6153fc82614f69565b815261540a60208301614f69565b602082015261541b6040830161509b565b604082015261542c60608301615004565b606082015261543d60808301614fc7565b608082015261544e60a08301614fc7565b60a082015260c08201516001600160401b0381111561546b575f80fd5b61547786828501615035565b60c083015250949350505050565b8051610d1281614349565b5f60608284031280156154a1575f80fd5b50604051606081016001600160401b03811182821017156154c4576154c46144a9565b60405282516154d281614349565b81526154e060208401615084565b60208201526040928301519281019290925250919050565b5f60208284031215615508575f80fd5b81516136ae8161435e565b6001600160a01b03841681526001600160601b0383166020820152610100810160408201835f5b600681101561555957815183526020928301929091019060010161553a565b505050949350505050565b5f82601f830112615573575f80fd5b61557d60a0614571565b8060a084018581111561558e575f80fd5b845b818110156146905780516155a38161446f565b845260209384019301615590565b5f805f60e084860312156155c3575f80fd5b835192506155d48560208601615564565b915060c0840151614ecc8161446f565b5f82601f8301126155f3575f80fd5b81516001600160401b0381111561560c5761560c6144a9565b8060051b61561c60208201614571565b91825260208185018101929081019086841115615637575f80fd5b6020860192505b838310156156625782516156518161446f565b82526020928301929091019061563e565b9695505050505050565b5f6020828403121561567c575f80fd5b81516001600160401b03811115615691575f80fd5b820161010081850312156156a3575f80fd5b6156ab61452b565b6156b482615004565b81526156c260208301615004565b60208201526156d360408301615004565b60408201526156e46060830161509b565b60608201526156f56080830161509b565b608082015261570660a08301614f69565b60a082015261571760c08301614f69565b60c082015260e08201516001600160401b03811115615734575f80fd5b615740868285016155e4565b60e083015250949350505050565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215615772575f80fd5b81516001600160401b03811115615787575f80fd5b61389f848285016150a6565b5f8060c083850312156157a4575f80fd5b82516157af8161446f565b91506157be8460208501615564565b90509250929050565b5f80858511156157d5575f80fd5b838611156157e1575f80fd5b5050820193919092039150565b80356001600160e01b03198116906004841015614c20576001600160e01b031960049490940360031b84901b1690921692915050565b6001600160a01b03841681526040602082018190528101829052818360608301375f818301606090810191909152601f909201601f1916010192915050565b5f805f60608486031215615875575f80fd5b835161588081614844565b602085015190935061589181614844565b6040850151909250614ecc8161435e565b8051610d1281614e80565b5f602082840312156158bd575f80fd5b81516001600160401b038111156158d2575f80fd5b820161020081850312156158e4575f80fd5b6158ec61454e565b6158f582614f69565b815261590360208301614f69565b602082015261591460408301615485565b604082015261592560608301614f69565b606082015261593660808301614f69565b608082015261594760a08301615485565b60a082015261595860c083016158a2565b60c082015261596960e083016158a2565b60e082015261597b6101008301614f69565b61010082015261598e6101208301614f69565b6101208201526159a1610140830161502a565b6101408201526159b4610160830161502a565b6101608201526159c7610180830161502a565b6101808201526159da6101a0830161502a565b6101a08201526101c08201516001600160401b038111156159f9575f80fd5b615a0586828501615035565b6101c0830152506101e08201516001600160401b03811115615a25575f80fd5b615a3186828501615035565b6101e083015250949350505050565b604081525f615a5260408301856143a3565b905060ff831660208301529392505050565b5f60208284031215615a74575f80fd5b81516136ae8161462f565b8181038181111561414d57634e487b7160e01b5f52601160045260245ffd5b5f60208284031215615aae575f80fd5b81516001600160401b03811115615ac3575f80fd5b61389f84828501615035565b6001600160601b0384168152606060208201525f615af060608301856143a3565b90506001600160601b0383166040830152949350505050565b5f60208284031215615b19575f80fd5b81516001600160401b03811115615b2e575f80fd5b820160408185031215615b3f575f80fd5b604080519081016001600160401b0381118282101715615b6157615b616144a9565b60405281516001600160401b03811115615b79575f80fd5b615b8586828501615035565b82525060208201519150615b98826145a1565b60208101919091529392505050565b5f82518060208501845e5f920191825250919050565b5f8060408385031215615bce575f80fd5b8251615bd981614844565b602084015190925063ffffffff81168114614398575f80fd5b5f60208284031215615c02575f80fd5b81516136ae81614349565b6001600160601b038316815260c081016136ae6020830184614c6d565b6001600160601b0385168152608060208201525f615c4b60808301866143a3565b64ffffffffff851660408401528281036060840152615c6a81856143a3565b979650505050505050565b5f60208284031215615c85575f80fd5b81516136ae81614a5b565b6001600160401b0360c01b83168152604060208201525f61389f60408301846143a3565b6001600160601b03841681526001600160601b0383166020820152606060408201525f6139a160608301846143a3565b5f60208284031215615cf4575f80fd5b81516136ae81614e80565b6001600160601b038516815261ffff841660208201526001600160601b0383166040820152608060608201525f61566260808301846143a3565b5f8060408385031215615d4a575f80fd5b8251615d558161446f565b60208401519092506143988161446f565b6001600160a01b03891681526001600160c01b031988811660208301526001600160601b038881166040840152878116606084015264ffffffffff87166080840152851660a0830152831660c082015261010060e082018190525f90615dce908301846143a3565b9a9950505050505050505050565b60018060a01b03881681526001600160601b038716602082015285604082015264ffffffffff8516606082015260e060808201525f615e1e60e08301866143a3565b64ffffffffff851660a084015282810360c0840152615dce81856143a3565b5f805f60608486031215615e4f575f80fd5b8351615e5a81614349565b6020850151909350615e6b81614844565b60408501519092506001600160401b03811115615e86575f80fd5b615e92868287016150a6565b9150509250925092565b828152604060208201525f61389f60408301846143a356fe07ebcf49758b6ed3af50fa146bec0abe157c0218fe65dc0874c286e9d5da4f00ffe8d4462baed26a47154f4b8f6db497d2f772496965791d25bd456e342b7f00f3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a000bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d6800a2646970667358221220b29fccb899298434e2a76e759e25e71e130d914680df214389448ab53f86026e64736f6c634300081a003300000000000000000000000048b24f2908bba1fd1a7d68395980e4c9eabbb4d300000000000000000000000000000000000000000000000000000000012ed0b80000000000000000000000000000000000000000000000000000000000000080000000000000000000000000893a65351f3cc68d6683af9fe0a1df2a2e1ea7300000000000000000000000000000000000000000000000000000000000000012464450726f647563745f37353263386432310000000000000000000000000000

Deployed Bytecode

0x608060405234801561000f575f80fd5b50600436106103bf575f3560e01c806360efe48c116101f5578063b277d78011610114578063de7b5d14116100a9578063ea15869f11610079578063ea15869f1461094f578063f0107c1514610963578063f179d0641461097b578063f3e349ae1461098e575f80fd5b8063de7b5d14146108e5578063deaa59df14610915578063e1f216fa14610928578063e9b2460c1461093b575f80fd5b8063bf7e214f116100e4578063bf7e214f14610866578063c108bd4c1461086e578063d11efdfa14610883578063d3c576d8146108cd575f80fd5b8063b277d78014610819578063b423086c1461082c578063b7b357ac14610834578063be5c8f441461084c575f80fd5b8063893d20e81161018a578063972e22eb1161015a578063972e22eb146107ba578063a1794029146107d2578063ad12ad60146107f2578063ada9652e14610805575f80fd5b8063893d20e8146107745780638b0af5d81461077c5780638fb360371461078457806390edbd35146107a5575f80fd5b806376b707b7116101c557806376b707b7146107225780637a9e5e4b1461072a578063844e0f201461073d578063846c9fce14610752575f80fd5b806360efe48c146106c7578063644c45e0146106d75780636bd1c0eb146106df578063768e87771461070f575f80fd5b80632c47e75a116102e15780634cdabb16116102765780635ab1bd53116102465780635ab1bd531461066257806360426af3146106735780636078a3b214610686578063608a5f8d14610699575f80fd5b80634cdabb16146106215780635349d2a81461063457806354d33735146106475780635741e5e91461065a575f80fd5b8063419197fe116102b1578063419197fe146105df57806343d752d3146105f25780634b396c4f146105fa5780634b9ddafe14610607575f80fd5b80632c47e75a146105855780632eb3e6f4146105a65780632f15ce0d146105cd57806339cee407146105d6575f80fd5b80631d0ed70211610357578063211e28b611610327578063211e28b61461055a57806321df0da71461056d578063223668441461057557806322f3e2d41461057d575f80fd5b80631d0ed702146104b25780631e6239c6146104ed5780631eff4b22146105125780631ff1292f14610547575f80fd5b80631329960411610392578063132996041461044a578063138461e01461046a57806317d7de7c1461048a5780631c6b21901461049f575f80fd5b806301ffc9a7146103c35780630b045f60146104045780630d8e6e2c146104195780630fec111c14610435575b5f80fd5b6103ef6103d1366004614322565b6001600160e01b0319165f9081526020819052604090205460ff1690565b60405190151581526020015b60405180910390f35b61041761041236600461436c565b6109a1565b005b6104216109fe565b60405162ffffff90911681526020016103fb565b61043d610a85565b6040516103fb91906143d1565b610452610c3f565b6040516001600160a01b0390911681526020016103fb565b610472610ca7565b6040516001600160601b0390911681526020016103fb565b610492610cb1565b6040516103fb919061445d565b6104726104ad36600461448e565b610cc1565b6104c56104c0366004614779565b610d17565b604080516001600160c01b031990931683526001600160601b039091166020830152016103fb565b6105006104fb36600461482d565b610dd9565b60405160ff90911681526020016103fb565b6105397f6548007c3f4340f82f348c576c0ff69f4f529cadd5ad41f96aae61abceeaa30081565b6040519081526020016103fb565b610417610555366004614851565b610e02565b610417610568366004614851565b610e60565b610452610ea7565b6103ef610eeb565b6103ef610fe1565b61059861059336600461486c565b611058565b6040516103fb929190614980565b6105397fffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f0081565b61053960065481565b61053960055481565b6104176105ed36600461499a565b611124565b610452611177565b6007546105009060ff1681565b60025461047290600160601b90046001600160601b031681565b600254610472906001600160601b031681565b6104726106423660046149c6565b611189565b610417610655366004614a6f565b61124b565b6103ef61125d565b6001546001600160a01b0316610452565b610417610681366004614a8a565b611313565b610472610694366004614acc565b61134a565b6003546106b190600160881b900464ffffffffff1681565b60405164ffffffffff90911681526020016103fb565b600754610100900460ff166103ef565b61047261141c565b7fffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f02546001600160a01b0316610452565b61041761071d366004614ae7565b611437565b610500611521565b610417610738366004614acc565b611589565b61074561160f565b6040516103fb9190614b99565b610765610760366004614c27565b6117e8565b6040516103fb93929190614c98565b61045261187c565b6104176119ad565b61078c611a78565b6040516001600160e01b031990911681526020016103fb565b6107ad611aad565b6040516103fb9190614cc3565b6003546106b190600160b01b900464ffffffffff1681565b6107e56107e0366004614cff565b611ade565b6040516103fb9190614d30565b610417610800366004614d42565b611afa565b6105395f80516020615eb583398151915281565b61041761082736600461448e565b611b9d565b6107ad611c08565b6003546106b190600160601b900464ffffffffff1681565b60015461047290600160a01b90046001600160601b031681565b610452611c24565b610876611c3f565b6040516103fb9190614dd5565b6108b5610891366004614e65565b6001600160c01b0319165f908152600860205260409020546001600160401b031690565b6040516001600160401b0390911681526020016103fb565b6003546106b190600160d81b900464ffffffffff1681565b7fffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f00546001600160a01b0316610452565b610417610923366004614acc565b611d93565b610417610936366004614e8f565b611de0565b6105395f80516020615f1583398151915281565b6105395f80516020615ed583398151915281565b600954600160601b90046001600160601b0316610472565b610472610989366004614ed7565b611df6565b600354610472906001600160601b031681565b6109ad335b5f36611e15565b6109b561187c565b6001600160a01b0316336001600160a01b0316146109ed5760405163086391f760e31b81523360048201526024015b60405180910390fd5b6109f78282611f13565b5050505050565b604051632efe011360e01b8152600160048201525f602482018190526044820181905290731c7f4780c639cb898b64769694d308a5a363a83e90632efe011390606401602060405180830381865af4158015610a5c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a809190614f47565b905090565b6040805160e0810182525f8082526020820181905291810182905260608082018390526080820183905260a0820183905260c0820152907f6548007c3f4340f82f348c576c0ff69f4f529cadd5ad41f96aae61abceeaa30090506040518060e0016040528073b6fe977f3e63d01a734dd238ba5fc05bf45bf32c63bc1b392d6040518163ffffffff1660e01b8152600401602060405180830381865af4158015610b31573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610b559190614f74565b6001600160601b0390811682528354908116602083015260ff600160601b820481166040840152600160681b909104161515606082015230608082015260a001610b9d61187c565b6001600160a01b03168152602001826001018054610bba90614f8f565b80601f0160208091040260200160405190810160405280929190818152602001828054610be690614f8f565b8015610c315780601f10610c0857610100808354040283529160200191610c31565b820191905f5260205f20905b815481529060010190602001808311610c1457829003601f168201915b505050505081525091505090565b5f610c48611177565b6001600160a01b031663132996046040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c83573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a809190614fd2565b5f610a80306123cc565b6060610cbb611aad565b51919050565b5f610ccb336109a6565b610cd361187c565b6001600160a01b0316336001600160a01b031614610d065760405163086391f760e31b81523360048201526024016109e4565b610d0f82612622565b90505b919050565b5f80610d22336109a6565b610d2b8461267a565b8351835160405163cdc23e6960e01b8152610dcd91839173b7336e875465659bad9659b25d078c6cace417069163cdc23e6991610d6b919060040161445d565b602060405180830381865af4158015610d86573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610daa9190614fed565b86602001518760400151886060015189608001518a60a001518b60c0015161272c565b90969095509350505050565b60048160068110610de8575f80fd5b60209182820401919006915054906101000a900460ff1681565b610e0b336109a6565b610e1361187c565b6001600160a01b0316336001600160a01b031614610e465760405163086391f760e31b81523360048201526024016109e4565b600780549115156101000261ff0019909216919091179055565b610e6861187c565b6001600160a01b0316336001600160a01b031614610e9b5760405163086391f760e31b81523360048201526024016109e4565b610ea481612b86565b50565b5f610eb0611177565b6001600160a01b03166382bfefc86040518163ffffffff1660e01b8152600401602060405180830381865afa158015610c83573d5f803e3d5ffd5b5f610efe6001546001600160a01b031690565b604051632f2a35f760e11b81523060048201526001600160a01b039190911690635e546bee90602401602060405180830381865afa158015610f42573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f669190614f74565b6040516330b8415f60e01b81526001600160601b03909116600482015273b6fe977f3e63d01a734dd238ba5fc05bf45bf32c906330b8415f90602401602060405180830381865af4158015610fbd573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a80919061500f565b5f610fea611c24565b60405163a166aa8960e01b81523060048201526001600160a01b03919091169063a166aa8990602401602060405180830381865afa15801561102e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611052919061500f565b15905090565b5f6110616141d7565b7308bd2a26fcf141f9244e9f71c3c99249451c89d3637bc17425611083612bf0565b61108b61141c565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526001600160601b031660248201526001600160c01b03198716604482015285151560648201526084015f60405180830381865af41580156110f2573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526111199190810190615193565b909590945092505050565b61112d336109a6565b61113561187c565b6001600160a01b0316336001600160a01b0316146111685760405163086391f760e31b81523360048201526024016109e4565b6111728282612c1e565b505050565b5f611180611aad565b60200151905090565b5f8260166111978282612d9f565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680e545f906001600160a01b031663f4bfe53a6111d161141c565b8b8d8c8c8c8c6040518863ffffffff1660e01b81526004016111f997969594939291906151d6565b61024060405180830381865afa158015611215573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112399190615253565b60a001519a9950505050505050505050565b611254336109a6565b610ea481612ed3565b5f611266610eeb565b156112ea576001546001600160a01b03166040516308b09a5f60e41b81523060048201526001600160a01b039190911690638b09a5f0906024015f60405180830381865afa1580156112ba573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526112e191908101906153b5565b60600151905090565b507fffe8d4462baed26a47154f4b8f6db497d2f772496965791d25bd456e342b7f015460ff1690565b61131c336109a6565b5f818060200190518101906113319190615490565b905061117283825f015183602001518460400151612f2b565b5f611354336109a6565b61135c61187c565b6001600160a01b0316336001600160a01b03161461138f5760405163086391f760e31b81523360048201526024016109e4565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d68095460405163303c51d960e11b81526001600160a01b03848116600483015290911690636078a3b2906024015b6020604051808303815f875af11580156113f8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d0f9190614f74565b5f80516020615eb5833981519152546001600160601b031690565b611440336109a6565b61144861187c565b6001600160a01b0316336001600160a01b03161461147b5760405163086391f760e31b81523360048201526024016109e4565b600180546001600160a01b0316600160a01b6001600160601b03998a1602179055600280549688166001600160c01b031990971696909617600160601b958816860217909555600380549390961670ffffffffffffffffffffffffffffffffff199093169290921764ffffffffff9182169093029290921764ffffffffff60881b1916600160881b9190921602179091556007805460ff191660ff909216919091179055565b5f61152a611c24565b6001600160a01b03166376b707b76040518163ffffffff1660e01b8152600401602060405180830381865afa158015611565573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610a8091906154f8565b33611592611c24565b6001600160a01b0316816001600160a01b0316146115cd5760405162d1953b60e31b81526001600160a01b03821660048201526024016109e4565b816001600160a01b03163b5f03611602576040516361798f2f60e11b81526001600160a01b03831660048201526024016109e4565b61160b826131a7565b5050565b61161761422c565b5060408051610120810182527f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d6802546001600160a01b0380821660e084019081526001600160601b03600160a01b938490048116610100860152908452845180860186527f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d6803548084168252849004821660208083019190915280860191909152855180870187527f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680454808516825285900483168183015285870152855180870187527f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d68055480851682528590048316818301526060860152855180870187527f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d68065480851682528590048316818301526080860152855180870187527f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680754808516825285900483168183015260a086015285518087019096527f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680854928316865292909104169083015260c081019190915290565b5f6117f1614304565b6040516355d166e160e01b81525f907308bd2a26fcf141f9244e9f71c3c99249451c89d3906355d166e19061182e90899089908990600401615513565b60e060405180830381865af4158015611849573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061186d91906155b1565b92509250925093509350939050565b5f805f80516020615eb583398151915280546040516330b8415f60e01b81526001600160601b03909116600482015290915073b6fe977f3e63d01a734dd238ba5fc05bf45bf32c906330b8415f90602401602060405180830381865af41580156118e8573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061190c919061500f565b15611997576001546001600160a01b03168154604051631c5da14d60e11b81526001600160601b0390911660048201526001600160a01b0391909116906338bb429a90602401602060405180830381865afa15801561196d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119919190614fd2565b91505090565b54600160601b90046001600160a01b0316919050565b6119b5612bf0565b6001600160a01b0316630b5d931c6119cb61141c565b6040516001600160e01b031960e084901b1681526001600160601b0390911660048201526024015f60405180830381865afa158015611a0c573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052611a33919081019061566c565b60e001515f81518110611a4857611a4861574e565b60200260200101516009600c6101000a8154816001600160601b0302191690836001600160601b03160217905550565b5f80516020615ef583398151915280545f9190600160a01b900460ff16611a9f575f611991565b638fb3603760e01b91505090565b60408051808201909152606081525f6020820152611ac9610eeb565b15611ad657610a80613207565b610a80611c08565b611ae66141d7565b81806020019051810190610d0f9190615762565b6001546001600160a01b03166001600160a01b031663120726c36040518163ffffffff1660e01b8152600401602060405180830381865afa158015611b41573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b659190614fd2565b6001600160a01b0316336001600160a01b031614611b975760405162f0630960e01b81523360048201526024016109e4565b50505050565b611ba6336109a6565b611bae61187c565b6001600160a01b0316336001600160a01b031614611be15760405163086391f760e31b81523360048201526024016109e4565b600980546bffffffffffffffffffffffff19166001600160601b0392909216919091179055565b60408051808201909152606081525f6020820152610a80613207565b5f80516020615ef5833981519152546001600160a01b031690565b60408051610100810182525f8082526020820181905291810182905260608082018390526080820183905260a0820183905260c082019290925260e08101919091525f80516020615f15833981519152604080516101008082018352835460ff808216151584529181048216151560208085019190915262010000820483161515848601526301000000820483166060850152640100000000820490921660808401526001600160601b03650100000000008204811660a0850152600160881b9091041660c083015260018401805484518184028101840190955280855292949360e08601939092830182828015611d8557602002820191905f5260205f20905f905b82829054906101000a90046001600160601b03166001600160601b0316815260200190600c0190602082600b01049283019260010382029150808411611d425790505b505050505081525050905090565b611d9c336109a6565b611da461187c565b6001600160a01b0316336001600160a01b031614611dd75760405163086391f760e31b81523360048201526024016109e4565b610ea4816133c0565b611de9336109a6565b8260156109f78282612d9f565b5f81806020019051810190611e0b9190615793565b5095945050505050565b5f80516020615ef58339815191525f80611e4d611e30611c24565b8730611e3f60045f8a8c6157c7565b611e48916157ee565b613435565b9150915081611f0b5763ffffffff811615611ee857825460ff60a01b1916600160a01b178355611e7b611c24565b6001600160a01b03166394c7d7ee8787876040518463ffffffff1660e01b8152600401611eaa93929190615824565b5f604051808303815f87803b158015611ec1575f80fd5b505af1158015611ed3573d5f803e3d5ffd5b5050845460ff60a01b1916855550611f0b9050565b60405162d1953b60e31b81526001600160a01b03871660048201526024016109e4565b505050505050565b5f805f80611f1f612bf0565b90507308bd2a26fcf141f9244e9f71c3c99249451c89d363bcfba16982611f4461141c565b6040516001600160e01b031960e085901b1681526001600160a01b0390921660048301526001600160601b031660248201526001600160c01b031989166044820152606401606060405180830381865af4158015611fa4573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fc89190615863565b91955093509150831580611fda575082155b15611fe557506123c5565b604051631954e65360e21b81526001600160c01b0319871660048201525f906001600160a01b03831690636553994c90602401602060405180830381865afa158015612033573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120579190614fed565b90505f8660ff16821061206d578660ff1661206f565b815b90505f816001600160401b0381111561208a5761208a6144a9565b6040519080825280602002602001820160405280156120b3578160200160208202803683370190505b5090505f5b82811015612169576040516325fb9d0d60e11b81526001600160c01b03198b166004820152602481018290526001600160a01b03861690634bf73a1a90604401602060405180830381865afa158015612113573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121379190614f74565b8282815181106121495761214961574e565b6001600160601b03909216602092830291909101909101526001016120b8565b505f5b82811015612361575f8282815181106121875761218761574e565b602002602001015190505f7308bd2a26fcf141f9244e9f71c3c99249451c89d3638a37ab63886001600160a01b031663bd7d9d85856040518263ffffffff1660e01b81526004016121e791906001600160601b0391909116815260200190565b5f60405180830381865afa158015612201573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261222891908101906158ad565b6101c001518a6040518363ffffffff1660e01b815260040161224b929190615a40565b602060405180830381865af4158015612266573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061228a9190614f74565b9050612296828261353d565b61230a8273fdbcee5ffb06ba0f107922a3294110ce43c3112e639fa6a6e36040518163ffffffff1660e01b8152600401602060405180830381865af41580156122e1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906123059190615a64565b61361d565b50612314826136b5565b604080516001600160601b038085168252831660208201527f1c424341cadf6615bb5fa2a96492cd7ed267c05f7d1dcef2f2229ae26d4d1a7a910160405180910390a1505060010161216c565b507f497e6cfdb1ef3149777822f9e0716eebbeef1264fbc8effc6004e0111ec600818986846123908188615a7f565b604080516001600160c01b0319909516855260ff909316602085015291830152606082015260800160405180910390a1505050505b9250925092565b5f805f80516020615eb583398151915280546040516330b8415f60e01b81526001600160601b03909116600482015290915073b6fe977f3e63d01a734dd238ba5fc05bf45bf32c906330b8415f90602401602060405180830381865af4158015612438573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061245c919061500f565b156124885780546040516301ab8b6760e21b81526001600160601b0390911660048201526024016109e4565b6001546001600160a01b031660405163c3c5a54760e01b81526001600160a01b038581166004830152919091169063c3c5a54790602401602060405180830381865afa1580156124da573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906124fe919061500f565b6125265760405163b9304b0d60e01b81526001600160a01b03841660048201526024016109e4565b6001546001600160a01b0316604051632f2a35f760e11b81526001600160a01b0385811660048301529190911690635e546bee90602401602060405180830381865afa158015612578573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061259c9190614f74565b81546bffffffffffffffffffffffff19166001600160601b039190911690811782557f88857b75cbef7f257c9d5a473e28aaf6a91cc28f717f60f2cda3bcf5623d7b54906125e861187c565b604080516001600160601b0390931683526001600160a01b0390911660208301520160405180910390a1546001600160601b031692915050565b7fffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f03546040516301c6b21960e41b81526001600160601b03831660048201525f916001600160a01b031690631c6b2190906024016113dc565b612683336109a6565b5f61268c610ea7565b825160208401516040808601516060870151608088015160a089015160c08a0151945163d505accf60e01b81526001600160a01b03978816600482015295871660248701526044860193909352606485019190915260ff16608484015260a483015260c482015291925082169063d505accf9060e4015b5f604051808303815f87803b15801561271a575f80fd5b505af1158015611f0b573d5f803e3d5ffd5b604051634617dc7960e11b81523060048201526024810188905264ffffffffff8088166044830152851660648201526001600160601b03831660848201525f9081907308bd2a26fcf141f9244e9f71c3c99249451c89d390638c2fb8f29060a4015f6040518083038186803b1580156127a3575f80fd5b505af41580156127b5573d5f803e3d5ffd5b505050506127c98a8a8a8a8a8a8a8a61370d565b80925081935050506128468173fdbcee5ffb06ba0f107922a3294110ce43c3112e63bc1b392d6040518163ffffffff1660e01b8152600401602060405180830381865af415801561281c573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128409190615a64565b86613806565b5061285181896138a7565b6001600160c01b031982165f908152600860205260409081902054905163015c60ad60e21b81526001600160401b039091166004820152736583a0cc1ea5b6841adae78a8ce62f319a0280539063057182b490602401602060405180830381865af41580156128c2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128e6919061500f565b15612ac95760095460408051606080820183526001600160c01b0319861680835260208084018f815264ffffffffff8f81169587019586528651928301939093525194810194909452915190911690820152612a9191600160601b90046001600160601b03169060800160408051601f19818403018152908290526209629960e41b8252601e60048301529073fdbcee5ffb06ba0f107922a3294110ce43c3112e9063a5798b4e9064ffffffffff8c1690733fb33d6b382d000f1ef0928f8942c56ef3af6c41906296299090602401602060405180830381865af41580156129d0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906129f49190615a64565b6040516001600160e01b031960e085901b16815264ffffffffff928316600482015291166024820152604401602060405180830381865af4158015612a3b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a5f9190615a64565b60405180604001604052806014815260200173666c6967687453746174757343616c6c6261636b60601b81525061390c565b6001600160c01b031983165f908152600860205260409020805467ffffffffffffffff19166001600160401b03929092169190911790555b604051631623433d60e31b8152600481018a90527fb51fd6991e0581f19175f5ef6b8bc59d44dbff4511eb57379f0ce2862228794490829073b7336e875465659bad9659b25d078c6cace417069063b11a19e8906024015f60405180830381865af4158015612b3a573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052612b619190810190615a9e565b86604051612b7193929190615acf565b60405180910390a19850989650505050505050565b5f80516020615ed58339815191526001015460405163108f145b60e11b815282151560048201526101009091046001600160a01b03169063211e28b6906024015b5f604051808303815f87803b158015612bde575f80fd5b505af11580156109f7573d5f803e3d5ffd5b7fffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f01546001600160a01b031690565b5f7382860d6202c009092d0d7507380ba8ef8f4fd567634c41dd96846001600160a01b031663dd62ed3e612c50611177565b6040516001600160e01b031960e084901b1681526001600160a01b039091166004820152306024820152604401602060405180830381865afa158015612c98573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612cbc9190614fed565b6040518263ffffffff1660e01b8152600401612cda91815260200190565b602060405180830381865af4158015612cf5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d199190614f74565b90505f80516020615ed5833981519152600101546040516320c8cbff60e11b81526001600160a01b0385811660048301526001600160601b03851660248301526101009092049091169063419197fe906044015f604051808303815f87803b158015612d83575f80fd5b505af1158015612d95573d5f803e3d5ffd5b5050505092915050565b60405163037c8cb160e51b815260ff8216600482015273a6b4725b0a1cf143baf5099fdd58abc8d862d34f90636f91962090602401602060405180830381865af4158015612def573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e13919061500f565b80612ea157506001546001600160a01b0316604051635b12715d60e11b81526001600160601b038416600482015260ff831660248201526001600160a01b03919091169063b624e2ba90604401602060405180830381865afa158015612e7b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e9f919061500f565b155b1561160b5760405163d711af7560e01b81526001600160601b038316600482015260ff821660248201526044016109e4565b7fffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f0454604051630b331f6960e21b81526001600160401b03831660048201526001600160a01b0390911690632ccc7da490602401612bc7565b5f612f37846001611058565b61012081015160405163790a38ad60e01b815264ffffffffff909116600482015290925073fdbcee5ffb06ba0f107922a3294110ce43c3112e915063790a38ad90602401602060405180830381865af4158015612f96573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612fba919061500f565b1561311c5773fdbcee5ffb06ba0f107922a3294110ce43c3112e639fa6a6e36040518163ffffffff1660e01b8152600401602060405180830381865af4158015613006573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061302a9190615a64565b64ffffffffff166101208201526001600160f81b0319831660c0820181905260e08201839052604051621600c960ea1b81526001600160401b03871660048201526001600160c01b0319861660248201526044810191909152606481018390527308bd2a26fcf141f9244e9f71c3c99249451c89d390635803240090608401602060405180830381865af41580156130c4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906130e891906154f8565b60ff1661010082015260405161311c908590613108908490602001614d30565b6040516020818303038152906040526139aa565b6007545f9061312f90869060ff16611f13565b604080516001600160401b038b1681526001600160c01b03198a1660208201526001600160f81b03198916818301526060810188905260ff8316608082015290519194507f6088f5fc3484ec0fdf466ecfcea487e5566da0db0bda62b4dbeb3ed86fd3ef0b935081900360a0019150a1505050505050565b5f80516020615ef583398151915280546001600160a01b0383166001600160a01b03199091168117825560408051918252517f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad9181900360200190a15050565b60408051808201909152606081525f60208201525f61322e6001546001600160a01b031690565b604051632f2a35f760e11b81523060048201526001600160a01b039190911690635e546bee90602401602060405180830381865afa158015613272573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906132969190614f74565b6040516330b8415f60e01b81526001600160601b038216600482015290915073b6fe977f3e63d01a734dd238ba5fc05bf45bf32c906330b8415f90602401602060405180830381865af41580156132ef573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613313919061500f565b156133b857613320612bf0565b6001600160a01b031663f0ea17c361333661141c565b6040516001600160e01b031960e084901b1681526001600160601b0390911660048201526024015f60405180830381865afa158015613377573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261339e9190810190615b09565b60208101519092506001600160a01b0316156133b8575090565b6119916139fb565b6133c861187c565b6001600160a01b0316336001600160a01b0316146133fb5760405163086391f760e31b81523360048201526024016109e4565b613405600b613ac5565b60405163deaa59df60e01b81526001600160a01b038381166004830152919091169063deaa59df90602401612bc7565b6040516001600160a01b03848116602483015283811660448301526001600160e01b0319831660648301525f9182918291829189169060840160408051601f198184030181529181526020820180516001600160e01b031663b700961360e01b179052516134a39190615ba7565b5f60405180830381855afa9150503d805f81146134db576040519150601f19603f3d011682016040523d82523d5f602084013e6134e0565b606091505b5091509150811561353257604081511061351257808060200190518101906135089190615bbd565b9094509250613532565b6020815110613532578080602001905181019061352f919061500f565b93505b505094509492505050565b604051630a4d29dd60e31b81526001600160601b03821660048201527382860d6202c009092d0d7507380ba8ef8f4fd567906352694ee890602401602060405180830381865af4158015613593573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906135b7919061500f565b156135c0575050565b5f6135da838360405180602001604052805f815250613b56565b90506135f683828460405180602001604052805f815250613be9565b5f61361184838560405180602001604052805f815250613c71565b9050611f0b8482613d06565b5f5f80516020615f15833981519152600c0154604051637baa9a0b60e01b81526001600160601b038516600482015264ffffffffff841660248201526001600160a01b0390911690637baa9a0b906044016020604051808303815f875af115801561368a573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136ae9190615a64565b9392505050565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680c54604051630155751d60e31b81526001600160601b03831660048201526001600160a01b0390911690630aaba8e890602401612bc7565b5f80613717614304565b5f6137278b8b8b8b8b8b8b613d97565b8093508194508296505050506137f58c8583896003601b9054906101000a900464ffffffffff1660095f9054906101000a90046001600160601b031673f41933c60fdea61085b0ff18b906cddcd547634c63bc1b392d6040518163ffffffff1660e01b8152600401602060405180830381865af41580156137aa573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906137ce9190615bf2565b8d8a6040516020016137e1929190615c0d565b604051602081830303815290604052613e42565b925050509850989650505050505050565b5f5f80516020615f15833981519152600c015460405163d22413e560e01b81526001600160601b03808716600483015264ffffffffff86166024830152841660448201526001600160a01b039091169063d22413e5906064016020604051808303815f875af115801561387b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061389f9190614f74565b949350505050565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680c54604051635dfddfdd60e11b81526001600160601b038416600482015264ffffffffff831660248201526001600160a01b039091169063bbfbbfba90604401612703565b7fffe3d4462bded26a47154f4b8f6db494d2f772496965791d25bd456e342b7f045460405163f684566360e01b81525f916001600160a01b03169063f684566390613961908890889088908890600401615c2a565b6020604051808303815f875af115801561397d573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906139a19190615c75565b95945050505050565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680a54604051638f997f6d60e01b81526001600160a01b0390911690638f997f6d906127039085908590600401615c90565b60408051808201909152606081525f60208201525f5f80516020615ed583398151915290506040518060400160405280825f018054613a3990614f8f565b80601f0160208091040260200160405190810160405280929190818152602001828054613a6590614f8f565b8015613ab05780601f10613a8757610100808354040283529160200191613ab0565b820191905f5260205f20905b815481529060010190602001808311613a9357829003601f168201915b50505091835250505f60209091015292915050565b5f613ad86001546001600160a01b031690565b6001600160a01b031663d39e604383613aef611521565b6040516001600160e01b031960e085901b16815260ff928316600482015291166024820152604401602060405180830381865afa158015613b32573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d0f9190614fd2565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680d546040516374b775eb60e01b81525f916001600160a01b0316906374b775eb90613ba990879087908790600401615cb4565b6020604051808303815f875af1158015613bc5573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061389f9190615ce4565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680d54604051636e2cdce760e01b81526001600160a01b0390911690636e2cdce790613c3e908790879087908790600401615cff565b5f604051808303815f87803b158015613c55575f80fd5b505af1158015613c67573d5f803e3d5ffd5b5050505050505050565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680d54604051631e2992c760e11b81525f916001600160a01b031690633c53258e90613cc6908890889088908890600401615cff565b6020604051808303815f875af1158015613ce2573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906139a19190615a64565b5f805f80516020615f15833981519152600d015460405163137a92a760e01b81526001600160601b038616600482015264ffffffffff851660248201526001600160a01b039091169063137a92a79060440160408051808303815f875af1158015613d73573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111199190615d39565b5f613da0614304565b5f807308bd2a26fcf141f9244e9f71c3c99249451c89d36355d166e13088886040518463ffffffff1660e01b8152600401613ddd93929190615513565b60e060405180830381865af4158015613df8573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613e1c91906155b1565b90945092509050613e328b8b8b8b8b8787613eec565b9350509750975097945050505050565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680b54604051639a83274160e01b81525f916001600160a01b031690639a83274190613e9f908c908c908c908c908c908c908c908c90600401615d66565b6020604051808303815f875af1158015613ebb573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613edf9190614f74565b9998505050505050505050565b5f80613ef66141d7565b7308bd2a26fcf141f9244e9f71c3c99249451c89d3634f339f15613f18612bf0565b613f2061141c565b8d8d8d8d8d6040518863ffffffff1660e01b8152600401613f479796959493929190615ddc565b5f60405180830381865af4158015613f61573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052613f889190810190615e3d565b9194509250905081613fcc575f613f9e8b61408b565b9050613fc98183604051602001613fb59190614d30565b6040516020818303038152906040526140bc565b50505b60a081015160035460405163586a46dd60e11b81526001600160601b0392831660048201528288166024820152911660448201527308bd2a26fcf141f9244e9f71c3c99249451c89d39063b0d48dba906064015f6040518083038186803b158015614035575f80fd5b505af4158015614047573d5f803e3d5ffd5b505050506140598160a0015186614153565b6001600160601b031660a082015260405161407e908490613108908490602001614d30565b5050979650505050505050565b5f8160405160200161409f91815260200190565b604051602081830303815290604052805190602001209050919050565b7f0bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d680a5460405163c3fa12bb60e01b81525f916001600160a01b03169063c3fa12bb9061410d9086908690600401615e9c565b6020604051808303815f875af1158015614129573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136ae9190615bf2565b92915050565b60405163274acb3560e01b81526001600160601b038084166004830152821660248201525f907382860d6202c009092d0d7507380ba8ef8f4fd5679063274acb3590604401602060405180830381865af41580156141b3573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136ae9190614f74565b60408051610140810182525f808252602082018190526060928201839052828201819052608082019290925260a0810182905260c0810182905260e08101829052610100810182905261012081019190915290565b6040805161012081019091525f60e0820181815261010083019190915281908152602001614269604080518082019091525f808252602082015290565b8152602001614287604080518082019091525f808252602082015290565b81526020016142a5604080518082019091525f808252602082015290565b81526020016142c3604080518082019091525f808252602082015290565b81526020016142e1604080518082019091525f808252602082015290565b81526020016142ff604080518082019091525f808252602082015290565b905290565b6040518060a001604052806005906020820280368337509192915050565b5f60208284031215614332575f80fd5b81356001600160e01b0319811681146136ae575f80fd5b6001600160c01b031981168114610ea4575f80fd5b60ff81168114610ea4575f80fd5b5f806040838503121561437d575f80fd5b823561438881614349565b915060208301356143988161435e565b809150509250929050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081526001600160601b0382511660208201526001600160601b03602083015116604082015260ff60408301511660608201525f6060830151614419608084018215159052565b5060808301516001600160a01b03811660a08401525060a08301516001600160a01b03811660c08401525060c083015160e08084015261389f6101008401826143a3565b602081525f6136ae60208301846143a3565b6001600160601b0381168114610ea4575f80fd5b8035610d128161446f565b5f6020828403121561449e575f80fd5b81356136ae8161446f565b634e487b7160e01b5f52604160045260245ffd5b60405160e081016001600160401b03811182821017156144df576144df6144a9565b60405290565b60405161014081016001600160401b03811182821017156144df576144df6144a9565b60405161024081016001600160401b03811182821017156144df576144df6144a9565b60405161010081016001600160401b03811182821017156144df576144df6144a9565b60405161020081016001600160401b03811182821017156144df576144df6144a9565b604051601f8201601f191681016001600160401b0381118282101715614599576145996144a9565b604052919050565b6001600160a01b0381168114610ea4575f80fd5b5f6001600160401b038211156145cd576145cd6144a9565b50601f01601f191660200190565b5f82601f8301126145ea575f80fd5b8135602083015f6146026145fd846145b5565b614571565b9050828152858383011115614615575f80fd5b828260208301375f92810160200192909252509392505050565b64ffffffffff81168114610ea4575f80fd5b8035610d128161462f565b5f82601f83011261465b575f80fd5b61466560c0614571565b8060c0840185811115614676575f80fd5b845b81811015614690578035845260209384019301614678565b509095945050505050565b5f61018082840312156146ac575f80fd5b6146b46144bd565b905081356001600160401b038111156146cb575f80fd5b6146d7848285016145db565b8252506146e660208301614641565b602082015260408201356001600160401b03811115614703575f80fd5b61470f848285016145db565b60408301525061472160608301614641565b606082015260808201356001600160401b0381111561473e575f80fd5b61474a848285016145db565b60808301525061475c60a08301614483565b60a082015261476e8360c0840161464c565b60c082015292915050565b5f8082840361010081121561478c575f80fd5b60e0811215614799575f80fd5b506147a26144bd565b83356147ad816145a1565b815260208401356147bd816145a1565b6020820152604084810135908201526060808501359082015260808401356147e48161435e565b608082015260a0848101359082015260c08085013590820152915060e08301356001600160401b03811115614817575f80fd5b6148238582860161469b565b9150509250929050565b5f6020828403121561483d575f80fd5b5035919050565b8015158114610ea4575f80fd5b5f60208284031215614861575f80fd5b81356136ae81614844565b5f806040838503121561487d575f80fd5b823561488881614349565b9150602083013561439881614844565b805182525f60208201516148b5602085018264ffffffffff169052565b50604082015161014060408501526148d16101408501826143a3565b905060608301516148eb606086018264ffffffffff169052565b506080830151848203608086015261490382826143a3565b91505060a083015161492060a08601826001600160601b03169052565b5060c083015161493c60c08601826001600160f81b0319169052565b5060e083015160e085015261010083015161495d61010086018260ff169052565b5061012083015161497861012086018264ffffffffff169052565b509392505050565b8215158152604060208201525f61389f6040830184614898565b5f80604083850312156149ab575f80fd5b82356149b6816145a1565b915060208301356143988161446f565b5f805f805f8060c087890312156149db575f80fd5b86356149e68161446f565b955060208701356149f681614349565b94506040870135614a068161462f565b935060608701356001600160401b03811115614a20575f80fd5b614a2c89828a016145db565b9350506080870135614a3d8161446f565b915060a0870135614a4d81614349565b809150509295509295509295565b6001600160401b0381168114610ea4575f80fd5b5f60208284031215614a7f575f80fd5b81356136ae81614a5b565b5f8060408385031215614a9b575f80fd5b8235614aa681614a5b565b915060208301356001600160401b03811115614ac0575f80fd5b614823858286016145db565b5f60208284031215614adc575f80fd5b81356136ae816145a1565b5f805f805f805f60e0888a031215614afd575f80fd5b8735614b088161446f565b96506020880135614b188161446f565b95506040880135614b288161446f565b94506060880135614b388161446f565b93506080880135614b488161462f565b925060a0880135614b588161462f565b915060c0880135614b688161435e565b8091505092959891949750929550565b80516001600160a01b031682526020908101516001600160601b0316910152565b5f6101c082019050614bac828451614b78565b6020830151614bbe6040840182614b78565b506040830151614bd16080840182614b78565b506060830151614be460c0840182614b78565b506080830151614bf8610100840182614b78565b5060a0830151614c0c610140840182614b78565b5060c0830151614c20610180840182614b78565b5092915050565b5f805f6101008486031215614c3a575f80fd5b8335614c45816145a1565b92506020840135614c558161446f565b9150614c64856040860161464c565b90509250925092565b805f5b6005811015611b975781516001600160601b0316845260209384019390910190600101614c70565b83815260e08101614cac6020830185614c6d565b6001600160601b03831660c0830152949350505050565b602081525f825160406020840152614cde60608401826143a3565b602094909401516001600160a01b0316604093909301929092525090919050565b5f60208284031215614d0f575f80fd5b81356001600160401b03811115614d24575f80fd5b61389f848285016145db565b602081525f6136ae6020830184614898565b5f805f8060808587031215614d55575f80fd5b8435614d60816145a1565b93506020850135614d70816145a1565b9250604085013591506060850135614d87816145a1565b939692955090935050565b5f8151808452602084019350602083015f5b82811015614dcb5781516001600160601b0316865260209586019590910190600101614da4565b5093949350505050565b602081528151151560208201526020820151151560408201525f6040830151614e02606084018215159052565b50606083015160ff8116608084015250608083015160ff811660a08401525060a08301516001600160601b03811660c08401525060c08301516001600160601b03811660e08401525060e08301516101008084015261389f610120840182614d92565b5f60208284031215614e75575f80fd5b81356136ae81614349565b61ffff81168114610ea4575f80fd5b5f805f60608486031215614ea1575f80fd5b8335614eac8161446f565b92506020840135614ebc81614e80565b91506040840135614ecc8161446f565b809150509250925092565b5f805f8060808587031215614eea575f80fd5b8435614ef58161446f565b93506020850135614f0581614349565b92506040850135614f158161462f565b915060608501356001600160401b03811115614f2f575f80fd5b614f3b878288016145db565b91505092959194509250565b5f60208284031215614f57575f80fd5b815162ffffff811681146136ae575f80fd5b8051610d128161446f565b5f60208284031215614f84575f80fd5b81516136ae8161446f565b600181811c90821680614fa357607f821691505b602082108103614fc157634e487b7160e01b5f52602260045260245ffd5b50919050565b8051610d12816145a1565b5f60208284031215614fe2575f80fd5b81516136ae816145a1565b5f60208284031215614ffd575f80fd5b5051919050565b8051610d1281614844565b5f6020828403121561501f575f80fd5b81516136ae81614844565b8051610d128161462f565b5f82601f830112615044575f80fd5b8151602083015f6150576145fd846145b5565b905082815285838301111561506a575f80fd5b8282602083015e5f92810160200192909252509392505050565b80516001600160f81b031981168114610d12575f80fd5b8051610d128161435e565b5f61014082840312156150b7575f80fd5b6150bf6144e5565b8251815290506150d16020830161502a565b602082015260408201516001600160401b038111156150ee575f80fd5b6150fa84828501615035565b60408301525061510c6060830161502a565b606082015260808201516001600160401b03811115615129575f80fd5b61513584828501615035565b60808301525061514760a08301614f69565b60a082015261515860c08301615084565b60c082015260e08281015190820152615174610100830161509b565b610100820152615187610120830161502a565b61012082015292915050565b5f80604083850312156151a4575f80fd5b82516151af81614844565b60208401519092506001600160401b038111156151ca575f80fd5b614823858286016150a6565b6001600160601b03881681526001600160401b0360c01b871660208201526001600160601b038616604082015264ffffffffff8516606082015260e060808201525f61522560e08301866143a3565b6001600160601b039490941660a0830152506001600160c01b03199190911660c09091015295945050505050565b5f610240828403128015615265575f80fd5b5061526e614508565b61527783614f69565b815261528560208401614f69565b602082015261529660408401614f69565b60408201526152a760608401614f69565b60608201526152b860808401614f69565b60808201526152c960a08401614f69565b60a08201526152da60c08401614f69565b60c08201526152eb60e08401614f69565b60e08201526152fd6101008401614f69565b6101008201526153106101208401614f69565b6101208201526153236101408401614f69565b6101408201526153366101608401614f69565b6101608201526153496101808401614f69565b61018082015261535c6101a08401614f69565b6101a082015261536f6101c08401614f69565b6101c08201526153826101e08401614f69565b6101e08201526153956102008401614f69565b6102008201526153a86102208401614f69565b6102208201529392505050565b5f602082840312156153c5575f80fd5b81516001600160401b038111156153da575f80fd5b820160e081850312156153eb575f80fd5b6153f36144bd565b6153fc82614f69565b815261540a60208301614f69565b602082015261541b6040830161509b565b604082015261542c60608301615004565b606082015261543d60808301614fc7565b608082015261544e60a08301614fc7565b60a082015260c08201516001600160401b0381111561546b575f80fd5b61547786828501615035565b60c083015250949350505050565b8051610d1281614349565b5f60608284031280156154a1575f80fd5b50604051606081016001600160401b03811182821017156154c4576154c46144a9565b60405282516154d281614349565b81526154e060208401615084565b60208201526040928301519281019290925250919050565b5f60208284031215615508575f80fd5b81516136ae8161435e565b6001600160a01b03841681526001600160601b0383166020820152610100810160408201835f5b600681101561555957815183526020928301929091019060010161553a565b505050949350505050565b5f82601f830112615573575f80fd5b61557d60a0614571565b8060a084018581111561558e575f80fd5b845b818110156146905780516155a38161446f565b845260209384019301615590565b5f805f60e084860312156155c3575f80fd5b835192506155d48560208601615564565b915060c0840151614ecc8161446f565b5f82601f8301126155f3575f80fd5b81516001600160401b0381111561560c5761560c6144a9565b8060051b61561c60208201614571565b91825260208185018101929081019086841115615637575f80fd5b6020860192505b838310156156625782516156518161446f565b82526020928301929091019061563e565b9695505050505050565b5f6020828403121561567c575f80fd5b81516001600160401b03811115615691575f80fd5b820161010081850312156156a3575f80fd5b6156ab61452b565b6156b482615004565b81526156c260208301615004565b60208201526156d360408301615004565b60408201526156e46060830161509b565b60608201526156f56080830161509b565b608082015261570660a08301614f69565b60a082015261571760c08301614f69565b60c082015260e08201516001600160401b03811115615734575f80fd5b615740868285016155e4565b60e083015250949350505050565b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215615772575f80fd5b81516001600160401b03811115615787575f80fd5b61389f848285016150a6565b5f8060c083850312156157a4575f80fd5b82516157af8161446f565b91506157be8460208501615564565b90509250929050565b5f80858511156157d5575f80fd5b838611156157e1575f80fd5b5050820193919092039150565b80356001600160e01b03198116906004841015614c20576001600160e01b031960049490940360031b84901b1690921692915050565b6001600160a01b03841681526040602082018190528101829052818360608301375f818301606090810191909152601f909201601f1916010192915050565b5f805f60608486031215615875575f80fd5b835161588081614844565b602085015190935061589181614844565b6040850151909250614ecc8161435e565b8051610d1281614e80565b5f602082840312156158bd575f80fd5b81516001600160401b038111156158d2575f80fd5b820161020081850312156158e4575f80fd5b6158ec61454e565b6158f582614f69565b815261590360208301614f69565b602082015261591460408301615485565b604082015261592560608301614f69565b606082015261593660808301614f69565b608082015261594760a08301615485565b60a082015261595860c083016158a2565b60c082015261596960e083016158a2565b60e082015261597b6101008301614f69565b61010082015261598e6101208301614f69565b6101208201526159a1610140830161502a565b6101408201526159b4610160830161502a565b6101608201526159c7610180830161502a565b6101808201526159da6101a0830161502a565b6101a08201526101c08201516001600160401b038111156159f9575f80fd5b615a0586828501615035565b6101c0830152506101e08201516001600160401b03811115615a25575f80fd5b615a3186828501615035565b6101e083015250949350505050565b604081525f615a5260408301856143a3565b905060ff831660208301529392505050565b5f60208284031215615a74575f80fd5b81516136ae8161462f565b8181038181111561414d57634e487b7160e01b5f52601160045260245ffd5b5f60208284031215615aae575f80fd5b81516001600160401b03811115615ac3575f80fd5b61389f84828501615035565b6001600160601b0384168152606060208201525f615af060608301856143a3565b90506001600160601b0383166040830152949350505050565b5f60208284031215615b19575f80fd5b81516001600160401b03811115615b2e575f80fd5b820160408185031215615b3f575f80fd5b604080519081016001600160401b0381118282101715615b6157615b616144a9565b60405281516001600160401b03811115615b79575f80fd5b615b8586828501615035565b82525060208201519150615b98826145a1565b60208101919091529392505050565b5f82518060208501845e5f920191825250919050565b5f8060408385031215615bce575f80fd5b8251615bd981614844565b602084015190925063ffffffff81168114614398575f80fd5b5f60208284031215615c02575f80fd5b81516136ae81614349565b6001600160601b038316815260c081016136ae6020830184614c6d565b6001600160601b0385168152608060208201525f615c4b60808301866143a3565b64ffffffffff851660408401528281036060840152615c6a81856143a3565b979650505050505050565b5f60208284031215615c85575f80fd5b81516136ae81614a5b565b6001600160401b0360c01b83168152604060208201525f61389f60408301846143a3565b6001600160601b03841681526001600160601b0383166020820152606060408201525f6139a160608301846143a3565b5f60208284031215615cf4575f80fd5b81516136ae81614e80565b6001600160601b038516815261ffff841660208201526001600160601b0383166040820152608060608201525f61566260808301846143a3565b5f8060408385031215615d4a575f80fd5b8251615d558161446f565b60208401519092506143988161446f565b6001600160a01b03891681526001600160c01b031988811660208301526001600160601b038881166040840152878116606084015264ffffffffff87166080840152851660a0830152831660c082015261010060e082018190525f90615dce908301846143a3565b9a9950505050505050505050565b60018060a01b03881681526001600160601b038716602082015285604082015264ffffffffff8516606082015260e060808201525f615e1e60e08301866143a3565b64ffffffffff851660a084015282810360c0840152615dce81856143a3565b5f805f60608486031215615e4f575f80fd5b8351615e5a81614349565b6020850151909350615e6b81614844565b60408501519092506001600160401b03811115615e86575f80fd5b615e92868287016150a6565b9150509250925092565b828152604060208201525f61389f60408301846143a356fe07ebcf49758b6ed3af50fa146bec0abe157c0218fe65dc0874c286e9d5da4f00ffe8d4462baed26a47154f4b8f6db497d2f772496965791d25bd456e342b7f00f3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a000bb7aafdb8e380f81267337bc5b5dfdf76e6d3a380ecadb51ec665246d9d6800a2646970667358221220b29fccb899298434e2a76e759e25e71e130d914680df214389448ab53f86026e64736f6c634300081a0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

00000000000000000000000048b24f2908bba1fd1a7d68395980e4c9eabbb4d300000000000000000000000000000000000000000000000000000000012ed0b80000000000000000000000000000000000000000000000000000000000000080000000000000000000000000893a65351f3cc68d6683af9fe0a1df2a2e1ea7300000000000000000000000000000000000000000000000000000000000000012464450726f647563745f37353263386432310000000000000000000000000000

-----Decoded View---------------
Arg [0] : registry (address): 0x48b24f2908Bba1fD1a7d68395980e4C9eAbBB4D3
Arg [1] : instanceNftId (uint96): 19845304
Arg [2] : componentName (string): FDProduct_752c8d21
Arg [3] : authorization (address): 0x893A65351f3cC68D6683af9FE0A1dF2A2e1EA730

-----Encoded View---------------
6 Constructor Arguments found :
Arg [0] : 00000000000000000000000048b24f2908bba1fd1a7d68395980e4c9eabbb4d3
Arg [1] : 00000000000000000000000000000000000000000000000000000000012ed0b8
Arg [2] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [3] : 000000000000000000000000893a65351f3cc68d6683af9fe0a1df2a2e1ea730
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000012
Arg [5] : 464450726f647563745f37353263386432310000000000000000000000000000


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.